Architecture

This document describes the architecture of the subenum tool, a Go-based command-line utility for subdomain enumeration.

1. Overview

The subenum tool operates through a sequence of steps to discover valid subdomains for a given target domain:

  1. Initialization: Parses command-line arguments, including the target domain, path to the wordlist file, concurrency level, and DNS timeout.
  2. Wildcard Detection: Resolves two random subdomains to detect wildcard DNS. If detected, exits unless -force is set.
  3. Wordlist Ingestion: Reads the wordlist file into memory, deduplicating entries in a single pass.
  4. Concurrent Resolution: A pool of worker goroutines is established. Each worker takes a prefix from the wordlist, constructs a full subdomain string (e.g., prefix.targetdomain.com), and attempts to resolve it using DNS.
  5. Output: Resolved subdomains are printed to stdout (pipe-friendly); all progress, verbose, and diagnostic output goes to stderr.
  6. Completion: The tool waits for all DNS lookups to complete before exiting.

This architecture is designed to be efficient by performing multiple DNS lookups concurrently, while also providing control over the level of concurrency and timeout settings.

Package Structure

main.go                        — CLI entry point (flag parsing, wiring, -tui dispatch)
internal/scan/runner.go        — Scan engine: Config, Event types, Run(ctx, cfg, events)
internal/dns/resolver.go       — ResolveDomain, ResolveDomainWithRetry, CheckWildcard
internal/dns/simulate.go       — SimulateResolution
internal/output/writer.go      — Thread-safe Writer (results→stdout, diagnostics→stderr)
internal/wordlist/reader.go    — LoadWordlist (dedup + sanitize)
internal/tui/model.go          — Root Bubble Tea model (form → scan state machine)
internal/tui/form.go           — Config form screen (textinput fields + toggles)
internal/tui/scan_view.go      — Live results screen (viewport + progress bar)
internal/tui/config.go         — Session persistence (load/save ~/.config/subenum/last.json)

2. Key Components / Modules

2.1. Argument Parsing

2.2. Wordlist Processing (internal/wordlist)

2.3. DNS Resolution Engine (internal/dns)

2.4. Concurrency Management (internal/scan)

2.5. Output Formatting (internal/output)

2.6. Progress Monitoring

2.7. Session Persistence (internal/tui/config.go)

3. Data Flow

The flow of data through the subenum application can be summarized as follows:

  1. Input: The user provides command-line arguments: the target domain, the path to a wordlist file (-w), a concurrency level (-t), a DNS timeout (-timeout), a DNS server (-dns-server), attempts (-attempts), and flags for verbose mode (-v), progress reporting (-progress), and force mode (-force).
  2. Configuration: These arguments are parsed and validated by the Argument Parsing component and used to configure the tool’s behavior.
  3. Wildcard Detection: Two random subdomains are resolved against the target domain. If both (or either) resolve, wildcard DNS is detected. The scan aborts unless -force is set.
  4. Wordlist Loading: wordlist.LoadWordlist reads the file in a single pass, sanitizes lines, and deduplicates entries into a slice.
    • Each entry is sent into the subdomains channel from the in-memory slice.
  5. Work Distribution: The subdomains channel acts as a queue for the Concurrency Management (Worker Pool) component.
    • Worker goroutines (number determined by the -t flag) pick up these prefixes from the channel.
  6. Subdomain Construction: Each worker goroutine takes a subdomainPrefix and concatenates it with the targetDomain (e.g., subdomainPrefix + "." + targetDomain) to form a fullDomain string.
  7. DNS Lookup: The fullDomain string, the timeout value, and the DNS server are passed to dns.ResolveDomainWithRetry within the DNS Resolution Engine.
    • dns.ResolveDomain attempts to resolve the fullDomain.
    • It returns true if the domain resolves successfully, false otherwise.
    • If verbose mode is enabled, it also prints detailed information about the resolution attempt.
  8. Output Generation:
    • If the resolution returns true, the worker goroutine uses the Output Formatting component to print the fullDomain to the standard output.
    • The atomic counter for found subdomains is incremented.
  9. Progress Tracking: After each DNS lookup:
    • The atomic counter for processed entries is incremented.
    • If progress reporting is enabled, a separate goroutine periodically updates the progress display.
  10. Loop/Termination:
    • Worker goroutines loop back to step 5 to pick up more work from the subdomains channel.
    • Once all prefixes are read from the wordlist, the Wordlist Processing component closes the subdomains channel.
    • Worker goroutines eventually terminate after the channel is closed and all in-flight DNS lookups are complete.
    • The main goroutine, which is waiting on a sync.WaitGroup, unblocks.
    • If verbose mode is enabled, a final summary is printed.
    • The program exits.

Visually, this can be seen as:

User Input -> Argument Parser -> [Wordlist File] -> Wordlist Processor -> scan.Run() -> Worker Goroutines -> DNS Resolver -> Event Channel -> Output (if resolved)

4. Error Handling Strategy

subenum handles different types of errors at various stages of its operation:

4.1. User Input Errors

4.2. File Operation Errors

4.3. DNS Resolution Errors

4.5. Graceful Shutdown

The tool listens for SIGINT and SIGTERM signals. Upon receiving an interrupt, it cancels the work context, drains in-flight workers, and exits cleanly with a summary of results processed so far.

4.6. Output File Support

When the -o flag is provided, resolved subdomains are written to the specified file (one per line) in addition to stdout. A mutex protects concurrent writes to both stdout and the output file.

4.7. Retry Mechanism

The -attempts flag (default: 1) controls the total number of DNS resolution attempts per subdomain. A value of 1 means no retries. A short linear backoff delay is applied between attempts to handle transient DNS failures. The deprecated -retries flag is still accepted as an alias but prints a warning to stderr.

Home Architecture Developer Guide Docker Contributing