222 lines
7.1 KiB
Go
222 lines
7.1 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/hits/ExploreDNS/internal/config"
|
|
"github.com/hits/ExploreDNS/internal/dns"
|
|
"github.com/hits/ExploreDNS/internal/output"
|
|
"github.com/hits/ExploreDNS/internal/traverse"
|
|
)
|
|
|
|
func main() {
|
|
cfg := config.DefaultConfig()
|
|
|
|
queryType := flag.String("type", cfg.QueryType, "Record type (A, AAAA, NS, CNAME, MX, TXT, SOA, PTR, ANY)")
|
|
rootServer := flag.String("root-server", cfg.RootServer, "Override root server")
|
|
allRootServers := flag.Bool("all-root-servers", cfg.AllRootServers, "Use all 13 root servers")
|
|
rootAAAA := flag.Bool("root-aaaa", cfg.RootAAAA, "Include IPv6 root addresses")
|
|
followAAAA := flag.Bool("follow-aaaa", cfg.FollowAAAA, "Only follow AAAA for referrals")
|
|
udpSize := flag.Int("udp-size", cfg.UDPSize, "EDNS0 buffer size (512-4096)")
|
|
allowTCP := flag.Bool("allow-tcp", cfg.AllowTCP, "TCP fallback on truncation")
|
|
alwaysTCP := flag.Bool("always-tcp", cfg.AlwaysTCP, "Always use TCP")
|
|
maxDepth := flag.Int("max-depth", cfg.MaxDepth, "Max traversal depth (1-100)")
|
|
retries := flag.Int("retries", cfg.Retries, "Retry count (0-10)")
|
|
fast := flag.Bool("fast", cfg.Fast, "Fast mode: reuse earlier branch cache")
|
|
jsonOutput := flag.Bool("json", false, "Output results as JSON")
|
|
|
|
// Verbose: long and short form share the same variable.
|
|
var verboseVal bool
|
|
flag.BoolVar(&verboseVal, "verbose", cfg.Verbose, "Verbose output")
|
|
flag.BoolVar(&verboseVal, "v", cfg.Verbose, "Verbose output (shorthand)")
|
|
|
|
// Debug: -d sets level 1, -dd sets level 2 (library debug).
|
|
var dFlag, ddFlag bool
|
|
flag.BoolVar(&dFlag, "d", false, "Debug mode (stackable: -dd for library debug)")
|
|
flag.BoolVar(&dFlag, "debug", false, "Debug mode")
|
|
flag.BoolVar(&ddFlag, "dd", false, "Library debug mode (equivalent to -d -d)")
|
|
|
|
// Quiet: long and short form share the same variable.
|
|
var quietVal bool
|
|
flag.BoolVar(&quietVal, "quiet", cfg.Quiet, "Suppress supplementary info")
|
|
flag.BoolVar(&quietVal, "q", cfg.Quiet, "Suppress supplementary info (shorthand)")
|
|
|
|
showProgress := flag.Bool("show-progress", cfg.ShowProgress, "Show traversal progress")
|
|
noShowProgress := flag.Bool("no-show-progress", false, "Hide traversal progress")
|
|
showResolves := flag.Bool("show-resolves", cfg.ShowResolves, "Show glue resolution details")
|
|
noShowResolves := flag.Bool("no-show-resolves", false, "Hide glue resolution details")
|
|
showServers := flag.Bool("show-servers", cfg.ShowServers, "Show servers queried")
|
|
noShowServers := flag.Bool("no-show-servers", false, "Hide servers queried")
|
|
showVersions := flag.Bool("show-versions", cfg.ShowVersions, "Show server versions")
|
|
noShowVersions := flag.Bool("no-show-versions", false, "Hide server versions")
|
|
showAllStats := flag.Bool("show-all-stats", cfg.ShowAllStats, "Show all statistics")
|
|
noShowAllStats := flag.Bool("no-show-all-stats", false, "Hide all statistics")
|
|
showResults := flag.Bool("show-results", cfg.ShowResults, "Show query results")
|
|
noShowResults := flag.Bool("no-show-results", false, "Hide query results")
|
|
showSummaryResults := flag.Bool("show-summary-results", cfg.ShowSummaryResults, "Show summary of results")
|
|
noShowSummaryResults := flag.Bool("no-show-summary-results", false, "Hide summary of results")
|
|
|
|
flag.Usage = config.PrintUsage
|
|
|
|
flag.Parse()
|
|
|
|
args := flag.Args()
|
|
|
|
cfg.QueryType = *queryType
|
|
cfg.RootServer = *rootServer
|
|
cfg.AllRootServers = *allRootServers
|
|
cfg.RootAAAA = *rootAAAA
|
|
cfg.FollowAAAA = *followAAAA
|
|
cfg.UDPSize = *udpSize
|
|
cfg.AllowTCP = *allowTCP
|
|
cfg.AlwaysTCP = *alwaysTCP
|
|
cfg.MaxDepth = *maxDepth
|
|
cfg.Retries = *retries
|
|
cfg.Fast = *fast
|
|
cfg.Verbose = verboseVal
|
|
cfg.Debug = config.ParseDebugLevel(dFlag, ddFlag)
|
|
cfg.Quiet = quietVal
|
|
|
|
if *noShowProgress {
|
|
cfg.ShowProgress = false
|
|
} else if *showProgress {
|
|
cfg.ShowProgress = true
|
|
}
|
|
if *noShowResolves {
|
|
cfg.ShowResolves = false
|
|
} else if *showResolves {
|
|
cfg.ShowResolves = true
|
|
}
|
|
if *noShowServers {
|
|
cfg.ShowServers = false
|
|
} else if *showServers {
|
|
cfg.ShowServers = true
|
|
}
|
|
if *noShowVersions {
|
|
cfg.ShowVersions = false
|
|
} else if *showVersions {
|
|
cfg.ShowVersions = true
|
|
}
|
|
if *noShowAllStats {
|
|
cfg.ShowAllStats = false
|
|
} else if *showAllStats {
|
|
cfg.ShowAllStats = true
|
|
}
|
|
if *noShowResults {
|
|
cfg.ShowResults = false
|
|
} else if *showResults {
|
|
cfg.ShowResults = true
|
|
}
|
|
if *noShowSummaryResults {
|
|
cfg.ShowSummaryResults = false
|
|
} else if *showSummaryResults {
|
|
cfg.ShowSummaryResults = true
|
|
}
|
|
|
|
if err := cfg.Validate(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
domain, err := cfg.GetDomain(args)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
rootIP, err := cfg.ParseRootServer()
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error: invalid root server: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
queryTypeValue, err := config.ParseQueryType(cfg.QueryType)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
var rootServerAddr string
|
|
if rootIP != nil {
|
|
rootServerAddr = rootIP.String()
|
|
}
|
|
|
|
queryConfig := &dns.QueryConfig{
|
|
UDPSize: cfg.UDPSize,
|
|
Timeout: 5 * time.Second,
|
|
Retries: cfg.Retries,
|
|
UseTCP: cfg.AlwaysTCP,
|
|
AllowTCP: cfg.AllowTCP,
|
|
}
|
|
|
|
rootConfig := &dns.RootDiscoveryConfig{
|
|
IncludeAAAA: cfg.RootAAAA,
|
|
Server: rootServerAddr,
|
|
AllRoots: cfg.AllRootServers,
|
|
}
|
|
|
|
traverserConfig := &traverse.TraverserConfig{
|
|
MaxDepth: cfg.MaxDepth,
|
|
QueryType: queryTypeValue,
|
|
RootConfig: rootConfig,
|
|
QueryConfig: queryConfig,
|
|
Fast: cfg.Fast,
|
|
}
|
|
|
|
traverser := traverse.NewTraverser(traverserConfig)
|
|
|
|
if cfg.Debug > 0 {
|
|
fmt.Fprintf(os.Stderr, "Debug: Config loaded\n")
|
|
fmt.Fprintf(os.Stderr, " Domain: %s\n", domain)
|
|
fmt.Fprintf(os.Stderr, " Query Type: %s\n", cfg.QueryType)
|
|
fmt.Fprintf(os.Stderr, " Max Depth: %d\n", cfg.MaxDepth)
|
|
fmt.Fprintf(os.Stderr, " UDP Size: %d\n", cfg.UDPSize)
|
|
fmt.Fprintf(os.Stderr, " Retries: %d\n", cfg.Retries)
|
|
fmt.Fprintf(os.Stderr, " Allow TCP: %v\n", cfg.AllowTCP)
|
|
fmt.Fprintf(os.Stderr, " Always TCP: %v\n", cfg.AlwaysTCP)
|
|
fmt.Fprintf(os.Stderr, " Fast Mode: %v\n", cfg.Fast)
|
|
}
|
|
|
|
if !cfg.Quiet && !*jsonOutput {
|
|
fmt.Printf("ExploreDNS - exploring: %s (type: %s)\n", domain, cfg.QueryType)
|
|
}
|
|
|
|
outFmt := output.FormatText
|
|
if *jsonOutput {
|
|
outFmt = output.FormatJSON
|
|
}
|
|
|
|
outCfg := &output.Config{
|
|
Format: outFmt,
|
|
Domain: domain,
|
|
QueryType: cfg.QueryType,
|
|
ShowProgress: cfg.ShowProgress,
|
|
ShowResolves: cfg.ShowResolves,
|
|
ShowServers: cfg.ShowServers,
|
|
ShowVersions: cfg.ShowVersions,
|
|
ShowAllStats: cfg.ShowAllStats,
|
|
ShowResults: cfg.ShowResults,
|
|
ShowSummaryResults: cfg.ShowSummaryResults,
|
|
Verbose: cfg.Verbose,
|
|
Quiet: cfg.Quiet,
|
|
Color: os.Getenv("NO_COLOR") == "",
|
|
Debug: cfg.Debug,
|
|
}
|
|
|
|
ctx := context.Background()
|
|
formatter := output.NewFormatter(outCfg, os.Stdout)
|
|
_, err = output.RunTraversal(ctx, traverser, outCfg, formatter, domain)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error: traversal failed: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
if cfg.Debug > 0 {
|
|
fmt.Fprintf(os.Stderr, "Debug: Traversal completed\n")
|
|
}
|
|
}
|