fix: address code review issues on PR #9
CI / test (pull_request) Waiting to run

- gofmt main.go to fix tab indentation in main() body
- stats.go ComputeSummary: return nil when Answers and ByType are empty
- dns/types.go: remove duplicate ParseQueryType (dead code)
- formatter.go: log hook errors to stderr when Debug > 0; add Debug to Config
- text.go referralID: remove redundant depth==0 branch
- text.go writeServers: remove version lookup placeholder string

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
Gary Hansen
2026-06-08 02:32:58 +10:00
co-authored by Copilot multica-agent
parent 5a54834c5f
commit 8e7beacc22
14 changed files with 268 additions and 294 deletions
+8 -8
View File
@@ -36,13 +36,13 @@ type Config struct {
Debug int
Quiet bool
ShowProgress bool
ShowResolves bool
ShowServers bool
ShowVersions bool
ShowAllStats bool
ShowResults bool
ShowSummaryResults bool
ShowProgress bool
ShowResolves bool
ShowServers bool
ShowVersions bool
ShowAllStats bool
ShowResults bool
ShowSummaryResults bool
}
func ParseQueryType(s string) (uint16, error) {
@@ -235,4 +235,4 @@ func DefaultConfig() *Config {
ShowResults: true,
ShowSummaryResults: true,
}
}
}
+5 -5
View File
@@ -314,8 +314,8 @@ func TestCNAMEChain(t *testing.T) {
func TestResponseClassificationString(t *testing.T) {
tests := []struct {
rc ResponseClassification
want string
rc ResponseClassification
want string
}{
{ResponseAnswer, "answer"},
{ResponseReferral, "referral"},
@@ -344,10 +344,10 @@ func TestFormatRecord(t *testing.T) {
t.Run("A record", func(t *testing.T) {
rr := &dns.A{
Hdr: dns.RR_Header{
Name: "example.com.",
Name: "example.com.",
Rrtype: dns.TypeA,
Class: dns.ClassINET,
Ttl: 300,
Class: dns.ClassINET,
Ttl: 300,
},
A: MustParseIP("93.184.216.34"),
}
-1
View File
@@ -328,4 +328,3 @@ func TestQueryNoTCPFallbackWhenDisabled(t *testing.T) {
t.Error("expected truncated response to be returned as-is")
}
}
-29
View File
@@ -1,9 +1,6 @@
package dns
import (
"fmt"
"strings"
"github.com/miekg/dns"
)
@@ -40,32 +37,6 @@ func QNameType(qtype uint16) string {
return dns.TypeToString[qtype]
}
func ParseQueryType(s string) (uint16, error) {
s = strings.ToUpper(strings.TrimSpace(s))
switch s {
case "A":
return TypeA, nil
case "AAAA":
return TypeAAAA, nil
case "NS":
return TypeNS, nil
case "CNAME":
return TypeCNAME, nil
case "MX":
return TypeMX, nil
case "TXT":
return TypeTXT, nil
case "SOA":
return TypeSOA, nil
case "PTR":
return TypePTR, nil
case "ANY":
return TypeANY, nil
default:
return 0, fmt.Errorf("invalid query type: %s", s)
}
}
func DefaultEDNS0UDPSize() int {
return 2048
}
+10 -3
View File
@@ -1,6 +1,7 @@
package output
import (
"fmt"
"io"
"os"
@@ -28,6 +29,7 @@ type Config struct {
Verbose bool
Quiet bool
Color bool
Debug int
}
func DefaultConfig() *Config {
@@ -69,16 +71,21 @@ 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:
_ = formatter.WriteResolve(event)
logErr("WriteResolve", formatter.WriteResolve(event))
case !event.IsResolve && cfg.ShowProgress:
_ = formatter.WriteProgress(event)
logErr("WriteProgress", formatter.WriteProgress(event))
}
if event.Stage == traverse.EventComplete && cfg.ShowAllStats {
_ = formatter.WriteResult(event.Result)
logErr("WriteResult", formatter.WriteResult(event.Result))
}
},
}
+2 -2
View File
@@ -54,8 +54,8 @@ type jsonSummary struct {
}
type jsonAnswerStat struct {
RData string `json:"rdata"`
Probability float64 `json:"probability"`
RData string `json:"rdata"`
Probability float64 `json:"probability"`
Records []string `json:"records,omitempty"`
}
+3
View File
@@ -73,6 +73,9 @@ func ComputeSummary(results []traverse.TraversalResult) *SummaryStats {
return stats.Answers[i].RData < stats.Answers[j].RData
})
if len(stats.Answers) == 0 && len(stats.ByType) == 0 {
return nil
}
return stats
}
+1 -8
View File
@@ -99,11 +99,7 @@ func (f *textFormatter) writeServers(results []traverse.TraversalResult) error {
for _, name := range names {
for _, ip := range servers[name] {
version := ""
if f.cfg.ShowVersions {
version = " (version lookup pending)"
}
if _, err := fmt.Fprintf(f.w, "%*s: %-15s%s\n", width, name, ip, version); err != nil {
if _, err := fmt.Fprintf(f.w, "%*s: %-15s\n", width, name, ip); err != nil {
return err
}
}
@@ -234,9 +230,6 @@ func referralID(ref *traverse.Referral) string {
if ref == nil {
return ""
}
if ref.Depth == 0 {
return "1"
}
return fmt.Sprintf("%d", ref.Depth+1)
}
+4 -4
View File
@@ -9,10 +9,10 @@ import (
)
type InfoCache struct {
parent *InfoCache
mu sync.RWMutex
ns map[string][]string
glue map[string][]net.IP
parent *InfoCache
mu sync.RWMutex
ns map[string][]string
glue map[string][]net.IP
}
func NewInfoCache(parent *InfoCache) *InfoCache {
+9 -9
View File
@@ -32,18 +32,18 @@ func (s ResolutionState) String() string {
}
type Referral struct {
Name string
Qtype uint16
Qclass uint16
Name string
Qtype uint16
Qclass uint16
Bailiwick string
Addresses []net.IP
State ResolutionState
NSName string
Parent *Referral
Depth int
Prob float64
NSName string
Parent *Referral
Depth int
Prob float64
}
func NewReferral(name string, qtype uint16, bailiwick string, depth int, prob float64, parent *Referral) *Referral {
@@ -81,7 +81,7 @@ func (r *Referral) SetAddresses(addrs []net.IP) {
}
type CircularReferralError struct {
Name string
Name string
Chain []string
}
@@ -90,7 +90,7 @@ func (e *CircularReferralError) Error() string {
}
type UnresolvableNameserverError struct {
Name string
Name string
Reason string
}
+1 -1
View File
@@ -10,7 +10,7 @@ import (
type ResponseType int
const (
RespReferral ResponseType = iota
RespReferral ResponseType = iota
RespAnswer
RespCNAMEFollow
RespNODATA
+2 -2
View File
@@ -3,7 +3,7 @@ package traverse
const DefaultMaxDepth = 20
type Stack struct {
items []*Referral
items []*Referral
maxDepth int
}
@@ -12,7 +12,7 @@ func NewStack(maxDepth int) *Stack {
maxDepth = DefaultMaxDepth
}
return &Stack{
items: make([]*Referral, 0),
items: make([]*Referral, 0),
maxDepth: maxDepth,
}
}
+36 -36
View File
@@ -44,9 +44,9 @@ func TestTraverserSimpleTraversal(t *testing.T) {
}()
tr := NewTraverser(&TraverserConfig{
MaxDepth: 5,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("198.41.0.4")},
MaxDepth: 5,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("198.41.0.4")},
})
tr.SetExchange(func(ctx context.Context, server string, msg *dns.Msg, useTCP bool) (*dns.Msg, error) {
return answerResp.Copy(), nil
@@ -94,9 +94,9 @@ func TestTraverserReferralTraversal(t *testing.T) {
})
tr := NewTraverser(&TraverserConfig{
MaxDepth: 5,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("198.41.0.4")},
MaxDepth: 5,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("198.41.0.4")},
})
tr.SetExchange(func(ctx context.Context, server string, msg *dns.Msg, useTCP bool) (*dns.Msg, error) {
q := msg.Question[0]
@@ -124,9 +124,9 @@ func TestTraverserReferralTraversal(t *testing.T) {
func TestTraverserMaxDepth(t *testing.T) {
callCount := 0
tr := NewTraverser(&TraverserConfig{
MaxDepth: 2,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("198.41.0.4")},
MaxDepth: 2,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("198.41.0.4")},
})
tr.SetExchange(func(ctx context.Context, server string, msg *dns.Msg, useTCP bool) (*dns.Msg, error) {
callCount++
@@ -170,9 +170,9 @@ func TestTraverserMaxDepth(t *testing.T) {
func TestTraverserContextCancellation(t *testing.T) {
tr := NewTraverser(&TraverserConfig{
MaxDepth: 5,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("198.41.0.4")},
MaxDepth: 5,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("198.41.0.4")},
})
tr.SetExchange(func(ctx context.Context, server string, msg *dns.Msg, useTCP bool) (*dns.Msg, error) {
m := new(dns.Msg)
@@ -203,9 +203,9 @@ func TestTraverserNXDOMAIN(t *testing.T) {
nxdResp.Rcode = dns.RcodeNameError
tr := NewTraverser(&TraverserConfig{
MaxDepth: 5,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("198.41.0.4")},
MaxDepth: 5,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("198.41.0.4")},
})
tr.SetExchange(func(ctx context.Context, server string, msg *dns.Msg, useTCP bool) (*dns.Msg, error) {
return nxdResp.Copy(), nil
@@ -229,9 +229,9 @@ func TestTraverserSERVFAIL(t *testing.T) {
sfResp.Rcode = dns.RcodeServerFailure
tr := NewTraverser(&TraverserConfig{
MaxDepth: 5,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("198.41.0.4")},
MaxDepth: 5,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("198.41.0.4")},
})
tr.SetExchange(func(ctx context.Context, server string, msg *dns.Msg, useTCP bool) (*dns.Msg, error) {
return sfResp.Copy(), nil
@@ -268,9 +268,9 @@ func TestTraverserCNAMEFollow(t *testing.T) {
})
tr := NewTraverser(&TraverserConfig{
MaxDepth: 5,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("198.41.0.4")},
MaxDepth: 5,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("198.41.0.4")},
})
tr.SetExchange(func(ctx context.Context, server string, msg *dns.Msg, useTCP bool) (*dns.Msg, error) {
q := msg.Question[0]
@@ -328,9 +328,9 @@ func TestTraverserProbabilityCalculation(t *testing.T) {
})
tr := NewTraverser(&TraverserConfig{
MaxDepth: 5,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("1.2.3.4")},
MaxDepth: 5,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("1.2.3.4")},
})
tr.SetExchange(func(ctx context.Context, server string, msg *dns.Msg, useTCP bool) (*dns.Msg, error) {
q := msg.Question[0]
@@ -364,9 +364,9 @@ func TestTraverserNODATA(t *testing.T) {
})
tr := NewTraverser(&TraverserConfig{
MaxDepth: 5,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("198.41.0.4")},
MaxDepth: 5,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("198.41.0.4")},
})
tr.SetExchange(func(ctx context.Context, server string, msg *dns.Msg, useTCP bool) (*dns.Msg, error) {
return nodataResp.Copy(), nil
@@ -394,9 +394,9 @@ func TestTraverserMultipleRoots(t *testing.T) {
})
tr := NewTraverser(&TraverserConfig{
MaxDepth: 5,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("198.41.0.4"), net.ParseIP("199.9.14.201")},
MaxDepth: 5,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("198.41.0.4"), net.ParseIP("199.9.14.201")},
})
tr.SetExchange(func(ctx context.Context, server string, msg *dns.Msg, useTCP bool) (*dns.Msg, error) {
return answerResp.Copy(), nil
@@ -414,9 +414,9 @@ func TestTraverserMultipleRoots(t *testing.T) {
func TestTraverserNilExchangeResponse(t *testing.T) {
tr := NewTraverser(&TraverserConfig{
MaxDepth: 5,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("198.41.0.4")},
MaxDepth: 5,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("198.41.0.4")},
})
tr.SetExchange(func(ctx context.Context, server string, msg *dns.Msg, useTCP bool) (*dns.Msg, error) {
return nil, nil
@@ -451,9 +451,9 @@ func TestTraverserCacheChaining(t *testing.T) {
})
tr := NewTraverser(&TraverserConfig{
MaxDepth: 5,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("198.41.0.4")},
MaxDepth: 5,
QueryType: dnsTypeA,
RootAddrs: []net.IP{net.ParseIP("198.41.0.4")},
})
tr.SetExchange(func(ctx context.Context, server string, msg *dns.Msg, useTCP bool) (*dns.Msg, error) {
q := msg.Question[0]