Browse Source

Improve local DNS server

世界 6 months ago
parent
commit
aadb44ebd6

+ 2 - 1
dns/transport/local/local.go

@@ -35,6 +35,7 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt
 	}
 	return &Transport{
 		TransportAdapter: dns.NewTransportAdapterWithLocalOptions(C.DNSTypeLocal, tag, options),
+		ctx:              ctx,
 		hosts:            hosts.NewFile(hosts.DefaultPath),
 		dialer:           transportDialer,
 	}, nil
@@ -57,7 +58,7 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
 			return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil
 		}
 	}
-	systemConfig := getSystemDNSConfig()
+	systemConfig := getSystemDNSConfig(t.ctx)
 	if systemConfig.singleRequest || !(message.Question[0].Qtype == mDNS.TypeA || message.Question[0].Qtype == mDNS.TypeAAAA) {
 		return t.exchangeSingleRequest(ctx, systemConfig, message, domain)
 	} else {

+ 10 - 7
dns/transport/local/resolv.go

@@ -1,6 +1,7 @@
 package local
 
 import (
+	"context"
 	"os"
 	"runtime"
 	"strings"
@@ -23,19 +24,21 @@ type resolverConfig struct {
 
 var resolvConf resolverConfig
 
-func getSystemDNSConfig() *dnsConfig {
-	resolvConf.tryUpdate("/etc/resolv.conf")
+func getSystemDNSConfig(ctx context.Context) *dnsConfig {
+	resolvConf.tryUpdate(ctx, "/etc/resolv.conf")
 	return resolvConf.dnsConfig.Load()
 }
 
-func (conf *resolverConfig) init() {
-	conf.dnsConfig.Store(dnsReadConfig("/etc/resolv.conf"))
+func (conf *resolverConfig) init(ctx context.Context) {
+	conf.dnsConfig.Store(dnsReadConfig(ctx, "/etc/resolv.conf"))
 	conf.lastChecked = time.Now()
 	conf.ch = make(chan struct{}, 1)
 }
 
-func (conf *resolverConfig) tryUpdate(name string) {
-	conf.initOnce.Do(conf.init)
+func (conf *resolverConfig) tryUpdate(ctx context.Context, name string) {
+	conf.initOnce.Do(func() {
+		conf.init(ctx)
+	})
 
 	if conf.dnsConfig.Load().noReload {
 		return
@@ -59,7 +62,7 @@ func (conf *resolverConfig) tryUpdate(name string) {
 			return
 		}
 	}
-	dnsConf := dnsReadConfig(name)
+	dnsConf := dnsReadConfig(ctx, name)
 	conf.dnsConfig.Store(dnsConf)
 }
 

+ 2 - 1
dns/transport/local/resolv_darwin_cgo.go

@@ -11,6 +11,7 @@ package local
 import "C"
 
 import (
+	"context"
 	"time"
 
 	E "github.com/sagernet/sing/common/exceptions"
@@ -18,7 +19,7 @@ import (
 	"github.com/miekg/dns"
 )
 
-func dnsReadConfig(_ string) *dnsConfig {
+func dnsReadConfig(_ context.Context, _ string) *dnsConfig {
 	if C.res_init() != 0 {
 		return &dnsConfig{
 			servers:  defaultNS,

+ 2 - 1
dns/transport/local/resolv_unix.go

@@ -4,6 +4,7 @@ package local
 
 import (
 	"bufio"
+	"context"
 	"net"
 	"net/netip"
 	"os"
@@ -13,7 +14,7 @@ import (
 	"github.com/miekg/dns"
 )
 
-func dnsReadConfig(name string) *dnsConfig {
+func dnsReadConfig(_ context.Context, name string) *dnsConfig {
 	conf := &dnsConfig{
 		ndots:    1,
 		timeout:  5 * time.Second,

+ 37 - 20
dns/transport/local/resolv_windows.go

@@ -1,6 +1,7 @@
 package local
 
 import (
+	"context"
 	"net"
 	"net/netip"
 	"os"
@@ -8,10 +9,13 @@ import (
 	"time"
 	"unsafe"
 
+	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing/service"
+
 	"golang.org/x/sys/windows"
 )
 
-func dnsReadConfig(_ string) *dnsConfig {
+func dnsReadConfig(ctx context.Context, _ string) *dnsConfig {
 	conf := &dnsConfig{
 		ndots:    1,
 		timeout:  5 * time.Second,
@@ -22,35 +26,35 @@ func dnsReadConfig(_ string) *dnsConfig {
 			conf.servers = defaultNS
 		}
 	}()
-	aas, err := adapterAddresses()
+	addresses, err := adapterAddresses()
 	if err != nil {
 		return nil
 	}
-
-	for _, aa := range aas {
-		// Only take interfaces whose OperStatus is IfOperStatusUp(0x01) into DNS configs.
-		if aa.OperStatus != windows.IfOperStatusUp {
+	var dnsAddresses []struct {
+		ifName string
+		netip.Addr
+	}
+	for _, address := range addresses {
+		if address.OperStatus != windows.IfOperStatusUp {
 			continue
 		}
-
-		// Only take interfaces which have at least one gateway
-		if aa.FirstGatewayAddress == nil {
+		if address.IfType == windows.IF_TYPE_TUNNEL {
 			continue
 		}
-
-		for dns := aa.FirstDnsServerAddress; dns != nil; dns = dns.Next {
-			sa, err := dns.Address.Sockaddr.Sockaddr()
+		if address.FirstGatewayAddress == nil {
+			continue
+		}
+		for dnsServerAddress := address.FirstDnsServerAddress; dnsServerAddress != nil; dnsServerAddress = dnsServerAddress.Next {
+			rawSockaddr, err := dnsServerAddress.Address.Sockaddr.Sockaddr()
 			if err != nil {
 				continue
 			}
-			var ip netip.Addr
-			switch sa := sa.(type) {
+			var dnsServerAddr netip.Addr
+			switch sockaddr := rawSockaddr.(type) {
 			case *syscall.SockaddrInet4:
-				ip = netip.AddrFrom4([4]byte{sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3]})
+				dnsServerAddr = netip.AddrFrom4(sockaddr.Addr)
 			case *syscall.SockaddrInet6:
-				var addr16 [16]byte
-				copy(addr16[:], sa.Addr[:])
-				if addr16[0] == 0xfe && addr16[1] == 0xc0 {
+				if sockaddr.Addr[0] == 0xfe && sockaddr.Addr[1] == 0xc0 {
 					// fec0/10 IPv6 addresses are site local anycast DNS
 					// addresses Microsoft sets by default if no other
 					// IPv6 DNS address is set. Site local anycast is
@@ -58,13 +62,26 @@ func dnsReadConfig(_ string) *dnsConfig {
 					// https://datatracker.ietf.org/doc/html/rfc3879
 					continue
 				}
-				ip = netip.AddrFrom16(addr16)
+				dnsServerAddr = netip.AddrFrom16(sockaddr.Addr)
 			default:
 				// Unexpected type.
 				continue
 			}
-			conf.servers = append(conf.servers, net.JoinHostPort(ip.String(), "53"))
+			dnsAddresses = append(dnsAddresses, struct {
+				ifName string
+				netip.Addr
+			}{ifName: windows.UTF16PtrToString(address.FriendlyName), Addr: dnsServerAddr})
+		}
+	}
+	var myInterface string
+	if networkManager := service.FromContext[adapter.NetworkManager](ctx); networkManager != nil {
+		myInterface = networkManager.InterfaceMonitor().MyInterface()
+	}
+	for _, address := range dnsAddresses {
+		if address.ifName == myInterface {
+			continue
 		}
+		conf.servers = append(conf.servers, net.JoinHostPort(address.String(), "53"))
 	}
 	return conf
 }

+ 9 - 2
experimental/libbox/config.go

@@ -116,6 +116,10 @@ func (s *platformInterfaceStub) FindProcessInfo(ctx context.Context, network str
 	return nil, os.ErrInvalid
 }
 
+func (s *platformInterfaceStub) SendNotification(notification *platform.Notification) error {
+	return nil
+}
+
 type interfaceMonitorStub struct{}
 
 func (s *interfaceMonitorStub) Start() error {
@@ -145,8 +149,11 @@ func (s *interfaceMonitorStub) RegisterCallback(callback tun.DefaultInterfaceUpd
 func (s *interfaceMonitorStub) UnregisterCallback(element *list.Element[tun.DefaultInterfaceUpdateCallback]) {
 }
 
-func (s *platformInterfaceStub) SendNotification(notification *platform.Notification) error {
-	return nil
+func (s *interfaceMonitorStub) RegisterMyInterface(interfaceName string) {
+}
+
+func (s *interfaceMonitorStub) MyInterface() string {
+	return ""
 }
 
 func FormatConfig(configContent string) (*StringBox, error) {

+ 16 - 3
experimental/libbox/monitor.go

@@ -15,9 +15,10 @@ var (
 
 type platformDefaultInterfaceMonitor struct {
 	*platformInterfaceWrapper
-	element   *list.Element[tun.NetworkUpdateCallback]
-	callbacks list.List[tun.DefaultInterfaceUpdateCallback]
-	logger    logger.Logger
+	logger      logger.Logger
+	element     *list.Element[tun.NetworkUpdateCallback]
+	callbacks   list.List[tun.DefaultInterfaceUpdateCallback]
+	myInterface string
 }
 
 func (m *platformDefaultInterfaceMonitor) Start() error {
@@ -102,3 +103,15 @@ func (m *platformDefaultInterfaceMonitor) updateDefaultInterface(interfaceName s
 		callback(newInterface, 0)
 	}
 }
+
+func (m *platformDefaultInterfaceMonitor) RegisterMyInterface(interfaceName string) {
+	m.defaultInterfaceAccess.Lock()
+	defer m.defaultInterfaceAccess.Unlock()
+	m.myInterface = interfaceName
+}
+
+func (m *platformDefaultInterfaceMonitor) MyInterface() string {
+	m.defaultInterfaceAccess.Lock()
+	defer m.defaultInterfaceAccess.Unlock()
+	return m.myInterface
+}

+ 1 - 0
experimental/libbox/service.go

@@ -164,6 +164,7 @@ func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions
 	if err != nil {
 		return nil, E.Cause(err, "query tun name")
 	}
+	options.InterfaceMonitor.RegisterMyInterface(options.Name)
 	dupFd, err := dup(int(tunFd))
 	if err != nil {
 		return nil, E.Cause(err, "dup tun file descriptor")

+ 1 - 1
go.mod

@@ -32,7 +32,7 @@ require (
 	github.com/sagernet/sing-shadowsocks v0.2.8
 	github.com/sagernet/sing-shadowsocks2 v0.2.1
 	github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056
-	github.com/sagernet/sing-tun v0.6.9
+	github.com/sagernet/sing-tun v0.6.10-0.20250620051458-5e343c4b66b2
 	github.com/sagernet/sing-vmess v0.2.3
 	github.com/sagernet/smux v1.5.34-mod.2
 	github.com/sagernet/tailscale v1.80.3-mod.5

+ 2 - 4
go.sum

@@ -186,14 +186,12 @@ github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnq
 github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
 github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056 h1:GFNJQAHhSXqAfxAw1wDG/QWbdpGH5Na3k8qUynqWnEA=
 github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056/go.mod h1:HyacBPIFiKihJQR8LQp56FM4hBtd/7MZXnRxxQIOPsc=
-github.com/sagernet/sing-tun v0.6.9 h1:uP8O4Q7U9QesjWumgxd2S9fjT3c6aEPWl5RB6uBdVB8=
-github.com/sagernet/sing-tun v0.6.9/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
+github.com/sagernet/sing-tun v0.6.10-0.20250620051458-5e343c4b66b2 h1:ykbqGFHDNVvp0jhgLime/XBAtQpcOcFpT8Rs5Hcc5n4=
+github.com/sagernet/sing-tun v0.6.10-0.20250620051458-5e343c4b66b2/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
 github.com/sagernet/sing-vmess v0.2.3 h1:z6Ym8dnZG7k1fP3+54vz8G0tvRVJeOoTFFeUPwXTD44=
 github.com/sagernet/sing-vmess v0.2.3/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA=
 github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=
 github.com/sagernet/smux v1.5.34-mod.2/go.mod h1:0KW0+R+ycvA2INW4gbsd7BNyg+HEfLIAxa5N02/28Zc=
-github.com/sagernet/tailscale v1.80.3-mod.4.0.20250512093633-e1bc1888c814 h1:B6ejgOuM1BrX4TzWvm1h/LQAOZW1T1jP4PSZe8b/49o=
-github.com/sagernet/tailscale v1.80.3-mod.4.0.20250512093633-e1bc1888c814/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI=
 github.com/sagernet/tailscale v1.80.3-mod.5 h1:7V7z+p2C//TGtff20pPnDCt3qP6uFyY62peJoKF9z/A=
 github.com/sagernet/tailscale v1.80.3-mod.5/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI=
 github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8=