--- a/bin/update-serial Thu May 26 09:41:30 2011 +0200
+++ b/bin/update-serial Fri May 27 10:16:02 2011 +0200
@@ -34,6 +34,7 @@
use POSIX qw(strftime);
use if $ENV{DEBUG} => "Smart::Comments";
use DNStools::Config qw(get_config);
+use DNS::ZoneParse;
sub uniq(@);
sub zones(@);
@@ -49,10 +50,12 @@
sub sign($);
sub update_serial($);
-sub mk_zone_conf;
+sub mk_zone_conf($$);
sub file_entry;
sub server_reload;
+sub dnssec_enabled($$);
+
my %config;
my %opt;
@@ -75,11 +78,10 @@
) or pod2usage;
# merge the config and the defined options from commandline
- %config = get_config("$ENV{DNSTOOLS_CONF}", "dnstools.conf",
- "$ENV{HOME}/.dnstools.conf", "/etc/dnstools.conf", \%opt);
-
- our $bind_dir = $config{bind_dir};
- our $conf_dir = $config{zone_conf_dir};
+ my @configs = ( "dnstools.conf", "$ENV{HOME}/.dnstools.conf",
+ "/etc/dnstools.conf");
+ unshift @configs, $ENV{DNSTOOLS_CONF} if defined $ENV{DNSTOOLS_CONF};
+ %config = get_config(@configs, \%opt);
my @candidates = @ARGV ? zones(@ARGV) : changed_zones;
push @candidates, update_index($config{indexzone});
@@ -92,15 +94,15 @@
push @candidates, end_rollover(@done_rollover);
foreach my $zone (uniq(@candidates)) {
+# say "XXX: candidate $zone";
update_serial($zone);
- sign($zone);
+ sign($zone) if dnssec_enabled($zone, "$config{master_dir}/$config{indexzone}/$config{indexzone}");
+# say "XXX: $zone should be signed" if dnssec_enabled($zone, "$config{master_dir}/$config{indexzone}/$config{indexzone}");
}
- say "Need to ... file_entry, mk_zone_conf, server_reload";
- exit;
- file_entry; # bearbeitet die file-eintraege der konfigurations-datei
- mk_zone_conf; # konfiguration zusammenfuegen
- server_reload; # server neu laden
+ file_entry;
+ mk_zone_conf($config{bind_dir}, $config{zone_conf_dir});
+ server_reload;
}
@@ -143,10 +145,15 @@
next;
}
- my $stamp_age = -M _;
- my $file_age = -M "$_/$zone";
+ my $stamp_mtime = (stat _)[8];
+ my $stamp_mtime2 = (stat "$_/.stamp")[8];
+ my $zone_file_mtime = (stat "$_/$zone")[8] or die "Can't stat '$_/$zone': $!";
+ # TODO: do this here?
+ my $kc_file_mtime = 0;
+ $kc_file_mtime = (stat "$_/.keycounter")[8] or die "Can't stat '$_/.keycounter': $!" if -f "$_/.keycounter";
+# say "XXX: zone: $zone | stamp_mtime: $stamp_mtime| stamp_mtime2: $stamp_mtime2 | zone_file_mtime: $zone_file_mtime | kc_file_mtime: $kc_file_mtime";
- next if $stamp_age <= $file_age; # should be only <
+ next unless $stamp_mtime < $zone_file_mtime or $stamp_mtime < $kc_file_mtime;
push @r, $zone;
say " * $zone: zone file modified";
@@ -210,6 +217,7 @@
sub update_serial($) {
my $zone = shift;
+# say "XXX: $zone: updating serial number";
my $file = "$config{master_dir}/$zone/$zone";
my $in = IO::File->new($file) or die "Can't open $file: $!\n";
@@ -232,10 +240,12 @@
$serial =~ s/\s*//g;
say " * $zone: serial incremented to $serial";
- open(my $stamp, ">", dirname($file) . "/.stamp");
- print $stamp time() . " " . localtime() . "\n";
+ my ($atime, $utime) = (time) x 2;
+ my $s = (dirname $file) . '/.stamp';
+ utime $atime, $utime, $s or die "Can't 'utime $atime, $utime, $s': $!";
say " * $zone: stamp aktualisiert";
+# say " XXX $zone: stamp '$s' aktualisiert";
}
sub new_serial($) {
@@ -250,11 +260,10 @@
}
-sub mk_zone_conf {
+sub mk_zone_conf($$) {
# erzeugt eine named.conf-datei aus den entsprechenden vorlagen.
- our $bind_dir;
- our $conf_dir;
+ my ($bind_dir, $conf_dir) = @_;
open(TO, ">$bind_dir/named.conf.zones")
or die "$bind_dir/named.conf.zones: $!\n";
@@ -268,6 +277,7 @@
}
sub update_index($) {
+
my $indexzone = shift;
my @iz;
@@ -281,7 +291,7 @@
for my $dir (glob "$config{master_dir}/*") {
my $zone = basename($dir);
my $info = -e ("$dir/.keycounter") ? "sec-on" : "sec-off";
- push @iz, join "::", "\t\tIN TXT\t\t\"ZONE", $zone, $info;
+ push @iz, join "::", "\t\tIN TXT\t\t\"ZONE", $zone, $info . '"';
}
{
@@ -292,6 +302,7 @@
or die "Can't rename "
. $fh->filename
. " to $config{master_dir}/$indexzone/$indexzone: $!\n";
+ $fh->unlink_on_destroy(0);
}
say "** index-zone aktualisiert";
@@ -303,50 +314,53 @@
# prueft jede domain, die ein verzeichnis in $config{master_dir} hat, ob sie
# dnssec nutzt.
# passt die eintraege in $config_file falls noetig an.
- our $conf_dir;
+ my $cd = $config{zone_conf_dir};
+ my $md = $config{master_dir};
- while (glob "$config{master_dir}/*") {
- s#($config{master_dir}/)(.*)#$2#;
- my $zone = $_;
- my $zone_file = "$config{master_dir}/$zone/$zone";
- my $conf_file = "$conf_dir/$zone";
- my @c_content;
+ while (glob "$md/*") {
+ m#($md/)(.*)#;
+ my $z = $2;
+ my $cf = "$cd/$z";
+ my $de = dnssec_enabled $z, "$md/$config{indexzone}/$config{indexzone}";
+ my $suf = $de ? '.signed' : '';
+ # TODO: assuming that paths in $md and in zone config snippets match somehow
+ my $zp = "$z/$z$suf";
+ my $zf = "$md/$z/$z$suf";
- unless (-f "$conf_file") {
- die "$conf_file: $! \n";
+ my ($files, $changed) = (0, 0);
+ my $czf;
+ open C, "+<$cf" or die "Cant't open '$cf': $!";
+ my @lines = <C>; # TODO: deal with race condition?
+ my ($mode, $uid, $gid, $atime, $mtime) = (stat C)[2, 4, 5, 8, 9] or die "Can't stat: $!";
+ $mode &= 07777;
+ for (@lines) {
+ next unless /^\s*file\s+"([^"]*)"\s*;\s*$/;
+ $czf = $1;
+ $files++;
+ $_ = qq(\tfile "$zf";) and $changed++ unless $czf =~ m#\Q$z/$z$suf\E$#;
}
- if (-e "$config{master_dir}/$zone/.keycounter") {
- open(FILE, "<$conf_file") or die "$conf_file: $!\n";
- @c_content = <FILE>;
- close(FILE);
- for (@c_content) {
- if (m{(.*)($zone_file)(";)}) {
- print
- " * zonekonfiguration aktualisiert ($2 ==> $2.signed)\n";
- $_ = "$1$2.signed$3\n";
- }
- }
- open(FILE, ">$conf_file") or die "$conf_file: $!\n";
- print FILE @c_content;
- close(FILE);
- }
- else {
- open(FILE, "<$conf_file") or die "$conf_file: $!\n";
- @c_content = <FILE>;
- close(FILE);
- for (@c_content) {
- if (m{(.*)($zone_file)\.signed(.*)}) {
- print
- " * zonekonfiguration aktualisiert ($2.signed ==> $2)\n";
- $_ = "$1$2$3\n";
- }
- }
- open(FILE, ">$conf_file") or die "$conf_file: $!\n";
- print FILE @c_content;
- close(FILE);
- }
+ die "Multiple file statements found in '$cf' (maybe inside multiline comments)" if $files > 1;
+ return unless $changed;
+
+ # file statement in config snippet doesnt match, so we make a backup first and write a new config
+ my $cb = "$cf.bak";
+ open B, ">$cb" or die "Can't open '$cb': $!";
+ print B @lines;
+ close B;
+ chown $uid, $gid, $cb or die "Can't 'chown $uid, $gid, $cb': $!";
+ chmod $mode, $cb or die "Can't 'chmod $mode, $cb': $!";
+ utime $atime, $mtime, $cb or die "Can't 'utime $atime, $mtime, $cb': $!";
+
+ seek C, 0, 0 or die "Can't seek C, 0, 0: $!";
+ # write back @lines we modified earlier
+ print C @lines;
+ close C;
+
+ print " * zonekonfiguration aktualisiert ($czf ==> $zf)\n";
+
}
+
}
sub server_reload {
@@ -534,6 +548,25 @@
return @r;
}
+# dnssec_enabled($zone, $path_to_indexzone_file)
+# return true if the index zone indicates that dnssec is enabled for a zone
+sub dnssec_enabled($$) {
+
+ my ($z, $if) = @_;
+ my $re = qr/^[^;]*IN\s+TXT\s+"ZONE::\Q$z\E::sec-(on|off)"/;
+ my $r;
+
+ open I, "<$if" or die "Can't open index zone file '<$if': $!";
+ while (<I>) {
+# say "XXX: match: $_" if /$re/;
+ $r = $1 eq 'on' and last if /$re/;
+ }
+ close I;
+
+ return $r;
+
+}
+
__END__
=pod