github.com/telepresenceio/telepresence/v2@v2.20.0-pro.6.0.20240517030216-236ea954e789/pkg/client/rootd/dns/server_darwin.go (about) 1 package dns 2 3 import ( 4 "context" 5 "net" 6 "os" 7 "path/filepath" 8 "strings" 9 "time" 10 11 "github.com/datawire/dlib/dgroup" 12 "github.com/datawire/dlib/dlog" 13 "github.com/telepresenceio/telepresence/v2/pkg/dnsproxy" 14 "github.com/telepresenceio/telepresence/v2/pkg/vif" 15 ) 16 17 const ( 18 maxRecursionTestRetries = 10 19 recursionTestTimeout = 500 * time.Millisecond 20 ) 21 22 // Worker places a file under the /etc/resolver directory so that it is picked up by the 23 // macOS resolver. The file is configured with a single nameserver that points to the local IP 24 // that the Telepresence DNS server listens to. The file is removed, and the DNS is flushed when 25 // the worker terminates 26 // 27 // For more information about /etc/resolver files, please view the man pages available at 28 // 29 // man 5 resolver 30 // 31 // or, if not on a Mac, follow this link: https://www.manpagez.com/man/5/resolver/ 32 func (s *Server) Worker(c context.Context, dev vif.Device, configureDNS func(net.IP, *net.UDPAddr)) error { 33 resolverDirName := filepath.Join("/etc", "resolver") 34 35 listener, err := newLocalUDPListener(c) 36 if err != nil { 37 return err 38 } 39 dnsAddr, err := splitToUDPAddr(listener.LocalAddr()) 40 if err != nil { 41 return err 42 } 43 configureDNS(nil, dnsAddr) 44 45 err = os.MkdirAll(resolverDirName, 0o755) 46 if err != nil { 47 return err 48 } 49 50 // Ensure lingering all telepresence.* files are removed. 51 if err := s.removeResolverFiles(c, resolverDirName); err != nil { 52 return err 53 } 54 55 defer func() { 56 _ = s.removeResolverFiles(c, resolverDirName) 57 s.flushDNS() 58 }() 59 60 // Start local DNS server 61 g := dgroup.NewGroup(c, dgroup.GroupConfig{}) 62 g.Go("Server", func(c context.Context) error { 63 if err := s.updateResolverFiles(c, resolverDirName, dnsAddr); err != nil { 64 return err 65 } 66 s.processSearchPaths(g, func(c context.Context, _ vif.Device) error { 67 return s.updateResolverFiles(c, resolverDirName, dnsAddr) 68 }, dev) 69 // Server will close the listener, so no need to close it here. 70 return s.Run(c, make(chan struct{}), []net.PacketConn{listener}, nil, s.resolveInCluster) 71 }) 72 return g.Wait() 73 } 74 75 // removeResolverFiles performs rm -f /etc/resolver/telepresence.*. 76 func (s *Server) removeResolverFiles(c context.Context, resolverDirName string) error { 77 files, err := os.ReadDir(resolverDirName) 78 if err != nil { 79 return err 80 } 81 for _, file := range files { 82 if n := file.Name(); strings.HasPrefix(n, "telepresence.") { 83 fn := filepath.Join(resolverDirName, n) 84 dlog.Debugf(c, "Removing file %q", fn) 85 if err := os.Remove(fn); err != nil { 86 return err 87 } 88 } 89 } 90 return nil 91 } 92 93 func (s *Server) updateResolverFiles(c context.Context, resolverDirName string, dnsAddr *net.UDPAddr) error { 94 s.Lock() 95 defer s.Unlock() 96 97 nameservers := []string{dnsAddr.IP.String()} 98 port := dnsAddr.Port 99 newDomainResolveFile := func(domain string) *dnsproxy.ResolveFile { 100 return &dnsproxy.ResolveFile{ 101 Port: port, 102 Domain: domain, 103 Nameservers: nameservers, 104 } 105 } 106 107 // All routes and include suffixes become domains 108 domains := make(map[string]*dnsproxy.ResolveFile, len(s.routes)+len(s.includeSuffixes)) 109 for route := range s.routes { 110 domains[route] = newDomainResolveFile(route) 111 } 112 for _, sfx := range s.includeSuffixes { 113 sfx = strings.TrimPrefix(sfx, ".") 114 domains[sfx] = newDomainResolveFile(sfx) 115 } 116 clusterDomain := strings.TrimSuffix(s.clusterDomain, ".") 117 domains[clusterDomain] = newDomainResolveFile(clusterDomain) 118 domains[tel2SubDomain] = newDomainResolveFile(tel2SubDomain) 119 120 nextSearch: 121 for _, search := range s.search { 122 search = strings.TrimSuffix(search, ".") 123 if df, ok := domains[search]; ok { 124 df.Search = append(df.Search, search) 125 continue 126 } 127 for domain, df := range domains { 128 if strings.HasSuffix(search, "."+domain) { 129 df.Search = append(df.Search, search) 130 continue nextSearch 131 } 132 } 133 } 134 135 for domain := range s.domains { 136 if _, ok := domains[domain]; !ok { 137 nsFile := domainResolverFile(resolverDirName, domain) 138 dlog.Infof(c, "Removing %s", nsFile) 139 if err := os.Remove(nsFile); err != nil { 140 dlog.Error(c, err) 141 } 142 delete(s.domains, domain) 143 } 144 } 145 146 for domain, rf := range domains { 147 nsFile := domainResolverFile(resolverDirName, domain) 148 if _, ok := s.domains[domain]; ok { 149 if oldRf, err := dnsproxy.ReadResolveFile(nsFile); err != nil && rf.Equals(oldRf) { 150 continue 151 } 152 dlog.Infof(c, "Regenerating %s", nsFile) 153 } else { 154 s.domains[domain] = struct{}{} 155 dlog.Infof(c, "Generating %s", nsFile) 156 } 157 if err := rf.Write(nsFile); err != nil { 158 dlog.Error(c, err) 159 } 160 } 161 s.flushDNS() 162 return nil 163 } 164 165 func domainResolverFile(resolverDirName, domain string) string { 166 return filepath.Join(resolverDirName, "telepresence."+domain) 167 }