CI / test (pull_request) Failing after 2m24s
- Add internal/fingerprint package with Fingerprinter type - Sends version.bind CHAOS TXT query to each server - Caches results per IP to avoid redundant queries - Concurrent batch fingerprinting via FingerprintAll - Gracefully handles non-responding servers (returns empty string) - Injectable exchange function for testability - Wire fingerprinting into RunTraversal - Triggered when both ShowVersions and ShowServers are enabled - Collects unique server IPs from traversal results - Stores results in output.Config.Fingerprints before WriteSummary - Display versions in text output (writeServers) - Appends version string after IP when ShowVersions is true - No output change when version is unknown - Include version in JSON output (jsonServer.Version field) - omitempty: field absent when version is unknown - Add comprehensive fingerprint tests (11 test cases) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: multica-agent <github@multica.ai>
97 lines
2.2 KiB
Go
97 lines
2.2 KiB
Go
package output
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
|
|
"github.com/hits/ExploreDNS/internal/traverse"
|
|
)
|
|
|
|
type Format int
|
|
|
|
const (
|
|
FormatText Format = iota
|
|
FormatJSON
|
|
)
|
|
|
|
type Config struct {
|
|
Format Format
|
|
Domain string
|
|
QueryType string
|
|
ShowProgress bool
|
|
ShowResolves bool
|
|
ShowServers bool
|
|
ShowVersions bool
|
|
ShowAllStats bool
|
|
ShowResults bool
|
|
ShowSummaryResults bool
|
|
Verbose bool
|
|
Quiet bool
|
|
Color bool
|
|
Debug int
|
|
|
|
// Fingerprints maps server IP strings to their version.bind version strings.
|
|
// Populated by RunTraversal when ShowVersions and ShowServers are both true.
|
|
Fingerprints map[string]string
|
|
}
|
|
|
|
func DefaultConfig() *Config {
|
|
return &Config{
|
|
Format: FormatText,
|
|
ShowProgress: true,
|
|
ShowResolves: true,
|
|
ShowServers: true,
|
|
ShowVersions: true,
|
|
ShowAllStats: true,
|
|
ShowResults: true,
|
|
ShowSummaryResults: true,
|
|
Color: os.Getenv("NO_COLOR") == "",
|
|
}
|
|
}
|
|
|
|
type Formatter interface {
|
|
WriteProgress(event traverse.TraversalEvent) error
|
|
WriteResolve(event traverse.TraversalEvent) error
|
|
WriteResult(result traverse.TraversalResult) error
|
|
WriteSummary(results []traverse.TraversalResult) error
|
|
Flush() error
|
|
}
|
|
|
|
func NewFormatter(cfg *Config, w io.Writer) Formatter {
|
|
if cfg == nil {
|
|
cfg = DefaultConfig()
|
|
}
|
|
if w == nil {
|
|
w = os.Stdout
|
|
}
|
|
if cfg.Format == FormatJSON {
|
|
return newJSONFormatter(cfg, w)
|
|
}
|
|
return newTextFormatter(cfg, w)
|
|
}
|
|
|
|
func AttachHooks(cfg *Config, formatter Formatter) *traverse.TraverserHooks {
|
|
if cfg == nil || formatter == nil {
|
|
return nil
|
|
}
|
|
logErr := func(context string, err error) {
|
|
if err != nil && cfg.Debug > 0 {
|
|
fmt.Fprintf(os.Stderr, "Debug: formatter %s: %v\n", context, err)
|
|
}
|
|
}
|
|
return &traverse.TraverserHooks{
|
|
OnEvent: func(event traverse.TraversalEvent) {
|
|
switch {
|
|
case event.IsResolve && cfg.ShowResolves:
|
|
logErr("WriteResolve", formatter.WriteResolve(event))
|
|
case !event.IsResolve && cfg.ShowProgress:
|
|
logErr("WriteProgress", formatter.WriteProgress(event))
|
|
}
|
|
if event.Stage == traverse.EventComplete && cfg.ShowAllStats {
|
|
logErr("WriteResult", formatter.WriteResult(event.Result))
|
|
}
|
|
},
|
|
}
|
|
}
|