#!/bin/bash
###############################################################################
#                                                                             #
# IPFire.org - A linux based firewall                                         #
# Copyright (C) 2024 Michael Tremer <michael.tremer@ipfire.org>               #
#                                                                             #
# This program is free software: you can redistribute it and/or modify        #
# it under the terms of the GNU General Public License as published by        #
# the Free Software Foundation, either version 3 of the License, or           #
# (at your option) any later version.                                         #
#                                                                             #
# This program is distributed in the hope that it will be useful,             #
# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
# GNU General Public License for more details.                                #
#                                                                             #
# You should have received a copy of the GNU General Public License           #
# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
#                                                                             #
###############################################################################
#                                                                             #
# This script tries to keep WireGuard connections with dynamic peers alive    #
#                                                                             #
# It resolves the endpoint if it is an FQDN, and if so, will check if the     #
# currently connected endpoint matches any of the resolved IP addresses. If   #
# not it will reload the WireGuard configuration in the hope that wg will     #
# update the kernel with the new IP address and the connection comes back up  #
# again.                                                                      #
#                                                                             #
###############################################################################

. /etc/sysconfig/rc
. ${rc_functions}

# Fetches the first endpoint that is currently active on the given interface
current_endpoint() {
	local intf="${1}"

	local pubkey
	local endpoint

	# List the first endpoint (are there even more than one?)
	wg show "${intf}" endpoints | while read -r pubkey endpoint; do
		echo "${endpoint%:*}"
		break
	done

	return 0
}

# Resolves a hostname
resolve() {
	local endpoint="${1}"

	dig +short "A" "${endpoint}" 2>/dev/null
}

main() {
	local -A settings=()

	# Read WireGuard settings
	readhash settings /var/ipfire/wireguard/settings

	# Do nothing if WireGuard is not enabled
	if [ "${settings[ENABLED]}" != "on" ]; then
		return 0
	fi

	local line
	while IFS=',' read -r -a line; do
		local id="${line[0]}"
		local enabled="${line[1]}"
		local type="${line[2]}"
		local name="${line[3]}"
		local endpoint="${line[7]}"

		# Only process enabled net-to-net connections
		case "${enabled},${type}" in
			on,net)
				;;
			*)
				continue
				;;
		esac

		# The endpoint must be an FQDN
		case "${endpoint}" in
			# Ignore IP addresses
			[0-9]*.[0-9]*.[0-9]*.[0-9]*)
				continue
				;;

			# Ignore if we don't know the endpoint
			"")
				continue
				;;
		esac

		local address
		local match=0

		# Fetch the current endpoint address
		local current_address="$(current_endpoint "wg${id}")"

		# Walk through all IP addresses the FQDN resolves to
		for address in $(resolve "${endpoint}"); do
			if [ "${current_address}" = "${address}" ]; then
				match=1
				break
			fi
		done

		# If there has been no match, we have to reload everything
		if [ "${match}" -eq 0 ]; then
			exec /etc/init.d/wireguard reload
		fi
	done < /var/ipfire/wireguard/peers

	return 0
}

main "$@" || exit $?
