mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-10-14 21:20:48 +08:00
Detached tasks spawned on every config generation set and restored the placeholder DNS without ordering, so a set could back up the placeholder itself and a later restore reported success while leaving it in place. Reconcile from the applied config under the lifecycle lock, and restore whenever the core stops. Keep one backup per network service, never back up the placeholder, and restore only while the service still holds it. Scripts verify every read and write and are killed after 5 s. Refs #8070, #8168
81 lines
2.6 KiB
Bash
81 lines
2.6 KiB
Bash
#!/bin/bash
|
|
|
|
function is_valid_ipv4() {
|
|
local ip=$1
|
|
local IFS='.'
|
|
local -a octets
|
|
|
|
[[ ! $ip =~ ^([0-9]+\.){3}[0-9]+$ ]] && return 1
|
|
read -r -a octets <<<"$ip"
|
|
[ "${#octets[@]}" -ne 4 ] && return 1
|
|
|
|
for octet in "${octets[@]}"; do
|
|
if ! [[ "$octet" =~ ^[0-9]+$ ]] || ((octet < 0 || octet > 255)); then
|
|
return 1
|
|
fi
|
|
done
|
|
return 0
|
|
}
|
|
|
|
function is_valid_ipv6() {
|
|
local ip=$1
|
|
if [[ ! $ip =~ ^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$ ]] &&
|
|
[[ ! $ip =~ ^(([0-9a-fA-F]{0,4}:){0,7}:|(:[0-9a-fA-F]{0,4}:){0,6}:[0-9a-fA-F]{0,4})$ ]]; then
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
function is_valid_ip() {
|
|
is_valid_ipv4 "$1" || is_valid_ipv6 "$1"
|
|
}
|
|
|
|
# Prints one value of a SystemConfiguration dictionary, or nothing when the key is absent.
|
|
function sc_value() {
|
|
printf 'show %s\nquit\n' "$1" | scutil | sed -n "s/^ *$2 : //p"
|
|
}
|
|
|
|
# Prints the service's DNS servers, or "empty" when none is set; fails on any other answer.
|
|
function dns_of() {
|
|
local dns line ip='^[0-9A-Fa-f]*[.:][0-9A-Fa-f.:]*(%[[:alnum:]_.-]+)?$'
|
|
dns=$(networksetup -getdnsservers "$1") || return 1
|
|
if [ "$dns" = "There aren't any DNS Servers set on $1." ]; then
|
|
echo "empty"
|
|
return
|
|
fi
|
|
[ -n "$dns" ] || return 1
|
|
while read -r line; do
|
|
[[ $line =~ $ip ]] || return 1
|
|
done <<<"$dns"
|
|
echo "$dns"
|
|
}
|
|
|
|
[ $# -lt 1 ] && echo "Usage: $0 <IP address>" && exit 1
|
|
! is_valid_ip "$1" && echo "$1 is not a valid IP address." && exit 1
|
|
|
|
# Backups are keyed by service ID, which survives renaming the service and renumbering its interface.
|
|
service_id=$(sc_value State:/Network/Global/IPv4 PrimaryService)
|
|
hardware_port=$(sc_value "Setup:/Network/Service/$service_id" UserDefinedName)
|
|
[ -z "$hardware_port" ] && echo "No primary network service." && exit 1
|
|
|
|
original_dns=$(dns_of "$hardware_port") || { echo "Cannot read DNS servers of $hardware_port." >&2; exit 1; }
|
|
|
|
backup=".original_dns/$service_id"
|
|
mkdir -p .original_dns || exit 1
|
|
# Older releases kept a single backup for whichever service was primary.
|
|
if [ -f .original_dns.txt ]; then
|
|
if [ -f "$backup" ]; then
|
|
rm -f .original_dns.txt || exit 1
|
|
else
|
|
mv .original_dns.txt "$backup" || exit 1
|
|
fi
|
|
fi
|
|
# The placeholder is ours, not the user's: keep the backup taken before it was applied.
|
|
if [ "$original_dns" != "$1" ]; then
|
|
echo "$original_dns" >"$backup" || exit 1
|
|
networksetup -setdnsservers "$hardware_port" "$1"
|
|
[ "$(dns_of "$hardware_port")" = "$1" ] || { echo "Failed to set DNS for $hardware_port." >&2; exit 1; }
|
|
elif [ ! -f "$backup" ]; then
|
|
echo "empty" >"$backup" || exit 1
|
|
fi
|