#!/usr/bin/perl # # This prog is designed to be called from a cron job to update # the cache for use by irrd. This prog updates the cache # by ftp'ing remote db's/registries. # The output can be easily parsed # from a checker script or output-filter script. Fatal error messages # are all prefixed with the tag 'ERROR:'. The 'ERROR' # line gives a description and location of the fatal error. 'WARN' # msg's are also given in non-fatal situations. # use Getopt::Std; # set 2 for solaris else set to 1 $sockstream = 2; local($wd,$db)=(`pwd`); chop($wd); if ($wd =~ /^\s*$/) { $wd = `/bin/pwd`; chop($wd); if ($wd =~ /^\s*$/) { print "Could not determine current working directory, exit!\n"; exit; } } if (!getopts('p:w:s:SCf:')) { print "irrdcacher: unknown command-line option or missing flag parm!\n"; &usage(); exit; } &usage() if ($#ARGV < 0); # canonicalize db names # want *.db, eg, mci.db %match = (); foreach $j (@ARGV) { if ($j =~ /(\S+):(\S+)/) { $j = $1; $k = $2; } else { $k = ''; } # if ($j !~ /CURRENTSERIAL$/i && # $j =~ /^(ans|mci|radb|ripe|canet)/i) { # ($j = $1) =~ tr/A-Z/a-z/; # $j .= '.db'; # push (@DB, $j); # if ($k ne '') { # if ($k !~ /\S+\.db$/) { # $k .= '.db'; # } # } # push (@DB_NEWNAME, $k); # } # else { # push (@OTHERS, $j); # push (@OTHERS_NEWNAME, $k); # } # canonicalize if ($j =~ /^([^ \t\.]+)((\.db(\.gz)?)|\.CURRENTSERIAL)?$/) { ($i = $1) =~ tr/A-Z/a-z/; } else { ($i = $j) =~ tr/A-Z/a-z/; } next if (defined($match{$i})); $match{$i} = 1; push (@DB, $i); push (@DB_NEWNAME, $k); } # load default values # remove possible trailing '/' #($CACHEDIR = $wd) =~ s/\/$//; $CACHEDIR = "/var/tmp"; $wd =~ s/\/$//; $IRRD_CONF = "/usr/local/etc/irrd.conf"; $IRRD_HOST = 'localhost'; $IRRD_PORT = 43 ; $FTPSVR='ftp.radb.net'; $FTPPTH='radb/dbase'; if ($opt_w) { #print "path-($ENV{PATH})\n\n"; $ENV{PATH} =~ s/:$//; $opt_w =~ s/^://; $ENV{PATH} .= ":$opt_w"; $opt_w =~ s/\/$//; #print "path-($ENV{PATH})\n"; } # can we find the executables we need? $ENV{PATH} .= ":/bin:/usr/bin:/usr/local/bin:$wd"; $out = `which wget`; if ($out =~ /^no/) { print "irrdcacher: wget not found in path!\n"; exit; } #print "JW: env $ENV{PATH}"; #print "JW: out $out\n"; if ($opt_C) { $out = `which ripe2rpsl`; if ($out =~ /^no/) { print "irrdcacher: ripe2rpsl not found in path!\n"; exit; } } if ($opt_s) { if ($opt_s =~ /ftp:\/\/([^ \t\/]+)\/(\S+)/) { $FTPSVR = $1; ($FTPPTH = $2) =~ s/\/$//; } else { print "Please specify the ftp server and remote path in URL format'\n"; exit; } } # irrd.conf file if ($opt_f) { $IRRD_CONF = $opt_f; } # see if we can open the irrd.conf file to get the tmp dir and port if (open (F, "<$IRRD_CONF")) { while () { next if (/^!/); if (/^tmp directory\s+(\S+)/) { # remove possible trailing '/' ($CACHEDIR = $1) =~ s/\/$//; # print "JW: setting cachedir ($CACHEDIR)\n"; # see if we can write to the tmp dir if (!-w $CACHEDIR) { die "irrdcacher: insufficient write permission to ($CACHEDIR)!\n"; } last; } elsif (/^irr_port\s+(\S+)/) { $IRRD_PORT = $1; } } close (F); } #$IRRD_HOST = $opt_h if ($opt_h); $IRRD_PORT = $opt_p if ($opt_p); #for ($x = 0, $y = $#OTHERS; $x <= $y; $x++) { # $db = pop (@OTHERS); # $newname = pop (@OTHERS_NEWNAME); # $ftppth = $FTPPTH.'/'.$db; # &updateBLOBs($db, $ftppth, $newname); #} #for ($x = 0, $y = $#DB; $x <= $y; $x++) { # $db = pop (@DB); # $newname = pop (@DB_NEWNAME); # $ftppth = $FTPPTH.'/'.$db.'.gz'; # last if (($ret_code = &updateDBs($db, $ftppth, $newname)) ne ''); #} # currentserial fetch for ($x = 0, $y = $#DB; $x <= $y; $x++) { ($db = $DB[$x]) =~ tr/a-z/A-Z/; $db .= '.CURRENTSERIAL'; $newname = ''; $ftppth = $FTPPTH.'/'.$db; &updateBLOBs($db, $ftppth, $newname); } # db fetch for ($x = 0, $y = $#DB; $x <= $y; $x++) { $db = pop (@DB).'.db'; $newname = ''; $ftppth = $FTPPTH.'/'.$db.'.gz'; last if (($ret_code = &updateDBs($db, $ftppth, $newname)) ne ''); } # let the user know the results of the request if ($ret_code ne '') { print $ret_code; } else { print "Successful operation\n"; } sub usage { print "usage: $0 [options] files...\n\n"; print "options: -p irrd port (default 43)\n"; print " -s ftp server and remote directory URL\n"; print " (default 'ftp://ftp.radb.net/routing.arbiter/radb/dbase')\n"; print " -w add component to your default search path\n"; print " -f full path name of the irrd.conf file (default /usr/local/etc/irrd.conf)\n"; print " -S suppress the cache refresh signal to irrd\n"; print " -C do RPSL conversion\n\n"; print "example: $0 -p 5555 radb mci RADB.CURRENTSERIAL\n"; print "\nspecial note: If you are running via cron be sure to use the '-w' flag\n"; exit; } # # Ftp the list of db's specified on the command line # into the cache area. Send a refresh signal # to irrd. # sub updateDBs { local($i, $fpath, $newnm)=@_; local($msg); $msg=&ftpDbNonSplit($i, $fpath, $newnm); if ($msg eq '') { if (!$opt_S) { if ($newnm ne '') { $i = $newnm; } $i=~s/(\S+)\.db$/$1/; $msg=&rebuildIndex("!B$i", $sockstream); } } return $msg; } sub updateBLOBs { local($remdb, $fpath, $newnm)=@_; local($msg); $msg = &ftpBlob ($remdb, $fpath); if ($msg eq '') { if ($newnm ne '') { if (system("mv -f $CACHEDIR/$remdb $CACHEDIR/$newnm")!=0) { $msg = "irrdcacher: Can't rename ($CACHEDIR/$remdb) to-($CACHEDIR/$newnm) to-($CACHEDIR/$newnm)\n"; } } } return $msg; } sub ftpBlob { local($db, $ftpt, $newnm)=@_; local($tmpdir, $msg)=("$CACHEDIR/tmp",''); # print "JW ftpblob ($db,$ftpt)\n"; # do we have write permission to the cache directory? if (!-w $CACHEDIR) { return "irrdcacher: insufficient write permission to ($CACHEDIR)!\n"; } # Remove old $tmpdir/$db.gz if one is lying around unlink (glob ("$tmpdir/$db")); # Create a 'tmp' directory to ftp the db's into if (($msg = &myCreateDir ($tmpdir, "$tmpdir/$db", "ftpBlob()")) ne '') { return $msg; } # ftp the cache file to $tmpdir work area if (($msg = &importBlob ($FTPSVR, $ftpt, $db, $tmpdir)) ne '') { &backout (\$msg, "ftpBlob()", $tmpdir, "$tmpdir/$db"); return $msg; } if ($newnm ne '') { $to = $newnm; } else { $to = $db; } # print "JW: moving file ($tmpdir/$db) to ($CACHEDIR/$to)\n"; if (system ("mv -f $tmpdir/$db $CACHEDIR/$to")!=0) { $msg = "irrdcacher: Can't move db from-($tmpdir/$db) to-($CACHEDIR/$to)\n"; } # unzip the file if necesary if ($to =~ /\.gz$/) { if (system("gunzip -fn $CACHEDIR/$to")!=0) { $msg = "irrdcacher: Can't unzip ($CACHEDIR/$to):($!)\n"; unlink ("$CACHEDIR/$to"); } } # Get rid of temp file and rm $tmpdir directory &backout (\$msg, "ftpBlob()", $tmpdir, "$tmpdir/$db"); return $msg; } sub importBlob { local($ftpsvr,$ftppth,$locfile,$locdir)=@_; local($outm,$m); $outm=`wget --passive-ftp -P $locdir ftp://$ftpsvr/$ftppth 2>&1`; # back out if not all of the ftp files were retrieved if ($outm !~ /${locfile}\'\s+saved/) { if ($outm ne '') { return "irrdcacher: unsuccessful ftp $locfile reason-($outm)\n"; } else { return "irrdcacher: unsuccessful ftp $locfile\n"; } } # unzip the file if necesary # if ($locfile =~ /\.gz$/) { # print "unzipping $locfile\n"; # if (system("gunzip -n $locdir/$locfile")!=0) { # $m = "irrdcacher: Can't unzip ($locdir/$locfile.gz):($!)\n"; # unlink ("$locdir/$locfile.gz"); # return $m; # } # } return ''; } # # This routine ftp's db's from a remote site and places # it into the cache area. ftpDbNonSplit() will return "ERROR..." # for operation fail, "WARN..." for operation success but something # unusual happened and return '' the null message for operation success. # sub ftpDbNonSplit { local($db, $ftpfile, $newnm)=@_; local($tmpdir,$msg,$to,$from)=("$CACHEDIR/tmp",'','',''); local($ii); # do we have write permission to the cache directory? if (!-w $CACHEDIR) { return "irrdcacher: insuffient write permission to ($CACHEDIR)!\n"; } # Remove old $tmpdir/$db.gz if one is lying around unlink (glob ("$tmpdir/$db.gz"), "$tmpdir/$db.tmp"); # Create a 'tmp' directory to ftp the db's into return $msg if (($msg=&myCreateDir($tmpdir,"$tmpdir/$db","ftpDbNonSplit()")) ne ''); # ftp the cache file to $tmpdir work area if (($msg=&importDB ($FTPSVR,$ftpfile,$db,$tmpdir)) ne '') { &backout(\$msg, "ftpNonDbSplit()", $tmpdir); return $msg; } $from = "$tmpdir/$db"; if ($opt_C) { if ($newnm eq '') { $msg=`ripe2rpsl < $tmpdir/$db > $tmpdir/$db.tmp`; } else { $ii = $newnm; $ii =~ s/(\S+)\.db$/$1/; $ii =~ tr/a-z/A-Z/; $msg=`ripe2rpsl -s $ii < $tmpdir/$db > $tmpdir/$db.tmp`; } unlink ("$tmpdir/$db"); if ($msg ne '') { &backout(\$msg,"ftpNonDbSplit()",$tmpdir,"$tmpdir/$db","$tmpdir/$db.tmp"); return $msg; } $from = "$tmpdir/$db.tmp"; } elsif ($newnm ne '') { $ii = $newnm; $ii =~ s/(\S+)\.db$/$1/; $ii =~ tr/a-z/A-Z/; $msg=`update_source $ii < $tmpdir/$db > $tmpdir/$db.tmp`; if ($msg ne '') { &backout(\$msg,"ftpNonDbSplit()",$tmpdir,"$tmpdir/$db","$tmpdir/$db.tmp"); return $msg; } unlink ("$tmpdir/$db"); $from = "$tmpdir/$db.tmp"; } if ($newnm ne '') { $to = $newnm; } else { $to = $db; } if (system("mv -f $from $CACHEDIR/$to")!=0) { $msg="irrdcacher: Can't move db from-($tmpdir/$db) to-($CACHEDIR/$db)\n"; &backout(\$msg, "ftpNonDbSplit()", $tmpdir, $from); return $msg; } # Get rid of temp file and rm $tmpdir directory &backout(\$msg, "ftpNonDbSplit()", $tmpdir, "$tmpdir/$db"); return $msg; } sub importDB { local($ftpsvr,$ftppth,$locfile,$locdir)=@_; local($outm,$m); $outm=`wget --passive-ftp -P $locdir ftp://$ftpsvr/$ftppth 2>&1`; # back out if not all of the ftp files were retrieved if ($outm !~ /${locfile}\.gz\'\s+saved/) { return "irrdcacher: unsuccessful ftp $locfile.gz reason-($outm)\n"; } if (system("gunzip -fn $locdir/$locfile.gz")!=0) { $m="irrdcacher: Can't unzip ($locdir/$locfile.gz):($!)\n"; unlink ("$locdir/$locfile.gz"); return $m; } return ''; } sub rebuildIndex { local($cmd,$SOCKSTREAM) = @_; local($whoishost,$port,$AF_INET,$SOCK_STREAM) = ($IRRD_HOST,$IRRD_PORT,2,$SOCKSTREAM); local($sockaddr,$name,$aliases,$type,$len,$thataddr,$proto,$that); $sockaddr='S n a4 x8'; ($name,$aliases,$type,$len,$thataddr)=gethostbyname($whoishost); $that=pack($sockaddr,$AF_INET,$port,$thataddr); socket (S,$AF_INET,$SOCK_STREAM,$proto) || return "irrdcacher: socket failed($cmd):($!)\n"; connect (S,$that) || return "irrdcacher: Connect failed($cmd):($!)\n"; select (S); $| = 1; select(STDOUT); print S "$cmd\n"; $devnull=; print S "!q\n"; $devnull=; close(S); return ''; } # # Create directory $dir # Directory permissions must allow read and write # $fname is the file we want to create so it cannot # already exist (ie, must be able to remove if it does # exist). # sub myCreateDir { local($dir,$fname,$tag)=@_; # Create a tmp directory to ftp the db's to if (-d $dir) { unlink($fname); if (-e $fname) { return "irrdcacher: Can't remove $fname:($!)\n"; } # check if I can cd and write to $dir if (!-w $dir || !-x $dir) { return "irrdcacher : insufficient write and/or excute permission to ($dir)!\n"; } } else { if (!mkdir($dir, 0750)) { return "irrdcacher: Can't mkdir($dir):($!)\n"; } } return ''; } # # This routine backs out of the temp dir that was used to # ftp files into. # rm files in @rmfiles (they must be full names, else default dir will # be used). $m is the initial message (could be non-null), $tag is # the name of the calling routine. $newdir is the dir to cd to and # $rmdir is the dir to be removed. # sub backout { local($m,$tag,$rmdir,@rmfiles)=@_; foreach $f (@rmfiles) { unlink($f); } chdir($CACHEDIR); rmdir($rmdir); } sub my_system { local ($command)=@_; if (system("$command")!=0) { if ($w_opt) { if (system("$w_opt/$command")==0) { return 0 } } return 1; } return 0; # maintain similar return code as "system()" }