diff --git a/README.md b/README.md index 107c448..c777597 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,246 @@ # ExploreDNS -DNS reconnaissance and exploration tool. +ExploreDNS is a comprehensive DNS traversal tool that explores every possible +resolution path for a domain — from the root servers all the way down — just +like a real iterative resolver, but without stopping at the first answer. It +follows every referral exhaustively, collates all results, and presents them in +a structured, human-readable (or JSON) report. -## Build +Inspired by the classic [dnstraverse](https://github.com/squish/dnstraverse) +Ruby tool, ExploreDNS is a modern Go rewrite that produces a self-contained +binary with no runtime dependencies. + +--- + +## Features + +- **Full iterative traversal** — queries root servers and follows every referral + branch, mirroring real resolver behaviour +- **All root servers** — optionally query all 13 root server sets in parallel +- **No-glue resolution** — automatically resolves nameserver addresses when + referrals lack glue records +- **DNS server fingerprinting** — identifies server software via `version.bind` + CHAOS queries +- **Multiple output formats** — coloured text tree and machine-readable JSON +- **Configurable transport** — UDP/TCP, EDNS0 buffer size, retries, timeouts +- **Fast mode** — shares glue across branches for speed; disable for independent + paths +- **CNAME tracking** — follows CNAME chains and detects loops + +--- + +## Installation + +### Pre-built binary + +Download the latest release binary for your platform from the +[Releases page](https://gitea.hansenits.com.au/hits/ExploreDNS/releases). + +### Build from source + +Requires Go 1.21 or later. ```sh -go build -o bin/exploredns ./cmd/exploredns +git clone https://gitea.hansenits.com.au/hits/ExploreDNS.git +cd ExploreDNS +make build # produces bin/exploredns ``` -Or use the Makefile: +### go install ```sh -make build +go install github.com/hits/ExploreDNS/cmd/exploredns@latest ``` -## Usage +--- + +## Quick Start ```sh -./bin/exploredns +# Basic A record traversal +exploredns www.example.com + +# MX records for a domain +exploredns --type MX example.com + +# Use all 13 root server sets +exploredns --all-root-servers www.example.com + +# JSON output +exploredns --json www.example.com + +# Quiet (results only) +exploredns --quiet www.example.com + +# Debug mode +exploredns --debug www.example.com + +# Library-level debug (very verbose) +exploredns --dd www.example.com + +# Force TCP +exploredns --allow-tcp --always-tcp www.example.com + +# Disable fast mode (independent paths per branch) +exploredns --fast=false www.example.com + +# Increase traversal depth limit +exploredns --max-depth 30 www.example.com ``` -## Development +--- + +## CLI Reference -```sh -make test # run tests -make lint # run go vet -make clean # remove build artifacts ``` +Usage: + exploredns [flags] + +Query Options: + --type Record type to query (default: A) + Supported: A, AAAA, NS, CNAME, MX, TXT, SOA, PTR, ANY + --root-server Override the root server IP address + --all-root-servers Query all 13 root server sets (default: false) + --root-aaaa Include IPv6 addresses for root servers (default: false) + --follow-aaaa Only follow AAAA addresses for referrals (default: false) + +Transport Options: + --udp-size EDNS0 UDP buffer size, 512–4096 (default: 2048) + --allow-tcp Fall back to TCP on truncation (default: true) + --always-tcp Always use TCP (requires --allow-tcp) + --retries Per-server retry count, 0–10 (default: 2) + +Traversal Options: + --max-depth Maximum referral depth, 1–100 (default: 20) + --fast / --fast=false Share glue cache across branches (default: true) + +Output Options: + --json Emit results as JSON instead of text + --verbose, -v Show extra detail in text output + --debug, -d Enable application debug messages (stderr) + --dd Enable library-level debug messages (very verbose) + --quiet, -q Suppress header and supplementary information + --show-progress Show live traversal progress (default: true) + --no-show-progress Hide traversal progress + --show-resolves Show glue-resolution steps (default: true) + --no-show-resolves Hide glue-resolution steps + --show-servers Show which servers were queried (default: true) + --no-show-servers Hide server list + --show-versions Show DNS server software versions (default: true) + --no-show-versions Hide server versions + --show-all-stats Show query statistics (default: true) + --no-show-all-stats Hide statistics + --show-results Show per-branch query results (default: true) + --no-show-results Hide per-branch results + --show-summary-results Show deduplicated summary section (default: true) + --no-show-summary-results Hide summary section +``` + +--- + +## Output Formats + +### Text (default) + +Coloured, hierarchical tree output showing each traversal branch, the servers +queried, referrals followed, and final answers. Disable colour by setting the +`NO_COLOR` environment variable. + +### JSON (`--json`) + +Structured JSON array of traversal results. Suitable for piping into `jq` or +ingesting into other tools. Each element contains the referral metadata, the +responding server, the response type, and the decoded DNS records. + +--- + +## Comparison with dnstraverse + +| Feature | dnstraverse (Ruby) | ExploreDNS (Go) | +|---|---|---| +| Language | Ruby | Go | +| Self-contained binary | No | Yes | +| All root servers | Yes | Yes | +| No-glue resolution | Yes | Yes | +| JSON output | No | Yes | +| Server fingerprinting | Yes | Yes | +| CNAME loop detection | Partial | Yes | +| Active development | Dormant | Active | + +--- ## Project Structure ``` -cmd/exploredns/ CLI entry point -internal/dns/ DNS query operations -internal/traverse/ DNS tree traversal -internal/fingerprint/ DNS server fingerprinting -internal/output/ Result formatting and output -internal/config/ Configuration management +cmd/exploredns/ CLI entry point and flag parsing +internal/config/ Configuration types, validation, and usage text +internal/dns/ DNS query layer, root discovery, transport +internal/traverse/ Core traversal engine, referral resolution, caching +internal/fingerprint/ DNS server version fingerprinting (version.bind CHAOS) +internal/output/ Result formatting — text tree and JSON renderers +internal/integration/ End-to-end integration tests ``` +--- + +## Development + +```sh +make build # compile binary to bin/exploredns +make test # run all unit and integration tests +make lint # run go vet +make clean # remove build artefacts +``` + +Run a single package's tests: + +```sh +go test ./internal/traverse/... +``` + +--- + +## Architecture Overview + +``` +main → config.Parse → traverse.NewTraverser → traverse.Traverse + │ + ┌─────────▼──────────┐ + │ Stack (BFS/DFS) │ + │ Referral queue │ + └─────────┬──────────┘ + │ per referral + ┌─────────▼──────────┐ + │ processReferral │ + │ ├─ resolveGlue │ (no-glue NS resolution) + │ └─ queryServer │ (dns.Query) + └─────────┬──────────┘ + │ + ┌─────────────▼──────────────┐ + │ Response classifier │ + │ Answer / Referral / │ + │ CNAME / NXDOMAIN / │ + │ SERVFAIL / Error │ + └─────────────┬──────────────┘ + │ + ┌─────────▼──────────┐ + │ output.Formatter │ + │ (text | JSON) │ + └────────────────────┘ +``` + +The traversal engine (`internal/traverse`) maintains a work stack of +`Referral` objects. Each referral represents a single query to a single set +of nameservers. When a response contains further referrals, child `Referral` +objects are pushed onto the stack and processed in turn. + +The `InfoCache` is used to store glue records discovered during traversal. In +fast mode (default), a single root cache is shared across all branches so that +glue discovered early is reused. In non-fast mode, each branch gets its own +independent cache. + +--- + ## License -MIT +MIT — see [LICENSE](LICENSE). diff --git a/internal/config/config.go b/internal/config/config.go index 5f7dd1d..4d7cf0e 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,3 +1,5 @@ +// Package config defines the configuration types and defaults for ExploreDNS, +// along with validation helpers and the CLI usage text. package config import ( diff --git a/internal/dns/query.go b/internal/dns/query.go index 61b1e82..389cb5f 100644 --- a/internal/dns/query.go +++ b/internal/dns/query.go @@ -1,3 +1,9 @@ +// Package dns provides the low-level DNS query primitives used by ExploreDNS. +// +// It wraps the github.com/miekg/dns library to provide retrying, TCP fallback, +// EDNS0 buffer size negotiation, and root server discovery. The package is +// intentionally narrow: it sends iterative (non-recursive) queries and returns +// the raw responses for the traversal engine to interpret. package dns import ( diff --git a/internal/fingerprint/fingerprint.go b/internal/fingerprint/fingerprint.go index ad1f890..df1617a 100644 --- a/internal/fingerprint/fingerprint.go +++ b/internal/fingerprint/fingerprint.go @@ -1,3 +1,10 @@ +// Package fingerprint identifies DNS server software by querying the +// version.bind name in the CHAOS class. Many authoritative and recursive DNS +// servers respond with a version string (e.g. "BIND 9.18.1-1") that can be +// used to identify the software and version in use. +// +// Queries are cached per server IP so that repeated lookups within a single +// traversal run do not incur extra network round-trips. package fingerprint import ( diff --git a/internal/output/formatter.go b/internal/output/formatter.go index 5cd44b7..cb30aa0 100644 --- a/internal/output/formatter.go +++ b/internal/output/formatter.go @@ -1,3 +1,13 @@ +// Package output renders ExploreDNS traversal results for human consumption or +// machine processing. +// +// Two formats are supported: +// +// - FormatText — a coloured hierarchical tree (default) +// - FormatJSON — a JSON array of traversal results +// +// Create a Formatter via NewFormatter and call RunTraversal to drive the +// traversal engine and stream output incrementally. package output import ( diff --git a/internal/traverse/traverse.go b/internal/traverse/traverse.go index 5550fe0..83914e1 100644 --- a/internal/traverse/traverse.go +++ b/internal/traverse/traverse.go @@ -1 +1,27 @@ +// Package traverse implements the core DNS traversal engine for ExploreDNS. +// +// The traversal engine starts from the DNS root servers and iteratively +// follows every referral it receives, building a complete picture of the +// delegation path for a domain. Unlike a standard recursive resolver, which +// stops at the first authoritative answer, the traversal engine explores every +// branch so that delegation mismatches, lame delegations, or split authorities +// are all visible in the output. +// +// # Architecture +// +// A Traverser maintains a stack of Referral objects. Each Referral +// represents a pending query to a specific set of nameservers for a specific +// name and record type. The engine pops referrals one at a time, sends the +// query, classifies the response, and pushes any child referrals back onto the +// stack. +// +// When a referral contains nameserver names but no glue records (IP addresses), +// the engine resolves them via a secondary traversal before continuing. +// +// # Caching +// +// An InfoCache stores discovered glue records. In fast mode (default) a +// single root cache is shared across all branches so that glue discovered in +// one branch is immediately available to sibling branches. Disable fast mode +// (TraverserConfig.Fast = false) for fully independent branch resolution. package traverse diff --git a/internal/traverse/traverser.go b/internal/traverse/traverser.go index c09f835..b6baea2 100644 --- a/internal/traverse/traverser.go +++ b/internal/traverse/traverser.go @@ -11,12 +11,20 @@ import ( miekgdns "github.com/miekg/dns" ) +// TraverserConfig configures the behaviour of a Traverser. type TraverserConfig struct { + // MaxDepth is the maximum referral depth before the traversal gives up. MaxDepth int + // QueryType is the DNS record type to query (e.g. dns.TypeA). QueryType uint16 + // RootConfig controls how root servers are discovered. RootConfig *dns.RootDiscoveryConfig + // QueryConfig controls per-query transport parameters. QueryConfig *dns.QueryConfig + // RootAddrs is an optional pre-seeded list of root server IP addresses. + // When non-empty, root discovery via RootConfig is skipped. RootAddrs []net.IP + // Hooks provides optional callbacks for traversal events. Hooks *TraverserHooks // Fast controls cache sharing across branches. When true (default), child // branches inherit glue discovered by earlier branches via the shared root @@ -37,11 +45,14 @@ func DefaultTraverserConfig() *TraverserConfig { } } +// TraversalResult pairs a Referral with the Response received when it was processed. type TraversalResult struct { Referral *Referral Response *Response } +// Traverser performs an exhaustive iterative DNS traversal starting from the +// root servers. Create one via NewTraverser and call Traverse to start a run. type Traverser struct { config *TraverserConfig exchange dns.ExchangeFunc