- Rewrite README.md with overview, features, installation (go install +
build from source), quick start, full CLI flag reference table,
output section descriptions, project structure, and development guide
- Add package-level doc comments to all five internal packages:
config, dns, fingerprint, output, traverse (via doc.go or existing
package-declaration files)
- Add GoDoc comments on every exported type, constant, function, and
method across all packages:
- internal/config: Config struct fields, all Parse*/Default/Validate
- internal/dns: QueryConfig, Resolver, BasicResolver, CachingResolver,
ExchangeFunc, RootServer, RootDiscoveryConfig, DecodedResponse,
ResponseClassification, all exported helpers
- internal/fingerprint: Fingerprinter, New, NewWithTimeout, Query,
FingerprintAll
- internal/traverse: Traverser, TraverserConfig, TraversalResult,
Referral, ResolutionState, Response, ResponseType, InfoCache,
Stack, TraverserHooks, EventStage, TraversalEvent, EventHandler,
CircularReferralError, UnresolvableNameserverError
- internal/output: Format, Config, Formatter, SummaryStats,
NewFormatter, AttachHooks, RunTraversal, DefaultConfig,
ComputeSummary
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
co-authored by
Copilot
multica-agent
parent
fe1afe2a97
commit
9aa85d8e5d
@@ -8,6 +8,13 @@ import (
|
||||
miekgdns "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// InfoCache is a two-level (parent/child) concurrent cache for NS records and
|
||||
// glue addresses discovered during traversal.
|
||||
//
|
||||
// Lookups walk the parent chain: a child cache falls back to its parent when
|
||||
// no local entry is found. Writes always go to the local cache, never to the
|
||||
// parent. This makes it safe to give sibling branches separate child caches
|
||||
// that share the root cache read-only in fast mode.
|
||||
type InfoCache struct {
|
||||
parent *InfoCache
|
||||
mu sync.RWMutex
|
||||
@@ -15,6 +22,8 @@ type InfoCache struct {
|
||||
glue map[string][]net.IP
|
||||
}
|
||||
|
||||
// NewInfoCache creates an InfoCache with an optional parent.
|
||||
// Pass nil for a root-level cache with no parent.
|
||||
func NewInfoCache(parent *InfoCache) *InfoCache {
|
||||
return &InfoCache{
|
||||
parent: parent,
|
||||
@@ -23,6 +32,8 @@ func NewInfoCache(parent *InfoCache) *InfoCache {
|
||||
}
|
||||
}
|
||||
|
||||
// StoreNS records the nameserver names for zone in the local cache.
|
||||
// Duplicate names within a zone are deduplicated.
|
||||
func (c *InfoCache) StoreNS(zone string, nameservers []string) {
|
||||
if len(nameservers) == 0 {
|
||||
return
|
||||
@@ -40,6 +51,8 @@ func (c *InfoCache) StoreNS(zone string, nameservers []string) {
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
// LookupNS returns the cached nameserver names for zone,
|
||||
// walking the parent chain when no local entry is found.
|
||||
func (c *InfoCache) LookupNS(zone string) []string {
|
||||
zone = normalize(zone)
|
||||
if names := c.localNS(zone); len(names) > 0 {
|
||||
@@ -63,6 +76,8 @@ func (c *InfoCache) localNS(zone string) []string {
|
||||
return result
|
||||
}
|
||||
|
||||
// StoreGlue records the IP addresses for a nameserver hostname in the local cache.
|
||||
// Duplicate addresses are deduplicated.
|
||||
func (c *InfoCache) StoreGlue(name string, addrs []net.IP) {
|
||||
if len(addrs) == 0 {
|
||||
return
|
||||
@@ -80,6 +95,8 @@ func (c *InfoCache) StoreGlue(name string, addrs []net.IP) {
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
// LookupGlue returns the cached IP addresses for a nameserver hostname,
|
||||
// walking the parent chain when no local entry is found.
|
||||
func (c *InfoCache) LookupGlue(name string) []net.IP {
|
||||
name = normalize(name)
|
||||
if addrs := c.localGlue(name); len(addrs) > 0 {
|
||||
@@ -103,16 +120,20 @@ func (c *InfoCache) localGlue(name string) []net.IP {
|
||||
return result
|
||||
}
|
||||
|
||||
// Child creates a new InfoCache that inherits from c.
|
||||
// The child reads from c when a local lookup misses, but never writes to c.
|
||||
func (c *InfoCache) Child() *InfoCache {
|
||||
return NewInfoCache(c)
|
||||
}
|
||||
|
||||
// NSCount returns the number of zone→nameservers entries in the local cache.
|
||||
func (c *InfoCache) NSCount() int {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
return len(c.ns)
|
||||
}
|
||||
|
||||
// GlueCount returns the number of nameserver→addresses entries in the local cache.
|
||||
func (c *InfoCache) GlueCount() int {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
@@ -1,20 +1,32 @@
|
||||
package traverse
|
||||
|
||||
// EventStage indicates whether a TraversalEvent is fired at the start or
|
||||
// completion of processing a Referral.
|
||||
type EventStage int
|
||||
|
||||
// Event stage constants.
|
||||
const (
|
||||
EventStart EventStage = iota
|
||||
EventComplete
|
||||
EventStart EventStage = iota // fired when a Referral is about to be queried
|
||||
EventComplete // fired after the Response has been produced
|
||||
)
|
||||
|
||||
// TraversalEvent carries context for a single traversal event delivered to
|
||||
// the OnEvent hook.
|
||||
type TraversalEvent struct {
|
||||
Stage EventStage
|
||||
Result TraversalResult
|
||||
// Stage is EventStart or EventComplete.
|
||||
Stage EventStage
|
||||
// Result carries the Referral and (for EventComplete) the Response.
|
||||
Result TraversalResult
|
||||
// IsResolve is true when the event relates to a nameserver address
|
||||
// resolution sub-traversal rather than the main traversal.
|
||||
IsResolve bool
|
||||
}
|
||||
|
||||
// EventHandler is the function signature for traversal event callbacks.
|
||||
type EventHandler func(TraversalEvent)
|
||||
|
||||
// TraverserHooks holds the optional event callback for a Traverser.
|
||||
// Assigning OnEvent enables progress and result notifications.
|
||||
type TraverserHooks struct {
|
||||
OnEvent EventHandler
|
||||
}
|
||||
|
||||
@@ -11,12 +11,14 @@ import (
|
||||
"golang.org/x/net/idna"
|
||||
)
|
||||
|
||||
// ResolutionState tracks whether a Referral's nameserver addresses have been resolved.
|
||||
type ResolutionState int
|
||||
|
||||
// Resolution state constants.
|
||||
const (
|
||||
StateUnresolved ResolutionState = iota
|
||||
StateResolving
|
||||
StateResolved
|
||||
StateUnresolved ResolutionState = iota // nameserver addresses are not yet known
|
||||
StateResolving // address resolution is in progress
|
||||
StateResolved // addresses are available in Addresses
|
||||
)
|
||||
|
||||
func (s ResolutionState) String() string {
|
||||
@@ -32,19 +34,32 @@ func (s ResolutionState) String() string {
|
||||
}
|
||||
}
|
||||
|
||||
// Referral represents a pending DNS query: a (name, qtype) pair delegated to a
|
||||
// set of nameserver addresses. Referrals form a linked-list chain through
|
||||
// Parent, enabling loop and depth detection.
|
||||
type Referral struct {
|
||||
Name string
|
||||
Qtype uint16
|
||||
Qclass uint16
|
||||
// Name is the fully-qualified domain name being queried.
|
||||
Name string
|
||||
// Qtype is the DNS record type being queried.
|
||||
Qtype uint16
|
||||
// Qclass is the DNS class (always ClassINET in practice).
|
||||
Qclass uint16
|
||||
// Bailiwick is the zone that delegated this referral.
|
||||
Bailiwick string
|
||||
|
||||
// Addresses holds the resolved IP addresses for this nameserver referral.
|
||||
Addresses []net.IP
|
||||
State ResolutionState
|
||||
// State tracks whether Addresses have been resolved.
|
||||
State ResolutionState
|
||||
|
||||
// NSName is the nameserver hostname (before IP resolution).
|
||||
NSName string
|
||||
// Parent is the Referral that triggered this one, or nil for the root.
|
||||
Parent *Referral
|
||||
Depth int
|
||||
Prob float64
|
||||
// Depth is the number of referral hops from the root.
|
||||
Depth int
|
||||
// Prob is the probability weight for this branch (product of 1/fanout at each step).
|
||||
Prob float64
|
||||
}
|
||||
|
||||
// idnaLookup is the IDN lookup profile used to convert internationalised domain
|
||||
@@ -70,6 +85,9 @@ func toASCII(name string) string {
|
||||
return ascii
|
||||
}
|
||||
|
||||
// NewReferral creates a Referral for (name, qtype) within bailiwick, at the
|
||||
// given depth and probability. The name and bailiwick are normalised to
|
||||
// lowercase FQDN, and internationalised labels are converted to punycode.
|
||||
func NewReferral(name string, qtype uint16, bailiwick string, depth int, prob float64, parent *Referral) *Referral {
|
||||
return &Referral{
|
||||
Name: miekgdns.Fqdn(strings.ToLower(toASCII(name))),
|
||||
@@ -83,6 +101,8 @@ func NewReferral(name string, qtype uint16, bailiwick string, depth int, prob fl
|
||||
}
|
||||
}
|
||||
|
||||
// InBailiwick reports whether name is within this referral's bailiwick zone.
|
||||
// A root bailiwick ("." or "") is treated as matching everything.
|
||||
func (r *Referral) InBailiwick(name string) bool {
|
||||
if r.Bailiwick == "" || r.Bailiwick == "." {
|
||||
return true
|
||||
@@ -91,10 +111,12 @@ func (r *Referral) InBailiwick(name string) bool {
|
||||
return miekgdns.IsSubDomain(r.Bailiwick, fqdn)
|
||||
}
|
||||
|
||||
// HasAddresses reports whether at least one nameserver IP address is known.
|
||||
func (r *Referral) HasAddresses() bool {
|
||||
return len(r.Addresses) > 0
|
||||
}
|
||||
|
||||
// SetAddresses stores addrs and updates State accordingly.
|
||||
func (r *Referral) SetAddresses(addrs []net.IP) {
|
||||
r.Addresses = addrs
|
||||
if len(addrs) > 0 {
|
||||
@@ -104,6 +126,8 @@ func (r *Referral) SetAddresses(addrs []net.IP) {
|
||||
}
|
||||
}
|
||||
|
||||
// CircularReferralError is returned when a referral chain revisits a nameserver,
|
||||
// indicating a circular delegation.
|
||||
type CircularReferralError struct {
|
||||
Name string
|
||||
Chain []string
|
||||
@@ -113,6 +137,8 @@ func (e *CircularReferralError) Error() string {
|
||||
return fmt.Sprintf("circular referral detected for %s: %v", e.Name, e.Chain)
|
||||
}
|
||||
|
||||
// UnresolvableNameserverError is returned when a nameserver hostname cannot be
|
||||
// resolved to any IP address.
|
||||
type UnresolvableNameserverError struct {
|
||||
Name string
|
||||
Reason string
|
||||
@@ -122,6 +148,9 @@ func (e *UnresolvableNameserverError) Error() string {
|
||||
return fmt.Sprintf("unresolvable nameserver %s: %s", e.Name, e.Reason)
|
||||
}
|
||||
|
||||
// Resolve attempts to resolve the nameserver addresses for this Referral by
|
||||
// performing a fresh iterative traversal. It stores the found addresses and
|
||||
// updates State. Returns an error when resolution fails.
|
||||
func (r *Referral) Resolve(ctx context.Context, traverser *Traverser, cache *InfoCache, visited map[string]bool, depth int) error {
|
||||
if r.HasAddresses() {
|
||||
r.State = StateResolved
|
||||
|
||||
@@ -7,19 +7,21 @@ import (
|
||||
miekgdns "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// ResponseType classifies the outcome of querying a single Referral.
|
||||
type ResponseType int
|
||||
|
||||
// Response type constants used to drive traversal logic.
|
||||
const (
|
||||
RespReferral ResponseType = iota
|
||||
RespAnswer
|
||||
RespCNAMEFollow
|
||||
RespNODATA
|
||||
RespNXDOMAIN
|
||||
RespSERVFAIL
|
||||
RespREFUSED
|
||||
RespNOTIMPL
|
||||
RespCNAMELoop
|
||||
RespError
|
||||
RespReferral ResponseType = iota // server returned an NS referral
|
||||
RespAnswer // server returned a final answer
|
||||
RespCNAMEFollow // answer contains a CNAME requiring further traversal
|
||||
RespNODATA // NOERROR with no matching records
|
||||
RespNXDOMAIN // name does not exist
|
||||
RespSERVFAIL // server failure
|
||||
RespREFUSED // query refused
|
||||
RespNOTIMPL // query type not implemented
|
||||
RespCNAMELoop // CNAME chain revisits a name already in the chain
|
||||
RespError // transport or decoding error
|
||||
)
|
||||
|
||||
func (rt ResponseType) String() string {
|
||||
@@ -49,15 +51,25 @@ func (rt ResponseType) String() string {
|
||||
}
|
||||
}
|
||||
|
||||
// Response is the result of querying a single Referral against a specific
|
||||
// nameserver. It contains the decoded DNS message, the classified ResponseType,
|
||||
// and any error message for display.
|
||||
type Response struct {
|
||||
Referral *Referral
|
||||
Server net.IP
|
||||
Cache *InfoCache
|
||||
Decoded *dns.DecodedResponse
|
||||
Type ResponseType
|
||||
// Referral is the query this response corresponds to.
|
||||
Referral *Referral
|
||||
// Server is the nameserver IP that was queried.
|
||||
Server net.IP
|
||||
// Cache is the InfoCache used during processing (for glue resolution).
|
||||
Cache *InfoCache
|
||||
// Decoded holds the structured DNS response fields.
|
||||
Decoded *dns.DecodedResponse
|
||||
// Type is the high-level classification of this response.
|
||||
Type ResponseType
|
||||
// ErrorMessage is a human-readable description when Type is RespError or RespCNAMELoop.
|
||||
ErrorMessage string
|
||||
}
|
||||
|
||||
// NewResponse creates a Response for the given Referral and server.
|
||||
func NewResponse(ref *Referral, server net.IP, cache *InfoCache) *Response {
|
||||
return &Response{
|
||||
Referral: ref,
|
||||
@@ -66,6 +78,9 @@ func NewResponse(ref *Referral, server net.IP, cache *InfoCache) *Response {
|
||||
}
|
||||
}
|
||||
|
||||
// Process decodes msg, classifies it, and populates r.Type and r.Decoded.
|
||||
// Synthesises CNAME records from DNAME mappings when no explicit CNAME is present.
|
||||
// Returns r for chaining.
|
||||
func (r *Response) Process(msg *miekgdns.Msg) *Response {
|
||||
if msg == nil {
|
||||
r.Type = RespError
|
||||
@@ -133,6 +148,10 @@ func (r *Response) hasFinalAnswer() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ChildReferrals returns the set of child Referrals implied by a referral
|
||||
// response. It extracts nameservers from the authority section, resolves
|
||||
// any glue from the additional section, and sets Prob proportionally.
|
||||
// Returns nil when Type != RespReferral.
|
||||
func (r *Response) ChildReferrals() []*Referral {
|
||||
if r.Type != RespReferral {
|
||||
return nil
|
||||
@@ -177,6 +196,8 @@ func (r *Response) ChildReferrals() []*Referral {
|
||||
return children
|
||||
}
|
||||
|
||||
// CNAMEFollowReferral constructs a follow-up Referral targeting the last CNAME
|
||||
// in the chain. Returns nil when Type != RespCNAMEFollow.
|
||||
func (r *Response) CNAMEFollowReferral() *Referral {
|
||||
if r.Type != RespCNAMEFollow || len(r.Decoded.CNAMEChain) == 0 {
|
||||
return nil
|
||||
@@ -229,6 +250,8 @@ func (r *Response) resolveGlue(child *Referral) {
|
||||
r.Cache.StoreGlue(nsName, child.Addresses)
|
||||
}
|
||||
|
||||
// IsTerminal reports whether this response ends a traversal branch (no further
|
||||
// referrals or CNAME follows are expected).
|
||||
func (r *Response) IsTerminal() bool {
|
||||
switch r.Type {
|
||||
case RespAnswer, RespNODATA, RespNXDOMAIN, RespSERVFAIL, RespREFUSED, RespNOTIMPL, RespCNAMELoop, RespError:
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
package traverse
|
||||
|
||||
// DefaultMaxDepth is the maximum traversal depth used when no explicit depth is configured.
|
||||
const DefaultMaxDepth = 20
|
||||
|
||||
// Stack is a depth-limited LIFO queue of Referrals used to drive iterative
|
||||
// DNS traversal. Pushing a Referral whose Depth exceeds maxDepth fails
|
||||
// silently and returns false, preventing unbounded traversal.
|
||||
type Stack struct {
|
||||
items []*Referral
|
||||
maxDepth int
|
||||
}
|
||||
|
||||
// NewStack creates a Stack with the given maximum depth.
|
||||
// When maxDepth is ≤ 0, DefaultMaxDepth is used.
|
||||
func NewStack(maxDepth int) *Stack {
|
||||
if maxDepth <= 0 {
|
||||
maxDepth = DefaultMaxDepth
|
||||
@@ -17,6 +23,8 @@ func NewStack(maxDepth int) *Stack {
|
||||
}
|
||||
}
|
||||
|
||||
// Push adds r to the stack. Returns false (and does not add) when r is nil
|
||||
// or r.Depth >= maxDepth.
|
||||
func (s *Stack) Push(r *Referral) bool {
|
||||
if r == nil {
|
||||
return false
|
||||
@@ -28,6 +36,7 @@ func (s *Stack) Push(r *Referral) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Pop removes and returns the top Referral, or nil when the stack is empty.
|
||||
func (s *Stack) Pop() *Referral {
|
||||
if len(s.items) == 0 {
|
||||
return nil
|
||||
@@ -38,6 +47,7 @@ func (s *Stack) Pop() *Referral {
|
||||
return item
|
||||
}
|
||||
|
||||
// Peek returns the top Referral without removing it, or nil when empty.
|
||||
func (s *Stack) Peek() *Referral {
|
||||
if len(s.items) == 0 {
|
||||
return nil
|
||||
@@ -45,14 +55,17 @@ func (s *Stack) Peek() *Referral {
|
||||
return s.items[len(s.items)-1]
|
||||
}
|
||||
|
||||
// Len returns the current number of items in the stack.
|
||||
func (s *Stack) Len() int {
|
||||
return len(s.items)
|
||||
}
|
||||
|
||||
// MaxDepth returns the configured maximum depth for this stack.
|
||||
func (s *Stack) MaxDepth() int {
|
||||
return s.maxDepth
|
||||
}
|
||||
|
||||
// IsEmpty reports whether the stack has no items.
|
||||
func (s *Stack) IsEmpty() bool {
|
||||
return len(s.items) == 0
|
||||
}
|
||||
|
||||
@@ -1 +1,17 @@
|
||||
// Package traverse implements iterative DNS tree traversal.
|
||||
//
|
||||
// Starting from the DNS root, the traverser follows referrals depth-first until
|
||||
// it reaches a terminal response (answer, NXDOMAIN, SERVFAIL, etc.) for the
|
||||
// queried name and type. CNAME chains are followed automatically; CNAME loops
|
||||
// are detected and reported as errors.
|
||||
//
|
||||
// Key types:
|
||||
//
|
||||
// - Traverser — entry point; call NewTraverser then Traverse.
|
||||
// - TraverserConfig — controls max depth, query type, root discovery and fast mode.
|
||||
// - Referral — a pending query to a set of nameserver addresses.
|
||||
// - Response — the classified, decoded result of querying a Referral.
|
||||
// - InfoCache — a two-level (parent/child) cache for NS records and glue.
|
||||
// - Stack — depth-limited LIFO work queue used by Traverser.
|
||||
// - TraverserHooks — callback interface for progress and result events.
|
||||
package traverse
|
||||
|
||||
@@ -11,13 +11,21 @@ import (
|
||||
miekgdns "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// TraverserConfig controls the behaviour of a Traverser.
|
||||
type TraverserConfig struct {
|
||||
MaxDepth int
|
||||
QueryType uint16
|
||||
RootConfig *dns.RootDiscoveryConfig
|
||||
// MaxDepth caps the traversal depth. Referrals at or beyond this depth
|
||||
// are rejected and reported as errors.
|
||||
MaxDepth int
|
||||
// QueryType is the DNS record type requested at each step (e.g. dns.TypeA).
|
||||
QueryType uint16
|
||||
// RootConfig controls how root servers are discovered at startup.
|
||||
RootConfig *dns.RootDiscoveryConfig
|
||||
// QueryConfig controls UDP/TCP transport settings for each DNS query.
|
||||
QueryConfig *dns.QueryConfig
|
||||
RootAddrs []net.IP
|
||||
Hooks *TraverserHooks
|
||||
// RootAddrs may be supplied directly to skip root discovery.
|
||||
RootAddrs []net.IP
|
||||
// Hooks receives events during traversal (progress, resolve, result).
|
||||
Hooks *TraverserHooks
|
||||
// Fast controls cache sharing across branches. When true (default), child
|
||||
// branches inherit glue discovered by earlier branches via the shared root
|
||||
// cache, trading accuracy for speed. When false, each branch gets a
|
||||
@@ -26,6 +34,8 @@ type TraverserConfig struct {
|
||||
Fast bool
|
||||
}
|
||||
|
||||
// DefaultTraverserConfig returns a TraverserConfig with sensible defaults:
|
||||
// max depth 20, query type A, fast mode on.
|
||||
func DefaultTraverserConfig() *TraverserConfig {
|
||||
return &TraverserConfig{
|
||||
MaxDepth: DefaultMaxDepth,
|
||||
@@ -37,11 +47,17 @@ func DefaultTraverserConfig() *TraverserConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// TraversalResult pairs a Referral (the query that was attempted) with the
|
||||
// Response (the outcome of that query). Response may be nil for referrals
|
||||
// that were never processed (e.g. depth-limit rejections).
|
||||
type TraversalResult struct {
|
||||
Referral *Referral
|
||||
Response *Response
|
||||
}
|
||||
|
||||
// Traverser performs iterative DNS traversal from the root down to the target
|
||||
// domain, following referrals and CNAME chains.
|
||||
// Create one with NewTraverser; call Traverse to run a traversal.
|
||||
type Traverser struct {
|
||||
config *TraverserConfig
|
||||
exchange dns.ExchangeFunc
|
||||
@@ -50,6 +66,8 @@ type Traverser struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// NewTraverser creates a Traverser using cfg.
|
||||
// When cfg is nil, DefaultTraverserConfig is used.
|
||||
func NewTraverser(cfg *TraverserConfig) *Traverser {
|
||||
if cfg == nil {
|
||||
cfg = DefaultTraverserConfig()
|
||||
@@ -62,10 +80,12 @@ func NewTraverser(cfg *TraverserConfig) *Traverser {
|
||||
}
|
||||
}
|
||||
|
||||
// SetExchange injects a custom exchange function, primarily for testing.
|
||||
func (t *Traverser) SetExchange(fn dns.ExchangeFunc) {
|
||||
t.exchange = fn
|
||||
}
|
||||
|
||||
// SetHooks attaches traversal event hooks to the Traverser.
|
||||
func (t *Traverser) SetHooks(hooks *TraverserHooks) {
|
||||
if t.config == nil {
|
||||
t.config = DefaultTraverserConfig()
|
||||
@@ -73,6 +93,10 @@ func (t *Traverser) SetHooks(hooks *TraverserHooks) {
|
||||
t.config.Hooks = hooks
|
||||
}
|
||||
|
||||
// Traverse performs an iterative DNS traversal for name, starting from the root
|
||||
// servers. It returns all TraversalResults, including intermediate referrals
|
||||
// and terminal outcomes. Hooks are called for each event during the traversal.
|
||||
// The context can be used to cancel a long-running traversal.
|
||||
func (t *Traverser) Traverse(ctx context.Context, name string) ([]TraversalResult, error) {
|
||||
name = miekgdns.Fqdn(name)
|
||||
|
||||
@@ -251,6 +275,9 @@ func (t *Traverser) processReferral(ctx context.Context, ref *Referral, cache *I
|
||||
}
|
||||
}
|
||||
|
||||
// ResolveNS resolves a nameserver hostname to its IP addresses by performing
|
||||
// a fresh iterative traversal for that name, using cache to avoid repeated
|
||||
// queries and visited to detect circular referrals.
|
||||
func (t *Traverser) ResolveNS(ctx context.Context, nsName string, cache *InfoCache, visited map[string]bool, depth int) ([]net.IP, error) {
|
||||
if cache != nil {
|
||||
if addrs := cache.LookupGlue(nsName); len(addrs) > 0 {
|
||||
|
||||
Reference in New Issue
Block a user