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

# dnsmasq subscriber for resolvconf

# 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.

# This is very important!
# We assume that we are a local dns cache - after all, why would a server
# use resolvconf?
# Now that we have assumed this, we also assume that generic DHCP clients
# will enter their domains and search domains ONLY in the "search" field
# in their resolv.confs and VPN clients will put the domain they are for
# into the domain field only.
# This allows dnsmasq to forward domains for a specific VPN domain to the
# VPN nameserver and everything else to the standard name servers.

# A sample dnsmasq config that works as above is like so.
# NOTE: The loopback interface on some systems maybe lo0.
#domain-needed
#interface=lo
#resolv-file=/etc/dnsmasq-resolv.conf
#conf-file=/etc/dnsmasq-resolvconf.conf

# The last step is to configure dns configuration for /etc/resolv.conf
# for the lo interface. You can do this in resolvconf as well by adding
#nameserver 127.0.0.1
# to resolv.conf.d/base

# Load our variables from resolvconf
VARS="$(resolvconf -v)"
eval "${VARS}"

PREFIX="/usr/local"
DNSMASQRESOLV="${PREFIX}/etc/dnsmasq-resolv.conf"
DNSMASQCONF="${PREFIX}/etc/dnsmasq-resolvconf.conf"

NEWCONF="# Generated by resolvconf\n"
NEWRESOLV="${NEWCONF}"

# Using DBUS means that we never have to restart the daemon
# This is important as it means we should not drop DNS queries
# whilst changing DNS options around. However, DBUS support is optional
# so we need to validate a few things first.
# Check for DBus support in the binary
DBUS=no
dbuspid=/var/run/dbus/dbus.pid
[ -s "${dbuspid}" ] || dbuspid=/var/run/dbus.pid
if [ -s "${dbuspid}" -a -s /var/run/dnsmasq.pid ]; then
	if dnsmasq --version 2>/dev/null | \
		grep -q "^Compile time options.*[[:space:]]DBus[[:space:]]" \
		; then
		# Sanity - check that dnsmasq and dbus are running
		if kill -0 $(cat "${dbuspid}") \
			&& kill -0 $(cat /var/run/dnsmasq.pid); then
			DBUS=yes
			NEWCONF="${NEWCONF}\n# Domain specific servers will be sent over dbus\nenable-dbus\n"
		fi
	fi
fi

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

# If we only have domain information then put it in search too
[ -z "${NEWSEARCH}" -a -z "${NEWNS}" ] && NEWSEARCH="${NEWDOMAIN}"

for N in ${NEWSEARCH}; do
	case " ${NEWSL} " in
		*" ${N%,*} "*);;
		*) NEWSL="${NEWSL} ${N%,*}";;
	esac
	case "\n${NEWRESOLV}\n" in
		*"\nnameserver ${N#*,}\n"*);;
		*) NEWRESOLV="${NEWRESOLV}nameserver ${N#*,}\n";;
	esac
done
for N in ${NEWNS}; do
	case "\n${NEWRESOLV}\n" in
		*"\nnameserver ${N}\n");;
		*) NEWRESOLV="${NEWRESOLV}nameserver ${N}\n";;
	esac
done
[ -n "${NEWSL}" ] && NEWRESOLV="${NEWRESOLV}search${NEWSL}\n"

DBUSDEST=
for DN in $(uniqify ${NEWDOMAIN}); do
	if [ "${DBUS}" = "yes" ]; then
		IP=${DN#*,}
		SIFS=${IFS-y} OIFS=$IFS
		IFS=.
		set -- ${IP}
		NUM="0x$(printf "%02x" $1 $2 $3 $4)"
		if [ "${SIFS}" = "y" ]; then
			unset IFS
		else
			IFS=$OIFS
		fi
		DBUSDEST="${DBUSDEST} uint32:$(printf "%d" ${NUM}) string:${DN%,*}"
	else
		NEWCONF="${NEWCONF}server=/${DN%,*}/${DN#*,}\n"
	fi
done

RELOAD="no"
if [ -e "${DNSMASQCONF}" ]; then
	if [ "$(cat "${DNSMASQCONF}")" != "$(printf "${NEWCONF}")" ]; then
		RELOAD="yes"
		printf "${NEWCONF}" > "${DNSMASQCONF}"
	fi
else
	RELOAD="yes"
	printf "${NEWCONF}" > "${DNSMASQCONF}"
fi
if [ -e "${DNSMASQRESOLV}" ]; then
	if [ "$(cat "${DNSMASQRESOLV}")" != "$(printf "${NEWRESOLV}")" ]; then
		RELOAD="yes"
		printf "${NEWRESOLV}" > "${DNSMASQRESOLV}"
	fi
else
	# dnsmasq polls this file so no need to set RELOAD="yes"
	printf "${NEWRESOLV}" > "${DNSMASQRESOLV}"
fi

[ "${RELOAD}" = "yes" ] && resolvconf -s dnsmasq restart
if [ "${DBUS}" = "yes" ]; then
	[ "${RELOAD}" != "yes" ] && resolvconf -s dnsmasq reload
	# Send even if emtpy so old servers are cleared
	dbus-send --system --dest=uk.org.thekelleys.dnsmasq \
 		/uk/org/thekelleys/dnsmasq uk.org.thekelleys.SetServers \
  		${DBUSDEST}
fi

# vim: ts=4 :
