feat: implement DNS server fingerprinting (HAN-384) (#10)
CI / test (push) Has been cancelled

This commit was merged in pull request #10.
This commit is contained in:
2026-06-07 16:56:51 +00:00
parent 35be7539b4
commit 76f5010a5e
6 changed files with 395 additions and 7 deletions
+4
View File
@@ -30,6 +30,10 @@ type Config struct {
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 {
+13 -6
View File
@@ -44,8 +44,9 @@ type jsonResult struct {
}
type jsonServer struct {
Name string `json:"name"`
IPs []string `json:"ips"`
Name string `json:"name"`
IPs []string `json:"ips"`
Version string `json:"version,omitempty"`
}
type jsonSummary struct {
@@ -104,10 +105,16 @@ func (f *jsonFormatter) WriteSummary(results []traverse.TraversalResult) error {
if f.cfg.ShowServers {
servers := collectServers(results)
for name, ips := range servers {
f.payload.Servers = append(f.payload.Servers, jsonServer{
Name: name,
IPs: ips,
})
srv := jsonServer{Name: name, IPs: ips}
if f.cfg.ShowVersions && f.cfg.Fingerprints != nil {
for _, ip := range ips {
if v := f.cfg.Fingerprints[ip]; v != "" {
srv.Version = v
break
}
}
}
f.payload.Servers = append(f.payload.Servers, srv)
}
}
+26
View File
@@ -3,7 +3,9 @@ package output
import (
"context"
"fmt"
"net"
"github.com/hits/ExploreDNS/internal/fingerprint"
"github.com/hits/ExploreDNS/internal/traverse"
)
@@ -25,6 +27,13 @@ func RunTraversal(ctx context.Context, traverser *traverse.Traverser, cfg *Confi
return results, err
}
// Fingerprint servers when both ShowVersions and ShowServers are enabled.
// Gating on ShowServers avoids unnecessary network calls when versions
// would not be displayed anyway.
if cfg.ShowVersions && cfg.ShowServers {
cfg.Fingerprints = fingerprint.New().FingerprintAll(ctx, collectUniqueServerIPs(results))
}
if err := formatter.WriteSummary(results); err != nil {
return results, err
}
@@ -34,3 +43,20 @@ func RunTraversal(ctx context.Context, traverser *traverse.Traverser, cfg *Confi
return results, nil
}
// collectUniqueServerIPs returns the set of unique server IPs seen in results.
func collectUniqueServerIPs(results []traverse.TraversalResult) []net.IP {
seen := make(map[string]bool)
var ips []net.IP
for _, r := range results {
if r.Response == nil || r.Response.Server == nil {
continue
}
key := r.Response.Server.String()
if !seen[key] {
seen[key] = true
ips = append(ips, r.Response.Server)
}
}
return ips
}
+7 -1
View File
@@ -99,7 +99,13 @@ func (f *textFormatter) writeServers(results []traverse.TraversalResult) error {
for _, name := range names {
for _, ip := range servers[name] {
if _, err := fmt.Fprintf(f.w, "%*s: %-15s\n", width, name, ip); err != nil {
line := fmt.Sprintf("%*s: %-15s", width, name, ip)
if f.cfg.ShowVersions {
if version, ok := f.cfg.Fingerprints[ip]; ok && version != "" {
line += " " + version
}
}
if _, err := fmt.Fprintln(f.w, line); err != nil {
return err
}
}