#!/bin/sh
# Copyright 2007 Roy Marples
# All rights reserved

# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above
#       copyright notice, this list of conditions and the following
#       disclaimer in the documentation and/or other materials provided
#       with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

argv0="$0"

PREFIX="/usr/local"
RESOLVCONF="${PREFIX}"/etc/resolvconf
UPDATED="${RESOLVCONF}"/update.d
VARDIR="${RESOLVCONF}"/run
IFACEDIR="${VARDIR}/interfaces"

error_exit() {
	echo "$*" >&2
	exit 1
}

usage() {
	cat <<-EOF
	Usage: ${argv0##*/} [options]

	Inform the system about any DNS updates.

	Options:
	  -a \$INTERFACE    Add DNS information to the specified interface
	                   (DNS supplied via stdin in resolv.conf format)
	  -d \$INTERFACE    Delete DNS information from the specified interface
	  -u               Run updates from our current DNS information
	  -l [\$PATTERN]    Show DNS information, optionally from interfaces
	                   that match the specified pattern
	  -i [\$PATTERN]    Show interfaces that have supplied DNS information
                   optionally from interfaces that match the specified
                   pattern
	  -v [\$PATTERN]    echo NEWDOMAIN, NEWSEARCH and NEWNS variables to
	  		   the console
	  -s \$SVC \$CMD     Do \$CMD for the system service \$SVC
	  -h               Show this help cruft
	EOF
	[ -z "$@" ] && exit 0
	echo
	error_exit "$*"
}

echo_resolv() {
	[ -n "$1" -a -e "${IFACEDIR}/$1" ] || return 1
	echo "# resolv.conf for interface $1"
	cat "${IFACEDIR}/$1"
	echo
}

uniqify() {
    local result=
    while [ -n "$1" ]; do
		case " ${result} " in
			*" $1 "*);;
			*) result="${result} $1";;
		esac
		shift
	done
    echo "${result# *}"
}

if [ -n "$1" ]; then
	CMD="$1"
	shift
fi

# We do our service restarting here so that our subscribers don't have to know
# about the OS's init system.
if [ "x${CMD}" = "x-s" ]; then
	if [ -n "$1" ]; then
		SERVICE="$1"
		shift
	fi
	[ -z "${SERVICE}" ] && usage "Service not specified"
	if [ -n "$1" ]; then
		ACTION="$1"
		shift
	fi
	[ -z "${ACTION}" ] && usage "Action not specified"

	# If restarting check if service is running or not if we can
	if [ "x${ACTION}" = "xrestart" ]; then
		if [ -s /var/run/"${SERVICE}".pid ]; then
			kill -0 $(cat /var/run/"${SERVICE}".pid)
		elif [ -s /var/run/"${SERVICE}"/"${SERVICE}".pid ]; then
			kill -0 $(cat /var/run/"${SERVICE}".pid)
		elif [ -s /var/run/"${SERVICE}"/pid ]; then
			kill -0 $(cat /var/run/"${SERVICE}"/pid)
		else
			false
		fi
		# Service not running, so don't restart
		[ $? != 0 ] && exit 1
	fi	
	if [ -x /sbin/service ]; then
		service "${SERVICE}" "${ACTION}" "$@" 
	elif [ -x /etc/init.d/"${SERVICE}" -a -x /sbin/runscript ]; then
		if [ "x${ACTION}" = "xrestart" ]; then
			/etc/init.d/"${SERVICE}" --quiet --nodeps conditionalrestart "$@"
		else
			/etc/init.d/"${SERVICE}" --quiet --nodeps "${ACTION}" "$@"
		fi
	elif [ -x /etc/init.d/"${SERVICE}" ]; then
		/etc/init.d/"${SERVICE}" "${ACTION}" "$@"
	elif [ -x /etc/rc.d/"${SERVICE}" ]; then
		/etc/rc.d/"${SERVICE}" "${ACTION}" "$@" 
	elif [ -x /etc/rc.d/rc."${SERVICE}" ]; then
		/etc/rc.d/rc."${SERVICE}" "${ACTION}" "$@"
	else
		error_exit "Don't know how to interact with services on this platform"
	fi
	exit $?
fi

if [ -n "$1" ]; then
	IFACE="$1"
	shift
fi

# -l lists our resolv files, optionally for a specific interface
if [ "x${CMD}" = "x-l" -o "x${CMD}" = "x-i" ]; then
	[ -d "${IFACEDIR}" ] || exit 0
	
	# If we have an interface ordering list, then use that.
	# It works by just using pathname expansion in the interface directory.
	if [ -n "${IFACE}" ]; then
		LIST="${IFACE} $@"
	elif [ -r "${RESOLVCONF}"/interface-order ]; then
		LIST="$(cat "${RESOLVCONF}"/interface-order)"
	fi

	# If we don't have a list then prefer lo, tunnels, ppp
	# and then anything else.
	if [ -z "${LIST}" ]; then
		LIST="lo lo[0-9]* tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]* *"
	fi

	cd "${IFACEDIR}"
	for IFACE in $(uniqify ${LIST}); do
		# Only list interfaces which we really have
		[ -e "${IFACE}" ] || continue
		
		if [ "x${CMD}" = "x-i" ]; then
			printf "${IFACE} "
		else
			echo_resolv "${IFACE}"
		fi
	done
	[ "x${CMD}" = "x-i" ] && echo
	exit 0
fi

if [ "x${CMD}" = "x-v" ]; then
	NS=
	DOMAIN=
	SEARCH=
	NEWSEARCH=
	NEWNS=
	NEWDOMAIN=
	LINES="$("${argv0}" -l "${IFACE}" | sed -e "s/'/'\\\\''/g" -e "s/^/'/g" -e "s/$/'/g")"
	eval set -- ${LINES}
	for LINE in "$@"; do
		case "${LINE}" in
			"nameserver "*)
				case "${LINE#* }" in
					127.*) continue;;
				esac
				NS="${NS}${LINE#* } "
				;;
			"domain "*)
				[ -z "${SEARCH}" ] && DOMAIN="${LINE#* }"
				;;
			"search "*)
				SEARCH="${LINE#* }"
				DOMAIN=
				;;
			*)
				if [ -z "${LINE}" ]; then
					for N in ${NS}; do
						if [ -n "${DOMAIN}" ]; then
							NEWDOMAIN="${NEWDOMAIN} ${DOMAIN},${N}"
						elif [ -n "${SEARCH}" ]; then
							for S in ${SEARCH}; do
								NEWSEARCH="${NEWSEARCH} ${S},${N}"
							done
						else
							NEWNS="${NEWNS} ${N}"
						fi
					done
					NS=
					DOMAIN=
					SEARCH=
				fi
				;;
		esac
	done

	# Prefer DOMAIN nameservers over SEARCH nameservers
	# if we are supplied both.
	NEWDOMAIN="$(uniqify ${NEWDOMAIN})"
	NEWSEARCH="$(uniqify ${NEWSEARCH})"
	NEWNS="$(uniqify ${NEWNS})"
	for S in ${NEWSEARCH}; do
		for DN in ${NEWDOMAIN}; do
			if [ "${S%,*}" = "${DN%,*}" ]; then
				NEWSEARCH="$(echo "${NEWSEARCH}" | sed -e "s/${S}/${DN}/g")"
				NEWDOMAIN="$(echo "${NEWDOMAIN}" | sed -e "s/${DN}//g")"
				break
			fi
		done
	done

	echo "NEWDOMAIN='${NEWDOMAIN}'"
	echo "NEWSEARCH='${NEWSEARCH}'"
	echo "NEWNS='${NEWNS}'"
	exit 0
fi

# Test that we have valid options
if [ "x${CMD}" = "x-a" -o "x${CMD}" = "x-d" ]; then
	if [ -z "${IFACE}" ]; then
		usage "Interface not specified"
	fi
elif [ "x${CMD}" != "x-u" ]; then
	[ -n "x${CMD}" -a "x${CMD}" != "x-h" ] && usage "Unknown option ${CMD}"
	usage
fi
if [ "x${CMD}" = "x-a" ]; then
	for x in '/' \\ ' ' '*'; do
		case "${IFACE}" in
			*[${x}]*) error_exit "${x} not allowed in interface name";;
		esac
	done
	for x in '.' '-' '~'; do
		case "${IFACE}" in
			[${x}]*) error_exit "${x} not allowed at start of interface name";;
		esac
	done
	[ "x${CMD}" = "x-a" -a -t 0 ] && error_exit "No file given via stdin"
	IFACERESOLV="${IFACEDIR}/${IFACE}"
fi

# Ensure that libdir exists
if [ ! -d "${IFACEDIR}" ]; then
	if [ ! -d "${VARDIR}" ]; then
		if [ -L "${VARDIR}" ]; then
			DIR="$(readlink "${VARDIR}")"
			# Change to /etc as link maybe relative
			cd "${VARDIR%/*}"
			if ! mkdir -m 0755 -p "${DIR}"; then
				error_exit "Failed to create needed directory ${DIR}"
			fi
		else
			if ! mkdir -m 0755 -p "${VARDIR}"; then
				error_exit "Failed to create needed directory ${VARDIR}"
			fi
		fi
	fi
	mkdir -m 0755 -p "${IFACEDIR}" || \
		error_exit "Failed to create needed directory ${IFACEDIR}"
else
	# Delete any existing information about the interface
	if [ "x${CMD}" = "x-a" -o "x${CMD}" = "x-d" ]; then
		cd "${IFACEDIR}"
		for iface in ${IFACE}; do
			rm -f "${iface}" || exit $?
		done
	fi
fi

if [ "x${CMD}" = "x-a" ]; then
	# Create our resolv.conf file
	cat >"${IFACEDIR}"/"${IFACE}" || exit $?
fi

retval=0
for x in "${UPDATED}"/*; do
	if [ -e "${x}" ]; then
		"${x}" "${CMD}" "${IFACE}"
		retval=$((${retval} + $?))
	fi
done

exit ${retval}

# vim: set ts=4 :
