even more rewritten, but no user visible changes hs12
authorHeiko Schlittermann <hs@schlittermann.de>
Mon, 03 Jan 2011 16:13:54 +0100
branchhs12
changeset 70 bffb3f2cca90
parent 69 fdf4df74d8c5
child 71 e25fc893e203
even more rewritten, but no user visible changes
update-serial.pl
--- a/update-serial.pl	Thu Dec 30 17:05:31 2010 +0100
+++ b/update-serial.pl	Mon Jan 03 16:13:54 2011 +0100
@@ -9,6 +9,7 @@
 use Pod::Usage;
 use Getopt::Long;
 use File::Temp;
+use IO::File;
 use POSIX qw(strftime);
 use if $ENV{DEBUG} => "Smart::Comments";
 
@@ -17,19 +18,19 @@
 sub zones(@);
 sub changed_zones();
 sub update_index($);
-sub sign_expired($);
+sub signature_expired($);
 sub need_rollover();
 sub done_rollover();
 sub begin_rollover(@);
+sub end_rollover(@);
 sub unlink_unused_keys($);
+sub include_keys($);
+sub sign($);
+sub update_serial($);
 
-sub sign_zone;
-sub update_serial;
 sub mk_zone_conf;
 sub file_entry;
 sub server_reload;
-sub key_to_zonefile;
-sub end_ro;
 
 my %config;
 my %opt;
@@ -38,7 +39,7 @@
 
     GetOptions(
         "sign-alert-time=i" => \$opt{sign_alert_time},
-		"key-counter-end=i" => \$opt{key_counter_end},
+        "key-counter-end=i" => \$opt{key_counter_end},
         "h|help"            => sub { pod2usage(-exit 0, -verbose => 1) },
         "m|man"             => sub {
             pod2usage(
@@ -55,33 +56,25 @@
         map { $_ => $opt{$_} } grep { defined $opt{$_} } keys %opt
     );
 
-    our @new_serial;     # DO NOT USE
-    our @end_ro_list;    # liste mit zonen deren key-rollover fertig ist
     our $bind_dir = $config{bind_dir};
     our $conf_dir = $config{zone_conf_dir};
 
     my @candidates = @ARGV ? zones(@ARGV) : changed_zones;
     push @candidates, update_index($config{indexzone});
-    push @candidates, sign_expired($config{sign_alert_time});
+    push @candidates, signature_expired($config{sign_alert_time});
 
     my @need_rollover = need_rollover;
     my @done_rollover = done_rollover;
-    ### @candidates
-    ### @need_rollover
-    ### @done_rollover
-    begin_rollover(@need_rollover);    # eine rollover-beginn-sequenz
-    exit;
+
+    push @candidates, begin_rollover(@need_rollover);
+    push @candidates, end_rollover(@done_rollover);
 
-    if (@end_ro_list) {
-        end_ro;                        # eine rollover-end-squenz
+    foreach my $zone (uniq(@candidates)) {
+        update_serial($zone);
+        sign($zone);
     }
-
-    if (@new_serial) {
-
-        #--update_index;     # index zone aktuallisieren
-        update_serial;                 # serial aktuallisieren
-        sign_zone;                     # zone signieren
-    }
+    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
@@ -159,7 +152,7 @@
     return @r;
 }
 
-sub sign_expired($) {
+sub signature_expired($) {
     my $sign_alert_time = shift;  # the time between the end and the new signing
                                   # (see external configuration)
     my @r;
@@ -186,88 +179,73 @@
     return @r;
 }
 
-sub sign_zone {
-
-    # signiert die zonen und erhoeht den wert in der keycounter-datei
-    our @new_serial;
-    my $zone;
-    my $kc;
+sub sign($) {
 
-    for (uniq(@new_serial)) {
-        $zone = $_;
+    my $zone = shift;
+    my $dir  = "$config{master_dir}/$zone";
 
-        unless (-e "$config{master_dir}/$zone/.index.zsk") {
-            next;
-        }
+    my $pid = fork // die "Can't fork: $!";
 
-        chdir "$config{master_dir}/$zone";
-        if (`dnssec-signzone $zone 2>/dev/null`) {
-            print " * $zone neu signiert \n";
+    if ($pid == 0) {
+        chdir $dir or die "Can't chdir to $dir: $!\n";
+        exec "dnssec-signzone" => $zone;
+        die "Can't exec: $!\n";
+    }
+
+    wait == $pid or die "Child is lost: $!";
+    die "Can't sign zone!" if $?;
 
-            # erhoeht den keycounter
-            if ("$config{master_dir}/$zone/.keycounter") {
-                open(KC, "$config{master_dir}/$zone/.keycounter");
-                $kc = <KC>;
-                close(KC);
-                $kc += 1;
-            }
-            else {
-                $kc = 1;
-            }
-            open(KC, ">$config{master_dir}/$zone/.keycounter");
-            print KC $kc;
-            close(KC);
-        }
-        else { print "$zone konnte nicht signiert werden \n"; }
-    }
+    say " * $zone neu signiert";
+
+    open(my $fh, "+>>$dir/.keycounter")
+      or die "Can't open $dir/.keycounter for update: $!\n";
+    seek($fh, 0, 0);
+    my $kc = <$fh>;
+    truncate($fh, 0);
+    say $fh ++$kc;
 }
 
-sub update_serial {
-    our @new_serial;
-    chomp(my $date = `date +%Y%m%d`);
-    my @new_content;
-    my $sdate;
-    my $scount;
+sub update_serial($) {
+
+    my $zone = shift;
+
+    my $file = "$config{master_dir}/$zone/$zone";
+    my $in   = IO::File->new($file) or die "Can't open $file: $!\n";
+    my $out  = File::Temp->new(DIR => dirname $file)
+      or die "Can't open tmpfile: $!\n";
+    my $_ = join "" => <$in>;
+
     my $serial;
-
-    for (uniq(@new_serial)) {
+    s/^(\s+)(\d{10})(?=\s*;\s*serial)/$1 . ($serial = new_serial($2))/emi
+      or die "Serial number not found for replacement!";
 
-        # erhoeht den serial
-        my $zone        = $_;
-        my $file        = "$config{master_dir}/$zone/$zone";
-        my @new_content = ();
+    print $out $_;
+
+    close($in);
+    close($out);
 
-        open(SER, "<$file") or die "$file: $!\n";
-        for (<SER>) {
-            if (/^\s+(\d+)(\d{2})\s*;\s*serial/i) {
-                $sdate  = $1;
-                $scount = $2;
-                $serial = "$sdate$scount";
-                if ($date eq $sdate) {
-                    $scount++;
-                }
-                else {
-                    $sdate  = $date;
-                    $scount = "00";
-                }
-            }
-            if ($serial) {
-                s/$serial/$sdate$scount/;
-            }
-            push @new_content, $_;
-        }
-        close(SER);
+    rename($out->filename => $file)
+      or die "Can't rename tmp to $file: $!\n";
+
+    $serial =~ s/\s*//g;
+    say " * $zone: serial incremented to $serial";
+
+    open(my $stamp, ">", dirname($file) . "/.stamp");
+    print $stamp time() . " " . localtime() . "\n";
 
-        open(RES, ">$file") or die "$file: $!\n";
-        print RES @new_content;
-        close(RES);
-        print " * $zone: serial erhoeht \n";
+    say " * $zone: stamp aktualisiert";
+}
+
+sub new_serial($) {
+
+    my ($date, $cnt) = $_[0] =~ /(\d{8})(\d\d)/;
 
-        open(STAMP, ">$config{master_dir}/$zone/.stamp")
-          or die "$config{master_dir}/$zone/.stamp: $!\n";
-        close(STAMP);
-        print " * $zone: stamp aktualisiert \n";
-    }
+    state $now = strftime("%4Y%02m%02d", localtime);
+
+    return $date eq $now
+      ? sprintf "%s%02d", $date, $cnt + 1
+      : "${now}00";
+
 }
 
 sub mk_zone_conf {
@@ -432,13 +410,13 @@
 
     # anfang des key-rollovers
 
-    #??  for (uniq(@begin_ro_list)) {
     foreach my $zone (@zones) {
 
         # erzeugt zsks
         my $dir = "$config{master_dir}/$zone";
         my ($keyname, @keys);
 
+        # create a new key
         {    # need to change the direcoty, thus some more effort
                 # alternativly: $keyname = `cd $dir && dnssec-keygen ...`;
                 # would do, but is more fragile on shell meta characters
@@ -448,24 +426,24 @@
                 exec "dnssec-keygen",
                   -a => "RSASHA1",
                   -b => 512,
-                  -n => "ZONE", 
-		  $zone;
+                  -n => "ZONE",
+                  $zone;
                 die "Can't exec: $!";
             };
             chomp($keyname = <$keygen>);
             close($keygen) or die "dnssec-keygen failed: $@";
         }
 
-        open(my $fh, "+<$dir/.index.zsk") or die "$dir/.index.zsk: $!\n";
+        open(my $fh, "+>>$dir/.index.zsk") or die "$dir/.index.zsk: $!\n";
+        seek($fh, 0, 0);
         chomp(@keys = <$fh>);
 
-	### @keys
+        ### @keys
 
         push @keys, $keyname;
         shift @keys if @keys > 2;
 
-        seek($fh, 0, 0) or die "seek";    # FIXME
-        truncate($fh, 0) or die "truncate";    # FIXME
+        truncate($fh, 0) or die "truncate";
         print $fh join "\n" => @keys;
 
         print " * $zone: neuer ZSK $keyname erstellt\n";
@@ -475,37 +453,30 @@
         close($fh);
 
         unlink_unused_keys($zone);
-        &key_to_zonefile($zone);
+        include_keys($zone);
         push @r, $zone;
     }
 
     return @r;
 }
 
-sub key_to_zonefile {
+sub include_keys($) {
 
     # die funktion fugt alle schluessel in eine zonedatei
-    my $zone = $_[0];
-    my $zpf  = "$config{master_dir}/$zone";
-    my @old_content;
-    my @new_content = ();
+    my $zone = shift;
+    my $dir  = "$config{master_dir}/$zone";
 
-    open(ZONEFILE, "<$zpf/$zone");
-    @old_content = <ZONEFILE>;
-    close(ZONEFILE);
+    my $in = IO::File->new("$dir/$zone") or die "Can't open $dir/$zone: $!\n";
+    my $out = File::Temp->new(DIR => $dir) or die "Can't open tmpfile: $!\n";
 
-    for (@old_content) {
-        unless (m#INCLUDE.*key#) { push @new_content, $_; }
-    }
+    print $out grep { !/\$include\s+.*key/i } $in;
+    print $out map  { "\$INCLUDE @{[basename $_]}\n" } glob "$dir/K*key";
 
-    for (<$zpf/*>) {
-        if (m#(.*\/)(K.*\.key)#) {
-            push @new_content, "\$INCLUDE \"$2\"\n";
-        }
-    }
-    open(ZONEFILE, ">$zpf/$zone") or die "$zpf/$zone: $!\n";
-    print ZONEFILE @new_content;
-    close(ZONEFILE);
+    close $in;
+    close $out;
+    rename($out->filename => "$dir/$zone")
+      or die "Can't rename tmp to $dir/$zone: $!\n";
+
 }
 
 sub unlink_unused_keys($) {
@@ -515,51 +486,50 @@
     my $zone = shift;
 
     my @keys;
-    my $dir  = "$config{master_dir}/$zone";
+    my $dir = "$config{master_dir}/$zone";
 
     {
-	# collect the keys and cut everything except the key id
-	# we cut the basenames (w/o the .private|.key suffix)
+
+        # collect the keys and cut everything except the key id
+        # we cut the basenames (w/o the .private|.key suffix)
         open(my $zsk, "<$dir/.index.zsk") or die "$dir/.index.zsk: $!\n";
         open(my $ksk, "<$dir/.index.ksk") or die "$dir/.index.ksk: $!\n";
-	@keys = (<$zsk>, <$ksk>);
+        @keys = (<$zsk>, <$ksk>);
     }
 
     # prueft alle schluesseldateien (ksk, zsk) ob sie in der jeweiligen
     # indexdatei beschrieben sind. wenn nicht werden sie geloescht.
     for my $file (glob "$dir/K*.key $dir/K*.private") {
-	unlink $file if basename($file, ".key", ".private") ~~ @keys;
+        unlink $file if basename($file, ".key", ".private") ~~ @keys;
     }
 }
 
-sub end_ro {
-    our @end_ro_list;
-    our @new_serial;
-    my @content;
+sub end_rollover(@) {
 
-    for (@end_ro_list) {
-        my $zone  = $_;
-        my $count = 0;
-        my @content;
-        my $last_key;
+    my @zones = @_;
+    my @r;
+
+    foreach my $zone (@zones) {
+
+        my $dir = "$config{master_dir}/$zone";
 
-        open(INDEX, "<$config{master_dir}/$zone/.index.zsk");
-        @content = <INDEX>;
-        close(INDEX);
+        open(my $fh, "+>>$dir/.index.zsk")
+          or die "Can't open $dir/.index.zsk: $!\n";
+        seek($fh, 0, 0);
+        chomp(my @keys = <$fh>);
 
-        for (@content) {
-            $count++;
-            $last_key = $_;
+        if (@keys > 1) {
+            truncate($fh, 0);
+            say $fh $keys[-1];
         }
-        if ($count > 1) {
-            open(INDEX, ">$config{master_dir}/$zone/.index.zsk");
-            print INDEX $last_key;
-            close(INDEX);
-        }
+        close($fh);
+
         unlink_unused_keys($zone);
-        &key_to_zonefile($zone);
-        push @new_serial, $zone;
+        include_keys($zone);
+        push @r => $zone;
     }
+
+    return @r;
 }
 
 __END__