#!/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