#!/usr/bin/env bash
# dns-client-verify.sh — Maximum client-side DNSSEC verification
# Requires BOTH: dig (from bind-utils or dnsutils) and drill (from ldns).

set -euo pipefail

have() { command -v "$1" >/dev/null 2>&1; }

# -------------------- Tool check --------------------
if ! have dig; then
  echo "ERROR: dig is not installed. Please install it and re-run." >&2
  exit 1
fi
if ! have drill; then
  echo "ERROR: drill is not installed. Please install it and re-run." >&2
  exit 1
fi

# -------------------- CLI --------------------
RESOLVER_ARG=""
TARGET_RESOLVER=""
while [ $# -gt 0 ]; do
  case "$1" in
    --resolver)
      shift; RESOLVER_ARG="${1:-}" ;;
    --resolver=*)
      RESOLVER_ARG="${1#*=}" ;;
    -h|--help)
      cat <<EOF
Usage: $0 [--resolver @IP|IP]
  --resolver: query a specific resolver (e.g. --resolver 192.168.7.1 or --resolver @192.168.7.1)
EOF
      exit 0 ;;
    *) echo "Unknown arg: $1" >&2; exit 2 ;;
  esac
  shift
done
if [ -n "$RESOLVER_ARG" ]; then
  case "$RESOLVER_ARG" in
    @*) TARGET_RESOLVER="$RESOLVER_ARG" ;;
    *)  TARGET_RESOLVER="@$RESOLVER_ARG" ;;
  esac
fi

# -------------------- Test Sets --------------------
GOOD_DOMAINS=(cloudflare.com dnssec-tools.org)
BAD_DOMAINS=(sigfail.verteiltesysteme.net dnssec-failed.org)

# -------------------- Helpers --------------------
show_resolvers() {
  echo "Resolvers in /etc/resolv.conf:"
  if [ -f /etc/resolv.conf ]; then
    grep -E '^\s*nameserver\s+' /etc/resolv.conf || echo "  (none listed)"
  else
    echo "  (no /etc/resolv.conf)"
  fi
  if [ -n "$TARGET_RESOLVER" ]; then
    echo "Target resolver override: $TARGET_RESOLVER"
  fi
}

extract_status() {
  sed -n 's/.*status: \([A-Z]\+\).*/\1/p' | head -n1
}

dig_header() {
  dig $TARGET_RESOLVER +dnssec +noall +comments "$1" 2>/dev/null
}

# -------------------- Main --------------------
echo "=== DNS client verification (dig + drill) ==="
date -u
echo
show_resolvers
echo

ad_ok=0
echo "== DNSSEC: AD flag present on signed domains (dig) =="
for d in "${GOOD_DOMAINS[@]}"; do
  echo "--- $d ---"
  hdr="$(dig_header "$d")"
  echo "$hdr" | awk '/HEADER|flags:|SERVER:/{print}'
  if echo "$hdr" | grep -qi 'flags:.* ad'; then
    echo "Result: OK (AD flag present)"
    ad_ok=$((ad_ok+1))
  else
    echo "Result: FAIL (no AD flag)"
  fi
  echo
done

sf_ok=0
echo "== DNSSEC: bogus-signature should SERVFAIL (dig) =="
for d in "${BAD_DOMAINS[@]}"; do
  echo "--- $d ---"
  hdr="$(dig $TARGET_RESOLVER +dnssec +noall +comments "$d" A || true)"
  echo "$hdr" | sed -n '1,8p'
  if echo "$hdr" | grep -qi 'status: SERVFAIL'; then
    echo "Result: OK (SERVFAIL as expected)"
    sf_ok=$((sf_ok+1))
  else
    echo "Result: FAIL (did not SERVFAIL)"
  fi
  echo
done

cd_ok=0
echo "== DNSSEC: CD flag behavior (proves resolver validates) (dig) =="
for d in "${BAD_DOMAINS[@]}"; do
  echo "--- $d ---"
  no_cd="$(dig $TARGET_RESOLVER +dnssec +noall +comments "$d" A || true)"
  with_cd="$(dig $TARGET_RESOLVER +dnssec +cdflag +noall +comments "$d" A || true)"
  s1="$(echo "$no_cd"   | extract_status)"
  s2="$(echo "$with_cd" | extract_status)"
  echo "Without +cdflag → status: ${s1:-UNKNOWN}"
  echo "With    +cdflag → status: ${s2:-UNKNOWN}"
  if echo "$no_cd" | grep -qi 'status: SERVFAIL' && echo "$with_cd" | grep -qi 'status: NOERROR'; then
    echo "Result: OK (resolver enforces DNSSEC; CD reveals raw data without AD)"
    cd_ok=$((cd_ok+1))
  else
    echo "Result: FAIL (unexpected statuses; resolver may not validate or ignores CD)"
  fi
  echo
done

echo "== Resolver that answered (dig SERVER line) =="
dig $TARGET_RESOLVER +dnssec cloudflare.com | sed -n '/^;; SERVER:/p'
echo

echo "== DNSSEC chain-of-trust (drill -S) =="
target="cloudflare.com"
echo "--- Trust path for $target ---"
if drill -S "$target" >/dev/null 2>&1; then
  drill -S "$target"
else
  echo "WARN: drill -S failed (transient?)."
fi
echo

echo "=== Summary ==="
echo "AD flag OK on $ad_ok/${#GOOD_DOMAINS[@]} signed domains."
echo "SERVFAIL OK on $sf_ok/${#BAD_DOMAINS[@]} bogus domains."
echo "CD behavior OK on $cd_ok/${#BAD_DOMAINS[@]} bogus domains."
if [ "$ad_ok" -eq "${#GOOD_DOMAINS[@]}" ] && [ "$sf_ok" -ge 1 ] && [ "$cd_ok" -ge 1 ]; then
  echo "Verdict: ✅ DNSSEC validation is ACTIVE and enforced by your resolver."
else
  echo "Verdict: ❌ One or more DNSSEC checks failed — investigate resolver settings."
fi
echo "Trust chain shown above with 'drill -S'."

cat <<'NOTE'

Notes:
- These are client-side tests. They prove validation (AD flag, SERVFAIL on bad sigs, CD toggle).
- They do NOT prove upstream DoT/DoH; check that on your resolver/firewall.
- Use --resolver to compare different resolvers, e.g.:
    ./dns-client-verify.sh --resolver 192.168.7.1
    ./dns-client-verify.sh --resolver @1.1.1.1
NOTE

