diff -r 743fd7ffb7c3 -r 0e966f19a616 bin/update-serial --- 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 = ; # 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 = ; - 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 = ; - 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 () { +# say "XXX: match: $_" if /$re/; + $r = $1 eq 'on' and last if /$re/; + } + close I; + + return $r; + +} + __END__ =pod