diff --git a/cmd/lopower/lopower.go b/cmd/lopower/lopower.go index 6c975d37b..9dd829982 100644 --- a/cmd/lopower/lopower.go +++ b/cmd/lopower/lopower.go @@ -623,14 +623,19 @@ func (lp *lpServer) startTSNet(ctx context.Context) { log.Fatal(err) } - lp.tsnet = &tsnet.Server{ + ts := &tsnet.Server{ Dir: filepath.Join(lp.dir, "tsnet"), Hostname: hostname, UserLogf: log.Printf, Ephemeral: false, } + lp.tsnet = ts + ts.PreStart = func() error { + ts.Sys().DNSManager.Get().SetForceAAAA(true) + return nil + } - if _, err := lp.tsnet.Up(ctx); err != nil { + if _, err := ts.Up(ctx); err != nil { log.Fatal(err) } } diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index edd56f7c4..ce938122c 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -4194,7 +4194,11 @@ func (b *LocalBackend) authReconfig() { disableSubnetsIfPAC := nm.HasCap(tailcfg.NodeAttrDisableSubnetsIfPAC) userDialUseRoutes := nm.HasCap(tailcfg.NodeAttrUserDialUseRoutes) dohURL, dohURLOK := exitNodeCanProxyDNS(nm, b.peers, prefs.ExitNodeID()) - dcfg := dnsConfigForNetmap(nm, b.peers, prefs, b.keyExpired, b.logf, version.OS()) + var forceAAAA bool + if dm, ok := b.sys.DNSManager.GetOK(); ok { + forceAAAA = dm.GetForceAAAA() + } + dcfg := dnsConfigForNetmap(nm, b.peers, prefs, b.keyExpired, forceAAAA, b.logf, version.OS()) // If the current node is an app connector, ensure the app connector machine is started b.reconfigAppConnectorLocked(nm, prefs) b.mu.Unlock() @@ -4294,7 +4298,7 @@ func shouldUseOneCGNATRoute(logf logger.Logf, controlKnobs *controlknobs.Knobs, // // The versionOS is a Tailscale-style version ("iOS", "macOS") and not // a runtime.GOOS. -func dnsConfigForNetmap(nm *netmap.NetworkMap, peers map[tailcfg.NodeID]tailcfg.NodeView, prefs ipn.PrefsView, selfExpired bool, logf logger.Logf, versionOS string) *dns.Config { +func dnsConfigForNetmap(nm *netmap.NetworkMap, peers map[tailcfg.NodeID]tailcfg.NodeView, prefs ipn.PrefsView, selfExpired, forceAAAA bool, logf logger.Logf, versionOS string) *dns.Config { if nm == nil { return nil } @@ -4357,7 +4361,7 @@ func dnsConfigForNetmap(nm *netmap.NetworkMap, peers map[tailcfg.NodeID]tailcfg. // https://github.com/tailscale/tailscale/issues/1152 // tracks adding the right capability reporting to // enable AAAA in MagicDNS. - if addr.Addr().Is6() && have4 { + if addr.Addr().Is6() && have4 && !forceAAAA { continue } ips = append(ips, addr.Addr()) diff --git a/ipn/ipnlocal/local_test.go b/ipn/ipnlocal/local_test.go index 433679dda..b0c4060ff 100644 --- a/ipn/ipnlocal/local_test.go +++ b/ipn/ipnlocal/local_test.go @@ -1298,7 +1298,7 @@ func TestDNSConfigForNetmapForExitNodeConfigs(t *testing.T) { } prefs := &ipn.Prefs{ExitNodeID: tc.exitNode, CorpDNS: true} - got := dnsConfigForNetmap(nm, peersMap(tc.peers), prefs.View(), false, t.Logf, "") + got := dnsConfigForNetmap(nm, peersMap(tc.peers), prefs.View(), false, false, t.Logf, "") if !resolversEqual(t, got.DefaultResolvers, tc.wantDefaultResolvers) { t.Errorf("DefaultResolvers: got %#v, want %#v", got.DefaultResolvers, tc.wantDefaultResolvers) } diff --git a/net/dns/manager.go b/net/dns/manager.go index 13cb2d84e..f527c9f71 100644 --- a/net/dns/manager.go +++ b/net/dns/manager.go @@ -63,7 +63,8 @@ type Manager struct { mu sync.Mutex // guards following // config is the last configuration we successfully compiled or nil if there // was any failure applying the last configuration. - config *Config + config *Config + forceAAAA bool // whether client wants MagicDNS AAAA even if unsure of host's IPv6 status } // NewManagers created a new manager from the given config. @@ -128,6 +129,18 @@ func (m *Manager) GetBaseConfig() (OSConfig, error) { return m.os.GetBaseConfig() } +func (m *Manager) GetForceAAAA() bool { + m.mu.Lock() + defer m.mu.Unlock() + return m.forceAAAA +} + +func (m *Manager) SetForceAAAA(v bool) { + m.mu.Lock() + defer m.mu.Unlock() + m.forceAAAA = v +} + // setLocked sets the DNS configuration. // // m.mu must be held. diff --git a/tsnet/tsnet.go b/tsnet/tsnet.go index 70084c103..04cad11e3 100644 --- a/tsnet/tsnet.go +++ b/tsnet/tsnet.go @@ -121,6 +121,11 @@ type Server struct { // field at zero unless you know what you are doing. Port uint16 + // PreStart is an optional hook to run just before LocalBackend.Start, + // to reconfigure internals. If it returns an error, Server.Start + // will return that error, wrapper. + PreStart func() error + getCertForTesting func(*tls.ClientHelloInfo) (*tls.Certificate, error) initOnce sync.Once @@ -616,6 +621,13 @@ func (s *Server) start() (reterr error) { prefs.ControlURL = s.ControlURL prefs.RunWebClient = s.RunWebClient authKey := s.getAuthKey() + + if f := s.PreStart; f != nil { + if err := f(); err != nil { + return fmt.Errorf("PreStart: %w", err) + } + } + err = lb.Start(ipn.Options{ UpdatePrefs: prefs, AuthKey: authKey,