2 # (c) 1998 Heiko Schlittermann <heiko@datom.de>  | 
     2 # (c) 1998 Heiko Schlittermann <heiko@datom.de>  | 
     3 #  | 
     3 #  | 
     4 # … work in progress do integrate dnssec (branch suess)  | 
     4 # … work in progress do integrate dnssec (branch suess)  | 
     5 #  | 
     5 #  | 
     6 # Update the serial numbers in zone files  | 
     6 # Update the serial numbers in zone files  | 
     7 # The serial number needs to match a specified pattern (see   | 
     7 # The serial number needs to match a specified pattern (see  | 
     8 # the line marked w/ PATTERN.  | 
     8 # the line marked w/ PATTERN.  | 
     9 #   | 
     9 #  | 
    10 # ToDo:  | 
    10 # ToDo:  | 
    11 # . test against an md5 sum, not just the date of the stamp file  | 
    11 # . test against an md5 sum, not just the date of the stamp file  | 
    12 # . FIXME: handle `/' in file names (currently only working in   | 
    12 # . FIXME: handle `/' in file names (currently only working in  | 
    13 #   the current directory)  | 
    13 #   the current directory)  | 
    14 # . optionally reload the named  | 
    14 # . optionally reload the named  | 
    15   | 
         | 
    16   | 
    15   | 
    17 use strict;  | 
    16 use strict;  | 
    18 use warnings;  | 
    17 use warnings;  | 
    19   | 
    18   | 
    20 use File::Basename;  | 
    19 use File::Basename;  | 
    21 use File::Copy;  | 
    20 use File::Copy;  | 
    22 use FindBin;  | 
    21 use FindBin;  | 
    23   | 
    22   | 
    24 my @configs = ( "$FindBin::Bin/dnstools.conf", "/etc/dnstools.conf" );  | 
    23 my @configs = ( "$FindBin::Bin/dnstools.conf", "/etc/dnstools.conf" );  | 
    25 my @dnssec_signs = ( "$FindBin::Bin/dnssec-sign", "/usr/bin/dnstools/dnssec-sign");  | 
    24 my @dnssec_signs  | 
         | 
    25     = ( "$FindBin::Bin/dnssec-sign", "/usr/bin/dnstools/dnssec-sign" );  | 
    26 my %config;  | 
    26 my %config;  | 
    27 my $dnssec_sign;  | 
    27 my $dnssec_sign;  | 
         | 
    28 my @change_names = ();  | 
    28   | 
    29   | 
    29 foreach ( grep {-f} @configs ) { | 
    30 foreach ( grep {-f} @configs ) { | 
    30     open( CONFIG, $_ ) or die "Can't open $_: $!\n";  | 
    31     open( CONFIG, $_ ) or die "Can't open $_: $!\n";  | 
    31 }  | 
    32 }  | 
    32   | 
    33   | 
    33 unless ( seek( CONFIG, 0, 0 ) ) { | 
    34 unless ( seek( CONFIG, 0, 0 ) ) { | 
    34     die "Can't open config (searched: @configs)\n";  | 
    35     die "Can't open config (searched: @configs)\n";  | 
    35 }  | 
    36 }  | 
    36 foreach ( grep {-f} @dnssec_signs ) { | 
    37 foreach ( grep {-f} @dnssec_signs ) { | 
    37 	if (-x $_) { | 
    38     if ( -x $_ ) { | 
    38 		$dnssec_sign = $_;  | 
    39         $dnssec_sign = $_;  | 
    39 	}  | 
    40     }  | 
    40 	else { | 
    41     else { | 
    41 		die "Can't run $_\n"  | 
    42         die "Can't run $_\n";  | 
    42 	}  | 
    43     }  | 
    43 }  | 
    44 }  | 
    44   | 
         | 
    45   | 
    45   | 
    46 while (<CONFIG>) { | 
    46 while (<CONFIG>) { | 
    47     chomp;  | 
    47     chomp;  | 
    48     s/#.*//;  | 
    48     s/#.*//;  | 
    49     s/\t//g;  | 
    49     s/\t//g;  | 
    67   | 
    67   | 
    68 sub cleanup() { unlink @tmpfiles; } | 
    68 sub cleanup() { unlink @tmpfiles; } | 
    69 END { cleanup(); } | 
    69 END { cleanup(); } | 
    70   | 
    70   | 
    71 for (@ARGV) { | 
    71 for (@ARGV) { | 
    72 	if ($_ eq "-y") { | 
    72     if ( $_ eq "-y" ) { | 
    73 		$opt_yes = 1;  | 
    73         $opt_yes = 1;  | 
    74 		shift @ARGV;  | 
    74         shift @ARGV;  | 
    75 	}  | 
    75     }  | 
    76 }  | 
    76 }  | 
    77   | 
    77   | 
    78 @Zones = @ARGV ? @ARGV : glob("$master_dir/*"); | 
    78 @Zones = @ARGV ? @ARGV : glob("$master_dir/*"); | 
    79   | 
    79   | 
         | 
    80 MAIN: { | 
         | 
    81     my $changed;  | 
         | 
    82     my ( $dd, $mm, $yy ) = ( localtime() )[ 3 .. 5 ];  | 
         | 
    83     my $date;  | 
         | 
    84     $mm++;  | 
    80   | 
    85   | 
    81 MAIN: { | 
    86     foreach ( $dd, $mm ) { s/^\d$/0$&/; } | 
    82 	my $changed;  | 
    87     $yy += 1900;  | 
    83 	my ($dd, $mm, $yy) =(localtime())[3..5];  | 
    88     $date = "$yy$mm$dd";  | 
    84 	my $date;  | 
         | 
    85 	$mm++;  | 
         | 
    86   | 
    89   | 
    87 	foreach ($dd, $mm) { s/^\d$/0$&/; } | 
    90     while ( my $file = shift @Zones ) { | 
    88 	$yy += 1900;  | 
         | 
    89 	$date = "$yy$mm$dd";  | 
         | 
    90   | 
    91   | 
         | 
    92         my $file_basename = basename($file);  | 
    91   | 
    93   | 
    92 	while (my $file = shift @Zones) { | 
    94         $file =~ s#($master_dir)(/.*)#$1$2$2#;  | 
         | 
    95         local ( *I, *O );  | 
         | 
    96         my $done = 0;  | 
    93   | 
    97   | 
    94 		my $file_basename = basename($file);  | 
    98         my $new   = "$file.$$.tmp";  | 
         | 
    99         my $bak   = "$file.bak";  | 
         | 
   100         my $stamp = $master_dir . "/.stamp/" . basename($file);  | 
    95   | 
   101   | 
    96 		$file =~ s#($master_dir)(/.*)#$1$2$2#;  | 
   102         $file =~ /(\.bak|~)$/ and next;  | 
    97 		local (*I, *O);  | 
   103         $file !~ /\./ and next;  | 
    98 		my $done = 0;  | 
         | 
    99   | 
   104   | 
   100 		my $new = "$file.$$.tmp";  | 
   105         $verbose && print "$file:";  | 
   101 		my $bak = "$file.bak";  | 
         | 
   102 		my $stamp = $master_dir . "/.stamp/" . basename($file);	  | 
         | 
   103   | 
   106   | 
   104 		$file =~ /(\.bak|~)$/ and next;  | 
   107         if ( -f $stamp && ( ( stat($stamp) )[9] >= ( stat($file) )[9] ) ) { | 
   105 		$file !~ /\./ and next;  | 
   108             $verbose && print " fresh, skipping.\n";  | 
         | 
   109             next;  | 
         | 
   110         }  | 
   106   | 
   111   | 
   107 		$verbose && print "$file:";  | 
   112         $done = 0;  | 
         | 
   113         push @tmpfiles, $new;  | 
         | 
   114         open( *I, "<$file" ) or die("Can't open < $file: $!\n"); | 
         | 
   115         open( *O, ">$new" )  or die("Can't open > $new: $!\n"); | 
   108   | 
   116   | 
         | 
   117         while (<I>) { | 
         | 
   118             /^\s+((\d+)(\d{2}))\s*;\s*serial/i and do {    # PATTERN | 
         | 
   119                 my ( $sdate, $scount, $serial ) = ( $2, $3, $1 );  | 
         | 
   120                 $done = 1;  | 
         | 
   121                 print " [$file] serial $sdate$scount";  | 
   109   | 
   122   | 
   110 		if (-f $stamp && ((stat($stamp))[9] >= (stat($file))[9])) { | 
   123                 if   ( $date eq $sdate ) { $scount++; } | 
   111 			$verbose && print " fresh, skipping.\n";  | 
   124                 else                     { $sdate = $date; $scount = "00"; } | 
   112 			next;  | 
         | 
   113 		}  | 
         | 
   114   | 
   125   | 
   115 		$done = 0;  | 
   126                 print " bumping to $sdate$scount";  | 
   116 		push @tmpfiles, $new;  | 
   127                 s/$serial/$sdate$scount/;  | 
   117 		open(*I, "<$file") or die("Can't open < $file: $!\n"); | 
         | 
   118 		open(*O, ">$new") or die("Can't open > $new: $!\n"); | 
         | 
   119   | 
   128   | 
   120 		while (<I>) { | 
   129             };  | 
   121 			/^\s+((\d+)(\d{2}))\s*;\s*serial/i and do {		# PATTERN | 
   130             print O;  | 
   122 				my ($sdate, $scount, $serial) = ($2, $3, $1);  | 
   131         }  | 
   123 				$done = 1;  | 
         | 
   124 				print " [$file] serial $sdate$scount";  | 
         | 
   125   | 
   132   | 
   126 				if ($date eq $sdate) { $scount++; } | 
   133         close(O);  | 
   127 				else { $sdate = $date; $scount = "00"; } | 
   134         close(I);  | 
   128   | 
   135   | 
   129 				print " bumping to $sdate$scount \n";  | 
   136         if ($done) { | 
   130 				s/$serial/$sdate$scount/;  | 
         | 
   131   | 
   137   | 
   132 			};  | 
   138             # copy($file, $bak) or die("Can't copy $file -> $bak: $!\n"); | 
   133 			print O;  | 
         | 
   134 		}  | 
         | 
   135   | 
   139   | 
   136 		close(O); close(I);  | 
   140             open( I, "<$new" )  or die("Can't open <$new: $!\n"); | 
         | 
   141             open( O, ">$file" ) or die("Can't open >$file: $!\n"); | 
         | 
   142             while (<I>) { print O or die("Can't write to $file: $!\n"); } | 
         | 
   143             close(I) or die("Can't close $new: $!\n"); | 
         | 
   144             close(O) or die("Can't close $file: $!\n"); | 
   137   | 
   145   | 
   138 		if ($done) { | 
   146             unlink $new;  | 
   139 			# copy($file, $bak) or die("Can't copy $file -> $bak: $!\n"); | 
         | 
   140   | 
   147   | 
   141 			open(I, "<$new") or die("Can't open <$new: $!\n"); | 
   148             open( O, ">$stamp" ) or die("Can't open >$stamp: $!\n"); | 
   142 			open(O, ">$file") or die("Can't open >$file: $!\n"); | 
   149             close(O);  | 
   143 			while (<I>) { print O or die("Can't write to $file: $!\n"); } | 
   150             $changed++;  | 
   144 			close(I) or die("Can't close $new: $!\n"); | 
         | 
   145 			close(O) or die("Can't close $file: $!\n"); | 
         | 
   146   | 
   151   | 
   147 			unlink $new;  | 
   152             push @change_names, $file_basename;  | 
   148   | 
   153   | 
   149 			open(O, ">$stamp") or die("Can't open >$stamp: $!\n"); | 
   154         }  | 
   150 			close(O);  | 
   155         else { | 
   151 			$changed++;  | 
   156             print " $file: no serial number found: no zone file?";  | 
         | 
   157         }  | 
         | 
   158         print "\n";  | 
         | 
   159     }  | 
   152   | 
   160   | 
   153 			# dnssec - new sign  | 
   161     if ($changed) { | 
   154 			system "$dnssec_sign $file_basename";  | 
   162         my $pidfile;  | 
   155 			die "$dnssec_sign not found ($!)" if $? == -1;  | 
         | 
   156 			exit 1 if $?;  | 
         | 
   157   | 
   163   | 
   158 		} else { | 
   164         # dnssec - new sign  | 
   159 			print " $file: no serial number found: no zone file?";  | 
   165         system "$dnssec_sign @change_names";  | 
   160 		}  | 
   166         die "$dnssec_sign not found ($!)" if $? == -1;  | 
   161 		print "\n";  | 
   167         exit 1 if $?;  | 
   162 	}  | 
         | 
   163   | 
   168   | 
   164 	if ($changed) { | 
   169         print  | 
   165 		my $pidfile;  | 
   170             "** Changed $changed files, the nameserver needs to be reloaded!\n";  | 
         | 
   171         foreach (  | 
         | 
   172             qw(/var/run/bind/run/named.pid /var/run/named.pid /etc/named.pid))  | 
         | 
   173         { | 
         | 
   174             -f $_ and $pidfile = $_ and last;  | 
         | 
   175         }  | 
   166   | 
   176   | 
   167 		print "** Changed $changed files, the nameserver needs to be reloaded!\n";  | 
   177         if ($pidfile) { | 
   168 		foreach (qw(/var/run/bind/run/named.pid /var/run/named.pid /etc/named.pid)) {  | 
   178             if ($opt_yes) { | 
   169 			-f $_ and $pidfile = $_ and last; }  | 
   179                 $_ = "y";  | 
         | 
   180                 print "** Nameserver will be reloaded\n";  | 
         | 
   181             }  | 
         | 
   182             else { print "** Reload now? [Y/n]: "; $_ = <STDIN>; } | 
         | 
   183             /^y|^$/i and system "rndc reload";  | 
         | 
   184         }  | 
         | 
   185         else { | 
         | 
   186             print  | 
         | 
   187                 "** No PID of a running named found.  Please reload manually.\n";  | 
         | 
   188         }  | 
   170   | 
   189   | 
   171 		if ($pidfile) { | 
   190     }  | 
   172 			if ($opt_yes) { $_ = "y"; print "** Nameserver will be reloaded\n"; }  | 
         | 
   173 			else { print "** Reload now? [Y/n]: "; $_ = <STDIN>; } | 
         | 
   174 			/^y|^$/i and system "rndc reload";  | 
         | 
   175 		} else { | 
         | 
   176 			print "** No PID of a running named found.  Please reload manually.\n";  | 
         | 
   177 		}  | 
         | 
   178   | 
         | 
   179 	}  | 
         | 
   180 }  | 
   191 }  | 
   181   | 
   192   | 
   182 # vim:ts=4:sw=4:ai:aw:  | 
   193 # vim:ts=4:sw=4:ai:aw:  |