scripts/cpanel-rdns-manager

320 lines
7.9 KiB
Bash
Executable File

#!/usr/bin/env bash
#rdns.sh - script for dealing with rDNS.
#TODO
#Add more _BIN variables to decrease reliance on $PATH
#IPv6 support
#Finish domain validation function
#RDNS_QUERY_SERVER round-robin?
#variables
PROVIDERNAME=""
DEFAULT_RDNS="hosted-by.$PROVIDERNAME."
ZONE_LOCATION="/var/named/"
ZONE_TAIL="in-addr.arpa"
ZONE_FTAIL=".db"
SYNC_SCRIPT="/scripts/dnscluster synczone"
RDNS_QUERY_SERVER="8.8.4.4"
DIG_BIN="$(command -v dig)"
if [ $? -ne 0 -o -z "$DIG_BIN" ]; then
echo "Failed to locate 'dig'."
exit 1
fi
#functions
function usage () {
# Usage function. Obvious.
echo "$0 - Usage
$0 ip.address : Show current rDNS for the given IP address.
$0 ip.address dns.address : Set given IP address's rDNS to the given DNS address.
$0 -v ip.address dns.address : Set given IP's rDNS, and verify it after syncing.
$0 -nv ip.address dns.address : Set given IP's rDNS without checking forward DNS first.
$0 -r ip.address : Reset given IP's rDNS to the default ($DEFAULT_RDNS).
$0 -rS ip.address : Reset given IP's rDNS as -r does, but don't sync to DNS cluster.
$0 -R ip.address : Remove the given IP's rDNS entry altogether.
$0 -S ip.address : Sync the authoritative zone for the given IP address to the DNS cluster.
$0 [-h|--help] : Show this help text."
}
function validateIP () {
#validateIP - validates a given IP to ensure it isn't invalid.
local IP=$1
local stat=1
#Regex to do basic IP validation.
if [[ $IP =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
OIFS=$IFS
IFS="."
IP=($IP)
IFS=$OIFS
if [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]; then
stat=0
else
stat=1
fi
fi
return $stat
}
function convertIPToDNSZone () {
#Converts a given IP to an in-addr.arpa zone (ie, convertIPToDNSZone 192.210.132.5 would return "132.210.192.in-addr.arpa."
OIFS=$IFS
set `IFS=".";echo $1`
IFS=$OIFS
echo $3.$2.$1.$ZONE_TAIL
}
function getLastOctet () {
#Gets the last octet of an IP. Since all our IP ranges are /24s, this is okay.
OIFS=$IFS
set `IFS=".";echo $1`
IFS=$OIFS
echo $4
}
function validateDomain () {
#Validate a given domain to ensure it's a real domain.
#This will be implemented later. For now, just please make sure the PTR value is valid.
return 0
}
function setRDNS () {
#Modify the rDNS record for the given IP.
TARGET_IP=$1
NEW_PTR=$2
ZONEFILE=$(convertIPToDNSZone $TARGET_IP)
RECORD=$(getLastOctet $TARGET_IP)
if [ ! -w "$ZONE_LOCATION$ZONEFILE$ZONE_FTAIL" ]; then
echo "Failed to locate: $ZONE_LOCATION$ZONEFILE$ZONE_FTAIL"
return 1
fi
CUR_REC=$(getRDNS $TARGET_IP)
if [ $? -ne 0 ]; then
#we create the record
APPENDREC="$RECORD 14400 IN PTR $DEFAULT_RDNS"
#This is disabled for now. We warn, but do not touch.
echo "$APPENDREC" >> $ZONE_LOCATION$ZONEFILE$ZONE_FTAIL
echo "Warning: Record for $TARGET_IP did not exist, new record has been created."
CUR_REC=$(getRDNS $TARGET_IP)
fi
#I would use sed for this, but for some reason I can't get match groups to work in sed.
perl -pi.bak -e "s/^($RECORD\s+.*)\s$CUR_REC/\1\t$NEW_PTR/" $ZONE_LOCATION$ZONEFILE$ZONE_FTAIL
return 0
}
function getRDNS () {
#Retrieve the current rDNS record (if any) for the given IP
ZONEFILE=$(convertIPToDNSZone $1)
RECORD=$(getLastOctet $1)
if [ ! -w "$ZONE_LOCATION$ZONEFILE$ZONE_FTAIL" ]; then
echo "Failed to locate: $ZONE_LOCATION$ZONEFILE$ZONE_FTAIL"
return 1
fi
ZRECORD=$(egrep "^$RECORD\s.*PTR" $ZONE_LOCATION$ZONEFILE$ZONE_FTAIL)
if [ $? -ne 0 ]; then
echo "ZONEFILE: $ZONEFILE$ZONE_FTAIL ; LAST OCTET: $RECORD"
return 1
fi
echo $ZRECORD|perl -pe 's/^.*PTR\s+//'
return 0
}
function removeRDNS () {
TARGET_IP=$1
ZONEFILE=$(convertIPToDNSZone $TARGET_IP)
RECORD=$(getLastOctet $TARGET_IP)
CUR_PTR="$(getRDNS $TARGET_IP)"
if [ $? -ne 0 ]; then
echo "rDNS entry doesn't exist for $TARGET_IP!"
return 1
fi
perl -pi.bak -e "s/^($RECORD\s+.*\n)//" $ZONE_LOCATION$ZONEFILE$ZONE_FTAIL
}
function verifyRDNS () {
#Check that the rDNS was actually set, using 'host'.
HOSTOUT=$($DIG_BIN @$RDNS_QUERY_SERVER +short -x $1|sed 's/^[\s ]+//'|sed 's/[\s ]+$//')
if [ "$HOSTOUT" != "$2" ]; then
echo $HOSTOUT
return 1
fi
return 0
}
function checkForwardDNS () {
#Verify that forward DNS is set properly, using 'dig'. This specifically requests an A record, we won't need to worry about AAAA records for a while.
DIGOUT="$($DIG_BIN @$RDNS_QUERY_SERVER +short $1 A)"
if [ "$DIGOUT" != "$2" ]; then
return 1
fi
return 0
}
#main script
MODE=""
GOPTS=""
IPADDR=""
DESTDNS=""
ZONE_FILE=""
DO_SYNC=0
#Get options and arguments. I do this old school because I'm too lazy to l2getopts
if [ "$1" == "-h" -o "$1" == "-r" -o "$1" == "-v" -o "$1" == "-R" -o "$1" == "-nv" -o "$1" == "-rS" -o "$1" == "-S" ]; then
GOPTS="$1"
IPADDR="$2"
DESTDNS="$3"
else
IPADDR="$1"
DESTDNS="$2"
fi
#First, we ensure that DESTDNS ends with a period.
#We make sure the new PTR value ends with a period.
if [ ! -z "$DESTDNS" ]; then
TAILREC="$(echo $DESTDNS|tail -c2)"
if [ "$TAILREC" != "." ]; then
DESTDNS="$DESTDNS."
fi
fi
#First we get usage out of the way. Usage is shown if there are no options, or if the only option given is -h, or if $IPADDR is empty.
if [ -z "$IPADDR" -o "$GOPTS" == "-h" ]; then
usage
exit
fi
#Then we validate input. $IPADDR should -always- be an IP address, and $DESTDNS should always be a valid DNS address that exists.
validateIP $IPADDR
if [ $? -ne 0 ]; then
echo "Error: IP address given ($IPADDR) was not valid."
usage
exit 1
fi
#Now we can be sure the IP address given exists. Get our "mode" and then start working.
case $GOPTS in
-r)
MODE="RESET"
;;
-rS)
MODE="RESETNOSYNC"
;;
-R)
MODE="REMOVE"
;;
-S)
MODE="NOOP"
DO_SYNC="1"
;;
-v) MODE="SETANDVALIDATE"
;;
*)
if [ ! -z "$DESTDNS" ]; then
MODE="SET"
elif [ ! -z "$IPADDR" ]; then
MODE="GET"
fi
;;
esac
if [ "$MODE" == "SET" -a "$GOPTS" == "-nv" ]; then
MODE="SETNOVERIFY"
fi
#We do the work.
case $MODE in
RESET)
setRDNS $IPADDR $DEFAULT_RDNS
if [ $? -ne 0 ]; then
echo "Failed to set rDNS!"
exit 1
fi
echo "rDNS set!"
DO_SYNC=1
;;
RESETNOSYNC)
setRDNS $IPADDR $DEFAULT_RDNS
if [ $? -ne 0 ]; then
echo "Failed to set rDNS!"
exit 1
fi
echo "rDNS set!"
;;
GET)
RDNS=$(getRDNS $IPADDR)
if [ $? -ne 0 ]; then
echo "Failed to get rDNS for '$IPADDR'! Message: $RDNS"
exit 1
fi
echo "rDNS for '$IPADDR': $RDNS"
;;
SET)
checkForwardDNS $DESTDNS $IPADDR
if [ $? -ne 0 ]; then
echo "Forward DNS does not match requested reverse!"
exit 1
fi
setRDNS $IPADDR $DESTDNS
if [ $? -ne 0 ]; then
echo "Failed to set rDNS!"
exit 1
fi
echo "rDNS set!"
DO_SYNC=1
;;
SETNOVERIFY)
checkForwardDNS $DESTDNS $IPADDR
if [ $? -ne 0 ]; then
echo "Warning: Forward DNS does not match requested reverse! Continuing anyway.."
fi
setRDNS $IPADDR $DESTDNS
if [ $? -ne 0 ]; then
echo "Failed to set rDNS!"
exit 1
fi
echo "rDNS set!"
DO_SYNC=1
;;
SETANDVALIDATE)
checkForwardDNS $DESTDNS $IPADDR
if [ $? -ne 0 ]; then
echo "Forward DNS does not match requested reverse!"
exit 1
fi
setRDNS $IPADDR $DESTDNS
if [ $? -ne 0 ]; then
echo "Failed to set rDNS!"
exit 1
fi
echo "rDNS set!"
DO_SYNC=1
;;
REMOVE)
removeRDNS $IPADDR
if [ $? -ne 0 ]; then
echo "Failed to remove rDNS record!"
exit 1
fi
echo "rDNS record removed!"
DO_SYNC=1
;;
NOOP)
;;
*)
echo "Clearly, you used this script incorrectly."
usage
exit 1
;;
esac
if [ $DO_SYNC -eq 1 ]; then
$SYNC_SCRIPT $(convertIPToDNSZone $IPADDR)
if [ $? -ne 0 ]; then
echo "Sync failed!"
fi
if [ "$MODE" == "SETANDVALIDATE" ]; then
sleep 5
FORWARDDNS=$(verifyRDNS $IPADDR $DESTDNS)
if [ $? -ne 0 ]; then
echo "Current rDNS for $IPADDR ($FORWARDDNS) does not match '$DESTDNS'!"
else
echo "rDNS was properly synchronised and is set to '$DESTDNS'."
fi
fi
fi