#!/bin/sh
# vpn switch for travelmate
# Copyright (c) 2020 Dirk Brenken (dev@brenken.org)
# This is free software, licensed under the GNU General Public License v3.

# set (s)hellcheck exceptions
# shellcheck disable=1091,2016,2039,2059,2086,2143,2181,2188

# Please note: you have to setup the package 'wireguard' or 'openvpn' before using this script

export LC_ALL=C
export PATH="/usr/sbin:/usr/bin:/sbin:/bin"
set -o pipefail

if [ "$(uci_get 2>/dev/null; printf "%u" "${?}")" = "127" ]
then
	. "/lib/functions.sh"
fi

vpn_action="${1}"
trm_vpnservice="$(uci_get travelmate global trm_vpnservice)"
trm_vpniface="$(uci_get travelmate global trm_vpniface)"
trm_landevice="$(uci_get travelmate global trm_landevice)"
trm_maxwait="$(uci_get travelmate global trm_maxwait "30")"
trm_captiveurl="$(uci_get travelmate global trm_captiveurl "http://captive.apple.com")"
trm_useragent="$(uci_get travelmate global trm_useragent "Mozilla/5.0 (Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0")"
trm_iptrule_accept="FORWARD -i ${trm_landevice} -p tcp  --match multiport --dports 80,443 -j ACCEPT"
trm_iptrule_drop="FORWARD -i ${trm_landevice} -j DROP"
trm_iptables="$(command -v iptables)"
trm_logger="$(command -v logger)"
trm_fetch="$(command -v curl)"

f_log()
{
	local class="${1}" log_msg="${2}"

	if [ -x "${trm_logger}" ]
	then
		"${trm_logger}" -p "${class}" -t "trm-vpn  [${$}]" "${log_msg}"
	else
		printf "%s %s %s\\n" "${class}" "trm-vpn  [${$}]" "${log_msg}"
	fi
}

f_net()
{
	local IFS json_raw json_rc result="net nok"

	json_raw="$(${trm_fetch} --user-agent "${trm_useragent}" --referer "http://www.example.com" --write-out "%{json}" --silent --show-error --connect-timeout $((trm_maxwait/10)) "${trm_captiveurl}" 2>/dev/null)"
	json_raw="${json_raw#*\{}"
	if [ -n "${json_raw}" ]
	then
		json_rc="$(printf "%s" "{${json_raw}" | jsonfilter -l1 -e '@.response_code' 2>/dev/null)"
		if [ "${json_rc}" = "200" ] || [ "${json_rc}" = "204" ]
		then
			result="net ok"
		fi
	fi
	printf "%s" "${result}"
}

if [ -n "${trm_vpnservice}" ] && [ -n "${trm_vpniface}" ] && [ -n "${trm_landevice}" ] && [ -f "/tmp/trm_runtime.json" ]
then
	status="$(jsonfilter -i "/tmp/trm_runtime.json" -l1 -e '@.data.travelmate_status' 2>/dev/null)"
	vpn_status="$(ubus -S call network.interface."${trm_vpniface}" status 2>/dev/null | jsonfilter -l1 -e '@.up')"
	if [ "${vpn_action}" = "disable" ] && [ "${vpn_status}" = "true" ]
	then
		if [ -n "$("${trm_iptables}" "-w $((trm_maxwait/6))" -C ${trm_iptrule_drop} 2>&1)" ] && \
			[ -n "$("${trm_iptables}" "-w $((trm_maxwait/6))" -C ${trm_iptrule_accept} 2>&1)" ]
		then
			"${trm_iptables}" "-w $((trm_maxwait/6))" -I ${trm_iptrule_drop} 2>&1
			f_log "info" "lan forward blocked for device '${trm_landevice}'"
		fi
	fi
	if [ "${vpn_action}" = "disable" ] && [ "${status%% (net cp *}" = "connected" ]
	then
		if [ -n "$("${trm_iptables}" "-w $((trm_maxwait/6))" -C ${trm_iptrule_accept} 2>&1)" ] && \
			[ -z "$("${trm_iptables}" "-w $((trm_maxwait/6))" -C ${trm_iptrule_drop} 2>&1)" ]
		then
			"${trm_iptables}" "-w $((trm_maxwait/6))" -I ${trm_iptrule_accept} 2>&1
			f_log "info" "lan forward on ports 80/443 freed for device '${trm_landevice}'"
		fi
	fi

	case "${trm_vpnservice}" in
		"wireguard")
			if [ "${vpn_action}" = "enable" ] && [ "${vpn_status}" != "true" ]
			then
				ubus call network.interface."${trm_vpniface}" up
			elif [ "${vpn_action}" = "disable" ] && [ "${vpn_status}" = "true" ]
			then
				ubus call network.interface."${trm_vpniface}" down
				f_log "info" "${trm_vpnservice} client connection disabled"
			fi
		;;
		"openvpn")
			if [ "${vpn_action}" = "enable" ] && [ "${vpn_status}" != "true" ]
			then
				ubus call network.interface."${trm_vpniface}" up
				/etc/init.d/openvpn restart >/dev/null 2>&1
			elif [ "${vpn_action}" = "disable" ] && [ "${vpn_status}" = "true" ]
			then
				ubus call network.interface."${trm_vpniface}" down
				/etc/init.d/openvpn stop >/dev/null 2>&1
				f_log "info" "${trm_vpnservice} client connection disabled"
			fi
		;;
	esac

	if [ "${vpn_action}" = "enable" ] && [ "${vpn_status}" != "true" ]
	then
		cnt=0
		while true
		do
			vpn_status="$(ubus -S call network.interface."${trm_vpniface}" status 2>/dev/null | jsonfilter -l1 -e '@.up')"
			if [ "${vpn_status}" = "true" ]
			then
				net_status="$(f_net)"
				if [ "${net_status}" = "net ok" ]
				then
					f_log "info" "${trm_vpnservice} client connection enabled"
					if [ -z "$("${trm_iptables}" "-w $((trm_maxwait/6))" -C ${trm_iptrule_drop} 2>&1)" ]
					then
						"${trm_iptables}" "-w $((trm_maxwait/6))" -D ${trm_iptrule_drop} 2>&1
						if [ -z "$("${trm_iptables}" "-w $((trm_maxwait/6))" -C ${trm_iptrule_accept} 2>&1)" ]
						then
							"${trm_iptables}" "-w $((trm_maxwait/6))" -D ${trm_iptrule_accept} 2>&1
						fi
						f_log "info" "lan forward freed for device '${trm_landevice}'"
					fi
					break
				fi
			fi
			if [ "${cnt}" -ge "$((trm_maxwait/6))" ]
			then
				f_log "info" "${trm_vpnservice} restart failed, lan forward for device '${trm_landevice}' still blocked"
				ubus call network.interface."${trm_vpniface}" down
				exit 2
			fi
			sleep 1
			cnt="$((cnt+1))"
		done
	fi
	if [ "${vpn_action}" = "enable" ] && [ "${vpn_status}" = "true" ]
	then
		if [ -f "/etc/init.d/sysntpd" ]
		then
			/etc/init.d/sysntpd restart >/dev/null 2>&1
		fi
	fi
	exit 0
fi
exit 1
