#!/bin/bash
#< Script to parse a basic named.conf file, and convert from master to slave configuration

# This script is part of my DNS named.conf transfer scripts. The scripts
# are designed to be used for the transferral of a frequently changing 
# named.conf file from a primary nameserver to a secondary nameserver.

# The scripts assume a lot. The zone files used in my test environment
# were very "bare-bones" and simple. Example:

# Any additional configuration options present in the file would
# require script modifications (sync_to_slave.sh) to accomodate parsing
# the other configuration options. This script is designed to be easily
# modified, and should be considered a template for named.conf transfers.

# The scripts revolve around a central lock file mechanism, which aims to
# ensure that no incomplete named.conf files are transferred whilst they are
# being edited - although it will not parse a file for syntactical correctness.
# Also, this assumes that all edits to the named.conf file are made using the
# vinamed wrapper, or a custom tool that uses the lockfile mechanism used in
# these scripts.

# The scripts provided are:
#   vinamed                     Lock file protected named.conf editing wrapper
#   sync_to_slave.sh            Intended to be scheduled via cron on the 
#                               primary nameserver, BEFORE the script below 
#                               runs on the slave nameserver.
#   install_slave_config.sh     Schedule this to run a few minutes after
#                               the sync_to_slave.sh script runs on the 
#                               primary nameserver. This script contains
#                               various checks (lockfile, confirms whether
#                               the newly transferred named.conf file is
#                               modified, etc.) This script also creates 
#                               backups of named.conf prior to, and after
#                               modification. Then it restarts BIND.

# Example cron jobs (depending on the frequency of your named.conf file
# edits). 

# master# crontab -l | grep sync_to_slave 
# 0,30 * * * * /usr/local/dns/bin/sync_to_slave.sh
# slave# crontab -l | grep install_slave_config
# 15,45 * * * * /usr/local/dns/bin/install_slave_config.sh

# You will also need to check the paths presented in the scripts, and ensure
# that the appropriate backup directories are created, or the paths
# referenced in the scripts are changed.

# For the key-based file transfer to work, I'm using a "namesync" user on each
# server.

# Executable paths
AWK="/usr/bin/awk"
CHMOD="/bin/chmod"
CHOWN="/bin/chown"
CP="/bin/cp"
DATE="/bin/date"
ECHO="/bin/echo"
ID="/usr/bin/id"
MV="/bin/mv"
RM="/bin/rm"
SCP="/usr/bin/scp"
SED="/usr/bin/sed"
TEE="/usr/bin/tee"

# Configuration and Temporary Files
NAMED_CONF="/etc/named.conf"

# Output Directory
NAMESYNC_HOME="/home/namesync"
DESTINATION="ns1.dnstest.com"
IDENTITY="${NAMESYNC_HOME}/.ssh/id_dsa"
OUTPUT_DIR="${NAMESYNC_HOME}/named.xfer"
OUTPUT_FILE="named.conf.$( ${DATE} +%Y%m%d ).$$"
LOCKFILE="/var/lock/vinamed.lock"

# Errors
SUCCESS="0"
ENOTROOT="1"
ENODIR="1"
ELOCKFILE="1"

# Function:     print_error
# Arguments:    $@
# Returns:      Nothing
# Description:  echo wrapper to output to STDERR

function print_error {
   ${ECHO} "Error: $@" >&2 
}

# Function:     print_msg
# Arguments:    $@
# Returns:      Nothing
# Description:  echo wrapper to output to STDOUT with prefix

function print_msg {
   ${ECHO} "--> $@" 
}

# Function:     check_root_user
# Arguments:    None
# Returns:      Nothing - will exit on failure
# Description:  Checks whether the script is being executed as root user

function check_root_user {
   WHOAMI=$( ${ID} -u )
   if [ "${WHOAMI}" -ne "0" ]; then
      print_error "You must be root to run this program"
      exit ${ENOTROOT}
   fi 
}

# Function:     check_output_dir
# Arguments:    None
# Returns:      Nothing - will exit on failure
# Description:  Checks to ensure that the output directory exists

function check_output_dir {
   if [ ! -d "${OUTPUT_DIR}" ]; then
      print_error "OUTPUT_DIR: ${OUTPUT_DIR} unavailable"
      exit ${ENODIR}
   fi
}

# Function:     check_lockfile
# Arguments:    None
# Returns:      Nothing - exits on failure
# Description:  Exits if the lockfile exists

function check_lockfile {
   if [ -e "${LOCKFILE}" ]; then
      print_error "Lockfile exists - is ${NAMED_CONF} being edited?"
      exit ${ELOCKFILE}
   fi
}

# Function:	process_conf_file
# Arguments:	None
# Returns:	Nothing
# Description:	Parses the named.conf file and converts it from a master
#		to a slave configuration

function process_conf_file {
   print_msg "Converting master named.conf to slave configuration"
   ${SED} -e '/^zone/,/};/ {
     /0\.0\.127\.in-addr\.arpa/,/};/ {
       b
     }
     s/type master/type slave/
   }' -e 's!// ns0!// ns1!' -e 's!// master!// slave! ' ${NAMED_CONF} | ${AWK} '{ 
      if ( $0 ~ /^[ 	]*file/ &&
      $0 !~ /^[ 	]*file \"127\.0\.0\.zone\"/ && 
      $0 !~ /^[ 	]*file \"root.hint\"/ ) {
         printf( "%s\n  masters { 192.168.0.130; };\n", $0 )
      } else if ( $0 ~ /^[ 	]*allow-transfer / ) {
         next 
      } else {
         printf( "%s\n", $0 )
      }
   }' | ${TEE} ${OUTPUT_DIR}/${OUTPUT_FILE}
}

# Function:	transfer_conf_file
# Arguments:	None
# Returns:	Nothing
# Description:	Transfers the newly generated slave named.conf file over to the secondary
#		nameserver via scp using the "namesync" user

function transfer_conf_file {
   print_msg "Transferring named.conf to staging area on ${DESTINATION}"
   ${SCP} -i ${IDENTITY} ${OUTPUT_DIR}/${OUTPUT_FILE} namesync@${DESTINATION}:${OUTPUT_DIR}
}

#
# main
#
#

check_root_user
check_lockfile
process_conf_file
transfer_conf_file

exit ${SUCCESS}