diff -r 8239b2754411 -r 49003d3e8a99 update-serial.pl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/update-serial.pl Tue Jun 19 09:26:40 2012 +0200 @@ -0,0 +1,232 @@ +#! /usr/bin/perl +# (c) 1998 Heiko Schlittermann +# (c) 2010 Heiko Schlittermann +# +# … work in progress do integrate dnssec (branch suess) +# +# Update the serial numbers in zone files +# The serial number needs to match a specified pattern (see +# the line marked w/ PATTERN) +# +# Limitations: +# - the zonefile needs to fit entirely into memory +# +# ToDo: +# . test against an md5 sum, not just the date of the stamp file + +use strict; +use warnings; +use 5.010; + +use File::Copy; +use File::Basename; +use Getopt::Long; +use Pod::Usage; + +#my $dnssec_sign = "../dnstools/dnssec-sign"; +my $ME = basename $0; +my $VERSION = '__VERSION__'; +my $URL = "https://keller.schlittermann.de/hg/ius/update-serial"; + +my $master_dir = "/etc/bind/master"; +my $opt_verbose = 0; +my $opt_reload = 0; +my $opt_dnssec = 0; + +{ + + # remove temporary files + my @cleanup; + + sub cleanup(@) { + return push @cleanup, @_ if @_; + unlink @cleanup; + } +} + +sub next_serial($); + +END { cleanup(); } +$SIG{INT} = sub { exit 1 }; + +MAIN: { + + GetOptions( + "v|verbose!" => \$opt_verbose, + "y|r|yes|reload!" => \$opt_reload, + "dnssec!" => \$opt_dnssec, + "h|help" => sub { pod2usage(-exit => 0, -verbose => 1) }, + "m|man" => sub { + pod2usage( + -noperldoc => system("perldoc -V &>/dev/null"), + -exit => 0, + -verbose => 2 + ); + }, + "version" => sub { print "$0 version:$VERSION from $URL\n"; exit 0; }, + ) or pod2usage(); + + warn "DNSSEC support is currently disabled!\n" + if not $opt_dnssec; + + -d $master_dir or die "directory $master_dir not found\n" if not @ARGV; + my @files = map { (-d) ? glob("$_/*") : $_ } @ARGV ? @ARGV : $master_dir; + + my $changed = 0; + foreach my $file (@files) { + + $file = undef, next if basename($file) !~ /[:.]/; + $file = undef, next if $file =~ /\.bak|~$/; + + # zone file could be + # $master_dir/xxx.de + # or $master_dir/xxx.de/xxx.de + $file = "$file/" . basename($file) if -d $file; + + my $stamp_file = dirname($file) . "/.stamp/" . basename($file); + print "$file:" if $opt_verbose; + + if (stat $stamp_file and (stat _)[9] >= (stat $file)[9]) { + print " fresh, skipping." if $opt_verbose; + next; + } + + $_ = dirname($stamp_file); + mkdir or die "mkdir $_: $!\n" if not -d; + + my $now = time; + + open(my $in, "+<", $file) or do { + print "??: $!" if $opt_verbose; + next; + }; + + $_ = join "", <$in>; + + # this pattern is too complicated + s/^(?!;)(?
			# skip lines starting with comment
+			 (?:\S+)?			# label
+			 (?:\s+\d+.)?		# ttl
+			 (?:\s+in)?			# class
+			 \s+soa				# everything before the SOA
+			 \s+\S+				# ns
+			 \s+\S+				# hostmaster
+			 (?:\s*\()?
+			 \s+)
+			 (?\d{10})		# serial
+		/$+{pre} . next_serial($+{serial})/exims or next;
+
+        print "$+{serial} ⇒ @{[next_serial($+{serial})]}" if $opt_verbose;
+
+        copy($file => "$file~") or die("Can't copy $file -> $file~: $!\n");
+        seek($in, 0, 0) or die "Can't seek in $file: $!\n";
+        truncate($in, 0) or die "Can't truncate $file: $!\n";
+        print $in $_;
+        close($in);
+
+        # touch the stamp
+        open(my $out, ">$stamp_file");
+        close($out);
+
+        print "$file\n" if not $opt_verbose;
+
+        $changed++;
+    }
+    continue {
+        print "\n" if $opt_verbose and defined $file;
+    }
+
+    if ($changed) {
+        my $pidfile;
+
+        print
+          "** Changed $changed files, the nameserver needs to be reloaded!\n";
+        foreach (
+            qw(/var/run/bind/run/named.pid /var/run/named.pid /etc/named.pid))
+        {
+            -f $_ and $pidfile = $_ and last;
+        }
+
+        if ($pidfile) {
+            if ($opt_reload) {
+                $_ = "y";
+                print "** Nameserver will be reloaded\n";
+            }
+            else { print "** Reload now? [Y/n]: "; $_ = ; }
+            /^y|^$/i and system "rndc reload";
+        }
+        else {
+            print
+              "** No PID of a running named found.  Please reload manually.\n";
+        }
+
+    }
+}
+
+{
+    my $date;
+
+    sub next_serial($) {
+        if (not defined $date) {
+            my ($dd, $mm, $yy) = (localtime)[3 .. 5];
+            $date = sprintf "%04d%02d%02d" => $yy < 1900 ? $yy + 1900 : $yy,
+              $mm + 1, $dd;
+        }
+
+        $_[0] =~ /(?\d{8})(?\d\d)/;
+        return $date . sprintf("%02d", $+{cnt} + 1) if $date eq $+{date};
+        return "${date}00";
+    }
+}
+
+__END__
+
+=head1 NAME
+
+  update-serial - update the serial numbers or dns zone files
+
+=head1 SYNOPSIS
+
+  update-serial [-r] [-v] [file...]
+
+  update-serial -h|--help
+  update-serial -m|--man
+
+=head1 DESCRIPTION
+
+This script scans DNS (bind9 format) zone files and increments the
+serial number if the file is newer than some timestamp file. 
+
+=head1 OPTIONS
+
+=over
+
+=item B<-h>|B<--help>
+
+=item B<-m>|B<--man>
+
+Show the reference help / man page. (default: off)
+
+=item B<-r>|B<--reload>
+
+Automatically reload the bind (rndc reload) if some changes are applied.
+(default: off)
+
+=item B<-v>|B<--verbose>
+
+Be more verbose about the actions we're doing. (default: off)
+
+=back
+
+=head1 AUTHOR
+
+The latest sources might be found on
+L
+
+ Heiko Schlittermann 
+ Andre Suess (dnssec specific parts)
+
+
+=cut
+
+# vim:ts=4:sw=4:ai:aw: