Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,9 @@ exists (notably Windows), or a name is available only from the hosts file, NSS,
or mDNS, the platform resolver supplies A/AAAA records without TTLs. If direct
DNS returned non-address records, they remain visible alongside platform
addresses. Each platform record identifies its source and shows
`TTL unavailable`; `Lookup` describes a platform-only or mixed path.
`TTL unavailable`; scoped IPv6 addresses retain their interface zone, and
duplicate platform addresses are collapsed. `Lookup` describes a platform-only
or mixed path.

Explicit resolvers support UDP, TCP, DoT, DoQ, and DoH. `Transport security`
describes only encryption and certificate verification to the resolver, not
Expand Down
1 change: 1 addition & 0 deletions internal/dnsinspect/dnsinspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ type record struct {
hasTTL bool
source recordSource
address net.IP
zone string
target string
target2 string
preference uint16
Expand Down
37 changes: 37 additions & 0 deletions internal/dnsinspect/dnsinspect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,43 @@ func TestLookupUsesPlatformResolver(t *testing.T) {
}
}

func TestPlatformResolverPreservesIPv6ZonesAndDeduplicatesAddresses(t *testing.T) {
origDefaultLookupIPAddr := defaultLookupIPAddr
t.Cleanup(func() {
defaultLookupIPAddr = origDefaultLookupIPAddr
})

defaultLookupIPAddr = func(ctx context.Context, host string) ([]net.IPAddr, error) {
return []net.IPAddr{
{IP: net.ParseIP("192.0.2.44")},
{IP: net.ParseIP("192.0.2.44")},
{IP: net.ParseIP("fe80::1"), Zone: "en0"},
{IP: net.ParseIP("fe80::1"), Zone: "en0"},
{IP: net.ParseIP("fe80::1"), Zone: "en1"},
}, nil
}

res, err := lookup(context.Background(), &Config{ResolvConfPath: emptyResolvConf(t)}, "printer.local", time.Now())
if err != nil {
t.Fatal(err)
}
if got, want := len(res.records["A"]), 1; got != want {
t.Fatalf("A record count = %d, want %d", got, want)
}
if got, want := len(res.records["AAAA"]), 2; got != want {
t.Fatalf("AAAA record count = %d, want %d", got, want)
}

p := core.TestPrinter(false)
render(p, res)
out := string(p.Bytes())
for _, want := range []string{"fe80::1%en0", "fe80::1%en1"} {
if strings.Count(out, want) != 1 {
t.Fatalf("output count for %q = %d, want 1:\n%s", want, strings.Count(out, want), out)
}
}
}

func TestLookupSystemNameserverReturnsTTL(t *testing.T) {
addr, stop := startUDPServer(t)
defer stop()
Expand Down
14 changes: 12 additions & 2 deletions internal/dnsinspect/lookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -472,15 +472,25 @@ func lookupDefaultResolverRecords(ctx context.Context, host string) ([]record, e
}

records := make([]record, 0, len(addrs))
seen := make(map[string]struct{}, len(addrs))
owner := normalizedOwner(host)
for _, addr := range addrs {
ip := addr.IP
var rec record
switch {
case ip.To4() != nil:
records = append(records, record{owner: owner, typ: dnsmessage.TypeA, address: append(net.IP(nil), ip.To4()...), source: recordSourcePlatform})
rec = record{owner: owner, typ: dnsmessage.TypeA, address: append(net.IP(nil), ip.To4()...), source: recordSourcePlatform}
case ip.To16() != nil:
records = append(records, record{owner: owner, typ: dnsmessage.TypeAAAA, address: append(net.IP(nil), ip.To16()...), source: recordSourcePlatform})
rec = record{owner: owner, typ: dnsmessage.TypeAAAA, address: append(net.IP(nil), ip.To16()...), zone: addr.Zone, source: recordSourcePlatform}
default:
continue
}
key := strconv.Itoa(int(rec.typ)) + "\x00" + rec.semanticKey()
if _, ok := seen[key]; ok {
continue
}
seen[key] = struct{}{}
records = append(records, rec)
}
return records, nil
}
Expand Down
4 changes: 4 additions & 0 deletions internal/dnsinspect/records.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,10 @@ func (rec record) semanticKey() string {
switch rec.typ {
case dnsmessage.TypeA, dnsmessage.TypeAAAA:
fmt.Fprintf(&b, "%x", []byte(rec.address))
if rec.zone != "" {
b.WriteByte('%')
b.WriteString(rec.zone)
}
case dnsmessage.TypeCNAME, dnsmessage.TypeNS:
if rec.target == "" && rec.presentation != "" {
return rec.presentation
Expand Down
2 changes: 1 addition & 1 deletion internal/dnsinspect/render_records.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func (rec record) renderValue() string {
switch rec.typ {
case dnsmessage.TypeA, dnsmessage.TypeAAAA:
if len(rec.address) > 0 {
return rec.address.String()
return (&net.IPAddr{IP: rec.address, Zone: rec.zone}).String()
}
case dnsmessage.TypeCNAME, dnsmessage.TypeNS:
if rec.target != "" {
Expand Down