mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-11-04 10:11:18 +01:00 
			
		
		
		
	Endeavour OS, at least, uses NetworkManager 1.44.2 and does not use systemd-resolved behind the scenes at all. If we find ourselves in that situation, return "direct" not "systemd-resolved" Fixes https://github.com/tailscale/tailscale/issues/9687 Signed-off-by: Denton Gentry <dgentry@tailscale.com>
		
			
				
	
	
		
			487 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			487 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright (c) Tailscale Inc & AUTHORS
 | 
						|
// SPDX-License-Identifier: BSD-3-Clause
 | 
						|
 | 
						|
package dns
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"io/fs"
 | 
						|
	"os"
 | 
						|
	"strings"
 | 
						|
	"testing"
 | 
						|
 | 
						|
	"tailscale.com/tstest"
 | 
						|
	"tailscale.com/util/cmpver"
 | 
						|
)
 | 
						|
 | 
						|
func TestLinuxDNSMode(t *testing.T) {
 | 
						|
	tests := []struct {
 | 
						|
		name    string
 | 
						|
		env     newOSConfigEnv
 | 
						|
		wantLog string
 | 
						|
		want    string
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			name:    "no_obvious_resolv.conf_owner",
 | 
						|
			env:     env(resolvDotConf("nameserver 10.0.0.1")),
 | 
						|
			wantLog: "dns: [rc=unknown ret=direct]",
 | 
						|
			want:    "direct",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "network_manager",
 | 
						|
			env: env(
 | 
						|
				resolvDotConf(
 | 
						|
					"# Managed by NetworkManager",
 | 
						|
					"nameserver 10.0.0.1")),
 | 
						|
			wantLog: "dns: resolvedIsActuallyResolver error: resolv.conf doesn't point to systemd-resolved; points to [10.0.0.1]\n" +
 | 
						|
				"dns: [rc=nm resolved=not-in-use ret=direct]",
 | 
						|
			want: "direct",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:    "resolvconf_but_no_resolvconf_binary",
 | 
						|
			env:     env(resolvDotConf("# Managed by resolvconf", "nameserver 10.0.0.1")),
 | 
						|
			wantLog: "dns: [rc=resolvconf resolvconf=no ret=direct]",
 | 
						|
			want:    "direct",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "debian_resolvconf",
 | 
						|
			env: env(
 | 
						|
				resolvDotConf("# Managed by resolvconf", "nameserver 10.0.0.1"),
 | 
						|
				resolvconf("debian")),
 | 
						|
			wantLog: "dns: [rc=resolvconf resolvconf=debian ret=debian-resolvconf]",
 | 
						|
			want:    "debian-resolvconf",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "openresolv",
 | 
						|
			env: env(
 | 
						|
				resolvDotConf("# Managed by resolvconf", "nameserver 10.0.0.1"),
 | 
						|
				resolvconf("openresolv")),
 | 
						|
			wantLog: "dns: [rc=resolvconf resolvconf=openresolv ret=openresolv]",
 | 
						|
			want:    "openresolv",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "unknown_resolvconf_flavor",
 | 
						|
			env: env(
 | 
						|
				resolvDotConf("# Managed by resolvconf", "nameserver 10.0.0.1"),
 | 
						|
				resolvconf("daves-discount-resolvconf")),
 | 
						|
			wantLog: "[unexpected] got unknown flavor of resolvconf \"daves-discount-resolvconf\", falling back to direct manager\ndns: [rc=resolvconf resolvconf=daves-discount-resolvconf ret=direct]",
 | 
						|
			want:    "direct",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:    "resolved_alone_without_ping",
 | 
						|
			env:     env(resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53")),
 | 
						|
			wantLog: "dns: ResolvConfMode error: dbus property not found\ndns: [rc=resolved resolved=file nm=no resolv-conf-mode=error ret=systemd-resolved]",
 | 
						|
			want:    "systemd-resolved",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "resolved_alone_with_ping",
 | 
						|
			env: env(
 | 
						|
				resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
 | 
						|
				resolvedRunning()),
 | 
						|
			wantLog: "dns: [resolved-ping=yes rc=resolved resolved=file nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
 | 
						|
			want:    "systemd-resolved",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "resolved_and_nsswitch_resolve",
 | 
						|
			env: env(
 | 
						|
				resolvDotConf("# Managed by systemd-resolved", "nameserver 1.1.1.1"),
 | 
						|
				resolvedRunning(),
 | 
						|
				nsswitchDotConf("hosts: files resolve [!UNAVAIL=return] dns"),
 | 
						|
			),
 | 
						|
			wantLog: "dns: [resolved-ping=yes rc=resolved resolved=nss nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
 | 
						|
			want:    "systemd-resolved",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "resolved_and_nsswitch_dns",
 | 
						|
			env: env(
 | 
						|
				resolvDotConf("# Managed by systemd-resolved", "nameserver 1.1.1.1"),
 | 
						|
				resolvedRunning(),
 | 
						|
				nsswitchDotConf("hosts: files dns resolve [!UNAVAIL=return]"),
 | 
						|
			),
 | 
						|
			wantLog: "dns: resolvedIsActuallyResolver error: resolv.conf doesn't point to systemd-resolved; points to [1.1.1.1]\ndns: [resolved-ping=yes rc=resolved resolved=not-in-use ret=direct]",
 | 
						|
			want:    "direct",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "resolved_and_nsswitch_none",
 | 
						|
			env: env(
 | 
						|
				resolvDotConf("# Managed by systemd-resolved", "nameserver 1.1.1.1"),
 | 
						|
				resolvedRunning(),
 | 
						|
				nsswitchDotConf("hosts:"),
 | 
						|
			),
 | 
						|
			wantLog: "dns: resolvedIsActuallyResolver error: resolv.conf doesn't point to systemd-resolved; points to [1.1.1.1]\ndns: [resolved-ping=yes rc=resolved resolved=not-in-use ret=direct]",
 | 
						|
			want:    "direct",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "resolved_and_networkmanager_not_using_resolved",
 | 
						|
			env: env(
 | 
						|
				resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
 | 
						|
				resolvedRunning(),
 | 
						|
				nmRunning("1.2.3", false)),
 | 
						|
			wantLog: "dns: [resolved-ping=yes rc=resolved resolved=file nm=yes nm-resolved=no resolv-conf-mode=fortests ret=systemd-resolved]",
 | 
						|
			want:    "systemd-resolved",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "resolved_and_mid_2020_networkmanager",
 | 
						|
			env: env(
 | 
						|
				resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
 | 
						|
				resolvedRunning(),
 | 
						|
				nmRunning("1.26.2", true)),
 | 
						|
			wantLog: "dns: [resolved-ping=yes rc=resolved resolved=file nm=yes nm-resolved=yes nm-safe=yes ret=network-manager]",
 | 
						|
			want:    "network-manager",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "resolved_and_2021_networkmanager",
 | 
						|
			env: env(
 | 
						|
				resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
 | 
						|
				resolvedRunning(),
 | 
						|
				nmRunning("1.27.0", true)),
 | 
						|
			wantLog: "dns: [resolved-ping=yes rc=resolved resolved=file nm=yes nm-resolved=yes nm-safe=no resolv-conf-mode=fortests ret=systemd-resolved]",
 | 
						|
			want:    "systemd-resolved",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "resolved_and_ancient_networkmanager",
 | 
						|
			env: env(
 | 
						|
				resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
 | 
						|
				resolvedRunning(),
 | 
						|
				nmRunning("1.22.0", true)),
 | 
						|
			wantLog: "dns: [resolved-ping=yes rc=resolved resolved=file nm=yes nm-resolved=yes nm-safe=no resolv-conf-mode=fortests ret=systemd-resolved]",
 | 
						|
			want:    "systemd-resolved",
 | 
						|
		},
 | 
						|
		// Regression tests for extreme corner cases below.
 | 
						|
		{
 | 
						|
			// One user reported a configuration whose comment string
 | 
						|
			// alleged that it was managed by systemd-resolved, but it
 | 
						|
			// was actually a completely static config file pointing
 | 
						|
			// elsewhere.
 | 
						|
			name: "allegedly_resolved_but_not_in_resolv.conf",
 | 
						|
			env:  env(resolvDotConf("# Managed by systemd-resolved", "nameserver 10.0.0.1")),
 | 
						|
			wantLog: "dns: resolvedIsActuallyResolver error: resolv.conf doesn't point to systemd-resolved; points to [10.0.0.1]\n" +
 | 
						|
				"dns: [rc=resolved resolved=not-in-use ret=direct]",
 | 
						|
			want: "direct",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			// We used to incorrectly decide that resolved wasn't in
 | 
						|
			// charge when handed this (admittedly weird and bugged)
 | 
						|
			// resolv.conf.
 | 
						|
			name: "resolved_with_duplicates_in_resolv.conf",
 | 
						|
			env: env(
 | 
						|
				resolvDotConf(
 | 
						|
					"# Managed by systemd-resolved",
 | 
						|
					"nameserver 127.0.0.53",
 | 
						|
					"nameserver 127.0.0.53"),
 | 
						|
				resolvedRunning()),
 | 
						|
			wantLog: "dns: [resolved-ping=yes rc=resolved resolved=file nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
 | 
						|
			want:    "systemd-resolved",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			// More than one user has had resolvconf write a config that points to
 | 
						|
			// systemd-resolved. We're better off using systemd-resolved.
 | 
						|
			// regression test for https://github.com/tailscale/tailscale/issues/3026
 | 
						|
			name: "allegedly_resolvconf_but_actually_systemd-resolved",
 | 
						|
			env: env(resolvDotConf(
 | 
						|
				"# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)",
 | 
						|
				"#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN",
 | 
						|
				"# 127.0.0.53 is the systemd-resolved stub resolver.",
 | 
						|
				"# run \"systemd-resolve --status\" to see details about the actual nameservers.",
 | 
						|
				"nameserver 127.0.0.53"),
 | 
						|
				resolvedRunning()),
 | 
						|
			wantLog: "dns: [resolved-ping=yes rc=resolved resolved=file nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
 | 
						|
			want:    "systemd-resolved",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			// More than one user has had resolvconf write a config that points to
 | 
						|
			// systemd-resolved. We're better off using systemd-resolved.
 | 
						|
			// and assuming that even if the ping doesn't show that env is correct
 | 
						|
			// regression test for https://github.com/tailscale/tailscale/issues/3026
 | 
						|
			name: "allegedly_resolvconf_but_actually_systemd-resolved_but_no_ping",
 | 
						|
			env: env(resolvDotConf(
 | 
						|
				"# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)",
 | 
						|
				"#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN",
 | 
						|
				"# 127.0.0.53 is the systemd-resolved stub resolver.",
 | 
						|
				"# run \"systemd-resolve --status\" to see details about the actual nameservers.",
 | 
						|
				"nameserver 127.0.0.53")),
 | 
						|
			wantLog: "dns: ResolvConfMode error: dbus property not found\ndns: [rc=resolved resolved=file nm=no resolv-conf-mode=error ret=systemd-resolved]",
 | 
						|
			want:    "systemd-resolved",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			// regression test for https://github.com/tailscale/tailscale/issues/3304
 | 
						|
			name: "networkmanager_but_pointing_at_systemd-resolved",
 | 
						|
			env: env(resolvDotConf(
 | 
						|
				"# Generated by NetworkManager",
 | 
						|
				"nameserver 127.0.0.53",
 | 
						|
				"options edns0 trust-ad"),
 | 
						|
				resolvedRunning(),
 | 
						|
				nmRunning("1.32.12", true)),
 | 
						|
			wantLog: "dns: [resolved-ping=yes rc=nm resolved=file nm-resolved=yes nm-safe=no resolv-conf-mode=fortests ret=systemd-resolved]",
 | 
						|
			want:    "systemd-resolved",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			// regression test for https://github.com/tailscale/tailscale/issues/3304
 | 
						|
			name: "networkmanager_but_pointing_at_systemd-resolved_but_no_resolved_ping",
 | 
						|
			env: env(resolvDotConf(
 | 
						|
				"# Generated by NetworkManager",
 | 
						|
				"nameserver 127.0.0.53",
 | 
						|
				"options edns0 trust-ad"),
 | 
						|
				nmRunning("1.32.12", true)),
 | 
						|
			wantLog: "dns: ResolvConfMode error: dbus property not found\ndns: [rc=nm resolved=file nm-resolved=yes nm-safe=no resolv-conf-mode=error ret=systemd-resolved]",
 | 
						|
			want:    "systemd-resolved",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			// regression test for https://github.com/tailscale/tailscale/issues/3304
 | 
						|
			name: "networkmanager_but_pointing_at_systemd-resolved_and_safe_nm",
 | 
						|
			env: env(resolvDotConf(
 | 
						|
				"# Generated by NetworkManager",
 | 
						|
				"nameserver 127.0.0.53",
 | 
						|
				"options edns0 trust-ad"),
 | 
						|
				resolvedRunning(),
 | 
						|
				nmRunning("1.26.3", true)),
 | 
						|
			wantLog: "dns: [resolved-ping=yes rc=nm resolved=file nm-resolved=yes nm-safe=yes ret=network-manager]",
 | 
						|
			want:    "network-manager",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			// regression test for https://github.com/tailscale/tailscale/issues/3304
 | 
						|
			name: "networkmanager_but_pointing_at_systemd-resolved_and_no_networkmanager",
 | 
						|
			env: env(resolvDotConf(
 | 
						|
				"# Generated by NetworkManager",
 | 
						|
				"nameserver 127.0.0.53",
 | 
						|
				"options edns0 trust-ad"),
 | 
						|
				resolvedRunning()),
 | 
						|
			wantLog: "dns: [resolved-ping=yes rc=nm resolved=file nm-resolved=yes nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
 | 
						|
			want:    "systemd-resolved",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			// regression test for https://github.com/tailscale/tailscale/issues/3531
 | 
						|
			name: "networkmanager_but_systemd-resolved_with_search_domain",
 | 
						|
			env: env(resolvDotConf(
 | 
						|
				"# Generated by NetworkManager",
 | 
						|
				"search lan",
 | 
						|
				"nameserver 127.0.0.53"),
 | 
						|
				resolvedRunning()),
 | 
						|
			wantLog: "dns: [resolved-ping=yes rc=nm resolved=file nm-resolved=yes nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
 | 
						|
			want:    "systemd-resolved",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			// Make sure that we ping systemd-resolved to let it start up and write its resolv.conf
 | 
						|
			// before we read its file.
 | 
						|
			env: env(resolvedStartOnPingAndThen(
 | 
						|
				resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
 | 
						|
				resolvedDbusProperty(),
 | 
						|
			)),
 | 
						|
			wantLog: "dns: [resolved-ping=yes rc=resolved resolved=file nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
 | 
						|
			want:    "systemd-resolved",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			// regression test for https://github.com/tailscale/tailscale/issues/9687
 | 
						|
			name: "networkmanager_endeavouros",
 | 
						|
			env: env(resolvDotConf(
 | 
						|
				"# Generated by NetworkManager",
 | 
						|
				"search example.com localdomain",
 | 
						|
				"nameserver 10.0.0.1"),
 | 
						|
				nmRunning("1.44.2", false)),
 | 
						|
			wantLog: "dns: resolvedIsActuallyResolver error: resolv.conf doesn't point to systemd-resolved; points to [10.0.0.1]\n" +
 | 
						|
				"dns: [rc=nm resolved=not-in-use ret=direct]",
 | 
						|
			want: "direct",
 | 
						|
		},
 | 
						|
	}
 | 
						|
	for _, tt := range tests {
 | 
						|
		t.Run(tt.name, func(t *testing.T) {
 | 
						|
			var logBuf tstest.MemLogger
 | 
						|
			got, err := dnsMode(logBuf.Logf, tt.env)
 | 
						|
			if err != nil {
 | 
						|
				t.Fatal(err)
 | 
						|
			}
 | 
						|
			if got != tt.want {
 | 
						|
				t.Errorf("got %s; want %s", got, tt.want)
 | 
						|
			}
 | 
						|
			if got := strings.TrimSpace(logBuf.String()); got != tt.wantLog {
 | 
						|
				t.Errorf("log output mismatch:\n got: %q\nwant: %q\n", got, tt.wantLog)
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type memFS map[string]any // full path => string for regular files
 | 
						|
 | 
						|
func (m memFS) Stat(name string) (isRegular bool, err error) {
 | 
						|
	v, ok := m[name]
 | 
						|
	if !ok {
 | 
						|
		return false, fs.ErrNotExist
 | 
						|
	}
 | 
						|
	if _, ok := v.(string); ok {
 | 
						|
		return true, nil
 | 
						|
	}
 | 
						|
	return false, nil
 | 
						|
}
 | 
						|
 | 
						|
func (m memFS) Rename(oldName, newName string) error { panic("TODO") }
 | 
						|
func (m memFS) Remove(name string) error             { panic("TODO") }
 | 
						|
func (m memFS) ReadFile(name string) ([]byte, error) {
 | 
						|
	v, ok := m[name]
 | 
						|
	if !ok {
 | 
						|
		return nil, fs.ErrNotExist
 | 
						|
	}
 | 
						|
	if s, ok := v.(string); ok {
 | 
						|
		return []byte(s), nil
 | 
						|
	}
 | 
						|
	panic("TODO")
 | 
						|
}
 | 
						|
 | 
						|
func (m memFS) Truncate(name string) error {
 | 
						|
	v, ok := m[name]
 | 
						|
	if !ok {
 | 
						|
		return fs.ErrNotExist
 | 
						|
	}
 | 
						|
	if s, ok := v.(string); ok {
 | 
						|
		m[name] = s[:0]
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (m memFS) WriteFile(name string, contents []byte, perm os.FileMode) error {
 | 
						|
	m[name] = string(contents)
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
type dbusService struct {
 | 
						|
	name, path string
 | 
						|
	hook       func() // if non-nil, run on ping
 | 
						|
}
 | 
						|
 | 
						|
type dbusProperty struct {
 | 
						|
	name, path    string
 | 
						|
	iface, member string
 | 
						|
	hook          func() (string, error) // what to return
 | 
						|
}
 | 
						|
 | 
						|
type envBuilder struct {
 | 
						|
	fs              memFS
 | 
						|
	dbus            []dbusService
 | 
						|
	dbusProperties  []dbusProperty
 | 
						|
	nmUsingResolved bool
 | 
						|
	nmVersion       string
 | 
						|
	resolvconfStyle string
 | 
						|
}
 | 
						|
 | 
						|
type envOption interface {
 | 
						|
	apply(*envBuilder)
 | 
						|
}
 | 
						|
 | 
						|
type envOpt func(*envBuilder)
 | 
						|
 | 
						|
func (e envOpt) apply(b *envBuilder) {
 | 
						|
	e(b)
 | 
						|
}
 | 
						|
 | 
						|
func env(opts ...envOption) newOSConfigEnv {
 | 
						|
	b := &envBuilder{
 | 
						|
		fs: memFS{},
 | 
						|
	}
 | 
						|
	for _, opt := range opts {
 | 
						|
		opt.apply(b)
 | 
						|
	}
 | 
						|
 | 
						|
	return newOSConfigEnv{
 | 
						|
		fs: b.fs,
 | 
						|
		dbusPing: func(name, path string) error {
 | 
						|
			for _, svc := range b.dbus {
 | 
						|
				if svc.name == name && svc.path == path {
 | 
						|
					if svc.hook != nil {
 | 
						|
						svc.hook()
 | 
						|
					}
 | 
						|
					return nil
 | 
						|
				}
 | 
						|
			}
 | 
						|
			return errors.New("dbus service not found")
 | 
						|
		},
 | 
						|
		dbusReadString: func(name, path, iface, member string) (string, error) {
 | 
						|
			for _, svc := range b.dbusProperties {
 | 
						|
				if svc.name == name && svc.path == path && svc.iface == iface && svc.member == member {
 | 
						|
					return svc.hook()
 | 
						|
				}
 | 
						|
			}
 | 
						|
			return "", errors.New("dbus property not found")
 | 
						|
		},
 | 
						|
		nmIsUsingResolved: func() error {
 | 
						|
			if !b.nmUsingResolved {
 | 
						|
				return errors.New("networkmanager not using resolved")
 | 
						|
			}
 | 
						|
			return nil
 | 
						|
		},
 | 
						|
		nmVersionBetween: func(first, last string) (bool, error) {
 | 
						|
			outside := cmpver.Compare(b.nmVersion, first) < 0 || cmpver.Compare(b.nmVersion, last) > 0
 | 
						|
			return !outside, nil
 | 
						|
		},
 | 
						|
		resolvconfStyle: func() string { return b.resolvconfStyle },
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func resolvDotConf(ss ...string) envOption {
 | 
						|
	return envOpt(func(b *envBuilder) {
 | 
						|
		b.fs["/etc/resolv.conf"] = strings.Join(ss, "\n")
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func nsswitchDotConf(ss ...string) envOption {
 | 
						|
	return envOpt(func(b *envBuilder) {
 | 
						|
		b.fs["/etc/nsswitch.conf"] = strings.Join(ss, "\n")
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
// resolvedRunning returns an option that makes resolved reply to a dbusPing
 | 
						|
// and the ResolvConfMode property.
 | 
						|
func resolvedRunning() envOption {
 | 
						|
	return resolvedStartOnPingAndThen(resolvedDbusProperty())
 | 
						|
}
 | 
						|
 | 
						|
// resolvedDbusProperty returns an option that responds to the ResolvConfMode
 | 
						|
// property that resolved exposes.
 | 
						|
func resolvedDbusProperty() envOption {
 | 
						|
	return setDbusProperty("org.freedesktop.resolve1", "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", "ResolvConfMode", "fortests")
 | 
						|
}
 | 
						|
 | 
						|
// resolvedStartOnPingAndThen returns an option that makes resolved be
 | 
						|
// active but not yet running.  On a dbus ping, it then applies the
 | 
						|
// provided options.
 | 
						|
func resolvedStartOnPingAndThen(opts ...envOption) envOption {
 | 
						|
	return envOpt(func(b *envBuilder) {
 | 
						|
		b.dbus = append(b.dbus, dbusService{
 | 
						|
			name: "org.freedesktop.resolve1",
 | 
						|
			path: "/org/freedesktop/resolve1",
 | 
						|
			hook: func() {
 | 
						|
				for _, opt := range opts {
 | 
						|
					opt.apply(b)
 | 
						|
				}
 | 
						|
			},
 | 
						|
		})
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func nmRunning(version string, usingResolved bool) envOption {
 | 
						|
	return envOpt(func(b *envBuilder) {
 | 
						|
		b.nmUsingResolved = usingResolved
 | 
						|
		b.nmVersion = version
 | 
						|
		b.dbus = append(b.dbus, dbusService{name: "org.freedesktop.NetworkManager", path: "/org/freedesktop/NetworkManager/DnsManager"})
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func resolvconf(s string) envOption {
 | 
						|
	return envOpt(func(b *envBuilder) {
 | 
						|
		b.resolvconfStyle = s
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func setDbusProperty(name, path, iface, member, value string) envOption {
 | 
						|
	return envOpt(func(b *envBuilder) {
 | 
						|
		b.dbusProperties = append(b.dbusProperties, dbusProperty{
 | 
						|
			name:   name,
 | 
						|
			path:   path,
 | 
						|
			iface:  iface,
 | 
						|
			member: member,
 | 
						|
			hook: func() (string, error) {
 | 
						|
				return value, nil
 | 
						|
			},
 | 
						|
		})
 | 
						|
	})
 | 
						|
}
 |