github.com/telepresenceio/telepresence/v2@v2.20.0-pro.6.0.20240517030216-236ea954e789/pkg/client/rootd/dns/resolved_linux.go (about) 1 package dns 2 3 import ( 4 "context" 5 "fmt" 6 "net" 7 "strings" 8 "time" 9 10 "github.com/datawire/dlib/dcontext" 11 "github.com/datawire/dlib/dgroup" 12 "github.com/datawire/dlib/dlog" 13 "github.com/datawire/dlib/dtime" 14 "github.com/telepresenceio/telepresence/v2/pkg/client/rootd/dbus" 15 "github.com/telepresenceio/telepresence/v2/pkg/vif" 16 ) 17 18 func (s *Server) tryResolveD(c context.Context, dev vif.Device, configureDNS func(net.IP, *net.UDPAddr)) error { 19 // Connect to ResolveD via DBUS. 20 if !dbus.IsResolveDRunning(c) { 21 dlog.Error(c, "systemd-resolved is not running") 22 return errResolveDNotConfigured 23 } 24 25 c, cancelResolveD := context.WithCancel(c) 26 defer cancelResolveD() 27 28 listeners, err := s.dnsListeners(c) 29 if err != nil { 30 return err 31 } 32 // Create a new local address that the DNS resolver can listen to. 33 dnsResolverAddr, err := splitToUDPAddr(listeners[0].LocalAddr()) 34 if err != nil { 35 return err 36 } 37 dnsIP := s.remoteIP 38 configureDNS(dnsIP, dnsResolverAddr) 39 40 g := dgroup.NewGroup(c, dgroup.GroupConfig{}) 41 42 // DNS resolver 43 initDone := make(chan struct{}) 44 45 g.Go("Server", func(c context.Context) error { 46 dlog.Infof(c, "Configuring DNS IP %s", dnsIP) 47 if err = dbus.SetLinkDNS(c, int(dev.Index()), dnsIP); err != nil { 48 dlog.Error(c, err) 49 initDone <- struct{}{} 50 return errResolveDNotConfigured 51 } 52 defer func() { 53 // It's very likely that the context is cancelled here. We use it 54 // anyway, stripped from cancellation, to retain logging. 55 c, cancel := context.WithTimeout(context.WithoutCancel(c), time.Second) 56 defer cancel() 57 dlog.Debugf(c, "Reverting Link settings for %s", dev.Name()) 58 configureDNS(nil, nil) // Don't route from TUN-device 59 if err = dbus.RevertLink(c, int(dev.Index())); err != nil { 60 dlog.Error(c, err) 61 } 62 // No need to close listeners here. They are closed by the dnsServer 63 }() 64 if err = s.updateLinkDomains(c, dev); err != nil { 65 dlog.Error(c, err) 66 initDone <- struct{}{} 67 return errResolveDNotConfigured 68 } 69 return s.Run(c, initDone, listeners, nil, s.resolveInCluster) 70 }) 71 72 g.Go("SanityCheck", func(c context.Context) error { 73 if _, ok := <-initDone; ok { 74 // initDone was not closed, bail out. 75 return errResolveDNotConfigured 76 } 77 78 // Check if an attempt to resolve a DNS address reaches our DNS resolver, Two seconds should be plenty 79 cmdC, cmdCancel := context.WithTimeout(c, 2*time.Second) 80 defer cmdCancel() 81 for cmdC.Err() == nil { 82 go func() { 83 dlog.Debug(cmdC, "sanity-check lookup") 84 _, _ = net.DefaultResolver.LookupHost(cmdC, santiyCheck) 85 if s.RequestCount() > 0 { 86 cmdCancel() 87 } 88 }() 89 dtime.SleepWithContext(cmdC, 200*time.Millisecond) 90 } 91 <-cmdC.Done() 92 if s.RequestCount() > 0 { 93 // The query went all way through. Start processing search paths systemd-resolved style 94 // and return nil for successful validation. 95 s.processSearchPaths(g, s.updateLinkDomains, dev) 96 return nil 97 } 98 s.flushDNS() 99 dlog.Error(c, "resolver did not receive requests from systemd-resolved") 100 return errResolveDNotConfigured 101 }) 102 return g.Wait() 103 } 104 105 func (s *Server) updateLinkDomains(c context.Context, dev vif.Device) error { 106 s.Lock() 107 paths := make([]string, len(s.search)+len(s.routes)+len(s.includeSuffixes)+1) 108 109 // Namespaces are copied verbatim. Entries that aren't prefixed with "~" are considered search path entries. 110 copy(paths, s.search) 111 i := len(s.search) 112 for ns := range s.routes { 113 paths[i] = "~" + ns 114 i++ 115 } 116 117 // Include-suffixes are routes, i.e. in contrast to search paths, they are never appended to the name, but 118 // used as a filter that will direct queries for names ending with them to this resolver. Routes must be 119 // prefixed with "~". 120 for _, sfx := range s.includeSuffixes { 121 if !strings.HasSuffix(sfx, ".") { 122 sfx += "." 123 } 124 paths[i] = "~" + strings.TrimPrefix(sfx, ".") 125 i++ 126 } 127 paths[i] = "~" + s.clusterDomain 128 s.Unlock() 129 130 if err := dbus.SetLinkDomains(dcontext.HardContext(c), int(dev.Index()), paths...); err != nil { 131 return fmt.Errorf("failed to set link domains on %q: %w", dev.Name(), err) 132 } 133 s.flushDNS() 134 dlog.Debugf(c, "Link domains on device %q set to [%s]", dev.Name(), strings.Join(paths, ",")) 135 return nil 136 }