From f90c9c91d2bc9e2642260b0c1bb332a5616701cf Mon Sep 17 00:00:00 2001 From: Viktor Liu Date: Sat, 11 Jan 2025 23:02:32 +0100 Subject: [PATCH] Support non-openresolv for DNS on Linux --- client/internal/dns/resolvconf_unix.go | 72 +++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 6 deletions(-) diff --git a/client/internal/dns/resolvconf_unix.go b/client/internal/dns/resolvconf_unix.go index a5d1cc8a225..6b5fdaf8698 100644 --- a/client/internal/dns/resolvconf_unix.go +++ b/client/internal/dns/resolvconf_unix.go @@ -7,6 +7,7 @@ import ( "fmt" "net/netip" "os/exec" + "strings" log "github.com/sirupsen/logrus" @@ -15,23 +16,64 @@ import ( const resolvconfCommand = "resolvconf" +// resolvconfType represents the type of resolvconf implementation +type resolvconfType int + +func (r resolvconfType) String() string { + switch r { + case typeOpenresolv: + return "openresolv" + case typeResolvconf: + return "resolvconf" + default: + return "unknown" + } +} + +const ( + typeOpenresolv resolvconfType = iota + typeResolvconf +) + type resolvconf struct { ifaceName string + implType resolvconfType originalSearchDomains []string originalNameServers []string othersConfigs []string } -// supported "openresolv" only +func detectResolvconfType() (resolvconfType, error) { + cmd := exec.Command(resolvconfCommand, "--version") + out, err := cmd.Output() + if err != nil { + return typeOpenresolv, fmt.Errorf("failed to determine resolvconf type: %w", err) + } + + if strings.Contains(string(out), "openresolv") { + return typeOpenresolv, nil + } + return typeResolvconf, nil +} + func newResolvConfConfigurator(wgInterface string) (*resolvconf, error) { resolvConfEntries, err := parseDefaultResolvConf() if err != nil { log.Errorf("could not read original search domains from %s: %s", defaultResolvConfPath, err) } + implType, err := detectResolvconfType() + if err != nil { + log.Warnf("failed to detect resolvconf type, defaulting to openresolv: %v", err) + implType = typeOpenresolv + } else { + log.Infof("detected resolvconf type: %v", implType) + } + return &resolvconf{ ifaceName: wgInterface, + implType: implType, originalSearchDomains: resolvConfEntries.searchDomains, originalNameServers: resolvConfEntries.nameServers, othersConfigs: resolvConfEntries.others, @@ -80,8 +122,15 @@ func (r *resolvconf) applyDNSConfig(config HostDNSConfig, stateManager *stateman } func (r *resolvconf) restoreHostDNS() error { - // openresolv only, debian resolvconf doesn't support "-f" - cmd := exec.Command(resolvconfCommand, "-f", "-d", r.ifaceName) + var cmd *exec.Cmd + + switch r.implType { + case typeOpenresolv: + cmd = exec.Command(resolvconfCommand, "-f", "-d", r.ifaceName) + case typeResolvconf: + cmd = exec.Command(resolvconfCommand, "-d", r.ifaceName) + } + _, err := cmd.Output() if err != nil { return fmt.Errorf("removing resolvconf configuration for %s interface: %w", r.ifaceName, err) @@ -91,10 +140,21 @@ func (r *resolvconf) restoreHostDNS() error { } func (r *resolvconf) applyConfig(content bytes.Buffer) error { - // openresolv only, debian resolvconf doesn't support "-x" - cmd := exec.Command(resolvconfCommand, "-x", "-a", r.ifaceName) + var cmd *exec.Cmd + + switch r.implType { + case typeOpenresolv: + // OpenResolv supports exclusive mode with -x + cmd = exec.Command(resolvconfCommand, "-x", "-a", r.ifaceName) + case typeResolvconf: + cmd = exec.Command(resolvconfCommand, "-a", r.ifaceName) + default: + return fmt.Errorf("unsupported resolvconf type: %v", r.implType) + } + cmd.Stdin = &content - _, err := cmd.Output() + out, err := cmd.Output() + log.Tracef("resolvconf output: %s", out) if err != nil { return fmt.Errorf("applying resolvconf configuration for %s interface: %w", r.ifaceName, err) }