#!/usr/bin/perl -w- # --------------------------------------------------------------------- # # reconf-exim4andbind - Modify named.conf.local and reconfig named # # when necessary. This is intended to allow # # lsa-svr to forward DNS request from outside # # to students' VM, by monitoring update request # # that will generate 'no authoritative' message # # in daemon.log file. # # - Plus add exim4 relay_domains config. # # # ## version 0.1 - cj (2012-11-28) init. # ## version 0.2 - cj (2012-12-02) modified form gen-lsa-namedconflocal # ## version 0.2a - cj (2012-12-11) some cosmetic added. # # version 0.3 - cj (2012-12-13) change to reconf-exim4andbind # # --------------------------------------------------------------------- # use strict; my $exim4updateconf = "/var/cache/exim4/update-exim4.conf.conf"; my $namedconflocal = "/var/cache/bind/conf/named.conf.local"; my $daemonlogfile = "/var/log/daemon.log"; my $namedconfnewfile = "${namedconflocal}.new"; my $exim4confnewfile = "${exim4updateconf}.new"; my $logfile = "/var/log/named/reconf-dnsandmail.log"; my %zonelist = (); my %tm = (); # last seen for that specific zone my $TOO_OLD = 600; # Number of seconds since last seen # Append log message to log file sub logappend() { my ($mesg) = @_; my $DATE = `date "+%Y%m%d %H:%M:%S"`; chomp($DATE); open LOG, ">> $logfile" or die "Can't append to log file : $!\n"; print LOG "$DATE\t$mesg\n"; close LOG; } # Check whether that provide IP is alive by using icmp 'ping' check sub isAlive() { my ($ip) = @_; # Make sure that the IP is defined and actual ip number # before send it to fping command. return 0 unless defined($ip); return 0 unless $ip =~ /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/; my $cmd = "fping -a $ip 2> /dev/null"; # then do it. open CMD, "$cmd |" or die "Can't execute command '$cmd'\n"; my $ret = ; close CMD; # check the result. return 0 unless defined($ret); chomp($ret); return "$ip" eq "$ret"; } # Generate new named.conf.local file, and run "rndc reconfig" sub newBindConf() { my $date = `date "+%Y-%m-%d %H:%M"`; chomp($date); if (-f $namedconfnewfile ) { unlink $namedconfnewfile; } die "Can't remove old '$namedconfnewfile', bail out!\n" if (-f $namedconfnewfile ); my $zonecount = 0; open OUTPUT, "> $namedconfnewfile" or die "Can't create '$namedconfnewfile' : $!\n"; for my $zone (keys %zonelist) { my $ip = $zonelist{$zone}->{'ip'}; $zonecount ++; print OUTPUT << "EOT"; zone "$zone" { type forward; forward only; forwarders { $ip; }; }; EOT } close OUTPUT; $date =~ s/[-: ]//g; # New named.conf.local file rename $namedconflocal, "${namedconflocal}-${date}" or die "Can't backup original $namedconflocal file\n"; rename $namedconfnewfile, $namedconflocal or die "Can't rename new $namedconflocal file\n"; system("rndc reconfig"); &logappend("Generate new Bind config with $zonecount zones ..."); } # Generate new update-exim4.conf.conf file, and run "service exim4 reload" sub newExim4Conf() { my $date = `date "+%Y-%m-%d %H:%M"`; chomp($date); if (-f $exim4confnewfile ) { unlink $exim4confnewfile; } die "Can't remove old '$exim4confnewfile', bail out!\n" if (-f $exim4confnewfile ); my $newrelaydomains = ""; my $zonecount = 0; for my $zone (keys %zonelist) { $newrelaydomains .= "$zone;"; $zonecount ++; } chop($newrelaydomains); # get rid of the last ';' $date =~ s/[-: ]//g; # New update-exim4.conf.conf file open CONF, "$exim4updateconf" or die "Can't open $exim4updateconf file : $!\n"; open OUTPUT, "> $exim4confnewfile" or die "Can't create $exim4confnewfile : $!\n"; while () { if (!/^dc_relay_domains/) { print OUTPUT $_; next; } print OUTPUT "dc_relay_domains='$newrelaydomains'\n"; } close OUTPUT; close CONF; rename $exim4updateconf, "${exim4updateconf}-${date}" or die "Can't backup original $exim4updateconf file\n"; rename $exim4confnewfile, $exim4updateconf or die "Can't rename new $exim4updateconf file\n"; system("sudo service exim4 reload"); &logappend("Generate new Exim4 config with $zonecount zones ..."); } # Timeout Check for zonelist sub timeoutCheck() { my $now = time(); my %newzonelist = (); my $ret = 0; for my $zone (keys %zonelist) { my $ip = $zonelist{$zone}->{'ip'}; my $t = $zonelist{$zone}->{'time'}; if (&isAlive($ip)) { $newzonelist{$zone}->{'ip'} = $ip; $newzonelist{$zone}->{'time'} = $now; } elsif (($now - $t) < $TOO_OLD) { $newzonelist{$zone}->{'ip'} = $ip; $newzonelist{$zone}->{'time'} = $t; } else { $ret = 1; } } %zonelist = %newzonelist; return $ret; } # Regular expression for match 'not authoriative' line. my $MSTR = "(.{15}) lsa-svr named\[[0-9]+\]: " . "client ([\.0-9]+)#[0-9]+: received notify for zone " . "'([^']+)': not authoritative\$"; my ($lastsize, $thissize); open INPUT, "$daemonlogfile" or die "Can't open '$daemonlogfile' : $!\n"; while (1) { my $newzone = 0; my $zonetimeout = 0; my %tmpzonelist = (); while (my $line = ) { chomp($line); next unless $line =~ /received notify for zone/ ; my ($tstamp,$ip,$zone) = ($line =~ /$MSTR/); if (!defined($tstamp) or !defined($ip) or !defined($zone)) { &logappend("ERROR:[$line]"); next; } $tmpzonelist{$zone}->{'ip'} = $ip; &logappend("$tstamp $ip $zone"); print "LOG: $tstamp $ip $zone\n"; } for my $zone (keys %tmpzonelist) { my $ip = $tmpzonelist{$zone}->{'ip'}; if (&isAlive($ip)) { $newzone++; my $t = time(); $zonelist{$zone}->{'ip'} = $ip; $zonelist{$zone}->{'time'} = $t; print "ZONE: $t $ip $zone\n"; } } $lastsize = (stat "$daemonlogfile")[7]; do { # Generate new config file only when there are new zones # or host for that zone is not active for sometimes. if (&timeoutCheck() or $newzone) { &newBindConf(); &newExim4Conf(); $newzone = 0; } sleep(10); $thissize = (stat "$daemonlogfile")[7]; } while ($lastsize == $thissize); if ($thissize < $lastsize) { close INPUT; open INPUT, "$daemonlogfile" or die "Can't open '$daemonlogfile' : $!\n"; &logappend("[ --- Reopen new daemon.log file --- ]"); print "LOG: --- new daemon.log ---\n"; } } close INPUT; # --------------------------------------------------------------------- # # end of file. # # --------------------------------------------------------------------- #