#!/bin/bash # Need bash because we use ${foo//bar/baz} # # Example script illustrating how to write a dynamic BIND named # options file containing an up-to-date fowarders list. # # To use the dynamically generated file, include it from named.conf. # # This example was prepared for BIND version 8. For use with BIND 9 # the script should first be modified so that it generates only the # forwarders{} substatement, not the whole options{} statement. # Much safer. See http://bugs.debian.org/483098 for the request that # such a file be included in the bind9 package. # # Resolvconf may run this script even if named is not running. # If a bind package is installed then we go ahead and update # the named configuration in case named is started later. # # Assumption: On entry, PWD contains the resolv.conf-type files. # # Written by Thomas Hood <jdthood@gmail.com> # # Licensed under the GNU GPL. See /usr/share/doc/resolvconf/copyright. set -e PATH=/sbin:/bin [ -x /usr/sbin/named ] || exit 0 [ -x /lib/resolvconf/list-records ] || exit 1 [ -f /etc/bind/named.conf.options ] || exit 0 # Defaults NAMED_RUN_DIR=/var/run/bind # Defaults overrides [ -r /etc/default/resolvconf ] && . /etc/default/resolvconf [ "$NAMED_RUN_DIR" ] || exit 0 [ -d "$NAMED_RUN_DIR" ] || mkdir --parents --mode=0755 "$NAMED_RUN_DIR" DYNAMIC_OPTS_FILE=named.options # Stores arguments (minus duplicates) in RSLT, separated by spaces # Doesn't work properly if an argument itself contains whitespace uniquify() { RSLT="" while [ "$1" ] ; do for E in $RSLT ; do [ "$1" = "$E" ] && { shift ; continue 2 ; } done RSLT="${RSLT:+$RSLT }$1" shift done } # Get list of records, excluding all those for the loopback interface RSLVCNFFILES="$(/lib/resolvconf/list-records | sed -e '/^lo$/d' -e '/^lo[.]/d')" ### Compile semicolon-separated list nameservers ### NMSRVRS="" if [ "$RSLVCNFFILES" ] ; then uniquify $(sed -n -e 's/^[[:space:]]*nameserver[[:space:]]\+//p' $RSLVCNFFILES) [ "$RSLT" ] && NMSRVRS="${RSLT// /; }; " fi # N.B.: After changing directory we no longer have access to the resolv.conf-type files cd "$NAMED_RUN_DIR" TMP_FILE="${DYNAMIC_OPTS_FILE}_new.$$" clean_up() { rm -f "${NAMED_RUN_DIR}/$TMP_FILE" ; } trap clean_up EXIT rm -f "$TMP_FILE" # We want to process named.conf.options such that the new forwarders # statement gets inserted but nothing else is corrupted in the process. # We want to do this using only commands available in /bin and /sbin, i.e., # with sh, sed and/or grep. Sed can be made to work -- with difficulty. # Even so, the following script does not work properly if comment # delimiters of one style of commenting appear inside another kind of # comment. (Named supports C, C++ and sh comment styles.) # # First, we do our best to delete all and only comments. # Then we delete any existing forwarders statement, taking into account # the fact that these can span several lines. Then we add a new # forwarders statement at the beginning of the options statement. # echo "// named.conf fragment automatically generated by $0" > "$TMP_FILE" echo "// DO NOT EDIT THIS FILE. Instead edit /etc/bind/named.conf.options ." >> "$TMP_FILE" cat /etc/bind/named.conf.options \ | sed -e 's%\*/%\*/\ %g' \ | sed -e '\%/\*%{ :x ; s%\*/%\*/% ; t y ; N ; b x ; :y ; s%/\*.*\*/%% ; }' \ | sed -e 's%//.*%%' -e 's%#.*%%' \ | sed -e '/forwarders/{ :x ; s/}/}/ ; t y ; N ; b x ; :y ; s/}[[:space:]]*;/};/ ; t z ; N ; b y ; :z s/forwarders[[:space:]]*{[^}]*};// ; }' \ | sed -e 's/options[[:space:]]*{/options {\ forwarders { '"${NMSRVRS}"'};/' | sed -e '/^[[:space:]]*$/{ d ; }' \ >> "$TMP_FILE" # Reload named unless we know its options haven't changed if [ -x /usr/bin/diff ] && [ -f "$DYNAMIC_OPTS_FILE" ] && /usr/bin/diff -q "$DYNAMIC_OPTS_FILE" "$TMP_FILE" > /dev/null ; then # No change rm -f "$TMP_FILE" else mv -f "$TMP_FILE" "$DYNAMIC_OPTS_FILE" [ -x /etc/init.d/bind9 ] && /etc/init.d/bind9 reload > /dev/null 2>&1 || : fi