subenum
Fast, concurrent subdomain enumeration via DNS brute-forcing. Written in pure Go.


Concurrent Workers · Context-Aware Cancellation · Retry with Backoff · Wildcard Detection · Simulation Mode · Interactive TUI
Quick Start | Documentation | Architecture | Changelog
Feature Matrix
| Module |
Capabilities |
| Worker Pool |
Spawn N goroutines for parallel DNS resolution with configurable concurrency ceiling. |
| DNS Engine |
Resolve subdomains against any custom DNS server with per-query timeouts and exponential-backoff retries. |
| Wildcard Detection |
Double-probe random subdomain check before scanning; exits early unless -force is set. |
| Graceful Shutdown |
Trap SIGINT/SIGTERM, drain in-flight workers, flush partial results to disk. |
| Input Validation |
Enforce RFC-compliant domain syntax and strict IP:port format for DNS server arguments. |
| Wordlist Dedup |
Automatically remove duplicate entries from the wordlist before scanning. |
| Simulation Mode |
Generate synthetic DNS results at a configurable hit rate without network I/O. |
| Output Pipeline |
Stream resolved domains to stdout (pipe-friendly); progress and diagnostics go to stderr. |
| Progress Reporting |
Live terminal progress with atomic counters, updated on a 2-second ticker. |
| Interactive TUI |
Form-based config screen and live-scrolling results view via -tui flag (Bubble Tea). No arguments required. |
System Architecture
flowchart LR
subgraph Input
A[Wordlist File] -->|"dedup + load"| B(Entry Slice)
C[CLI Flags] --> D(Argument Parser)
end
subgraph PreScan
D --> W{Wildcard\nDetection}
W -->|"no wildcard / --force"| E
end
subgraph Engine
B --> E{Worker Pool\nN Goroutines}
E -->|subdomain.domain| F[DNS Resolver]
F -->|retry + backoff| F
G[Context] -->|cancel| E
G -->|timeout| F
end
subgraph OutputLayer ["Output"]
F -->|resolved| H["stdout (results only)"]
F -->|resolved| I[Output File]
E -->|atomic counters| J["stderr (progress)"]
end
K[SIGINT / SIGTERM] -->|cancel| G
[!IMPORTANT]
Authorized use only. Only scan domains you own or have explicit written permission to test. Unauthorized scanning may violate applicable laws. Users are solely responsible for compliance.
[!NOTE]
Wildcard DNS detection. Before scanning, subenum resolves two random subdomains to check for wildcard DNS. If the domain uses wildcard records, the tool exits with a warning (all subdomains would resolve, making results meaningless). Pass -force to override and scan anyway.
[!CAUTION]
Simulation mode (-simulate) generates synthetic results and performs zero network I/O. Do not confuse simulated output with real DNS data.
Installation
Prerequisites: Go >= 1.22 · Git · Make (optional) · Docker (optional)
One-liner (build from source):
git clone https://github.com/TMHSDigital/subenum.git && cd subenum && go build -buildvcs=false -o subenum
Pre-built binaries: download from the Releases page (Linux, macOS, Windows).
Docker:
docker build -t subenum . && docker run --rm -v $(pwd)/data:/data subenum -w /data/wordlist.txt example.com
Make:
make build # compile binary
make simulate # safe run, no DNS queries
make help # list all targets
Configuration
| Flag |
Default |
Description |
-w <file> |
-- |
Wordlist file, one prefix per line (required) |
-t <n> |
100 |
Concurrent worker goroutines |
-timeout <ms> |
1000 |
Per-query DNS timeout in milliseconds |
-dns-server <ip:port> |
8.8.8.8:53 |
DNS server address (validated on startup) |
-attempts <n> |
1 |
Total DNS resolution attempts per subdomain (1 = no retry) |
-force |
false |
Continue scanning even if wildcard DNS is detected |
-o <file> |
-- |
Write results to file in addition to stdout |
-v |
false |
Verbose output: IPs, timings, per-query status (written to stderr) |
-progress |
true |
Live progress line on stderr (disable with -progress=false) |
-simulate |
false |
Simulation mode: no real DNS queries |
-hit-rate <n> |
15 |
Simulated resolution rate, percent (1-100) |
-tui |
false |
Launch the interactive Terminal UI (no other flags required) |
-version |
-- |
Print version and exit |
-retries <n> |
-- |
Deprecated: alias for -attempts, prints a warning |
Usage
subenum -w <wordlist> [flags] <domain>
Basic scan:
./subenum -w wordlist.txt example.com
High-throughput with Cloudflare DNS, saving results:
./subenum -w wordlist.txt -t 300 -timeout 500 -dns-server 1.1.1.1:53 -o results.txt example.com
Resilient scan for flaky networks:
./subenum -w wordlist.txt -attempts 3 -timeout 2000 example.com
Pipe-friendly output (progress goes to stderr, only results on stdout):
./subenum -w wordlist.txt example.com | cut -d' ' -f2 | your-takeover-scanner
Force scan on wildcard domain:
./subenum -w wordlist.txt -force example.com
Simulation (zero network I/O):
./subenum -simulate -hit-rate 20 -w examples/sample_wordlist.txt example.com
Graceful shutdown: press Ctrl+C at any time. In-flight queries drain, partial results are flushed.
Interactive TUI (no arguments):
./subenum -tui
# or
make tui
Fill in the form, press ctrl+r to scan. Last-used values are saved to ~/.config/subenum/last.json and restored on next launch.
| Key |
Action |
tab / shift+tab / ↑↓ |
Navigate fields |
space |
Toggle Simulate / Force |
ctrl+r |
Start scan |
ctrl+c |
Abort scan (in scan view) / quit (in form) |
r |
New scan — returns to form with last values pre-filled |
q |
Quit after scan completes |
Project Anatomy
subenum/
├── .github/
│ ├── workflows/
│ │ ├── go.yml # CI: build, test, lint, release
│ │ ├── codeql.yml # Weekly CodeQL security analysis
│ │ └── pages.yml # GitHub Pages deployment
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md # Structured bug report form
│ │ └── feature_request.md # Feature proposal template
│ ├── dependabot.yml # Automated dependency updates
│ └── PULL_REQUEST_TEMPLATE.md
├── data/
│ └── wordlist.txt # Default wordlist for Docker/Make
├── docs/
│ ├── ARCHITECTURE.md # Internals: worker pool, context, output
│ ├── CONTRIBUTING.md # PR workflow, testing, ethical guidelines
│ ├── DEVELOPER_GUIDE.md # Build, test, project structure
│ ├── DOCUMENTATION_STRUCTURE.md
│ ├── docker.md # Container setup and volume mounting
│ ├── _config.yml # Jekyll config for GitHub Pages
│ └── index.md # GitHub Pages landing page
├── examples/
│ ├── sample_wordlist.txt # 50-entry starter wordlist
│ ├── advanced_usage.md # Scripting and integration patterns
│ ├── demo.sh # Quick demo script
│ └── multi_domain_scan.sh # Batch scanning example
├── internal/
│ ├── dns/
│ │ ├── resolver.go # ResolveDomain, ResolveDomainWithRetry, CheckWildcard
│ │ ├── resolver_test.go # DNS resolution and wildcard detection tests
│ │ ├── simulate.go # SimulateResolution (synthetic DNS)
│ │ └── simulate_test.go # Simulation logic tests
│ ├── output/
│ │ ├── writer.go # Thread-safe output (results→stdout, rest→stderr)
│ │ └── writer_test.go # Output writer tests
│ └── wordlist/
│ ├── reader.go # LoadWordlist (dedup + sanitize)
│ └── reader_test.go # Wordlist loading and dedup tests
├── tools/
│ └── wordlist-gen.go # Custom wordlist generator utility
├── main.go # CLI entry point: flag parsing, wiring
├── main_test.go # CLI-level tests: validation, flag logic
├── go.mod # Go module (zero external dependencies)
├── Dockerfile # Multi-stage Alpine build
├── docker-compose.yml # Compose orchestration
├── Makefile # Build, test, lint, simulate, Docker targets
├── .gitattributes # Line-ending normalization rules
├── .golangci.yml # Linter configuration (golangci-lint v2)
├── CHANGELOG.md # Versioned release history
├── SECURITY.md # Vulnerability disclosure policy
└── LICENSE # GNU General Public License v3.0
Tech Stack
|
|
| Core Engine |
Go 1.22 · net.Resolver · context · sync/atomic |
| Concurrency |
goroutines · channels · sync.WaitGroup · sync.Mutex |
| Infrastructure |
Docker · Alpine · Make · docker-compose |
| CI/CD |
GitHub Actions · CodeQL · Dependabot · golangci-lint |
| Quality |
go test -race · golangci-lint v2 · gosec · govet |
Development
See CONTRIBUTING.md for the pull request workflow and ethical guidelines.
See DEVELOPER_GUIDE.md for build instructions, testing, and project structure.