subenum v0.9.0

Go library

pkg/subenum embeds the same engine the command line runs, so a recon pipeline or a custom tool can use it without shelling out. Wildcard filtering, rate limiting, retries, takeover hints, the reliability guard and the per-outcome accounting are all the same code.

go get github.com/TMHSDigital/subenum/pkg/subenum

The package follows semantic versioning from v1.0.0. Until then it may change between minor releases, while the CLI stays stable. The full API reference, with runnable examples, is on pkg.go.dev.

Scan: a finished run

Scan runs to completion and returns every result in the order the names resolved, plus the final Stats:

results, stats, err := subenum.Scan(ctx, subenum.Config{
    Domain: "example.com",            // URLs, ports and IDNs are normalized
    Rate:   100,                      // queries per second on the wire
    Types:  []string{"A", "AAAA", "CNAME"},
})
if err != nil {
    // Partial: cancelled, a wildcard zone without Force, a failed resolver
    // preflight, or the reliability guard stopped the scan. results and
    // stats still hold what was gathered.
}
for _, r := range results {
    fmt.Println(r.Name, r.Records, r.Takeover)
}

Every zero value in Config takes the same default as the CLI, so Config{Domain: "example.com"} is a complete configuration that uses the bundled 5,000-word list.

Run: a stream of events

Run returns a channel that streams results, notices and progress as they happen. It always ends with exactly one KindDone event carrying the final Stats, and then closes:

events, err := subenum.Run(ctx, subenum.Config{Domain: "example.com"})
if err != nil {
    return err // the Config itself was invalid; no scan started
}
for ev := range events {
    switch ev.Kind {
    case subenum.KindResult:
        fmt.Println(ev.Result.Name)
    case subenum.KindNotice, subenum.KindError:
        log.Println(ev.Message)
    case subenum.KindDone:
        fmt.Printf("found %d, %d timeouts\n", ev.Stats.Found, ev.Stats.Timeout)
    }
}

Read the channel until it closes, or cancel ctx. Breaking out of the loop with a deferred cancel() is safe: once ctx is cancelled the scan stops without waiting for a reader, and its goroutines exit within about a second whether or not the channel is read. A consumer that keeps reading after cancelling still gets KindDone with the counts so far. While ctx is live, a consumer that stops reading pauses the scan once the buffer fills.

Configuration

Field Default What it does
Domain required Apex to enumerate under.
Words DefaultWords() Labels to try, or dotted paths such as api.eu.
Resolver 8.8.8.8:53 Trusted server: ip:port, tls://host, or https://host/path.
ResolverPool none Extra plain-UDP resolvers. Every hit is re-checked against Resolver.
Concurrency 100 Workers looking up names at once.
Timeout 1s Bound on one lookup of one record type.
Attempts 1 Tries per name. Only failures are retried, never NXDOMAIN.
Rate unlimited Queries per second on the wire, counting every type, retry and probe.
Types A, AAAA Record types. CNAME populates Result.Takeover.
Recursive, Depth off, 1 Enumerate again under each name found.
MaxQueries unlimited Cap on candidate names tested.
Exclude none Out-of-scope names and *.parent branches, never queried.
Force off Scan a wildcard zone, dropping answers that match its fingerprint.
NoAbort off Keep going when more than a fifth of lookups fail.
Simulate, HitRate, Seed off, 15, random Synthetic results with no DNS traffic, for tests and demos.

Accounting

Stats puts each candidate name into exactly one outcome: Found, NXDomain, Timeout, Refused, Other or WildcardFiltered. Alongside are QueriesSent, Skipped (left untested by MaxQueries), Excluded, and the resolver-pool counters PoolHits, Confirmed and Unconfirmed. Aborted means the reliability guard stopped the scan, so the negatives mean nothing.

Testing your integration

Set Simulate in tests and examples. It exercises the full pipeline with no DNS queries, and a fixed Seed makes the results reproducible:

results, _, err := subenum.Scan(ctx, subenum.Config{
    Domain:   "example.com",
    Words:    []string{"www", "api", "mail", "dev"},
    Simulate: true,
    HitRate:  50,
    Seed:     42,
})

Authorized use only. Only enumerate domains you own or have permission to test.