From 8014ebac9e9fdbf32e693c5f94ccca940177b64f Mon Sep 17 00:00:00 2001 From: Razvan Dimescu Date: Sun, 19 Apr 2026 05:52:29 +0300 Subject: [PATCH] test(integration): add Suite 7 for filter_aaaa + SUITES env filter Suite 7 exercises the full pipeline end-to-end: A resolves, AAAA returns NODATA, local [[zones]] AAAA bypasses the filter, and HTTPS ipv6hint is stripped from a real cloudflare.com response. A second config run with the flag unset guards against network-failure false-positives. SUITES=N (comma list) runs a subset, e.g. `SUITES=7 bash tests/integration.sh` skips suites 1-6 for fast iteration. --- tests/integration.sh | 158 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 157 insertions(+), 1 deletion(-) diff --git a/tests/integration.sh b/tests/integration.sh index c70ec59..81bd28d 100755 --- a/tests/integration.sh +++ b/tests/integration.sh @@ -1,7 +1,10 @@ #!/usr/bin/env bash # Integration test suite for Numa # Runs a test instance on port 5354, validates all features, exits with status. -# Usage: ./tests/integration.sh [release|debug] +# Usage: +# ./tests/integration.sh [release|debug] # all suites +# SUITES=7 ./tests/integration.sh # only Suite 7 +# SUITES=1,3,7 ./tests/integration.sh # Suites 1, 3, and 7 set -euo pipefail @@ -14,6 +17,14 @@ LOG="/tmp/numa-integration-test.log" PASSED=0 FAILED=0 +# Suite filter: empty runs all; comma list runs a subset. +SUITES="${SUITES:-}" +should_run_suite() { + [ -z "$SUITES" ] && return 0 + case ",$SUITES," in *",$1,"*) return 0;; esac + return 1 +} + # Colors GREEN="\033[32m" RED="\033[31m" @@ -166,6 +177,7 @@ CONF } # ---- Suite 1: Recursive mode + DNSSEC ---- +if should_run_suite 1; then echo "" echo "╔══════════════════════════════════════════╗" echo "║ Suite 1: Recursive + DNSSEC + Blocking ║" @@ -234,7 +246,10 @@ kill "$NUMA_PID" 2>/dev/null || true wait "$NUMA_PID" 2>/dev/null || true sleep 1 +fi # end Suite 1 + # ---- Suite 2: Forward mode (backward compat) ---- +if should_run_suite 2; then echo "" echo "╔══════════════════════════════════════════╗" echo "║ Suite 2: Forward (DoH) + Blocking ║" @@ -261,7 +276,10 @@ enabled = true enabled = false " +fi # end Suite 2 + # ---- Suite 3: Forward UDP (plain, no DoH) ---- +if should_run_suite 3; then echo "" echo "╔══════════════════════════════════════════╗" echo "║ Suite 3: Forward (UDP) + No Blocking ║" @@ -307,7 +325,10 @@ kill "$NUMA_PID" 2>/dev/null || true wait "$NUMA_PID" 2>/dev/null || true sleep 1 +fi # end Suite 3 + # ---- Suite 4: Local zones + Overrides API ---- +if should_run_suite 4; then echo "" echo "╔══════════════════════════════════════════╗" echo "║ Suite 4: Local Zones + Overrides API ║" @@ -416,7 +437,10 @@ kill "$NUMA_PID" 2>/dev/null || true wait "$NUMA_PID" 2>/dev/null || true sleep 1 +fi # end Suite 4 + # ---- Suite 5: DNS-over-TLS (RFC 7858) ---- +if should_run_suite 5; then echo "" echo "╔══════════════════════════════════════════╗" echo "║ Suite 5: DNS-over-TLS (RFC 7858) ║" @@ -538,7 +562,10 @@ CONF fi sleep 1 +fi # end Suite 5 + # ---- Suite 6: Proxy + DoT coexistence ---- +if should_run_suite 6; then echo "" echo "╔══════════════════════════════════════════╗" echo "║ Suite 6: Proxy + DoT Coexistence ║" @@ -698,6 +725,135 @@ CONF rm -rf "$NUMA_DATA" fi +fi # end Suite 6 + +# ---- Suite 7: filter_aaaa (IPv4-only networks) ---- +if should_run_suite 7; then +echo "" +echo "╔══════════════════════════════════════════╗" +echo "║ Suite 7: filter_aaaa ║" +echo "╚══════════════════════════════════════════╝" + +# Config A — filter on, with a local AAAA zone to prove local data bypass. +cat > "$CONFIG" << 'CONF' +[server] +bind_addr = "127.0.0.1:5354" +api_port = 5381 +filter_aaaa = true + +[upstream] +mode = "forward" +address = "9.9.9.9" +port = 53 + +[cache] +max_entries = 10000 + +[blocking] +enabled = false + +[proxy] +enabled = false + +[[zones]] +domain = "v6.test" +record_type = "AAAA" +value = "2001:db8::1" +ttl = 60 +CONF + +RUST_LOG=info "$BINARY" "$CONFIG" > "$LOG" 2>&1 & +NUMA_PID=$! +sleep 3 + +DIG="dig @127.0.0.1 -p $PORT +time=5 +tries=1" + +echo "" +echo "=== filter_aaaa = true ===" + +# A queries must be untouched. +check "A record resolves under filter_aaaa" \ + "." \ + "$($DIG google.com A +short | head -1)" + +# AAAA must be NOERROR (NODATA), not NXDOMAIN, not SERVFAIL. +check "AAAA returns NOERROR (not NXDOMAIN)" \ + "status: NOERROR" \ + "$($DIG google.com AAAA 2>&1 | grep 'status:')" + +check "AAAA returns zero answers (NODATA shape)" \ + "ANSWER: 0" \ + "$($DIG google.com AAAA 2>&1 | grep -oE 'ANSWER: [0-9]+' | head -1)" + +# Local zone AAAA must survive the filter (PR claim: local data bypasses). +check "Local [[zones]] AAAA bypasses filter" \ + "2001:db8::1" \ + "$($DIG v6.test AAAA +short)" + +# HTTPS RR: ipv6hint (SvcParamKey 6) must be stripped. Query as `type65` +# because dig 9.10.6 (macOS) misparses `HTTPS` as a domain name; `type65` +# works on both 9.10.6 and 9.18. Assert on the raw rdata hex (RFC 3597 +# generic format), since dig 9.10.6 doesn't pretty-print HTTPS params. +# cloudflare.com's ipv6hint values sit under the 2606:4700 prefix — +# checking that `26064700` is absent from the rdata hex is a precise, +# upstream-stable signal that the TLV was stripped. +HTTPS_OUT=$($DIG cloudflare.com type65 2>&1) +if echo "$HTTPS_OUT" | grep -qE "cloudflare\.com\..*IN[[:space:]]+TYPE65"; then + HTTPS_HEX=$(echo "$HTTPS_OUT" | grep -A5 "IN[[:space:]]*TYPE65" | tr -d " \t\n") + if echo "$HTTPS_HEX" | grep -qi "26064700"; then + check "HTTPS ipv6hint stripped (2606:4700 absent from rdata)" "absent" "present" + else + check "HTTPS ipv6hint stripped (2606:4700 absent from rdata)" "absent" "absent" + fi +else + # Upstream didn't return an HTTPS record — skip rather than false-pass. + printf " ${DIM}~ HTTPS ipv6hint stripped (skipped: no HTTPS RR returned by upstream)${RESET}\n" +fi + +kill "$NUMA_PID" 2>/dev/null || true +wait "$NUMA_PID" 2>/dev/null || true +sleep 1 + +# Config B — filter off. Regression guard: prove AAAA answers come back +# when the flag isn't set, so a network failure in Config A can't silently +# pass as "filter working". +cat > "$CONFIG" << 'CONF' +[server] +bind_addr = "127.0.0.1:5354" +api_port = 5381 + +[upstream] +mode = "forward" +address = "9.9.9.9" +port = 53 + +[cache] +max_entries = 10000 + +[blocking] +enabled = false + +[proxy] +enabled = false +CONF + +RUST_LOG=info "$BINARY" "$CONFIG" > "$LOG" 2>&1 & +NUMA_PID=$! +sleep 3 + +echo "" +echo "=== filter_aaaa unset (regression guard) ===" + +check "AAAA returns real answers with filter off" \ + ":" \ + "$($DIG google.com AAAA +short | head -1)" + +kill "$NUMA_PID" 2>/dev/null || true +wait "$NUMA_PID" 2>/dev/null || true +sleep 1 + +fi # end Suite 7 + # Summary echo "" TOTAL=$((PASSED + FAILED))