update-serial.pl
changeset 16 638a74f94981
parent 15 d7e673f7e596
child 19 df3ab24a3069
equal deleted inserted replaced
15:d7e673f7e596 16:638a74f94981
     3 # (c) 2010 Heiko Schlittermann <hs@schlittermann.de>
     3 # (c) 2010 Heiko Schlittermann <hs@schlittermann.de>
     4 #
     4 #
     5 # … work in progress do integrate dnssec (branch suess)
     5 # … work in progress do integrate dnssec (branch suess)
     6 #
     6 #
     7 # Update the serial numbers in zone files
     7 # Update the serial numbers in zone files
     8 # The serial number needs to match a specified pattern (see 
     8 # The serial number needs to match a specified pattern (see
     9 # the line marked w/ PATTERN)
     9 # the line marked w/ PATTERN)
    10 # 
    10 #
    11 # Limitations:
    11 # Limitations:
    12 # - the zonefile needs to fit entirely into memory
    12 # - the zonefile needs to fit entirely into memory
    13 # 
    13 #
    14 # ToDo:
    14 # ToDo:
    15 # . test against an md5 sum, not just the date of the stamp file
    15 # . test against an md5 sum, not just the date of the stamp file
    16 
    16 
    17 use strict;
    17 use strict;
    18 use warnings;
    18 use warnings;
    23 use Pod::Usage;
    23 use Pod::Usage;
    24 
    24 
    25 #my $dnssec_sign = "../dnstools/dnssec-sign";
    25 #my $dnssec_sign = "../dnstools/dnssec-sign";
    26 my $ME = basename $0;
    26 my $ME = basename $0;
    27 
    27 
    28 my $master_dir = "/etc/bind/master";
    28 my $master_dir  = "/etc/bind/master";
    29 my $opt_verbose = 0;
    29 my $opt_verbose = 0;
    30 my $opt_reload = 0;
    30 my $opt_reload  = 0;
    31 my $opt_dnssec = 0;
    31 my $opt_dnssec  = 0;
    32 
       
    33 
    32 
    34 {
    33 {
    35 	my @cleanup;
    34     my @cleanup;
    36 	sub cleanup(@) { 
    35 
    37 		return push @cleanup, @_ if @_;
    36     sub cleanup(@) {
    38 		unlink @cleanup; 
    37         return push @cleanup, @_ if @_;
    39 	}
    38         unlink @cleanup;
       
    39     }
    40 }
    40 }
    41 
    41 
    42 END { cleanup(); }
    42 END { cleanup(); }
    43 
    43 
    44 sub next_serial($);
    44 sub next_serial($);
    45 
    45 
    46 MAIN: {
    46 MAIN: {
    47 
    47 
    48 	GetOptions(
    48     GetOptions(
    49 		"verbose!" => \$opt_verbose,
    49         "verbose!"    => \$opt_verbose,
    50 		"yes|reload!" => \$opt_reload,
    50         "yes|reload!" => \$opt_reload,
    51 		"dnssec!" => \$opt_dnssec,
    51         "dnssec!"     => \$opt_dnssec,
    52 	) or pod2usage();
    52     ) or pod2usage();
    53 
    53 
    54 	warn "DNSSEC support is currently disabled!\n"
    54     warn "DNSSEC support is currently disabled!\n"
    55 		if not $opt_dnssec;
    55       if not $opt_dnssec;
    56 
    56 
    57 	-d $master_dir or die "directory $master_dir not found\n" if not @ARGV;
    57     -d $master_dir or die "directory $master_dir not found\n" if not @ARGV;
    58 	my @files = map { (-d) ? glob("$_/*") : $_ } @ARGV ? @ARGV : $master_dir;
    58     my @files = map { (-d) ? glob("$_/*") : $_ } @ARGV ? @ARGV : $master_dir;
    59 
    59 
    60 	my $changed = 0;
    60     my $changed = 0;
    61 	foreach my $file (@files) {
    61     foreach my $file (@files) {
    62 
    62 
    63 		$file = undef, next if basename($file) !~ /\./;
    63         $file = undef, next if basename($file) !~ /\./;
    64 		$file = undef, next if $file =~ /\.bak|~$/;
    64         $file = undef, next if $file =~ /\.bak|~$/;
    65 
    65 
    66 		# zone file could be
    66         # zone file could be
    67 		#	$master_dir/xxx.de
    67         #	$master_dir/xxx.de
    68 		#    or $master_dir/xxx.de/xxx.de
    68         #    or $master_dir/xxx.de/xxx.de
    69 		$file = "$file/" . basename($file) if -d $file;
    69         $file = "$file/" . basename($file) if -d $file;
    70 
    70 
    71 		my $stamp_file = dirname($file) . "/.stamp/" . basename($file);	
    71         my $stamp_file = dirname($file) . "/.stamp/" . basename($file);
    72 		print "$file:" if $opt_verbose;
    72         print "$file:" if $opt_verbose;
    73 
    73 
    74 		if (stat $stamp_file and (stat _)[9] >= (stat $file)[9]) {
    74         if (stat $stamp_file and (stat _)[9] >= (stat $file)[9]) {
    75 			print " fresh, skipping." if $opt_verbose;
    75             print " fresh, skipping." if $opt_verbose;
    76 			next;
    76             next;
    77 		}
    77         }
    78 
    78 
    79 		$_ = dirname($stamp_file);
    79         $_ = dirname($stamp_file);
    80 		mkdir or die "mkdir $_: $!\n" if not -d;
    80         mkdir or die "mkdir $_: $!\n" if not -d;
    81 
    81 
    82 		my $now = time;
    82         my $now = time;
    83 
    83 
    84 		open(my $in, "+<", $file) or do {
    84         open(my $in, "+<", $file) or do {
    85 			print "??: $!" if $opt_verbose;
    85             print "??: $!" if $opt_verbose;
    86 			next;
    86             next;
    87 		};
    87         };
    88 
    88 
    89 		$_ = join "", <$in>;
    89         $_ = join "", <$in>;
    90 
    90 
    91 		# this pattern is too complicated
    91         # this pattern is too complicated
    92 		s/^(?!;)(?<pre>			# skip lines starting with comment
    92         s/^(?!;)(?<pre>			# skip lines starting with comment
    93 			 (?:\S+)?			# label
    93 			 (?:\S+)?			# label
    94 			 (?:\s+\d+.)?		# ttl
    94 			 (?:\s+\d+.)?		# ttl
    95 			 (?:\s+in)?			# class
    95 			 (?:\s+in)?			# class
    96 			 \s+soa				# everything before the SOA
    96 			 \s+soa				# everything before the SOA
    97 			 \s+\S+				# ns
    97 			 \s+\S+				# ns
    99 			 (?:\s*\()?
    99 			 (?:\s*\()?
   100 			 \s+)
   100 			 \s+)
   101 			 (?<serial>\d{10})		# serial
   101 			 (?<serial>\d{10})		# serial
   102 		/$+{pre} . next_serial($+{serial})/exims or next;
   102 		/$+{pre} . next_serial($+{serial})/exims or next;
   103 
   103 
   104 		print "$+{serial} ⇒ @{[next_serial($+{serial})]}" if $opt_verbose;
   104         print "$+{serial} ⇒ @{[next_serial($+{serial})]}" if $opt_verbose;
   105 
   105 
   106 		copy($file => "$file~") or die("Can't copy $file -> $file~: $!\n");
   106         copy($file => "$file~") or die("Can't copy $file -> $file~: $!\n");
   107 		seek($in, 0, 0) or die "Can't seek in $file: $!\n";
   107         seek($in, 0, 0) or die "Can't seek in $file: $!\n";
   108 		truncate($in, 0) or die "Can't truncate $file: $!\n";
   108         truncate($in, 0) or die "Can't truncate $file: $!\n";
   109 		print $in $_;
   109         print $in $_;
   110 
   110 
   111 		open(my $out, ">$stamp_file");
   111         open(my $out, ">$stamp_file");
   112 		close($out);
   112         close($out);
   113 
   113 
   114 		print "$file\n" if not $opt_verbose;
   114         print "$file\n" if not $opt_verbose;
   115 
   115 
   116 		$changed++;
   116         $changed++;
   117 	} continue {
   117     }
   118 		print "\n" if $opt_verbose and defined $file;
   118     continue {
   119 	}
   119         print "\n" if $opt_verbose and defined $file;
   120 
   120     }
   121 	if ($changed) {
   121 
   122 		my $pidfile;
   122     if ($changed) {
   123 
   123         my $pidfile;
   124 		print "** Changed $changed files, the nameserver needs to be reloaded!\n";
   124 
   125 		foreach (qw(/var/run/bind/run/named.pid /var/run/named.pid /etc/named.pid)) { 
   125         print
   126 			-f $_ and $pidfile = $_ and last; }
   126           "** Changed $changed files, the nameserver needs to be reloaded!\n";
   127 
   127         foreach (
   128 		if ($pidfile) {
   128             qw(/var/run/bind/run/named.pid /var/run/named.pid /etc/named.pid))
   129 			if ($opt_reload) { $_ = "y"; print "** Nameserver will be reloaded\n"; } 
   129         {
   130 			else { print "** Reload now? [Y/n]: "; $_ = <STDIN>; }
   130             -f $_ and $pidfile = $_ and last;
   131 			/^y|^$/i and system "rndc reload";
   131         }
   132 		} else {
   132 
   133 			print "** No PID of a running named found.  Please reload manually.\n";
   133         if ($pidfile) {
   134 		}
   134             if ($opt_reload) {
   135 
   135                 $_ = "y";
   136 	}
   136                 print "** Nameserver will be reloaded\n";
       
   137             }
       
   138             else { print "** Reload now? [Y/n]: "; $_ = <STDIN>; }
       
   139             /^y|^$/i and system "rndc reload";
       
   140         }
       
   141         else {
       
   142             print
       
   143               "** No PID of a running named found.  Please reload manually.\n";
       
   144         }
       
   145 
       
   146     }
   137 }
   147 }
   138 
   148 
   139 {
   149 {
   140 	my $date;
   150     my $date;
   141 sub next_serial($) {
   151 
   142 	if (not defined $date) {
   152     sub next_serial($) {
   143 		my ($dd, $mm, $yy) = (localtime)[3..5];
   153         if (not defined $date) {
   144 		$date = sprintf "%04d%02d%02d" => $yy < 1900 ? $yy + 1900 : $yy,  $mm + 1, $dd;
   154             my ($dd, $mm, $yy) = (localtime)[3 .. 5];
   145 	}
   155             $date = sprintf "%04d%02d%02d" => $yy < 1900 ? $yy + 1900 : $yy,
   146 
   156               $mm + 1, $dd;
   147 	$_[0] =~ /(?<date>\d{8})(?<cnt>\d\d)/;
   157         }
   148 	return $date . sprintf("%02d", $+{cnt}+1) if $date eq $+{date};
   158 
   149 	return "${date}00";
   159         $_[0] =~ /(?<date>\d{8})(?<cnt>\d\d)/;
   150 }
   160         return $date . sprintf("%02d", $+{cnt} + 1) if $date eq $+{date};
       
   161         return "${date}00";
       
   162     }
   151 }
   163 }
   152 
   164 
   153 __END__
   165 __END__
   154 
   166 
   155 =head1 NAME
   167 =head1 NAME