Subject: [PATCH] Added support for split-dns if unbound or dnsmasq are present.

From: Nikos Mavrogiannopoulos <[email-masked]>
Date: Thu, 8 Jan 2015 14:22:27 +0100

Note that for dnsmasq we only modify /etc/dnsmasq.d (or the equivalent
directory in conf-dir) if an empty file /etc/dnsmasq.d/allow-vpnc-script
exists.

Signed-off-by: Nikos Mavrogiannopoulos <[email-masked]>
---
 vpnc-script | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 89 insertions(+), 7 deletions(-)

diff --git a/vpnc-script b/vpnc-script
index 2a38bcd..928f9b2 100755
--- a/vpnc-script
+++ b/vpnc-script
@@ -64,9 +64,14 @@
 # 4) Replies to a dhcp-server should never be sent into the tunnel
 
 # Section B: Split DNS handling
-
-# 1) Maybe dnsmasq can do something like that
-# 2) Parse dns packets going out via tunnel and redirect them to original dns-server
+#
+# This is only handled if unbound or dnsmasq are setup and are running as 
+# local resolvers. For systems with dnsmasq ensure that an empty 
+# '/etc/dnsmasq.d/allow-vpnc-script' file is present. That file instructs
+# vpnc-script to make config changes and restart dnsmasq.
+#
+# In that the case where dnsmasq or unbound are detected we parse CISCO_SPLIT_DNS 
+# set the split-dns settings.
 
 #env | sort
 #set -x
@@ -116,6 +121,9 @@ else
 	ifconfig_syntax_ptpv6=""
 fi
 
+/usr/sbin/unbound-control status > /dev/null 2>&1
+UNBOUND_STATUS=$?
+
 if [ -r /etc/openwrt_release ] && [ -n "$OPENWRT_INTERFACE" ]; then
         . /etc/functions.sh
 	include /lib/network
@@ -130,7 +138,7 @@ elif [ -x /sbin/netconfig ]; then # tool on Suse after 11.1
 elif [ -x /sbin/modify_resolvconf ]; then # Mandatory tool on Suse earlier than 11.1
 	MODIFYRESOLVCONF=modify_resolvconf_suse
 	RESTORERESOLVCONF=restore_resolvconf_suse
-elif [ -x /usr/sbin/unbound-control ] && /usr/sbin/unbound-control status > /dev/null 2>&1; then
+elif [ -x /usr/sbin/unbound-control ] && [ $UNBOUND_STATUS = 0 ]; then
 	MODIFYRESOLVCONF=modify_resolvconf_unbound
 	RESTORERESOLVCONF=restore_resolvconf_unbound
 else # Generic for any OS
@@ -138,6 +146,29 @@ else # Generic for any OS
 	RESTORERESOLVCONF=restore_resolvconf_generic
 fi
 
+# we do restart dnsmasq because conf-dir is not reloaded on SIGHUP
+if [ -e /etc/init.d/dnsmasq ];then
+	RESTARTDNSMASQ="/etc/init.d/dnsmasq restart"
+	STATUSDNSMASQ="/etc/init.d/dnsmasq status"
+else
+	RESTARTDNSMASQ="systemctl restart dnsmasq"
+	STATUSDNSMASQ="systemctl status dnsmasq"
+fi
+
+if [ -x /usr/sbin/unbound-control ] && [ $UNBOUND_STATUS = 0 ]; then
+	MODIFYSPLITDNS=setup_split_dns_unbound
+	RESTORESPLITDNS=remove_split_dns_unbound
+elif [ -x /usr/sbin/dnsmasq -a -f /etc/dnsmasq.conf ];then
+	# only modify dnsmasq if it is running
+	$STATUSDNSMASQ
+	if [ $? = 0 ];then
+		DNSMASQ_DIR=`grep "^conf-dir=" /etc/dnsmasq.conf|sed 's/conf-dir=//g'|cut -d ',' -f1`
+		if [ -n "$DNSMASQ_DIR" -a -f "$DNSMASQ_DIR"/allow-vpnc-script ];then
+			MODIFYSPLITDNS=setup_split_dns_dnsmasq
+			RESTORESPLITDNS=remove_split_dns_dnsmasq
+		fi
+	fi
+fi
 
 # =========== script hooks =================================================
 
@@ -611,6 +642,54 @@ restore_resolvconf_unbound() {
 	fi
 }
 
+# ========= Split-DNS handling  =======================================
+setup_split_dns_unbound() {
+	SDNS=`echo $CISCO_SPLIT_DNS|sed 's/,/\n/g'`
+	echo "$SDNS" | while read i; do
+		if [ -n "$INTERNAL_IP4_DNS" -o -n "$INTERNAL_IP6_DNS" ];then
+			/usr/sbin/unbound-control forward_add $i $INTERNAL_IP4_DNS $INTERNAL_IP6_DNS  >/dev/null 2>&1
+		fi
+		/usr/sbin/unbound-control flush_zone $i >/dev/null 2>&1
+	done
+	/usr/sbin/unbound-control flush_requestlist >/dev/null 2>&1
+}
+
+remove_split_dns_unbound() {
+	SDNS=`echo $CISCO_SPLIT_DNS|sed 's/,/\n/g'`
+	echo "$SDNS" | while read i; do
+		/usr/sbin/unbound-control forward_remove $i >/dev/null 2>&1
+		/usr/sbin/unbound-control flush_zone $i >/dev/null 2>&1
+	done
+}
+
+setup_split_dns_dnsmasq() {
+	SDNS=`echo $CISCO_SPLIT_DNS|sed 's/,/\n/g'`
+	if [ -n "$DNSMASQ_DIR" ];then
+		DNSMASQ_FILE="$DNSMASQ_DIR/openconnect.$TUNDEV"
+		rm -f $DNSMASQ_FILE
+		echo "$SDNS" | while read i; do
+			if [ -n "$INTERNAL_IP4_DNS" ];then
+				echo "server=/$i/$INTERNAL_IP4_DNS" >> $DNSMASQ_FILE
+			fi
+			if [ -n "$INTERNAL_IP6_DNS" ];then
+				echo "server=/$i/$INTERNAL_IP6_DNS" >> $DNSMASQ_FILE
+			fi
+			echo "rebind-domain-ok=$i" >> $DNSMASQ_FILE
+		done
+		$RESTARTDNSMASQ
+	fi
+}
+
+
+remove_split_dns_dnsmasq() {
+	SDNS=`echo $CISCO_SPLIT_DNS|sed 's/,/\n/g'`
+	if [ -n "$DNSMASQ_DIR" ];then
+		DNSMASQ_FILE="$DNSMASQ_DIR/openconnect.$TUNDEV"
+		rm -f $DNSMASQ_FILE
+		$RESTARTDNSMASQ
+	fi
+}
+
 # ========= Toplevel state handling  =======================================
 
 kernel_is_2_6_or_above() {
@@ -725,7 +804,9 @@ do_connect() {
 		set_ipv6_default_route
 	fi
 
-	if [ -n "$INTERNAL_IP4_DNS" ]; then
+	if [ -n "$CISCO_SPLIT_DNS" -a -n "$MODIFYSPLITDNS" ];then
+		$MODIFYSPLITDNS
+	elif [ -n "$INTERNAL_IP4_DNS" ]; then
 		$MODIFYRESOLVCONF
 	fi
 }
@@ -773,11 +854,12 @@ do_disconnect() {
 
 	del_vpngateway_route
 
-	if [ -n "$INTERNAL_IP4_DNS" ]; then
+	if [ -n "$CISCO_SPLIT_DNS" -a -n "$RESTORESPLITDNS" ];then
+		$RESTORESPLITDNS
+	elif [ -n "$INTERNAL_IP4_DNS" ]; then
 		$RESTORERESOLVCONF
 	fi
 
-
 	if [ -n "$IPROUTE" ]; then
 		if [ -n "$INTERNAL_IP4_ADDRESS" ]; then
 			$IPROUTE addr del "$INTERNAL_IP4_ADDRESS/255.255.255.255" peer "$INTERNAL_IP4_ADDRESS" dev "$TUNDEV"
-- 
2.5.0