mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-08-10 13:20:33 +02:00
cmd/cli: set DNS for all physical interfaces on Windows/Darwin
This commit is contained in:
@@ -42,3 +42,21 @@ func networkServiceName(ifaceName string, r io.Reader) string {
|
|||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validInterface reports whether the *net.Interface is a valid one, which includes:
|
||||||
|
//
|
||||||
|
// - en0: physical wireless
|
||||||
|
// - en1: Thunderbolt 1
|
||||||
|
// - en2: Thunderbolt 2
|
||||||
|
// - en3: Thunderbolt 3
|
||||||
|
// - en4: Thunderbolt 4
|
||||||
|
//
|
||||||
|
// For full list, see: https://unix.stackexchange.com/questions/603506/what-are-these-ifconfig-interfaces-on-macos
|
||||||
|
func validInterface(iface *net.Interface) bool {
|
||||||
|
switch iface.Name {
|
||||||
|
case "en0", "en1", "en2", "en3", "en4":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,3 +5,5 @@ package cli
|
|||||||
import "net"
|
import "net"
|
||||||
|
|
||||||
func patchNetIfaceName(iface *net.Interface) error { return nil }
|
func patchNetIfaceName(iface *net.Interface) error { return nil }
|
||||||
|
|
||||||
|
func validInterface(iface *net.Interface) bool { return true }
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
@@ -417,8 +418,14 @@ func (p *prog) setDNS() {
|
|||||||
if iface == "" {
|
if iface == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// allIfaces tracks whether we should set DNS for all physical interfaces.
|
||||||
|
allIfaces := false
|
||||||
if iface == "auto" {
|
if iface == "auto" {
|
||||||
iface = defaultIfaceName()
|
iface = defaultIfaceName()
|
||||||
|
// If iface is "auto", it means user does not specify "--iface" flag.
|
||||||
|
// In this case, ctrld has to set DNS for all physical interfaces, so
|
||||||
|
// thing will still work when user switch from one to the other.
|
||||||
|
allIfaces = requiredMultiNICsConfig()
|
||||||
}
|
}
|
||||||
lc := cfg.FirstListener()
|
lc := cfg.FirstListener()
|
||||||
if lc == nil {
|
if lc == nil {
|
||||||
@@ -460,14 +467,22 @@ func (p *prog) setDNS() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
logger.Debug().Msg("setting DNS successfully")
|
logger.Debug().Msg("setting DNS successfully")
|
||||||
|
if allIfaces {
|
||||||
|
withEachPhysicalInterfaces(netIface.Name, "set DNS", func(i *net.Interface) error {
|
||||||
|
return setDNS(i, nameservers)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *prog) resetDNS() {
|
func (p *prog) resetDNS() {
|
||||||
if iface == "" {
|
if iface == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
allIfaces := false
|
||||||
if iface == "auto" {
|
if iface == "auto" {
|
||||||
iface = defaultIfaceName()
|
iface = defaultIfaceName()
|
||||||
|
// See corresponding comments in (*prog).setDNS function.
|
||||||
|
allIfaces = requiredMultiNICsConfig()
|
||||||
}
|
}
|
||||||
logger := mainLog.Load().With().Str("iface", iface).Logger()
|
logger := mainLog.Load().With().Str("iface", iface).Logger()
|
||||||
netIface, err := netInterface(iface)
|
netIface, err := netInterface(iface)
|
||||||
@@ -485,6 +500,9 @@ func (p *prog) resetDNS() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
logger.Debug().Msg("Restoring DNS successfully")
|
logger.Debug().Msg("Restoring DNS successfully")
|
||||||
|
if allIfaces {
|
||||||
|
withEachPhysicalInterfaces(netIface.Name, "reset DNS", resetDNS)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func randomLocalIP() string {
|
func randomLocalIP() string {
|
||||||
@@ -649,3 +667,51 @@ func canBeLocalUpstream(addr string) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// withEachPhysicalInterfaces runs the function f with each physical interfaces, excluding
|
||||||
|
// the interface that matches excludeIfaceName. The context is used to clarify the
|
||||||
|
// log message when error happens.
|
||||||
|
func withEachPhysicalInterfaces(excludeIfaceName, context string, f func(i *net.Interface) error) {
|
||||||
|
interfaces.ForeachInterface(func(i interfaces.Interface, prefixes []netip.Prefix) {
|
||||||
|
// Skip not-running/local/virtual interface.
|
||||||
|
if !i.IsUp() || i.IsLoopback() || len(i.HardwareAddr) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Skip non-configured interfaces.
|
||||||
|
if addrs, _ := i.Addrs(); len(addrs) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Skip invalid interface.
|
||||||
|
if !validInterface(i.Interface) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
netIface := i.Interface
|
||||||
|
if err := patchNetIfaceName(netIface); err != nil {
|
||||||
|
mainLog.Load().Debug().Err(err).Msg("failed to patch net interface name")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Skip excluded interface.
|
||||||
|
if netIface.Name == excludeIfaceName {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Skip Windows Hyper-V Default Switch.
|
||||||
|
if strings.Contains(netIface.Name, "vEthernet") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := f(netIface); err != nil {
|
||||||
|
mainLog.Load().Warn().Err(err).Msgf("failed to %s for interface: %q", context, i.Name)
|
||||||
|
} else {
|
||||||
|
mainLog.Load().Debug().Msgf("%s for interface %q successfully", context, i.Name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// requiredMultiNicConfig reports whether ctrld needs to set/reset DNS for multiple NICs.
|
||||||
|
func requiredMultiNICsConfig() bool {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "windows", "darwin":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user