#! /usr/bin/perl
# (c) 1998 Heiko Schlittermann <heiko@datom.de>
#
# … work in progress do integrate dnssec (branch suess)
#
# Update the serial numbers in zone files
# The serial number needs to match a specified pattern (see 
# the line marked w/ PATTERN.
# 
# ToDo:
# . test against an md5 sum, not just the date of the stamp file
# . FIXME: handle `/' in file names (currently only working in 
#   the current directory)
# . optionally reload the named


use strict;
use warnings;

use File::Basename;
use File::Copy;

#my $dnssec_sign = "../dnstools/dnssec-sign";
my $master_dir = "/etc/bind/master";

my $ME = basename $0;
my @tmpfiles;
my $verbose = 0;
my $opt_yes = 0;
my @Zones;
my $file;

sub cleanup() { unlink @tmpfiles; }
END { cleanup(); }

@ARGV and $ARGV[0] eq "-y" and $opt_yes = 1, shift;
@Zones = @ARGV ? @ARGV : glob("$master_dir/*");


MAIN: {
	my $changed;
	my ($dd, $mm, $yy) =(localtime())[3..5];
	my $date;
	$mm++;

	foreach ($dd, $mm) { s/^\d$/0$&/; }
	$yy += 1900;
	$date = "$yy$mm$dd";


	while (my $file = shift @Zones) {

		my $file_basename = basename($file);

		$file =~ s#($master_dir)(/.*)#$1$2$2#;
		local (*I, *O);
		my $done = 0;

		my $new = "$file.$$.tmp";
		my $bak = "$file.bak";
		my $stamp = $master_dir . "/.stamp/" . basename($file);	

		$file =~ /(\.bak|~)$/ and next;
		$file !~ /\./ and next;

		$verbose && print "$file:";


		if (-f $stamp && ((stat($stamp))[9] >= (stat($file))[9])) {
			$verbose && print " fresh, skipping.\n";
			next;
		}

		$done = 0;
		push @tmpfiles, $new;
		open(*I, "<$file") or die("Can't open < $file: $!\n");
		open(*O, ">$new") or die("Can't open > $new: $!\n");

		while (<I>) {
			/^\s+((\d+)(\d{2}))\s*;\s*serial/i and do {		# PATTERN
				my ($sdate, $scount, $serial) = ($2, $3, $1);
				$done = 1;
				print " [$file] serial $sdate$scount";

				if ($date eq $sdate) { $scount++; }
				else { $sdate = $date; $scount = "00"; }

				print " bumping to $sdate$scount";
				s/$serial/$sdate$scount/;

				# dnssec - new sign
				system "cd ../dnstools && ./dnssec-sign $file_basename";
				#die "$dnssec_sign not found ($!)" if $? == -1;
				exit 1 if $?;
			};
			print O;
		}

		close(O); close(I);

		if ($done) {
			# copy($file, $bak) or die("Can't copy $file -> $bak: $!\n");

			open(I, "<$new") or die("Can't open <$new: $!\n");
			open(O, ">$file") or die("Can't open >$file: $!\n");
			while (<I>) { print O or die("Can't write to $file: $!\n"); }
			close(I) or die("Can't close $new: $!\n");
			close(O) or die("Can't close $file: $!\n");

			unlink $new;

			open(O, ">$stamp") or die("Can't open >$stamp: $!\n");
			close(O);
			$changed++;
		} else {
			print " $file: no serial number found: no zone file?";
		}
		print "\n";
	}

	if ($changed) {
		my $pidfile;

		print "** Changed $changed files, the nameserver needs to be reloaded!\n";
		foreach (qw(/var/run/bind/run/named.pid /var/run/named.pid /etc/named.pid)) { 
			-f $_ and $pidfile = $_ and last; }

		if ($pidfile) {
			if ($opt_yes) { $_ = "y"; print "** Nameserver will be reloaded\n"; } 
			else { print "** Reload now? [Y/n]: "; $_ = <STDIN>; }
			/^y|^$/i and system "rndc reload";
		} else {
			print "** No PID of a running named found.  Please reload manually.\n";
		}

	}
}

# vim:ts=4:sw=4:ai:aw:
