#!/bin/sh
# Copyright (c) 2010-2023 Roy Marples
# All rights reserved

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

[ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0
. "@SYSCONFDIR@/resolvconf.conf" || exit 1
[ -z "${pdnsd_conf}${pdnsd_resolv}" ] && exit 0
[ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)"
NL="
"

: ${pdnsd_restart:=pdnsd-ctl config $pdnsd_conf}
signature="# Generated by resolvconf"
signature_end="# End of resolvconf"

# We normally use sed to remove markers from a configuration file
# but sed may not always be available at the time.
remove_markers()
{
	m1="$1"
	m2="$2"
	in_marker=0

	shift; shift
	if command -v sed >/dev/null 2>&1; then
		sed "/^$m1/,/^$m2/d" $@
	else
		for x do
			while read line; do
				case "$line" in
				"$m1"*) in_marker=1;;
				"$m2"*) in_marker=0;;
				*) [ $in_marker = 0 ] && echo "$line";;
				esac
			done < "$x"
		done
	fi
}

# Compare two files
# If different, replace first with second otherwise remove second
change_file()
{
	if [ -e "$1" ]; then
		if command -v cmp >/dev/null 2>&1; then
			cmp -s "$1" "$2"
		elif command -v diff >/dev/null 2>&1; then
			diff -q "$1" "$2" >/dev/null
		else
			# Hopefully we're only working on small text files ...
			[ "$(cat "$1")" = "$(cat "$2")" ]
		fi
		if [ $? -eq 0 ]; then
			rm -f "$2"
			return 1
		fi
	fi
	cat "$2" > "$1"
	rm -f "$2"
	return 0
}

newresolv="# Generated by resolvconf$NL"
changed=false

# Try to ensure that config dirs exist
if command -v config_mkdirs >/dev/null 2>&1; then
	config_mkdirs "$pdnsd_resolv" "$pdnsd_conf"
else
	@SBINDIR@/resolvconf -D "$pdnsd_resolv" "$pdnsd_conf"
fi

if [ -n "$pdnsd_resolv" ]; then
	for n in $NAMESERVERS; do
		newresolv="${newresolv}nameserver $n$NL"
	done
fi

# Only modify the configuration if it exists and we can write to it
if [ -w "$pdnsd_conf" ]; then
	cf="$pdnsd_conf.new"
	newconf=

	if [ -z "$pdnsd_resolv" ]; then
		newconf="${newconf}server {$NL"
		newconf="${newconf}	label=resolvconf;$NL"
		if [ -n "$NAMESERVERS" ]; then
			newconf="${newconf}	ip="
			first=true
			for n in $NAMESERVERS; do
				if $first; then
					first=false
				else
					newconf="${newconf},"
				fi
				newconf="$newconf$n"
			done
			newconf="${newconf};$NL"
		fi
		newconf="${newconf}}$NL"
	fi

	for d in $DOMAINS; do
		newconf="${newconf}server {$NL"
		newconf="${newconf}	include=.${d%%:*}.;$NL"
		newconf="${newconf}	policy=excluded;$NL"
		newconf="${newconf}	ip="
		ns="${d#*:}"
		while [ -n "$ns" ]; do
			newconf="${newconf}${ns%%,*}"
			[ "$ns" = "${ns#*,}" ] && break
			ns="${ns#*,}"
			newconf="${newconf},"
		done
		newconf="${newconf};$NL}$NL"
	done

	rm -f "$cf"
	remove_markers "$signature" "$signature_end" "$pdnsd_conf" > "$cf"
	if [ -n "$newconf" ]; then
		echo "$signature" >> "$cf"
		printf %s "$newconf" >> "$cf"
		echo "$signature_end" >> "$cf"
	fi
	if change_file "$pdnsd_conf" "$cf"; then
		changed=true
	fi
fi

if [ -n "$pdnsd_resolv" ]; then
	if [ ! -f "$pdnsd_resolv" ] || \
		[ "$(cat "$pdnsd_resolv")" != "$(printf %s "$newresolv")" ]
	then
		changed=true
		printf %s "$newresolv" >"$pdnsd_resolv"
	fi
fi

if $changed; then
	eval $pdnsd_restart
fi
