#!/usr/local/bin/perl -U

$ver = "1.1.1";
$progname = "soa++";
$ext = ".bak";

# Specifiy an input file, or get a description of the funtion
#
unless($INfilename = shift){
  print<<"INFO";
  $progname v.$ver

  Usage: $progname filename [filename]

  where filename is a valid BIND configuration file (eg db.erols.com)
  and [filename] is an optional destination (use '-' for STDOUT).

  If the given file name has a SOA record $progname updates the SOA 
  serial number. 

INFO
exit();
}

# Specify an output file, or use a temp file
# based on input file.
#
$OUTfilename = $INfilename.$ext unless $OUTfilename = shift;

# protect ourselves from foolishnes..
if (-e $OUTfilename) {
  die "$progname: $OUTfilename already exists! \n";
}


# Lets not be clever...
#
if ($INfilename eq $OUTfilename){
  die "$progname: Use '$progname $INfilename' to update $INfilename.\n";
}

open(IN,$INfilename) || die "$progname: Unable to open $INfilename for input!\n";
open(OUT,">".$OUTfilename) || die "$progname: Unable to open $OUTfilename for output!\n";


# Is first line id'able as a SOA record?
# (note - this assumes the SOA record is split into two lines)
# 
$FoundSOA = '';
while(<IN>){
  if (  /\bDNS\b/ ){
    next;
  }
  if (  /\bTTL\b/ ){
    $ttl = $_;
    print OUT "$ttl";
    next;
  }
  if (  /\bORIGIN\b/ ){
    $origin = $_;
    print OUT "$origin";
    next;
  }
  if (!/\bSOA\b/ ){
    close(OUT);
    unlink($OUTfilename);
    die "$progname: $INfilename is not a BIND file!\n";
  }else{
    print OUT $_;
    last;
  }
}

while(<IN>){
  # Find and replace the existing SOA record
  #
  # print STDOUT "NextSOA is $sNextSOA", "\n";
  # if (($sNextSOA eq '' )  && (/\)$/)){ <==THIS IS THE OLD WAY.
  if (($sNextSOA eq '' )) { # && (/\)$/)){
    $sCurrentSOA = (split)[0];
    
    unless( $sNextSOA = &NextSOASerialNum($sCurrentSOA)){
      unlink($OUTfilename);
      die "$progname: bad SOA serial number\n" 
    }
    s/$sCurrentSOA/$sNextSOA/;  

    # Don't muck up the standard output
    print STDERR "[$sCurrentSOA] => [$sNextSOA]\n";
    
  }

  # send it to output.
  #
  print OUT $_;
  
}
close(IN);
close(OUT);

# if user did not specify output file, 
# replace input file.
#
if($OUTfilename eq $INfilename.$ext){
  rename($OUTfilename,$INfilename);
}

#-------------------------------------
# Takes a serial number as sole argument. Returns the next valid serial 
# number 
#
sub NextSOASerialNum{
  local($sBaseSOA);
  local($sNextSOA);
  local($sCurrentSOA) = $_[0];
  
  chop($sBaseSOA = `date '+%Y%m%d'`);

  if($sCurrentSOA =~ /^$sBaseSOA/ ){
    $sNextSOA = $sCurrentSOA + 1;

  }else{
    $sNextSOA = $sBaseSOA."01";

  }
  if ($sCurrentSOA > $sNextSOA){
    print STDERR "$progname: Badly formed current SOA #: $sCurrentSOA. \nShould be in form: YYYYMMDDnn where nn is a *two* digit \nnumber ranging from 01 to 99.\n"; 
    return "";
         
  }elsif($sNextSOA > 2**32){
    # this is a BIND restriction..
    #
    print STDERR "$progname: Next number in sequence is greater than 2^32!\n";
    return "";
    
  }else{
    return $sNextSOA; 
  }
}
