#!/usr/bin/perl -i ($IDENT = '@(#)zsu: update DNS zone serial number') =~ s/^[^:]*: *//; # # edit zone file in-place # only update files where the SOA indicates we are authoritative # only update serial numbers in format (yy)yymmddn(n) # otherwise assume a deliberate forced zone refresh and preserve serial # handles timewarp (old datestamp is in future) by using future date # # Returns: 0 if success, 1 if failure # # This software is covered by the GPL, version 2 or later. # ($BCMD = $0) =~ s/.*\///; ($REVISION) = ('$Revision: 1.12 $' =~ /[^\d\.]*([\d\.]*)/); $HELPSTRING = "For help, type: $BCMD -h"; $USAGE = "Usage: $BCMD [-dfLv] zone"; # parse command line arguments #----------------------------- require('getopts.pl'); if (! &Getopts('dfhLv')) { print STDERR "$USAGE\n$HELPSTRING\n"; exit 2; } if ($opt_h) { print < This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. If you do not already have a copy of the GNU General Public License, you can obtain a copy by anonymous ftp from prep.ai.mit.edu (file COPYING in directory /pub/gnu) or write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. EOT exit 0; } $VERBOSE = $opt_v; $DEBUG = $opt_d || $VERBOSE; if (@ARGV < 1) { print STDERR "$USAGE\n$HELPSTRING\n"; exit 2; } # Set up fully qualified host name #--------------------------------- # zone files must have originating host fully qualified, since information # from outside zone may be necessary to understand non-FQ domain names if (! $opt_f) { ($myhost = `hostname` || `uname -n`) =~ s/\s*$//; if ($myhost !~ /\./) { if (! ($mydom = `domainname`)) { warn "cannot get FQDN of host" if $DEBUG; } else { if ($mydom !~ /^\./) { $mydom = ".$mydom"; } $myhost .= $mydom; } } if ($myhost !~ /\.$/) { $myhost .= '.'; } } # generate a sensible serial number #---------------------------------- sub generate_new { local($oldserial) = (@_); $newserial = $oldserial; if ((length($oldserial) < 7) || (length($oldserial) > 10)) { warn "serial $oldserial appears to be nonstandard, leaving as is"; $exitcode ++; } else { ($c, $d, $oldcount) = ($oldserial =~ /^([12][09])?([0-9][0-9][01][0-9][0-3][0-9])(..?)/); $olddate = $c.$d; if ($d !~ /^[0-9][0-9][01][0-9][0-3][0-9]/) { warn "serial $oldserial does not match heuristics, leaving as is"; $exitcode ++; } else { ($mday,$mon,$year) = (localtime(time))[3..5]; $mon ++; # preserve yy or yyyy format # force yyyy if year>=2000 or if count==99 if ($c == 0) { $year += 1900 if $year < 100; $c = ($year - $year % 100)/100 if ($year >= 2000 || $oldcount == 99); $oldcount = -1 if $oldcount == 99; # can reset since yyyy > yy } $year %= 100; $newcount = $oldcount + 1; $newdate = sprintf("%s%02d%02d%02d", $c, $year, $mon, $mday); if (($newcount >= 100) && ($newdate <= $olddate)) { warn "cannot increment $oldserial, leaving as is"; $exitcode ++; } else { if ($newdate > $olddate) { $newcount = 0; } $newserial = $newdate . sprintf("%02d", $newcount); if ($newdate < $olddate) { # actually need 2^31 hack here warn "serial $oldserial is in future" if $DEBUG; $newserial = $olddate . sprintf("%02d", $newcount); } } } } $newserial; } # now parse zone file #-------------------- # state table: 0 looking for SOA # 1 found SOA, looking for serial # 2 found serial, looking for next file $state = 0; while (<>) { if ($state == 0) { if (/^([^;]*\bSOA\s+)([^\s;]+)(\s+[^\s;]+\s+\(\s*)([^\s;]*)(.*)/i) { if ($opt_f || ($2 eq $myhost)) { $ours = 1; } else { $ours = 0; $state = 2; } if ($ours) { if ("$4" ne '') { $oldserial = $4; if ($oldserial !~ /^[\d.]+$/) { warn "cannot parse serial number, skipping" if $DEBUG; $ours = 0; $exitcode ++; } else { $_ = "$1$2$3". &generate_new($oldserial) ."$5\n"; } $state = 2; } else { $state = 1; } } } } elsif ($state == 1) { if (/^(\s*)([0-9.]+)(.*)/) { $_ = "$1". &generate_new($2) ."$3\n"; $state = 2; } } elsif ($state == 2) { $state = 0 if eof; } } continue { print; } if ($state == 2) { warn "internal error: did not detect end of last file" if $DEBUG; $exitcode ++; } elsif ($state == 1) { warn "could not locate serial number, skipping" if $DEBUG; $exitcode ++; } exit $exitcode; # $Log: zsu,v $ # Revision 1.12 1999/02/26 14:30:52 andras # now understands yy and yyyy formats # will shift yy to yyyy if y2k or if run out of nn's # # Revision 1.11 1996/04/02 10:20:43 andras # updated contact info # # Revision 1.10 1995/12/28 11:52:06 andras # perl5: quoted @'s # # Revision 1.9 1995/11/17 13:50:05 andras # added force option # # Revision 1.8 1995/11/17 12:49:06 andras # now handles yyyymmddn(n) formats also # # Revision 1.7 1995/10/27 11:19:56 andras # fixed multifile handling and $ours detection # # Revision 1.6 1995/10/27 10:45:06 andras # fixed state table; now goes back to 0 at eof # used to ignore second and subsequent files # # Revision 1.5 1995/05/15 16:16:23 andras # fixed hstname typo # # Revision 1.4 1995/05/09 00:38:34 andras # added in command line processing # # Revision 1.3 1995/05/08 20:45:59 andras # now supposed to understand a fairly general zone format # tested OK on standard format primary # tested OK on easy secondary # tested OK on secondary with serial in future # # Revision 1.2 1995/04/26 07:52:57 andras # cleaner logic # # Revision 1.1 1994/10/25 10:13:47 andras # Initial revision #