From e46c05e3c83da6531ad6bab82ae72ae6b4c8ec93 Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Wed, 20 May 2026 00:14:20 -0600 Subject: [PATCH] =?UTF-8?q?scripts:=20add=20check-he.sh=20=E2=80=94=20para?= =?UTF-8?q?llel=20query=20across=20HE=20anycast=20NS=20for=20divergence=20?= =?UTF-8?q?detection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/check-he.sh | 55 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100755 scripts/check-he.sh diff --git a/scripts/check-he.sh b/scripts/check-he.sh new file mode 100755 index 0000000..69028bd --- /dev/null +++ b/scripts/check-he.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +# Query all 4 public Hurricane Electric anycast nameservers in parallel +# for a given name + record type. Surfaces inter-cluster divergence +# (different anycast IPs at different sync states during AXFR). +# +# Usage: +# ./scripts/check-he.sh # defaults to A +# ./scripts/check-he.sh AAAA +# ./scripts/check-he.sh SOA +# +# 216.218.133.2 (ns4 / slave.dns.he.net) is intentionally omitted — +# it's HE's AXFR puller and refuses public DNS queries. +set -euo pipefail + +NAME="${1:?usage: $0 [type]}" +TYPE="${2:-A}" + +declare -A HE_NS=( + ["216.218.130.2"]="ns1.he.net" + ["216.218.131.2"]="ns2.he.net" + ["216.218.132.2"]="ns3.he.net" + ["216.66.1.2"]="ns5.he.net" +) + +tmpdir=$(mktemp -d) +trap 'rm -rf "$tmpdir"' EXIT + +# Fire all queries in parallel. Each writes one line to $tmpdir/. +for ip in "${!HE_NS[@]}"; do + ( + # SOA only appears in `+short` when querying the apex. For subdomain + # queries we have to read it from the AUTHORITY section instead. + serial=$(dig @"$ip" "$NAME" SOA +noall +answer +authority +timeout=4 2>/dev/null \ + | awk '$4=="SOA"{print $7; exit}') + result=$(dig @"$ip" "$NAME" "$TYPE" +short +timeout=4 2>/dev/null | head -3 | paste -sd ',' -) + printf "%-15s %-12s serial=%-12s %s=%s\n" \ + "$ip" "${HE_NS[$ip]}" "${serial:-FAIL}" "$TYPE" "${result:-(empty)}" \ + > "$tmpdir/$ip" + ) & +done +wait + +echo "=== $NAME ($TYPE) — view across HE public anycast NS ===" +sort "$tmpdir"/* | sed 's/^/ /' + +# Divergence detection +serials=$(awk '{for(i=1;i<=NF;i++) if($i ~ /^serial=/) print $i}' "$tmpdir"/* | sort -u) +answers=$(awk -F= '{print $NF}' "$tmpdir"/* | sort -u) + +echo "" +if [[ $(echo "$serials" | wc -l) -gt 1 || $(echo "$answers" | wc -l) -gt 1 ]]; then + echo "⚠️ divergent — HE anycast clusters are mid-replication" +else + echo "✓ all 4 anycast clusters in sync" +fi