resolved-reset.sh 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. #!/usr/bin/env bash
  2. # net-ops :: linux/resolved-reset.sh
  3. # Reset systemd-resolved state when per-link DNS gets stuck (typical after
  4. # VPN disconnect leaves stale per-link DNS / domain settings).
  5. #
  6. # Defaults to DRY RUN — pass --apply to actually act.
  7. # Requires sudo for the apply path.
  8. set -eu
  9. # Shared terminal toolkit (skills/_lib/term.sh) — colorized, ASCII-aware section
  10. # headers. Dump/fixer output isn't a checklist, so it uses the bare-header style
  11. # (a deliberate exception per docs/TERMINAL-DESIGN.md), not the enclosing panel.
  12. __nlib="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../_lib" 2>/dev/null && pwd || true)"
  13. if [ -n "${__nlib:-}" ] && [ -f "$__nlib/term.sh" ]; then . "$__nlib/term.sh"; term_init
  14. else term_header() { printf '== %s ==\n' "${1:-}"; }; fi
  15. APPLY=0
  16. for arg in "$@"; do
  17. case "$arg" in
  18. --apply) APPLY=1 ;;
  19. --help|-h)
  20. cat <<EOF
  21. Usage: $0 [--apply]
  22. Diagnoses and (with --apply) resets systemd-resolved per-link DNS state.
  23. --apply Flush caches and revert each link's DNS to NetworkManager/networkd defaults
  24. (default: dry-run, prints what would happen)
  25. EOF
  26. exit 0 ;;
  27. esac
  28. done
  29. if ! systemctl is-active systemd-resolved >/dev/null 2>&1; then
  30. echo "systemd-resolved is not active. This script only applies when it is."
  31. echo "On non-systemd-resolved systems, edit /etc/resolv.conf or NetworkManager config directly."
  32. exit 0
  33. fi
  34. term_header "BEFORE"
  35. resolvectl status 2>/dev/null | head -60
  36. # Find links with non-empty per-link DNS (potential stale state)
  37. LINKS_WITH_DNS=$(resolvectl status 2>/dev/null | awk '
  38. /^Link [0-9]+ \(/{ split($0,a," \\("); split(a[2],b,")"); link=b[1]; ifn=a[1]; sub("Link ","",ifn); has=0 }
  39. /Current DNS Server:|DNS Servers:/{ if(NF>3){print ifn"|"link} }
  40. ' | sort -u)
  41. if [[ -z "$LINKS_WITH_DNS" ]]; then
  42. echo
  43. echo "No links have explicit DNS set. Nothing to reset."
  44. exit 0
  45. fi
  46. echo
  47. term_header "LINKS WITH EXPLICIT DNS"
  48. echo "$LINKS_WITH_DNS" | while IFS='|' read -r idx name; do
  49. echo " Link $idx ($name)"
  50. done
  51. if [[ "$APPLY" -eq 0 ]]; then
  52. echo
  53. echo "DRY RUN — pass --apply to actually reset these links and flush caches."
  54. exit 0
  55. fi
  56. if [[ "$EUID" -ne 0 ]]; then
  57. echo "Need root. Re-running with sudo..."
  58. exec sudo "$0" --apply
  59. fi
  60. echo
  61. term_header "RESETTING"
  62. echo "$LINKS_WITH_DNS" | while IFS='|' read -r idx name; do
  63. if resolvectl revert "$name" 2>/dev/null; then
  64. echo "[OK] reverted $name"
  65. else
  66. echo "[WARN] revert failed for $name (may be a VPN tunnel — manual cleanup may be needed)"
  67. fi
  68. done
  69. echo
  70. term_header "FLUSHING CACHE"
  71. resolvectl flush-caches && echo " cache flushed"
  72. # Restart for good measure if user really wanted a reset
  73. systemctl restart systemd-resolved
  74. echo " systemd-resolved restarted"
  75. echo
  76. term_header "VERIFICATION"
  77. if out=$(getent hosts google.com 2>&1) && [[ -n "$out" ]]; then
  78. echo "[PASS] getent hosts google.com -> $(echo "$out" | awk '{print $1}')"
  79. else
  80. echo "[FAIL] getent still failing. Check /etc/nsswitch.conf and /etc/resolv.conf."
  81. fi
  82. if curl -sS -o /dev/null -w "[PASS] HTTPS google.com -> HTTP %{http_code}\n" --max-time 8 https://www.google.com 2>&1; then
  83. :
  84. else
  85. echo "[FAIL] HTTPS still broken."
  86. fi
  87. echo
  88. term_header "AFTER"
  89. resolvectl status 2>/dev/null | head -40