diff --git a/dns/transport/local/local.go b/dns/transport/local/local.go index f50405a5..df473b3c 100644 --- a/dns/transport/local/local.go +++ b/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 { diff --git a/dns/transport/local/resolv.go b/dns/transport/local/resolv.go index 5484d0ec..754d4539 100644 --- a/dns/transport/local/resolv.go +++ b/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) } diff --git a/dns/transport/local/resolv_darwin_cgo.go b/dns/transport/local/resolv_darwin_cgo.go index dfcb8a8a..f9d82a36 100644 --- a/dns/transport/local/resolv_darwin_cgo.go +++ b/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, diff --git a/dns/transport/local/resolv_unix.go b/dns/transport/local/resolv_unix.go index c3953145..f77f3553 100644 --- a/dns/transport/local/resolv_unix.go +++ b/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, diff --git a/dns/transport/local/resolv_windows.go b/dns/transport/local/resolv_windows.go index f6b81090..76f758c6 100644 --- a/dns/transport/local/resolv_windows.go +++ b/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,14 +62,27 @@ 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 } diff --git a/experimental/libbox/config.go b/experimental/libbox/config.go index d3149dc2..8fc1b66c 100644 --- a/experimental/libbox/config.go +++ b/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) { diff --git a/experimental/libbox/monitor.go b/experimental/libbox/monitor.go index 05973ec6..00f63abd 100644 --- a/experimental/libbox/monitor.go +++ b/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 +} diff --git a/experimental/libbox/service.go b/experimental/libbox/service.go index 48d614ff..7f80e6fe 100644 --- a/experimental/libbox/service.go +++ b/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") diff --git a/go.mod b/go.mod index bcfdb8a0..01f5f666 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( github.com/sagernet/sing-shadowsocks v0.2.7 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.5 + github.com/sagernet/sing-tun v0.6.6-0.20250428031943-0686f8c4f210 github.com/sagernet/sing-vmess v0.2.1 github.com/sagernet/smux v1.5.34-mod.2 github.com/sagernet/tailscale v1.80.3-mod.5 diff --git a/go.sum b/go.sum index 0dba8b42..488e1dca 100644 --- a/go.sum +++ b/go.sum @@ -186,8 +186,8 @@ 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.5 h1:nGfD6GNq/r0tEjdZHOV3BS6fydSmd4kBAokU5rffssg= -github.com/sagernet/sing-tun v0.6.5/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE= +github.com/sagernet/sing-tun v0.6.6-0.20250428031943-0686f8c4f210 h1:6H4BZaTqKI3YcDMyTV3E576LuJM4S4wY99xoq2T1ECw= +github.com/sagernet/sing-tun v0.6.6-0.20250428031943-0686f8c4f210/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE= github.com/sagernet/sing-vmess v0.2.1 h1:6izHC2+B68aQCxTagki6eZZc+g5eh4dYwxOV5a2Lhug= github.com/sagernet/sing-vmess v0.2.1/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA= github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=