320 lines
7.9 KiB
Bash
Executable File
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
|