#!/bin/sh
. /lib/functions.sh
. "${OMR_LIB_DIR:-/usr/share/omr/lib}/omr-vps-curl.sh"

intf=$1

timeout=$(uci -q get openmptcprouter.settings.status_getip_timeout)
[ -z "$timeout" ] && timeout="1"

# The answer comes from plain http and ends up in shell commands, uci state
# and the VPS API: accept an IPv6 address only (hex digits, colons, an
# optional dotted IPv4 tail)
valid_ip6() {
	case "$1" in
		*:*) ;;
		*) return 1 ;;
	esac
	[ "${#1}" -le 45 ] && expr "$1" : '[0-9A-Fa-f:.]*$' >/dev/null
}

# serverip was never set (omr-ip-intf does it) and an IPv6 literal had no
# brackets: every lookup asked https://:port/clienthost, so the VPS was never
# asked and the public IPv6 always came from the plain http website.
get_ip_from_server() {
	[ -n "$ip" ] && return
	serversection=$1
	serverport=$(uci -q get openmptcprouter.$1.port)
	get_ip() {
		[ -n "$ip" ] && return
		serverip=$1
		serverip="${serverip#\[}"
		serverip="${serverip%\]}"
		# An IPv6 literal goes in brackets in a URL, its zone id (link-local)
		# percent-encoded
		case "$serverip" in
			*:*%*) urlhost="[${serverip%%\%*}%25${serverip#*%}]" ;;
			*:*) urlhost="[${serverip}]" ;;
			*) urlhost="$serverip" ;;
		esac
		# Pinned with this server's key, however its address is spelled
		getip="$(OMR_VPS_SERVER="$serversection" omr_vps_curl -s -k -6 -m ${timeout} --interface $intf "https://${urlhost}:${serverport}/clienthost" 2>/dev/null)"
		[ -n "$getip" ] && getip=$(echo $getip | jsonfilter -e '@.client_host')
		if valid_ip6 "$getip"; then
			ip=$getip
			return
		fi
	}
	config_list_foreach $1 ip get_ip
}

get_ip_from_website() {
	local host addresses address ipset_added="" nft_added="" getip
	check_ipv6_website="$(uci -q get openmptcprouter.settings.check_ipv6_website)"
	[ -z "$check_ipv6_website" ] && check_ipv6_website="http://ipv6.openmptcprouter.com"
	# Query the URL's host, keeping each DNS answer separate.
	host="${check_ipv6_website#*://}"
	host="${host%%[/?#]*}"
	case "$host" in
		\[*) host="${host#\[}"; host="${host%%\]*}" ;;
		*) host="${host%%:*}" ;;
	esac
	addresses=$(dig +nocmd +noall +answer AAAA "$host" | awk '$4 == "AAAA" {print $5}')
	for address in $addresses; do
		# A failed add must not give this probe ownership of an entry.
		if ! ipset test ss_rules6_dst_bypass_all "$address" >/dev/null 2>&1 &&
			ipset add ss_rules6_dst_bypass_all "$address" >/dev/null 2>&1; then
			ipset_added="$ipset_added $address"
		fi
		if ! nft get element inet fw4 omr_dst_bypass_all_6 "{ $address }" >/dev/null 2>&1 &&
			nft add element inet fw4 omr_dst_bypass_all_6 "{ $address }" >/dev/null 2>&1; then
			nft_added="$nft_added $address"
		fi
	done
	getip="$(curl -s -6 -m "$timeout" --interface "$intf" "$check_ipv6_website" 2>/dev/null)"
	# Preserve entries that belonged to bypass policy before the probe.
	for address in $ipset_added; do
		ipset del ss_rules6_dst_bypass_all "$address" >/dev/null 2>&1
	done
	for address in $nft_added; do
		nft delete element inet fw4 omr_dst_bypass_all_6 "{ $address }" >/dev/null 2>&1
	done
	if valid_ip6 "$getip"; then
		ip=$getip
	fi
}

[ -z "$intf" ] && return
if [ -n "$(ip -6 a show dev $intf)" ]; then
	ip=""
	config_load openmptcprouter
	config_foreach get_ip_from_server server
	[ -z "$ip" ] && get_ip_from_website
	echo $ip
fi