fix(windows): improve DNS server discovery for domain-joined machines

Add DNS suffix matching for non-physical adapters when domain-joined.
This allows interfaces with matching DNS suffix to be considered valid
even if not in validInterfacesMap, improving DNS server discovery for
remote VPN scenarios.

While at it, also replacing context.Background() with proper ctx
parameter throughout the function for consistent context propagation.
This commit is contained in:
Cuong Manh Le
2026-01-14 17:17:55 +07:00
committed by Cuong Manh Le
parent d0341497d1
commit 1804e6db67
+46 -40
View File
@@ -17,6 +17,7 @@ import (
"github.com/microsoft/wmi/pkg/base/query" "github.com/microsoft/wmi/pkg/base/query"
"github.com/microsoft/wmi/pkg/constant" "github.com/microsoft/wmi/pkg/constant"
"github.com/microsoft/wmi/pkg/hardware/network/netadapter" "github.com/microsoft/wmi/pkg/hardware/network/netadapter"
"github.com/miekg/dns"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
"tailscale.com/net/netmon" "tailscale.com/net/netmon"
@@ -128,13 +129,15 @@ func getDNSServers(ctx context.Context) ([]string, error) {
// Try to get domain controller info if domain-joined // Try to get domain controller info if domain-joined
var dcServers []string var dcServers []string
var adDomain string
isDomain := checkDomainJoined() isDomain := checkDomainJoined()
if isDomain { if isDomain {
domainName, err := system.GetActiveDirectoryDomain() domainName, err := system.GetActiveDirectoryDomain()
if err != nil { if err != nil {
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(),
"Failed to get local AD domain: %v", err) "Failed to get local AD domain: %v", err)
} else { } else {
adDomain = domainName
// Load netapi32.dll // Load netapi32.dll
netapi32 := windows.NewLazySystemDLL("netapi32.dll") netapi32 := windows.NewLazySystemDLL("netapi32.dll")
dsDcName := netapi32.NewProc("DsGetDcNameW") dsDcName := netapi32.NewProc("DsGetDcNameW")
@@ -144,10 +147,9 @@ func getDNSServers(ctx context.Context) ([]string, error) {
domainUTF16, err := windows.UTF16PtrFromString(domainName) domainUTF16, err := windows.UTF16PtrFromString(domainName)
if err != nil { if err != nil {
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(), "Failed to convert domain name to UTF16: %v", err)
"Failed to convert domain name to UTF16: %v", err)
} else { } else {
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(),
"Attempting to get DC for domain: %s with flags: 0x%x", domainName, flags) "Attempting to get DC for domain: %s with flags: 0x%x", domainName, flags)
// Call DsGetDcNameW with domain name // Call DsGetDcNameW with domain name
@@ -162,19 +164,19 @@ func getDNSServers(ctx context.Context) ([]string, error) {
if ret != 0 { if ret != 0 {
switch ret { switch ret {
case 1355: // ERROR_NO_SUCH_DOMAIN case 1355: // ERROR_NO_SUCH_DOMAIN
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(),
"Domain not found: %s (%d)", domainName, ret) "Domain not found: %s (%d)", domainName, ret)
case 1311: // ERROR_NO_LOGON_SERVERS case 1311: // ERROR_NO_LOGON_SERVERS
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(),
"No logon servers available for domain: %s (%d)", domainName, ret) "No logon servers available for domain: %s (%d)", domainName, ret)
case 1004: // ERROR_DC_NOT_FOUND case 1004: // ERROR_DC_NOT_FOUND
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(),
"Domain controller not found for domain: %s (%d)", domainName, ret) "Domain controller not found for domain: %s (%d)", domainName, ret)
case 1722: // RPC_S_SERVER_UNAVAILABLE case 1722: // RPC_S_SERVER_UNAVAILABLE
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(),
"RPC server unavailable for domain: %s (%d)", domainName, ret) "RPC server unavailable for domain: %s (%d)", domainName, ret)
default: default:
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(),
"Failed to get domain controller info for domain %s: %d, %v", domainName, ret, err) "Failed to get domain controller info for domain %s: %d, %v", domainName, ret, err)
} }
} else if info != nil { } else if info != nil {
@@ -183,17 +185,16 @@ func getDNSServers(ctx context.Context) ([]string, error) {
if info.DomainControllerAddress != nil { if info.DomainControllerAddress != nil {
dcAddr := windows.UTF16PtrToString(info.DomainControllerAddress) dcAddr := windows.UTF16PtrToString(info.DomainControllerAddress)
dcAddr = strings.TrimPrefix(dcAddr, "\\\\") dcAddr = strings.TrimPrefix(dcAddr, "\\\\")
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(),
"Found domain controller address: %s", dcAddr) "Found domain controller address: %s", dcAddr)
if ip := net.ParseIP(dcAddr); ip != nil { if ip := net.ParseIP(dcAddr); ip != nil {
dcServers = append(dcServers, ip.String()) dcServers = append(dcServers, ip.String())
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(),
"Added domain controller DNS servers: %v", dcServers) "Added domain controller DNS servers: %v", dcServers)
} }
} else { } else {
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(), "No domain controller address found")
"No domain controller address found")
} }
} }
} }
@@ -208,7 +209,7 @@ func getDNSServers(ctx context.Context) ([]string, error) {
// Collect all local IPs // Collect all local IPs
for _, aa := range aas { for _, aa := range aas {
if aa.OperStatus != winipcfg.IfOperStatusUp { if aa.OperStatus != winipcfg.IfOperStatusUp {
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(),
"Skipping adapter %s - not up, status: %d", aa.FriendlyName(), aa.OperStatus) "Skipping adapter %s - not up, status: %d", aa.FriendlyName(), aa.OperStatus)
continue continue
} }
@@ -216,24 +217,25 @@ func getDNSServers(ctx context.Context) ([]string, error) {
// Skip if software loopback or other non-physical types // Skip if software loopback or other non-physical types
// This is to avoid the "Loopback Pseudo-Interface 1" issue we see on windows // This is to avoid the "Loopback Pseudo-Interface 1" issue we see on windows
if aa.IfType == winipcfg.IfTypeSoftwareLoopback { if aa.IfType == winipcfg.IfTypeSoftwareLoopback {
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(), "Skipping %s (software loopback)", aa.FriendlyName())
"Skipping %s (software loopback)", aa.FriendlyName())
continue continue
} }
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(), "Processing adapter %s", aa.FriendlyName())
"Processing adapter %s", aa.FriendlyName())
for a := aa.FirstUnicastAddress; a != nil; a = a.Next { for a := aa.FirstUnicastAddress; a != nil; a = a.Next {
ip := a.Address.IP().String() ip := a.Address.IP().String()
addressMap[ip] = struct{}{} addressMap[ip] = struct{}{}
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(), "Added local IP %s from adapter %s", ip, aa.FriendlyName())
"Added local IP %s from adapter %s", ip, aa.FriendlyName())
} }
} }
validInterfacesMap := validInterfaces() validInterfacesMap := validInterfaces()
if isDomain && adDomain == "" {
Log(ctx, logger.Warn(), "The machine is joined domain, but domain name is empty")
}
checkDnsSuffix := isDomain && adDomain != ""
// Collect DNS servers // Collect DNS servers
for _, aa := range aas { for _, aa := range aas {
if aa.OperStatus != winipcfg.IfOperStatusUp { if aa.OperStatus != winipcfg.IfOperStatusUp {
@@ -243,23 +245,33 @@ func getDNSServers(ctx context.Context) ([]string, error) {
// Skip if software loopback or other non-physical types // Skip if software loopback or other non-physical types
// This is to avoid the "Loopback Pseudo-Interface 1" issue we see on windows // This is to avoid the "Loopback Pseudo-Interface 1" issue we see on windows
if aa.IfType == winipcfg.IfTypeSoftwareLoopback { if aa.IfType == winipcfg.IfTypeSoftwareLoopback {
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(), "Skipping %s (software loopback)", aa.FriendlyName())
"Skipping %s (software loopback)", aa.FriendlyName())
continue continue
} }
// if not in the validInterfacesMap, skip _, valid := validInterfacesMap[aa.FriendlyName()]
if _, ok := validInterfacesMap[aa.FriendlyName()]; !ok { if !valid && checkDnsSuffix {
Log(context.Background(), logger.Debug(), for suffix := aa.FirstDNSSuffix; suffix != nil; suffix = suffix.Next {
"Skipping %s (not in validInterfacesMap)", aa.FriendlyName()) // For non-physical adapters but have the DNS suffix that matches the domain name,
// (or vice versa) consider it valid. This can happen when remote VPN machines.
ds := strings.TrimSpace(suffix.String())
if dns.IsSubDomain(adDomain, ds) || dns.IsSubDomain(ds, adDomain) {
Log(ctx, logger.Debug(), "Found valid interface %s with DNS suffix %s", aa.FriendlyName(), suffix.String())
valid = true
break
}
}
}
// if not a valid interface, skip it
if !valid {
Log(ctx, logger.Debug(), "Skipping %s (not in validInterfacesMap)", aa.FriendlyName())
continue continue
} }
for dns := aa.FirstDNSServerAddress; dns != nil; dns = dns.Next { for dns := aa.FirstDNSServerAddress; dns != nil; dns = dns.Next {
ip := dns.Address.IP() ip := dns.Address.IP()
if ip == nil { if ip == nil {
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(), "Skipping nil IP from adapter %s", aa.FriendlyName())
"Skipping nil IP from adapter %s", aa.FriendlyName())
continue continue
} }
@@ -292,28 +304,23 @@ func getDNSServers(ctx context.Context) ([]string, error) {
if !seen[dcServer] { if !seen[dcServer] {
seen[dcServer] = true seen[dcServer] = true
ns = append(ns, dcServer) ns = append(ns, dcServer)
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(), "Added additional domain controller DNS server: %s", dcServer)
"Added additional domain controller DNS server: %s", dcServer)
} }
} }
// if we have static DNS servers saved for the current default route, we should add them to the list // if we have static DNS servers saved for the current default route, we should add them to the list
drIfaceName, err := netmon.DefaultRouteInterface() drIfaceName, err := netmon.DefaultRouteInterface()
if err != nil { if err != nil {
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(), "Failed to get default route interface: %v", err)
"Failed to get default route interface: %v", err)
} else { } else {
drIface, err := net.InterfaceByName(drIfaceName) drIface, err := net.InterfaceByName(drIfaceName)
if err != nil { if err != nil {
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(), "Failed to get interface by name %s: %v", drIfaceName, err)
"Failed to get interface by name %s: %v", drIfaceName, err)
} else { } else {
staticNs, file := SavedStaticNameservers(drIface) staticNs, file := SavedStaticNameservers(drIface)
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(), "static dns servers from %s: %v", file, staticNs)
"static dns servers from %s: %v", file, staticNs)
if len(staticNs) > 0 { if len(staticNs) > 0 {
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(), "Adding static DNS servers from %s: %v", drIfaceName, staticNs)
"Adding static DNS servers from %s: %v", drIfaceName, staticNs)
ns = append(ns, staticNs...) ns = append(ns, staticNs...)
} }
} }
@@ -323,8 +330,7 @@ func getDNSServers(ctx context.Context) ([]string, error) {
return nil, fmt.Errorf("no valid DNS servers found") return nil, fmt.Errorf("no valid DNS servers found")
} }
Log(context.Background(), logger.Debug(), Log(ctx, logger.Debug(), "DNS server discovery completed, count=%d, servers=%v (including %d DC servers)",
"DNS server discovery completed, count=%d, servers=%v (including %d DC servers)",
len(ns), ns, len(dcServers)) len(ns), ns, len(dcServers))
return ns, nil return ns, nil
} }