Skip to content

Instantly share code, notes, and snippets.

@j0ju
Last active August 10, 2016 17:46
Show Gist options
  • Save j0ju/f9cf1d13899ecbfd82b7ec2079860fa0 to your computer and use it in GitHub Desktop.
Save j0ju/f9cf1d13899ecbfd82b7ec2079860fa0 to your computer and use it in GitHub Desktop.
/etc/bash_completion.d/pdnsutil
#!/bin/bash
# vim: ts=2 sw=2 et ft=sh
# self-calling editor for pdns-util edit-zone
# goal: prevent SOA SERIAL exhaustion
# usage: vizone <ZONE>
set -e
if [ "$0" != "$EDITOR" ]; then # not called from pdnsutil
EDITOR="$0" pdnsutil edit-zone ${1:+"$1"}
exit $?
fi
# cleanup after exit
cleanup() {
local rc=$?
rm -f "$TMP" "$DIFF"
exit $rc
}
trap cleanup EXIT HUP INT QUIT TERM
# working files
TMP="$(mktemp)"
DIFF="$(mktemp)"
# accept zone file to edit via $1
ZONE="$1"
# get current SOA serial
OLD_SOA="$( < "$ZONE" awk '$3 == "IN" && $4 == "SOA" {print $0; exit 0}' )"
if [ -z "$OLD_SOA" ]; then
echo "$0: no SOA found, something wrong, ABORT"
exit 1
fi >&2
OLD_SERIAL="$( < "$ZONE" awk '$3 == "IN" && $4 == "SOA" {print $7}' )"
if [ -z "$OLD_SERIAL" ]; then
echo "$0: no serial found, something wrong, ABORT"
exit 1
fi >&2
# evaluate next serial
# a) at first try current EPOCH, but EPOCH might be lower than OLD_SERIAL
# b) then YYYYMMDDnn
DATE="$(date +%Y%m%d)"
EPOCH="$(date +%s)"
SERIAL="$(( OLD_SERIAL + 1 ))" # Fallback and fafest Serial to use
# it will aotumagically result in YYYYMMDDnn for
# 99 edits a day
if [ "$EPOCH" -ge "$SERIAL" ]; then # is EPOCH greater equal than SERIAL use it.
SERIAL="$EPOCH"
elif [ "$DATE"00 -gt "$SERIAL" ]; then # okay not using EPOCH or OLD_SERIAL++,
SERIAL="$DATE"00 # advance to todays date generated serial
fi
# get editor, honour VISUAL as EDITOR is used by pdnsutil, fallback to vi
for editor in "$VISUAL" "$(which vim)" "$(which vi)" vi; do
[ -z "$editor" ] && continue
[ -x "$editor" ] && break || continue
done
# generate a canonical form of zonefile via sort
sort < "$ZONE" > "$TMP"
# edit zonefile
"$editor" "$ZONE"
# diff canonniocal outputs
rc=0
sort < "$ZONE" | diff -uw - "$TMP" > "$DIFF" || rc=$?
if [ $rc = 1 ]; then # there is at least a change in the zone file
# replace SOA with new SERIAL
< "$ZONE" awk '$3 == "IN" && $4 != "SOA" {print $0}' > "$TMP"
< "$ZONE" awk '$3 == "IN" && $4 == "SOA" && $7 = "'"$SERIAL"'" {print $0; exit 0}' >> "$TMP"
SERIAL="$( < "$TMP" awk '$3 == "IN" && $4 == "SOA" {print $7}' )"
if [ -z "$SERIAL" ]; then
echo "$0: someone deleted the SOA in new zone, something wrong, ABORT"
exit 1
else
cat "$TMP" > "$ZONE"
fi >&2
fi
echo
echo ---
# simple & stupid implementation of bash completion for bash
#
# put it into /etc/bash_completion.d/
#
# pdnsutil <TAB> - expands to known operations given as $1
# pdnsutil YOUNAMEIT <TAB> - completees to available zones, might be expensive with many (>10000) zones
#
have pdnsutil && {
_pdnsutil_helper_local_() {
local cur prev cmd
local _PDNSUTIL_ALL_CMDS="activate-tsig-key activate-zone-key add-record add-zone-key backend-cmd b2b-migrate bench-db check-zone check-all-zones clear-zone
create-bind-db create-slave-zone create-zone deactivate-tsig-key deactivate-zone-key delete-rrset delete-tsig-key delete-zone disable-dnssec
edit-zone export-zone-dnskey export-zone-key generate-tsig-key generate-zone-key get-meta hash-zone-record increase-serial import-tsig-key
import-zone-key load-zone list-algorithms list-keys list-zone list-all-zones list-tsig-keys rectify-zone rectify-all-zones remove-zone-key
replace-rrset secure-all-zones secure-zone set-kind set-nsec3 set-presigned set-publish-cdnskey set-publish-cds set-meta show-zone
unset-nsec3 unset-presigned unset-publish-cdnskey unset-publish-cds test-schema"
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
case "$prev" in
pdnsutil )
COMPREPLY=( $(compgen -W "$_PDNSUTIL_ALL_CMDS" -- $cur) )
return 0
;;
esac
case "$_PDNSUTIL_ALL_CMDS" in
"$prev "* | *" $prev "* | *" $prev" )
prevprev="${COMP_WORDS[COMP_CWORD-2]}"
COMPREPLY=( $(compgen -W "$($prevprev list-all-zones | head -n -1 )" -- $cur) )
return 0
;;
esac
}
complete -o default -F _pdnsutil_helper_local_ pdnsutil
}
# vim: ft=sh ts=s sw=2 et
have vizone && have pdnsutil && {
_vizone_helper_local_() {
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
case "$prev" in
vizone )
COMPREPLY=( $(compgen -W "$(pdnsutil list-all-zones | head -n -1 )" -- $cur) )
return 0
;;
esac
}
complete -o default -F _vizone_helper_local_ vizone
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment