From e97209c6bf45f34f238a5cf58303d9f467e0d8fd Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Sat, 23 Apr 2022 12:49:07 -0700 Subject: [PATCH] net/dns: add tailscaled-on-macOS DNS OSConfigurator This populates DNS suffixes ("ts.net", etc) in /etc/resolver/* files to point to 100.100.100.100 so MagicDNS works. It also sets search domains. Updates #4276 Signed-off-by: Brad Fitzpatrick --- net/dns/manager_darwin.go | 127 +++++++++++++++++++++++++++++++++++++ net/dns/manager_default.go | 4 +- 2 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 net/dns/manager_darwin.go diff --git a/net/dns/manager_darwin.go b/net/dns/manager_darwin.go new file mode 100644 index 000000000..e92352c80 --- /dev/null +++ b/net/dns/manager_darwin.go @@ -0,0 +1,127 @@ +// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package dns + +import ( + "bytes" + "errors" + "os" + + "go4.org/mem" + "tailscale.com/types/logger" + "tailscale.com/util/mak" +) + +func NewOSConfigurator(logf logger.Logf, ifName string) (OSConfigurator, error) { + return &darwinConfigurator{logf: logf, ifName: ifName}, nil +} + +// darwinConfigurator is the tailscaled-on-macOS DNS OS configurator that +// maintains the Split DNS nameserver entries pointing MagicDNS DNS suffixes +// to 100.100.100.100 using the macOS /etc/resolver/$SUFFIX files. +type darwinConfigurator struct { + logf logger.Logf + ifName string +} + +func (c *darwinConfigurator) Close() error { + c.removeResolverFiles(func(domain string) bool { return true }) + return nil +} + +func (c *darwinConfigurator) SupportsSplitDNS() bool { + return true +} + +func (c *darwinConfigurator) SetDNS(cfg OSConfig) error { + var buf bytes.Buffer + buf.WriteString(macResolverFileHeader) + for i, ip := range cfg.Nameservers { + if i == 0 { + buf.WriteString("nameserver ") + } else { + buf.WriteString(" ") + } + buf.WriteString(ip.String()) + } + buf.WriteString("\n") + + if err := os.MkdirAll("/etc/resolver", 0755); err != nil { + return err + } + + var keep map[string]bool + + // Add a dummy file to /etc/resolver with a "search ..." directive if we have + // search suffixes to add. + if len(cfg.SearchDomains) > 0 { + const searchFile = "search.tailscale" // fake DNS suffix+TLD to put our search + mak.Set(&keep, searchFile, true) + var sbuf bytes.Buffer + sbuf.WriteString(macResolverFileHeader) + sbuf.WriteString("search") + for _, d := range cfg.SearchDomains { + sbuf.WriteString(" ") + sbuf.WriteString(string(d.WithoutTrailingDot())) + } + sbuf.WriteString("\n") + if err := os.WriteFile("/etc/resolver/"+searchFile, sbuf.Bytes(), 0644); err != nil { + return err + } + } + + for _, d := range cfg.MatchDomains { + fileBase := string(d.WithoutTrailingDot()) + mak.Set(&keep, fileBase, true) + fullPath := "/etc/resolver/" + fileBase + + if err := os.WriteFile(fullPath, buf.Bytes(), 0644); err != nil { + return err + } + } + return c.removeResolverFiles(func(domain string) bool { return !keep[domain] }) +} + +func (c *darwinConfigurator) GetBaseConfig() (OSConfig, error) { + return OSConfig{}, errors.New("[unexpected] unreachable") +} + +const macResolverFileHeader = "# Added by tailscaled\n" + +// removeResolverFiles deletes all files in /etc/resolver for which the shouldDelete +// func returns true. +func (c *darwinConfigurator) removeResolverFiles(shouldDelete func(domain string) bool) error { + dents, err := os.ReadDir("/etc/resolver") + if os.IsNotExist(err) { + return nil + } + if err != nil { + return err + } + for _, de := range dents { + if !de.Type().IsRegular() { + continue + } + name := de.Name() + if !shouldDelete(name) { + continue + } + fullPath := "/etc/resolver/" + name + contents, err := os.ReadFile(fullPath) + if err != nil { + if os.IsNotExist(err) { // race? + continue + } + return err + } + if !mem.HasPrefix(mem.B(contents), mem.S(macResolverFileHeader)) { + continue + } + if err := os.Remove(fullPath); err != nil { + return err + } + } + return nil +} diff --git a/net/dns/manager_default.go b/net/dns/manager_default.go index 5c3eeac67..4bc40ef62 100644 --- a/net/dns/manager_default.go +++ b/net/dns/manager_default.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !linux && !freebsd && !openbsd && !windows -// +build !linux,!freebsd,!openbsd,!windows +//go:build !linux && !freebsd && !openbsd && !windows && !darwin +// +build !linux,!freebsd,!openbsd,!windows,!darwin package dns