Resolve a peer from tailscale status before falling back to DNS

MagicDNS does not work on this mesh, so `tailscale ip -4` missed and blocked on
a system DNS lookup until it timed out: 5.04s per call against 0.010s for the
status parse, paid by every mesh operation on both hosts. Exact match first, so
no precision is traded for the speed.
This commit is contained in:
Gmer4Lfe
2026-08-21 08:38:37 -04:00
parent fe23c59e0c
commit 331b7b6b13
2 changed files with 67 additions and 26 deletions
+34 -12
View File
@@ -921,26 +921,48 @@ resolve_remote_ip() {
info "$ICON_NET Remote IP: $REMOTE_SERVER"
}
# Resolve any hostname to a Tailscale IPv4 — tries direct lookup, falls back to status parse.
# Resolve any hostname to a Tailscale IPv4 — status parse first, DNS as a bounded last resort.
# Usage: ip=$(resolve_tailscale_ip "hostname") — returns empty string on failure.
#
# `tailscale ip -4` used to be tried first, because Tailscale owns the mapping. It also resolves
# through MagicDNS, which does not work on this mesh — the two hosts are on separate tailnets
# shared into each other — so the name misses and the call blocks on a system DNS lookup until it
# times out. Five seconds, every call, on every rsync, partnership check and remote collector.
# The status parse costs ten milliseconds and carries the same mapping.
#
# Exactness is not traded away for the speed: an exact name match against `tailscale status` is as
# precise as the lookup it replaces. The prefix match keeps its ambiguity guard, and the DNS path
# still runs — bounded, and only when status cannot decide.
#
# Mirror of vv_resolve_tailscale_ip() in Plugin/unraid/include/config.php; the two must agree.
resolve_tailscale_ip() {
local hostname="${1,,}"
local ip
ip=$(tailscale ip -4 "$hostname" 2>/dev/null)
if [[ -n "$ip" ]]; then
echo "$ip"
local status_out exact matches count ip
status_out=$(tailscale status 2>/dev/null)
exact=$(echo "$status_out" | awk -v name="$hostname" '
{ split(tolower($2), parts, "."); if (parts[1] == name) { print $1; exit } }')
if [[ -n "$exact" ]]; then
echo "$exact"
return
fi
# Fallback: unambiguous prefix match against tailscale status (either direction) — handles
# Unraid's 15-char NetBIOS hostname truncation vs. a longer name recorded in master.conf.
# Only accept the match when exactly one peer could qualify; never guess between multiple
# candidates that happen to share a prefix (e.g. server1/server10).
local matches count
matches=$(tailscale status 2>/dev/null | awk -v name="$hostname" '
# Unambiguous prefix match, either direction — handles Unraid's 15-char NetBIOS hostname
# truncation vs. a longer name recorded in master.conf. Only accept the match when exactly one
# peer could qualify; never guess between candidates that share a prefix (server1/server10).
matches=$(echo "$status_out" | awk -v name="$hostname" '
{ split(tolower($2), parts, "."); host = parts[1];
if (index(host, name) == 1 || index(name, host) == 1) print $1 }')
count=$(echo "$matches" | grep -c .)
[[ "$count" -eq 1 ]] && echo "$matches"
if [[ "$count" -eq 1 ]]; then
echo "$matches"
return
fi
# Last resort, and the slow one. Bounded so a broken MagicDNS costs two seconds, not five.
ip=$(timeout 2 tailscale ip -4 "$hostname" 2>/dev/null)
[[ -n "$ip" ]] && echo "$ip"
}
# Strips the "unraid-" prefix (case-insensitive) and title-cases what remains.