A comprehensive, platform-native DNS troubleshooting tool for macOS. This first iteration nails the fundamentals of two features: nslookup (what does a name resolve to, from which server, and why) and DNS leak testing (are my queries actually going where I think they are).
See DESIGN.md for the full architecture and rationale.
| Component | What's built | Validated |
|---|---|---|
| DNSKit (engine) | Wire-format codec (RFC 1035 + EDNS0), UDP/TCP transport, multi-server resolver, system-DNS reader, idns CLI |
✅ swift build + swift test (16 tests), cross-checked against system dig for every record type |
| iiadns.app (macOS) | SwiftUI app: window (NavigationSplitView) + MenuBarExtra, working Lookup feature, Resolvers view, leak-test scaffold |
✅ Builds & runs — xcodebuild produces iiadns.app and it launches on macOS 26.5 |
| leakd (backend) | Authoritative DNS responder (miekg/dns) + session/results HTTP API + enrichment, anti-abuse | ✅ go build + go vet + go test; end-to-end validated with dig + curl |
itisalwaysdns/
├── DESIGN.md Architecture & rationale
├── packages/DNSKit/ Swift package: the DNS engine + `iiadns` CLI
├── apps/iiadns/ SwiftUI macOS app (project generated by xcodegen)
└── server/leakd/ Go: authoritative DNS + leak-test API
- macOS 26 (Tahoe), Xcode 26, Swift 6.3+
xcodegen—brew install xcodegen(for the app project)- Go 1.25+ (for
leakd)
cd packages/DNSKit
swift build # build library + CLI
swift test # 11 offline codec tests (golden vectors, compression, fuzz)
IIADNS_LIVE=1 swift test # + 5 live tests against public resolversThe idns CLI is a dig-style front end (and the engine's headless test harness).
It's named idns rather than iiadns so it doesn't collide with the app's
iiadns scheme under xcodebuild:
.build/debug/idns @1.1.1.1 example.com A # query a specific resolver
.build/debug/idns example.com MX +short # short output
.build/debug/idns -x 8.8.8.8 # reverse (PTR) lookup
.build/debug/idns cloudflare.com A +dnssec --raw # DO bit + raw hexdump
.build/debug/idns --compare example.com A # compare across resolvers
.build/debug/idns example.com A +tcp # force TCPcd apps/iiadns
xcodegen generate # regenerate iiadns.xcodeproj from project.yml
open iiadns.xcodeproj # build & run from Xcode (⌘R)
# or headless:
xcodebuild -project iiadns.xcodeproj -scheme iiadns -destination 'platform=macOS' buildThe Lookup tab is wired to the engine; the Resolvers tab reads the live system DNS
config; the Leak Test tab is scaffolded for the leakd backend.
If xcodebuild ever aborts at startup with a loader error in IDESimulatorFoundation
(a stale DVTDownloads.framework), Xcode's component install is incomplete. It's an
Xcode issue, not a project issue — fix it once with sudo xcodebuild -runFirstLaunch.
cd server/leakd
go test ./... # unit + end-to-end DNS observation tests
go build -o leakd .
# Run locally (high ports so no root needed):
./leakd -zone leak.example.test -dns 127.0.0.1:5354 -http 127.0.0.1:8088End-to-end smoke test against the running server:
SESSION=$(curl -s -XPOST http://127.0.0.1:8088/v1/session)
TOKEN=$(echo "$SESSION" | python3 -c 'import sys,json;print(json.load(sys.stdin)["token"])')
PROBE=$(echo "$SESSION" | python3 -c 'import sys,json;print(json.load(sys.stdin)["probes"][0])')
dig @127.0.0.1 -p 5354 "$PROBE" A +short # -> 192.0.2.1 (sink)
curl -s http://127.0.0.1:8088/v1/session/$TOKEN/results | python3 -m json.tool
# observed_resolvers now lists 127.0.0.1 (the resolver that queried us)In production, delegate a real zone (e.g. a subdomain you own) to leakd's
authoritative server, run it dual-stack, and put per-source rate limiting in front.
- Wire the app's Leak Test tab to a deployed
leakdendpoint + a verdict engine. - Local-network permission handling for system-resolver queries (incl. the macOS 26 reboot bug).
- ASN/geo enrichment (MaxMind GeoLite2) in
leakd. - Encrypted transports (DoH/DoT/DoQ) and
dig +tracein the engine.