From d7a4f9d31c6413c4b02ae3beafdd3ecb4b994f38 Mon Sep 17 00:00:00 2001 From: Aaron Klotz Date: Fri, 21 Jun 2024 12:25:17 -0600 Subject: [PATCH] net/dns: ensure multiple hosts with the same IP address are combined into a single HostEntry This ensures that each line has a unique IP address. Fixes #11939 Signed-off-by: Aaron Klotz --- net/dns/manager.go | 18 ++++++++++++++---- net/dns/manager_test.go | 12 ++++-------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/net/dns/manager.go b/net/dns/manager.go index 5bffc56b8..dbfcb1def 100644 --- a/net/dns/manager.go +++ b/net/dns/manager.go @@ -17,6 +17,7 @@ import ( "sync/atomic" "time" + xmaps "golang.org/x/exp/maps" "tailscale.com/control/controlknobs" "tailscale.com/health" "tailscale.com/net/dns/resolver" @@ -122,6 +123,7 @@ func (m *Manager) Set(cfg Config) error { // The returned list is sorted by the first hostname in each entry. func compileHostEntries(cfg Config) (hosts []*HostEntry) { didLabel := make(map[string]bool, len(cfg.Hosts)) + hostsMap := make(map[netip.Addr]*HostEntry, len(cfg.Hosts)) for _, sd := range cfg.SearchDomains { for h, ips := range cfg.Hosts { if !sd.Contains(h) || h.NumLabels() != (sd.NumLabels()+1) { @@ -136,15 +138,23 @@ func compileHostEntries(cfg Config) (hosts []*HostEntry) { if cfg.OnlyIPv6 && ip.Is4() { continue } - hosts = append(hosts, &HostEntry{ - Addr: ip, - Hosts: ipHosts, - }) + if e := hostsMap[ip]; e != nil { + e.Hosts = append(e.Hosts, ipHosts...) + } else { + hostsMap[ip] = &HostEntry{ + Addr: ip, + Hosts: ipHosts, + } + } // Only add IPv4 or IPv6 per host, like we do in the resolver. break } } } + if len(hostsMap) == 0 { + return nil + } + hosts = xmaps.Values(hostsMap) slices.SortFunc(hosts, func(a, b *HostEntry) int { if len(a.Hosts) == 0 && len(b.Hosts) == 0 { return 0 diff --git a/net/dns/manager_test.go b/net/dns/manager_test.go index e59d3d7ce..366e08bbf 100644 --- a/net/dns/manager_test.go +++ b/net/dns/manager_test.go @@ -87,8 +87,7 @@ func TestCompileHostEntries(t *testing.T) { {Addr: netip.MustParseAddr("1.1.1.1"), Hosts: []string{"a.foo.ts.net.", "a"}}, {Addr: netip.MustParseAddr("1.1.1.2"), Hosts: []string{"b.foo.ts.net.", "b"}}, {Addr: netip.MustParseAddr("1.1.1.3"), Hosts: []string{"c.foo.ts.net.", "c"}}, - {Addr: netip.MustParseAddr("1.1.1.4"), Hosts: []string{"d.foo.beta.tailscale.net."}}, - {Addr: netip.MustParseAddr("1.1.1.4"), Hosts: []string{"d.foo.ts.net.", "d"}}, + {Addr: netip.MustParseAddr("1.1.1.4"), Hosts: []string{"d.foo.ts.net.", "d", "d.foo.beta.tailscale.net."}}, {Addr: netip.MustParseAddr("1.1.1.5"), Hosts: []string{"e.foo.beta.tailscale.net.", "e"}}, }, }, @@ -103,8 +102,7 @@ func TestCompileHostEntries(t *testing.T) { SearchDomains: []dnsname.FQDN{"foo.ts.net.", "foo.beta.tailscale.net."}, }, want: []*HostEntry{ - {Addr: netip.MustParseAddr("1.1.1.5"), Hosts: []string{"e.foo.beta.tailscale.net."}}, - {Addr: netip.MustParseAddr("1.1.1.5"), Hosts: []string{"e.foo.ts.net.", "e"}}, + {Addr: netip.MustParseAddr("1.1.1.5"), Hosts: []string{"e.foo.ts.net.", "e", "e.foo.beta.tailscale.net."}}, }, }, { @@ -120,8 +118,7 @@ func TestCompileHostEntries(t *testing.T) { SearchDomains: []dnsname.FQDN{"foo.ts.net.", "foo.beta.tailscale.net."}, }, want: []*HostEntry{ - {Addr: netip.MustParseAddr("1.1.1.4"), Hosts: []string{"d.foo.beta.tailscale.net."}}, - {Addr: netip.MustParseAddr("1.1.1.4"), Hosts: []string{"d.foo.ts.net.", "d"}}, + {Addr: netip.MustParseAddr("1.1.1.4"), Hosts: []string{"d.foo.ts.net.", "d", "d.foo.beta.tailscale.net."}}, }, }, { @@ -139,8 +136,7 @@ func TestCompileHostEntries(t *testing.T) { want: []*HostEntry{ {Addr: netip.MustParseAddr("1.1.1.2"), Hosts: []string{"h1.foo.beta.tailscale.net."}}, {Addr: netip.MustParseAddr("1.1.1.3"), Hosts: []string{"h1.foo.ts.net.", "h1"}}, - {Addr: netip.MustParseAddr("1.1.1.1"), Hosts: []string{"h2.foo.beta.tailscale.net."}}, - {Addr: netip.MustParseAddr("1.1.1.1"), Hosts: []string{"h2.foo.ts.net.", "h2"}}, + {Addr: netip.MustParseAddr("1.1.1.1"), Hosts: []string{"h2.foo.ts.net.", "h2", "h2.foo.beta.tailscale.net."}}, }, }, }