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  }