feat: implement DNS server fingerprinting (HAN-384) (#10)
CI / test (push) Has been cancelled
CI / test (push) Has been cancelled
This commit was merged in pull request #10.
This commit is contained in:
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user