mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-07-04 01:07:49 +02:00
Removing Windows Server support
This commit is contained in:
committed by
Cuong Manh Le
parent
2e63624f6c
commit
f7fb555c89
@@ -32,8 +32,7 @@ All DNS protocols are supported, including:
|
|||||||
|
|
||||||
|
|
||||||
## OS Support
|
## OS Support
|
||||||
- Windows (386, amd64, arm)
|
- Windows Desktop (386, amd64, arm)
|
||||||
- Windows Server (386, amd64)
|
|
||||||
- MacOS (amd64, arm64)
|
- MacOS (amd64, arm64)
|
||||||
- Linux (386, amd64, arm, mips)
|
- Linux (386, amd64, arm, mips)
|
||||||
- FreeBSD (386, amd64, arm)
|
- FreeBSD (386, amd64, arm)
|
||||||
|
|||||||
@@ -8,8 +8,3 @@ import (
|
|||||||
|
|
||||||
// addExtraSplitDnsRule adds split DNS rule if present.
|
// addExtraSplitDnsRule adds split DNS rule if present.
|
||||||
func addExtraSplitDnsRule(_ *ctrld.Config) bool { return false }
|
func addExtraSplitDnsRule(_ *ctrld.Config) bool { return false }
|
||||||
|
|
||||||
// getActiveDirectoryDomain returns AD domain name of this computer.
|
|
||||||
func getActiveDirectoryDomain() (string, error) {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|||||||
+1
-44
@@ -1147,20 +1147,15 @@ func tryUpdateListenerConfig(cfg *ctrld.Config, notifyFunc func(), fatal bool) (
|
|||||||
lcc := make(map[string]*listenerConfigCheck)
|
lcc := make(map[string]*listenerConfigCheck)
|
||||||
cdMode := cdUID != ""
|
cdMode := cdUID != ""
|
||||||
nextdnsMode := nextdns != ""
|
nextdnsMode := nextdns != ""
|
||||||
// For Windows server with local Dns server running, we can only try on random local IP.
|
|
||||||
hasLocalDnsServer := hasLocalDnsServerRunning()
|
|
||||||
isDesktop := ctrld.IsDesktopPlatform()
|
isDesktop := ctrld.IsDesktopPlatform()
|
||||||
for n, listener := range cfg.Listener {
|
for n, listener := range cfg.Listener {
|
||||||
lcc[n] = &listenerConfigCheck{}
|
lcc[n] = &listenerConfigCheck{}
|
||||||
if listener.IP == "" {
|
if listener.IP == "" {
|
||||||
listener.IP = "0.0.0.0"
|
listener.IP = "0.0.0.0"
|
||||||
// Windows Server lies to us that we could listen on 0.0.0.0:53
|
|
||||||
// even there's a process already done that, stick to local IP only.
|
|
||||||
//
|
|
||||||
// For desktop clients, also stick the listener to the local IP only.
|
// For desktop clients, also stick the listener to the local IP only.
|
||||||
// Listening on 0.0.0.0 would expose it to the entire local network, potentially
|
// Listening on 0.0.0.0 would expose it to the entire local network, potentially
|
||||||
// creating security vulnerabilities (such as DNS amplification or abusing).
|
// creating security vulnerabilities (such as DNS amplification or abusing).
|
||||||
if hasLocalDnsServer || isDesktop {
|
if isDesktop {
|
||||||
listener.IP = "127.0.0.1"
|
listener.IP = "127.0.0.1"
|
||||||
}
|
}
|
||||||
lcc[n].IP = true
|
lcc[n].IP = true
|
||||||
@@ -1171,15 +1166,9 @@ func tryUpdateListenerConfig(cfg *ctrld.Config, notifyFunc func(), fatal bool) (
|
|||||||
}
|
}
|
||||||
// In cd mode, we always try to pick an ip:port pair to work.
|
// In cd mode, we always try to pick an ip:port pair to work.
|
||||||
// Same if nextdns resolver is used.
|
// Same if nextdns resolver is used.
|
||||||
//
|
|
||||||
// Except on Windows Server with local Dns running,
|
|
||||||
// we could only listen on random local IP port 53.
|
|
||||||
if cdMode || nextdnsMode {
|
if cdMode || nextdnsMode {
|
||||||
lcc[n].IP = true
|
lcc[n].IP = true
|
||||||
lcc[n].Port = true
|
lcc[n].Port = true
|
||||||
if hasLocalDnsServer {
|
|
||||||
lcc[n].Port = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
updated = updated || lcc[n].IP || lcc[n].Port
|
updated = updated || lcc[n].IP || lcc[n].Port
|
||||||
}
|
}
|
||||||
@@ -1258,16 +1247,6 @@ func tryUpdateListenerConfig(cfg *ctrld.Config, notifyFunc func(), fatal bool) (
|
|||||||
// config, so we can always listen on localhost port 53, but no traffic could be routed there.
|
// config, so we can always listen on localhost port 53, but no traffic could be routed there.
|
||||||
tryLocalhost := !isLoopback(listener.IP)
|
tryLocalhost := !isLoopback(listener.IP)
|
||||||
tryAllPort53 := true
|
tryAllPort53 := true
|
||||||
// We should not try to listen on any port other than 53,
|
|
||||||
// if we do, this will break the dns resolution for the system.
|
|
||||||
// TODO: cleanup these codes when refactoring this function.
|
|
||||||
tryOldIPPort5354 := false
|
|
||||||
tryPort5354 := false
|
|
||||||
if hasLocalDnsServer {
|
|
||||||
tryAllPort53 = false
|
|
||||||
tryOldIPPort5354 = false
|
|
||||||
tryPort5354 = false
|
|
||||||
}
|
|
||||||
|
|
||||||
attempts := 0
|
attempts := 0
|
||||||
maxAttempts := 10
|
maxAttempts := 10
|
||||||
@@ -1318,28 +1297,6 @@ func tryUpdateListenerConfig(cfg *ctrld.Config, notifyFunc func(), fatal bool) (
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if tryOldIPPort5354 {
|
|
||||||
tryOldIPPort5354 = false
|
|
||||||
if check.IP {
|
|
||||||
listener.IP = oldIP
|
|
||||||
}
|
|
||||||
if check.Port {
|
|
||||||
listener.Port = 5354
|
|
||||||
}
|
|
||||||
logMsg(il.Info(), n, "could not listen on address: %s, trying current ip with port 5354", addr)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if tryPort5354 {
|
|
||||||
tryPort5354 = false
|
|
||||||
if check.IP {
|
|
||||||
listener.IP = "0.0.0.0"
|
|
||||||
}
|
|
||||||
if check.Port {
|
|
||||||
listener.Port = 5354
|
|
||||||
}
|
|
||||||
logMsg(il.Info(), n, "could not listen on address: %s, trying 0.0.0.0:5354", addr)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if check.IP && !isZeroIP { // for "0.0.0.0" or "::", we only need to try new port.
|
if check.IP && !isZeroIP { // for "0.0.0.0" or "::", we only need to try new port.
|
||||||
listener.IP = randomLocalIP()
|
listener.IP = randomLocalIP()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+1
-4
@@ -899,10 +899,7 @@ NOTE: Uninstalling will set DNS to values provided by DHCP.`,
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
// Windows forwarders file.
|
|
||||||
if hasLocalDnsServerRunning() {
|
|
||||||
files = append(files, ctrld.AbsHomeDir(windowsForwardersFilename))
|
|
||||||
}
|
|
||||||
// Binary itself.
|
// Binary itself.
|
||||||
bin, _ := os.Executable()
|
bin, _ := os.Executable()
|
||||||
if bin != "" && supportedSelfDelete {
|
if bin != "" && supportedSelfDelete {
|
||||||
|
|||||||
@@ -51,12 +51,6 @@ var privateUpstreamConfig = &ctrld.UpstreamConfig{
|
|||||||
Timeout: 2000,
|
Timeout: 2000,
|
||||||
}
|
}
|
||||||
|
|
||||||
var localUpstreamConfig = &ctrld.UpstreamConfig{
|
|
||||||
Name: "Local resolver",
|
|
||||||
Type: ctrld.ResolverTypeLocal,
|
|
||||||
Timeout: 2000,
|
|
||||||
}
|
|
||||||
|
|
||||||
// proxyRequest contains data for proxying a DNS query to upstream.
|
// proxyRequest contains data for proxying a DNS query to upstream.
|
||||||
type proxyRequest struct {
|
type proxyRequest struct {
|
||||||
msg *dns.Msg
|
msg *dns.Msg
|
||||||
@@ -500,17 +494,6 @@ func (p *prog) proxy(ctx context.Context, req *proxyRequest) *proxyResponse {
|
|||||||
if len(upstreamConfigs) == 0 {
|
if len(upstreamConfigs) == 0 {
|
||||||
upstreamConfigs = []*ctrld.UpstreamConfig{osUpstreamConfig}
|
upstreamConfigs = []*ctrld.UpstreamConfig{osUpstreamConfig}
|
||||||
upstreams = []string{upstreamOS}
|
upstreams = []string{upstreamOS}
|
||||||
// For OS resolver, local addresses are ignored to prevent possible looping.
|
|
||||||
// However, on Active Directory Domain Controller, where it has local DNS server
|
|
||||||
// running and listening on local addresses, these local addresses must be used
|
|
||||||
// as nameservers, so queries for ADDC could be resolved as expected.
|
|
||||||
if p.isAdDomainQuery(req.msg) {
|
|
||||||
ctrld.Log(ctx, p.Debug(),
|
|
||||||
"AD domain query detected for %s in domain %s",
|
|
||||||
req.msg.Question[0].Name, p.adDomain)
|
|
||||||
upstreamConfigs = []*ctrld.UpstreamConfig{localUpstreamConfig}
|
|
||||||
upstreams = []string{upstreamOSLocal}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res := &proxyResponse{}
|
res := &proxyResponse{}
|
||||||
@@ -631,7 +614,6 @@ func (p *prog) proxy(ctx context.Context, req *proxyRequest) *proxyResponse {
|
|||||||
logger := p.Debug().
|
logger := p.Debug().
|
||||||
Str("upstream", upstreamConfig.String()).
|
Str("upstream", upstreamConfig.String()).
|
||||||
Str("query", req.msg.Question[0].Name).
|
Str("query", req.msg.Question[0].Name).
|
||||||
Bool("is_ad_query", p.isAdDomainQuery(req.msg)).
|
|
||||||
Bool("is_lan_query", isLanOrPtrQuery)
|
Bool("is_lan_query", isLanOrPtrQuery)
|
||||||
|
|
||||||
if p.isLoop(upstreamConfig) {
|
if p.isLoop(upstreamConfig) {
|
||||||
@@ -747,14 +729,6 @@ func (p *prog) upstreamConfigsFromUpstreamNumbers(upstreams []string) []*ctrld.U
|
|||||||
return upstreamConfigs
|
return upstreamConfigs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *prog) isAdDomainQuery(msg *dns.Msg) bool {
|
|
||||||
if p.adDomain == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
cDomainName := canonicalName(msg.Question[0].Name)
|
|
||||||
return dns.IsSubDomain(p.adDomain, cDomainName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// canonicalName returns canonical name from FQDN with "." trimmed.
|
// canonicalName returns canonical name from FQDN with "." trimmed.
|
||||||
func canonicalName(fqdn string) string {
|
func canonicalName(fqdn string) string {
|
||||||
q := strings.TrimSpace(fqdn)
|
q := strings.TrimSpace(fqdn)
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
"golang.org/x/sys/windows/registry"
|
"golang.org/x/sys/windows/registry"
|
||||||
@@ -25,11 +21,6 @@ const (
|
|||||||
v6InterfaceKeyPathFormat = `SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\Interfaces\`
|
v6InterfaceKeyPathFormat = `SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\Interfaces\`
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
setDNSOnce sync.Once
|
|
||||||
resetDNSOnce sync.Once
|
|
||||||
)
|
|
||||||
|
|
||||||
// setDnsIgnoreUnusableInterface likes setDNS, but return a nil error if the interface is not usable.
|
// setDnsIgnoreUnusableInterface likes setDNS, but return a nil error if the interface is not usable.
|
||||||
func setDnsIgnoreUnusableInterface(iface *net.Interface, nameservers []string) error {
|
func setDnsIgnoreUnusableInterface(iface *net.Interface, nameservers []string) error {
|
||||||
return setDNS(iface, nameservers)
|
return setDNS(iface, nameservers)
|
||||||
@@ -40,49 +31,7 @@ func setDNS(iface *net.Interface, nameservers []string) error {
|
|||||||
if len(nameservers) == 0 {
|
if len(nameservers) == 0 {
|
||||||
return errors.New("empty DNS nameservers")
|
return errors.New("empty DNS nameservers")
|
||||||
}
|
}
|
||||||
setDNSOnce.Do(func() {
|
|
||||||
// If there's a Dns server running, that means we are on AD with Dns feature enabled.
|
|
||||||
// Configuring the Dns server to forward queries to ctrld instead.
|
|
||||||
if hasLocalDnsServerRunning() {
|
|
||||||
mainLog.Load().Debug().Msg("Local DNS server detected, configuring forwarders")
|
|
||||||
|
|
||||||
file := ctrld.AbsHomeDir(windowsForwardersFilename)
|
|
||||||
mainLog.Load().Debug().Msgf("Using forwarders file: %s", file)
|
|
||||||
|
|
||||||
oldForwardersContent, err := os.ReadFile(file)
|
|
||||||
if err != nil {
|
|
||||||
mainLog.Load().Debug().Err(err).Msg("Could not read existing forwarders file")
|
|
||||||
} else {
|
|
||||||
mainLog.Load().Debug().Msgf("Existing forwarders content: %s", string(oldForwardersContent))
|
|
||||||
}
|
|
||||||
|
|
||||||
hasLocalIPv6Listener := needLocalIPv6Listener()
|
|
||||||
mainLog.Load().Debug().Bool("has_ipv6_listener", hasLocalIPv6Listener).Msg("IPv6 listener status")
|
|
||||||
|
|
||||||
forwarders := slices.DeleteFunc(slices.Clone(nameservers), func(s string) bool {
|
|
||||||
if !hasLocalIPv6Listener {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return s == "::1"
|
|
||||||
})
|
|
||||||
mainLog.Load().Debug().Strs("forwarders", forwarders).Msg("Filtered forwarders list")
|
|
||||||
|
|
||||||
if err := os.WriteFile(file, []byte(strings.Join(forwarders, ",")), 0600); err != nil {
|
|
||||||
mainLog.Load().Warn().Err(err).Msg("could not save forwarders settings")
|
|
||||||
} else {
|
|
||||||
mainLog.Load().Debug().Msg("Successfully wrote new forwarders file")
|
|
||||||
}
|
|
||||||
|
|
||||||
oldForwarders := strings.Split(string(oldForwardersContent), ",")
|
|
||||||
mainLog.Load().Debug().Strs("old_forwarders", oldForwarders).Msg("Previous forwarders")
|
|
||||||
|
|
||||||
if err := addDnsServerForwarders(forwarders, oldForwarders); err != nil {
|
|
||||||
mainLog.Load().Warn().Err(err).Msg("could not set forwarders settings")
|
|
||||||
} else {
|
|
||||||
mainLog.Load().Debug().Msg("Successfully configured DNS server forwarders")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
luid, err := winipcfg.LUIDFromIndex(uint32(iface.Index))
|
luid, err := winipcfg.LUIDFromIndex(uint32(iface.Index))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("setDNS: %w", err)
|
return fmt.Errorf("setDNS: %w", err)
|
||||||
@@ -126,25 +75,7 @@ func resetDnsIgnoreUnusableInterface(iface *net.Interface) error {
|
|||||||
return resetDNS(iface)
|
return resetDNS(iface)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(cuonglm): should we use system API?
|
|
||||||
func resetDNS(iface *net.Interface) error {
|
func resetDNS(iface *net.Interface) error {
|
||||||
resetDNSOnce.Do(func() {
|
|
||||||
// See corresponding comment in setDNS.
|
|
||||||
if hasLocalDnsServerRunning() {
|
|
||||||
file := ctrld.AbsHomeDir(windowsForwardersFilename)
|
|
||||||
content, err := os.ReadFile(file)
|
|
||||||
if err != nil {
|
|
||||||
mainLog.Load().Error().Err(err).Msg("could not read forwarders settings")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
nameservers := strings.Split(string(content), ",")
|
|
||||||
if err := removeDnsServerForwarders(nameservers); err != nil {
|
|
||||||
mainLog.Load().Error().Err(err).Msg("could not remove forwarders settings")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
luid, err := winipcfg.LUIDFromIndex(uint32(iface.Index))
|
luid, err := winipcfg.LUIDFromIndex(uint32(iface.Index))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("resetDNS: %w", err)
|
return fmt.Errorf("resetDNS: %w", err)
|
||||||
@@ -285,49 +216,3 @@ func parseDNSServers(val string) []string {
|
|||||||
}
|
}
|
||||||
return servers
|
return servers
|
||||||
}
|
}
|
||||||
|
|
||||||
// addDnsServerForwarders adds given nameservers to DNS server forwarders list,
|
|
||||||
// and also removing old forwarders if provided.
|
|
||||||
func addDnsServerForwarders(nameservers, old []string) error {
|
|
||||||
newForwardersMap := make(map[string]struct{})
|
|
||||||
newForwarders := make([]string, len(nameservers))
|
|
||||||
for i := range nameservers {
|
|
||||||
newForwardersMap[nameservers[i]] = struct{}{}
|
|
||||||
newForwarders[i] = fmt.Sprintf("%q", nameservers[i])
|
|
||||||
}
|
|
||||||
oldForwarders := old[:0]
|
|
||||||
for _, fwd := range old {
|
|
||||||
if _, ok := newForwardersMap[fwd]; !ok {
|
|
||||||
oldForwarders = append(oldForwarders, fwd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// NOTE: It is important to add new forwarder before removing old one.
|
|
||||||
// Testing on Windows Server 2022 shows that removing forwarder1
|
|
||||||
// then adding forwarder2 sometimes ends up adding both of them
|
|
||||||
// to the forwarders list.
|
|
||||||
cmd := fmt.Sprintf("Add-DnsServerForwarder -IPAddress %s", strings.Join(newForwarders, ","))
|
|
||||||
if len(oldForwarders) > 0 {
|
|
||||||
cmd = fmt.Sprintf("%s ; Remove-DnsServerForwarder -IPAddress %s -Force", cmd, strings.Join(oldForwarders, ","))
|
|
||||||
}
|
|
||||||
if out, err := powershell(cmd); err != nil {
|
|
||||||
return fmt.Errorf("%w: %s", err, string(out))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// removeDnsServerForwarders removes given nameservers from DNS server forwarders list.
|
|
||||||
func removeDnsServerForwarders(nameservers []string) error {
|
|
||||||
for _, ns := range nameservers {
|
|
||||||
cmd := fmt.Sprintf("Remove-DnsServerForwarder -IPAddress %s -Force", ns)
|
|
||||||
if out, err := powershell(cmd); err != nil {
|
|
||||||
return fmt.Errorf("%w: %s", err, string(out))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// powershell runs the given powershell command.
|
|
||||||
func powershell(cmd string) ([]byte, error) {
|
|
||||||
out, err := exec.Command("powershell", "-Command", cmd).CombinedOutput()
|
|
||||||
return bytes.TrimSpace(out), err
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"os/exec"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -66,3 +68,9 @@ func currentStaticDnsPowershell(iface *net.Interface) ([]string, error) {
|
|||||||
}
|
}
|
||||||
return ns, nil
|
return ns, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// powershell runs the given powershell command.
|
||||||
|
func powershell(cmd string) ([]byte, error) {
|
||||||
|
out, err := exec.Command("powershell", "-Command", cmd).CombinedOutput()
|
||||||
|
return bytes.TrimSpace(out), err
|
||||||
|
}
|
||||||
|
|||||||
@@ -128,8 +128,6 @@ type prog struct {
|
|||||||
internalLogSent time.Time
|
internalLogSent time.Time
|
||||||
runningIface string
|
runningIface string
|
||||||
requiredMultiNICsConfig bool
|
requiredMultiNICsConfig bool
|
||||||
adDomain string
|
|
||||||
runningOnDomainController bool
|
|
||||||
|
|
||||||
selfUninstallMu sync.Mutex
|
selfUninstallMu sync.Mutex
|
||||||
refusedQueryCount int
|
refusedQueryCount int
|
||||||
@@ -279,11 +277,6 @@ func (p *prog) preRun() {
|
|||||||
|
|
||||||
func (p *prog) postRun() {
|
func (p *prog) postRun() {
|
||||||
if !service.Interactive() {
|
if !service.Interactive() {
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
isDC, roleInt := isRunningOnDomainController()
|
|
||||||
p.runningOnDomainController = isDC
|
|
||||||
p.Debug().Msgf("running on domain controller: %t, role: %d", p.runningOnDomainController, roleInt)
|
|
||||||
}
|
|
||||||
p.resetDNS(false, false)
|
p.resetDNS(false, false)
|
||||||
ns := ctrld.InitializeOsResolver(ctrld.LoggerCtx(context.Background(), p.logger.Load()), false)
|
ns := ctrld.InitializeOsResolver(ctrld.LoggerCtx(context.Background(), p.logger.Load()), false)
|
||||||
p.Debug().Msgf("initialized OS resolver with nameservers: %v", ns)
|
p.Debug().Msgf("initialized OS resolver with nameservers: %v", ns)
|
||||||
@@ -481,10 +474,6 @@ func (p *prog) run(reload bool, reloadCh chan struct{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if domain, err := getActiveDirectoryDomain(); err == nil && domain != "" && hasLocalDnsServerRunning() {
|
|
||||||
p.Debug().Msgf("active directory domain: %s", domain)
|
|
||||||
p.adDomain = domain
|
|
||||||
}
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(len(p.cfg.Listener))
|
wg.Add(len(p.cfg.Listener))
|
||||||
@@ -1481,26 +1470,5 @@ func (p *prog) leakOnUpstreamFailure() bool {
|
|||||||
if ptr := p.cfg.Service.LeakOnUpstreamFailure; ptr != nil {
|
if ptr := p.cfg.Service.LeakOnUpstreamFailure; ptr != nil {
|
||||||
return *ptr
|
return *ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we are running on ADDC, we should not leak on upstream failure
|
|
||||||
if p.runningOnDomainController {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Domain controller role values from Win32_ComputerSystem
|
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-computersystem
|
|
||||||
const (
|
|
||||||
BackupDomainController = 4
|
|
||||||
PrimaryDomainController = 5
|
|
||||||
)
|
|
||||||
|
|
||||||
// isRunningOnDomainController checks if the current machine is a domain controller
|
|
||||||
// by querying the DomainRole property from Win32_ComputerSystem via WMI.
|
|
||||||
func isRunningOnDomainController() (bool, int) {
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
return false, 0
|
|
||||||
}
|
|
||||||
return isRunningOnDomainControllerWindows()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,11 +2,7 @@ package cli
|
|||||||
|
|
||||||
import "github.com/kardianos/service"
|
import "github.com/kardianos/service"
|
||||||
|
|
||||||
func setDependencies(svc *service.Config) {
|
func setDependencies(svc *service.Config) {}
|
||||||
if hasLocalDnsServerRunning() {
|
|
||||||
svc.Dependencies = []string{"DNS"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setWorkingDirectory(svc *service.Config, dir string) {
|
func setWorkingDirectory(svc *service.Config, dir string) {
|
||||||
// WorkingDirectory is not supported on Windows.
|
// WorkingDirectory is not supported on Windows.
|
||||||
|
|||||||
@@ -14,9 +14,4 @@ func openLogFile(path string, flags int) (*os.File, error) {
|
|||||||
return os.OpenFile(path, flags, os.FileMode(0o600))
|
return os.OpenFile(path, flags, os.FileMode(0o600))
|
||||||
}
|
}
|
||||||
|
|
||||||
// hasLocalDnsServerRunning reports whether we are on Windows and having Dns server running.
|
|
||||||
func hasLocalDnsServerRunning() bool { return false }
|
|
||||||
|
|
||||||
func ConfigureWindowsServiceFailureActions(serviceName string) error { return nil }
|
func ConfigureWindowsServiceFailureActions(serviceName string) error { return nil }
|
||||||
|
|
||||||
func isRunningOnDomainControllerWindows() (bool, int) { return false, 0 }
|
|
||||||
|
|||||||
@@ -2,18 +2,11 @@ package cli
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/microsoft/wmi/pkg/base/host"
|
|
||||||
"github.com/microsoft/wmi/pkg/base/instance"
|
|
||||||
"github.com/microsoft/wmi/pkg/base/query"
|
|
||||||
"github.com/microsoft/wmi/pkg/constant"
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
"golang.org/x/sys/windows/svc/mgr"
|
"golang.org/x/sys/windows/svc/mgr"
|
||||||
)
|
)
|
||||||
@@ -151,77 +144,3 @@ func openLogFile(path string, mode int) (*os.File, error) {
|
|||||||
|
|
||||||
return os.NewFile(uintptr(handle), path), nil
|
return os.NewFile(uintptr(handle), path), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const processEntrySize = uint32(unsafe.Sizeof(windows.ProcessEntry32{}))
|
|
||||||
|
|
||||||
// hasLocalDnsServerRunning reports whether we are on Windows and having Dns server running.
|
|
||||||
func hasLocalDnsServerRunning() bool {
|
|
||||||
h, e := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0)
|
|
||||||
if e != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
p := windows.ProcessEntry32{Size: processEntrySize}
|
|
||||||
for {
|
|
||||||
e := windows.Process32Next(h, &p)
|
|
||||||
if e != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if strings.ToLower(windows.UTF16ToString(p.ExeFile[:])) == "dns.exe" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isRunningOnDomainControllerWindows() (bool, int) {
|
|
||||||
whost := host.NewWmiLocalHost()
|
|
||||||
q := query.NewWmiQuery("Win32_ComputerSystem")
|
|
||||||
instances, err := instance.GetWmiInstancesFromHost(whost, string(constant.CimV2), q)
|
|
||||||
if err != nil {
|
|
||||||
mainLog.Load().Debug().Err(err).Msg("WMI query failed")
|
|
||||||
return false, 0
|
|
||||||
}
|
|
||||||
if instances == nil {
|
|
||||||
mainLog.Load().Debug().Msg("WMI query returned nil instances")
|
|
||||||
return false, 0
|
|
||||||
}
|
|
||||||
defer instances.Close()
|
|
||||||
|
|
||||||
if len(instances) == 0 {
|
|
||||||
mainLog.Load().Debug().Msg("no rows returned from Win32_ComputerSystem")
|
|
||||||
return false, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
val, err := instances[0].GetProperty("DomainRole")
|
|
||||||
if err != nil {
|
|
||||||
mainLog.Load().Debug().Err(err).Msg("failed to get DomainRole property")
|
|
||||||
return false, 0
|
|
||||||
}
|
|
||||||
if val == nil {
|
|
||||||
mainLog.Load().Debug().Msg("DomainRole property is nil")
|
|
||||||
return false, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Safely handle varied types: string or integer
|
|
||||||
var roleInt int
|
|
||||||
switch v := val.(type) {
|
|
||||||
case string:
|
|
||||||
// "4", "5", etc.
|
|
||||||
parsed, parseErr := strconv.Atoi(v)
|
|
||||||
if parseErr != nil {
|
|
||||||
mainLog.Load().Debug().Err(parseErr).Msgf("failed to parse DomainRole value %q", v)
|
|
||||||
return false, 0
|
|
||||||
}
|
|
||||||
roleInt = parsed
|
|
||||||
case int8, int16, int32, int64:
|
|
||||||
roleInt = int(reflect.ValueOf(v).Int())
|
|
||||||
case uint8, uint16, uint32, uint64:
|
|
||||||
roleInt = int(reflect.ValueOf(v).Uint())
|
|
||||||
default:
|
|
||||||
mainLog.Load().Debug().Msgf("unexpected DomainRole type: %T value=%v", v, v)
|
|
||||||
return false, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if role indicates a domain controller
|
|
||||||
isDC := roleInt == BackupDomainController || roleInt == PrimaryDomainController
|
|
||||||
return isDC, roleInt
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_hasLocalDnsServerRunning(t *testing.T) {
|
|
||||||
start := time.Now()
|
|
||||||
hasDns := hasLocalDnsServerRunning()
|
|
||||||
t.Logf("Using Windows API takes: %d", time.Since(start).Milliseconds())
|
|
||||||
|
|
||||||
start = time.Now()
|
|
||||||
hasDnsPowershell := hasLocalDnsServerRunningPowershell()
|
|
||||||
t.Logf("Using Powershell takes: %d", time.Since(start).Milliseconds())
|
|
||||||
|
|
||||||
if hasDns != hasDnsPowershell {
|
|
||||||
t.Fatalf("result mismatch, want: %v, got: %v", hasDnsPowershell, hasDns)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasLocalDnsServerRunningPowershell() bool {
|
|
||||||
_, err := powershell("Get-Process -Name DNS")
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
@@ -403,7 +403,7 @@ func (uc *UpstreamConfig) IsDiscoverable() bool {
|
|||||||
return *uc.Discoverable
|
return *uc.Discoverable
|
||||||
}
|
}
|
||||||
switch uc.Type {
|
switch uc.Type {
|
||||||
case ResolverTypeOS, ResolverTypeLegacy, ResolverTypePrivate, ResolverTypeLocal:
|
case ResolverTypeOS, ResolverTypeLegacy, ResolverTypePrivate:
|
||||||
if ip, err := netip.ParseAddr(uc.Domain); err == nil {
|
if ip, err := netip.ParseAddr(uc.Domain); err == nil {
|
||||||
return ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() || tsaddr.CGNATRange().Contains(ip)
|
return ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() || tsaddr.CGNATRange().Contains(ip)
|
||||||
}
|
}
|
||||||
|
|||||||
-18
@@ -34,8 +34,6 @@ const (
|
|||||||
ResolverTypeLegacy = "legacy"
|
ResolverTypeLegacy = "legacy"
|
||||||
// ResolverTypePrivate is like ResolverTypeOS, but use for private resolver only.
|
// ResolverTypePrivate is like ResolverTypeOS, but use for private resolver only.
|
||||||
ResolverTypePrivate = "private"
|
ResolverTypePrivate = "private"
|
||||||
// ResolverTypeLocal is like ResolverTypeOS, but use for local resolver only.
|
|
||||||
ResolverTypeLocal = "local"
|
|
||||||
// ResolverTypeSDNS specifies resolver with information encoded using DNS Stamps.
|
// ResolverTypeSDNS specifies resolver with information encoded using DNS Stamps.
|
||||||
// See: https://dnscrypt.info/stamps-specifications/
|
// See: https://dnscrypt.info/stamps-specifications/
|
||||||
ResolverTypeSDNS = "sdns"
|
ResolverTypeSDNS = "sdns"
|
||||||
@@ -45,12 +43,6 @@ const controldPublicDns = "76.76.2.0"
|
|||||||
|
|
||||||
var controldPublicDnsWithPort = net.JoinHostPort(controldPublicDns, "53")
|
var controldPublicDnsWithPort = net.JoinHostPort(controldPublicDns, "53")
|
||||||
|
|
||||||
var localResolver Resolver
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
localResolver = newLocalResolver()
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
resolverMutex sync.Mutex
|
resolverMutex sync.Mutex
|
||||||
or *osResolver
|
or *osResolver
|
||||||
@@ -58,14 +50,6 @@ var (
|
|||||||
defaultLocalIPv6 atomic.Value // holds net.IP (IPv6)
|
defaultLocalIPv6 atomic.Value // holds net.IP (IPv6)
|
||||||
)
|
)
|
||||||
|
|
||||||
func newLocalResolver() Resolver {
|
|
||||||
var nss []string
|
|
||||||
for _, addr := range Rfc1918Addresses() {
|
|
||||||
nss = append(nss, net.JoinHostPort(addr, "53"))
|
|
||||||
}
|
|
||||||
return NewResolverWithNameserver(nss)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LanQueryCtxKey is the context.Context key to indicate that the request is for LAN network.
|
// LanQueryCtxKey is the context.Context key to indicate that the request is for LAN network.
|
||||||
type LanQueryCtxKey struct{}
|
type LanQueryCtxKey struct{}
|
||||||
|
|
||||||
@@ -198,8 +182,6 @@ func NewResolver(ctx context.Context, uc *UpstreamConfig) (Resolver, error) {
|
|||||||
return &legacyResolver{uc: uc}, nil
|
return &legacyResolver{uc: uc}, nil
|
||||||
case ResolverTypePrivate:
|
case ResolverTypePrivate:
|
||||||
return NewPrivateResolver(ctx), nil
|
return NewPrivateResolver(ctx), nil
|
||||||
case ResolverTypeLocal:
|
|
||||||
return localResolver, nil
|
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("%w: %s", errUnknownResolver, typ)
|
return nil, fmt.Errorf("%w: %s", errUnknownResolver, typ)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user