From feb80efcbeb0d82f3f91c9b7b66901207ff7cecc Mon Sep 17 00:00:00 2001 From: Matthew Saunders Brown Date: Wed, 23 Aug 2023 15:42:06 -0700 Subject: [PATCH] add pdns-ptr-set.sh and pdns-zone-ttl.sh --- bin/pdns-ptr-set.sh | 82 ++++++++++++++++++++++ bin/pdns-zone-ttl.sh | 157 +++++++++++++++++++++++++++++++++++++++++++ bin/pdns.sh | 26 ++++++- 3 files changed, 264 insertions(+), 1 deletion(-) create mode 100755 bin/pdns-ptr-set.sh create mode 100755 bin/pdns-zone-ttl.sh diff --git a/bin/pdns-ptr-set.sh b/bin/pdns-ptr-set.sh new file mode 100755 index 0000000..8a69153 --- /dev/null +++ b/bin/pdns-ptr-set.sh @@ -0,0 +1,82 @@ +#!/bin/bash +# +# pdns-tools +# https://git.stack-source.com/msb/pdns-tools +# Copyright (c) 2023 Matthew Saunders Brown +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# load include file +source $(dirname $0)/pdns.sh + +help() +{ + thisfilename=$(basename -- "$0") + echo "$thisfilename" + echo "Set/update PTR Record." + echo "" + echo "usage: $thisfilename -i [-r ] [-l ] [-s ] [-c ] [-a ] [-h]" + echo "" + echo " -h Print this help." + echo " -i IP address to set PTR record for." + echo " -r Data/value (hostname) of PTR record, optional." + echo " -l TTL, optional, defaults to $zone_defaults_ttl." + echo " -s <0|1> Status, optional. O (default) for active or 1 for disabled." + echo " -c An optional comment/note about the record." + echo " -a The account that the comment gets attributed too." + echo " Only used if comment is set. Optional, defaults to hostname of server running this script." + echo + echo " This script sets or updates the PTR record of a given IP." + echo " The should be a FQDN (Fully Qualified Domain Name) or left blank." + echo " If the is blank then a default value will be set, effectively re-setting the PTR record to it's default." +} + +pdns:getoptions "$@" + +# check for IP +if [[ -z $ip ]]; then + echo "IP is required" + exit +fi + +# set zone & name +OIFS=$IFS +IFS='.' +ip_parts=($ip) +IFS=$OIFS +zone=${ip_parts[2]}.${ip_parts[1]}.${ip_parts[0]}.in-addr.arpa. +name=${ip_parts[3]}.$zone + +# check that zone exists +if pdns-zone-ext.sh -z $zone >> /dev/null; then + + # zone exists, build & execute command + cmd="pdns-rr-rep.sh -z $zone -n $name -t PTR" + + # check for record data, create default if unset + if [[ -z $record ]]; then + record=${ip_parts[0]}-${ip_parts[1]}-${ip_parts[2]}-${ip_parts[3]}.in-addr.arpa.`dig +short $zone soa|cut -d ' ' -f 2|cut -d "." -f 2-` + fi + cmd="$cmd -r $record" + + if [[ -n $ttl ]]; then + cmd="$cmd -l $ttl" + fi + + if [[ -n $status ]]; then + cmd="$cmd -s $status" + fi + + if [[ -n $comment ]]; then + cmd="$cmd -c $comment" + fi + + if [[ -n $account ]]; then + cmd="$cmd -a $account" + fi + + echo $cmd + +else + echo "Zone for $zone does not exist, can't set PTR." +fi + diff --git a/bin/pdns-zone-ttl.sh b/bin/pdns-zone-ttl.sh new file mode 100755 index 0000000..04e6d03 --- /dev/null +++ b/bin/pdns-zone-ttl.sh @@ -0,0 +1,157 @@ +#!/bin/bash +# +# pdns-tools +# https://git.stack-source.com/msb/pdns-tools +# Copyright (c) 2022 Matthew Saunders Brown +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# load include file +source $(dirname $0)/pdns.sh + +help() +{ + thisfilename=$(basename -- "$0") + echo "$thisfilename" + echo "Replace Resource Record set in zone." + echo "" + echo "usage: $thisfilename -z -n -t -r [-l ] [-s ] [-c ] [-a ] [-h]" + echo "" + echo " -h Print this help." + echo " -z Zone (domain name) to modify TTL." + echo " -l TTL to set all records to." + echo + echo " Adjust TTL for all records of the given zone (domain name)." + echo " Typical ranges are from 300 (5 minutes) to 86400 (1 day)" + echo " with 'normal settings being 3600 (1 hour)." +} + +/usr/bin/curl --silent --output /dev/null --write-out "%{http_code}" -H "X-API-Key: $api_key" "$api_base_url/zones/$zone?rrsets=false" + +/usr/bin/curl --silent --output /dev/null --write-out "%{http_code}" -H "X-API-Key: aejahluadoolap6zootoochoib9Xooyi" "https://pdnsapi.jcihosting.net/api/v1/servers/localhost/zones/lamphost.com?rrsets=true" + + GET /servers/{server_id}/zones/{zone_id}ΒΆ + + + +pdns:getoptions "$@" + +# check for zone, make sure it ends with a . +if [[ -z $zone ]]; then + echo "zone is required" + exit +elif [[ $zone != *\. ]]; then + zone="$zone." +fi + +# check for name, make sure it ends with a . +if [[ -z $name ]]; then + echo "name is required" + exit +elif [[ $name = "@" ]]; then + name=$zone +elif [[ $name != *\. ]]; then + name="$name." +fi + +# make sure name is equal to or part of zone +if [[ $name != $zone ]] && [[ $name != *\.$zone ]]; then + name="$name$zone" +fi + +# check for type +if [[ -z $type ]]; then + echo "type is required" + exit +fi + +# check for record data +if [[ -z $record ]]; then + echo "record is required" + exit +fi + +# check for ttl +if [[ -z $ttl ]]; then + ttl=$zone_defaults_ttl +fi + +# first query to see if zone already exists +zone_status=$(/usr/bin/curl --silent --output /dev/null --write-out "%{http_code}" -H "X-API-Key: $api_key" "$api_base_url/zones/$zone?rrsets=false") + +if [[ $zone_status = 200 ]]; then + # verified zone exists, add record(s) + + data="{\"rrsets\":[{\"name\":\"$name\",\"type\":\"$type\",\"ttl\":$ttl,\"changetype\":\"REPLACE\",\"records\":[" + + # turn record in to array of records + orig_ifs="$IFS" + IFS='|' + read -r -a resourcerecords <<< "$record" + IFS="$orig_ifs" + + # get number of records in set + resourcerecords_records_count=${#resourcerecords[@]} + records_count=0 + + for resourcerecord in "${resourcerecords[@]}"; do + + records_count=$((records_count+1)) + + # make sure hostnames end in a . + declare -a types_that_require_dot=("CNAME MX NS PTR SRV") + if [[ " ${types_that_require_dot[*]} " =~ " ${type} " ]]; then + if [[ $resourcerecord != *\. ]]; then + resourcerecord="$resourcerecord." + fi + fi + + # quote TXT records + if [[ $type = "TXT" ]]; then + resourcerecord="\\\"$resourcerecord\\\"" + fi + + # set disabled status + if [[ $status = 1 ]]; then + disabled=true + else + disabled=false + fi + + data="$data{\"content\":\"$resourcerecord\",\"disabled\":$disabled}" + + if [[ $records_count < $resourcerecords_records_count ]]; then + data="$data," + else + data="$data]" + fi + + done + + # add comment, if set + if [[ -n $comment ]]; then + # set account to hostname if not specified with -a option + if [[ -z $account ]]; then + account=$(/usr/bin/hostname -f) + fi + data= "$data,\"comments\":[{\"content\":\"$comment\",\"account\":\"$account\"}]" + fi + + # close out json string + data="$data}]}" + + # add record(s) + zone_status=$(/usr/bin/curl --silent --request PATCH --output /dev/null --write-out "%{http_code}" --header "X-API-Key: $api_key" --data "$data" "$api_base_url/zones/$zone") + + if [[ $zone_status = 204 ]]; then + echo "Success. Record(s) for $zone created/updated." + else + echo "Error. http response updating record(s) for $zone was: $zone_status" + fi + +elif [[ $zone_status = 404 ]]; then + + echo "Zone $zone does not exist, can't update records." + +else + echo "Unexpected http response checking for Zone $zone: $zone_status" +fi diff --git a/bin/pdns.sh b/bin/pdns.sh index 4b3fd93..8ba42f5 100755 --- a/bin/pdns.sh +++ b/bin/pdns.sh @@ -80,6 +80,23 @@ function pdns::validate_domain () { fi } +# crude but good enough IP address format validation +function pdns::validate_ip() +{ + local ip=$1 + local status=1 + + 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 + [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] + status=$? + fi + return $status +} + # yesno prompt # # Examples: @@ -122,7 +139,7 @@ function pdns::yesno() { function pdns:getoptions () { local OPTIND - while getopts "hbz:d:m:t:a:n:r:c:l:s:x" opt ; do + while getopts "hbz:d:i:m:t:a:n:r:c:l:s:x" opt ; do case "${opt}" in h ) # display help and exit help @@ -153,6 +170,13 @@ function pdns:getoptions () { exit fi ;; + i ) # IP + ip=${OPTARG} + if ! pdns::validate_ip $ip; then + echo "ERROR: $ip is not a valid IP address." + exit + fi + ;; m ) # master - IP of master if this is a slave zone master=${OPTARG} ;;