mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-06-08 11:44:13 +08:00
Improve local DNS server
This commit is contained in:
parent
4d43653795
commit
2011148ef4
@ -35,6 +35,7 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt
|
|||||||
}
|
}
|
||||||
return &Transport{
|
return &Transport{
|
||||||
TransportAdapter: dns.NewTransportAdapterWithLocalOptions(C.DNSTypeLocal, tag, options),
|
TransportAdapter: dns.NewTransportAdapterWithLocalOptions(C.DNSTypeLocal, tag, options),
|
||||||
|
ctx: ctx,
|
||||||
hosts: hosts.NewFile(hosts.DefaultPath),
|
hosts: hosts.NewFile(hosts.DefaultPath),
|
||||||
dialer: transportDialer,
|
dialer: transportDialer,
|
||||||
}, nil
|
}, 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
|
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) {
|
if systemConfig.singleRequest || !(message.Question[0].Qtype == mDNS.TypeA || message.Question[0].Qtype == mDNS.TypeAAAA) {
|
||||||
return t.exchangeSingleRequest(ctx, systemConfig, message, domain)
|
return t.exchangeSingleRequest(ctx, systemConfig, message, domain)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package local
|
package local
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
@ -23,19 +24,21 @@ type resolverConfig struct {
|
|||||||
|
|
||||||
var resolvConf resolverConfig
|
var resolvConf resolverConfig
|
||||||
|
|
||||||
func getSystemDNSConfig() *dnsConfig {
|
func getSystemDNSConfig(ctx context.Context) *dnsConfig {
|
||||||
resolvConf.tryUpdate("/etc/resolv.conf")
|
resolvConf.tryUpdate(ctx, "/etc/resolv.conf")
|
||||||
return resolvConf.dnsConfig.Load()
|
return resolvConf.dnsConfig.Load()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conf *resolverConfig) init() {
|
func (conf *resolverConfig) init(ctx context.Context) {
|
||||||
conf.dnsConfig.Store(dnsReadConfig("/etc/resolv.conf"))
|
conf.dnsConfig.Store(dnsReadConfig(ctx, "/etc/resolv.conf"))
|
||||||
conf.lastChecked = time.Now()
|
conf.lastChecked = time.Now()
|
||||||
conf.ch = make(chan struct{}, 1)
|
conf.ch = make(chan struct{}, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conf *resolverConfig) tryUpdate(name string) {
|
func (conf *resolverConfig) tryUpdate(ctx context.Context, name string) {
|
||||||
conf.initOnce.Do(conf.init)
|
conf.initOnce.Do(func() {
|
||||||
|
conf.init(ctx)
|
||||||
|
})
|
||||||
|
|
||||||
if conf.dnsConfig.Load().noReload {
|
if conf.dnsConfig.Load().noReload {
|
||||||
return
|
return
|
||||||
@ -59,7 +62,7 @@ func (conf *resolverConfig) tryUpdate(name string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dnsConf := dnsReadConfig(name)
|
dnsConf := dnsReadConfig(ctx, name)
|
||||||
conf.dnsConfig.Store(dnsConf)
|
conf.dnsConfig.Store(dnsConf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ package local
|
|||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
@ -18,7 +19,7 @@ import (
|
|||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
func dnsReadConfig(_ string) *dnsConfig {
|
func dnsReadConfig(_ context.Context, _ string) *dnsConfig {
|
||||||
if C.res_init() != 0 {
|
if C.res_init() != 0 {
|
||||||
return &dnsConfig{
|
return &dnsConfig{
|
||||||
servers: defaultNS,
|
servers: defaultNS,
|
||||||
|
@ -4,6 +4,7 @@ package local
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
@ -13,7 +14,7 @@ import (
|
|||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
func dnsReadConfig(name string) *dnsConfig {
|
func dnsReadConfig(_ context.Context, name string) *dnsConfig {
|
||||||
conf := &dnsConfig{
|
conf := &dnsConfig{
|
||||||
ndots: 1,
|
ndots: 1,
|
||||||
timeout: 5 * time.Second,
|
timeout: 5 * time.Second,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package local
|
package local
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
@ -8,10 +9,13 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing/service"
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
func dnsReadConfig(_ string) *dnsConfig {
|
func dnsReadConfig(ctx context.Context, _ string) *dnsConfig {
|
||||||
conf := &dnsConfig{
|
conf := &dnsConfig{
|
||||||
ndots: 1,
|
ndots: 1,
|
||||||
timeout: 5 * time.Second,
|
timeout: 5 * time.Second,
|
||||||
@ -22,35 +26,35 @@ func dnsReadConfig(_ string) *dnsConfig {
|
|||||||
conf.servers = defaultNS
|
conf.servers = defaultNS
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
aas, err := adapterAddresses()
|
addresses, err := adapterAddresses()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
var dnsAddresses []struct {
|
||||||
for _, aa := range aas {
|
ifName string
|
||||||
// Only take interfaces whose OperStatus is IfOperStatusUp(0x01) into DNS configs.
|
netip.Addr
|
||||||
if aa.OperStatus != windows.IfOperStatusUp {
|
}
|
||||||
|
for _, address := range addresses {
|
||||||
|
if address.OperStatus != windows.IfOperStatusUp {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if address.IfType == windows.IF_TYPE_TUNNEL {
|
||||||
// Only take interfaces which have at least one gateway
|
|
||||||
if aa.FirstGatewayAddress == nil {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if address.FirstGatewayAddress == nil {
|
||||||
for dns := aa.FirstDnsServerAddress; dns != nil; dns = dns.Next {
|
continue
|
||||||
sa, err := dns.Address.Sockaddr.Sockaddr()
|
}
|
||||||
|
for dnsServerAddress := address.FirstDnsServerAddress; dnsServerAddress != nil; dnsServerAddress = dnsServerAddress.Next {
|
||||||
|
rawSockaddr, err := dnsServerAddress.Address.Sockaddr.Sockaddr()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var ip netip.Addr
|
var dnsServerAddr netip.Addr
|
||||||
switch sa := sa.(type) {
|
switch sockaddr := rawSockaddr.(type) {
|
||||||
case *syscall.SockaddrInet4:
|
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:
|
case *syscall.SockaddrInet6:
|
||||||
var addr16 [16]byte
|
if sockaddr.Addr[0] == 0xfe && sockaddr.Addr[1] == 0xc0 {
|
||||||
copy(addr16[:], sa.Addr[:])
|
|
||||||
if addr16[0] == 0xfe && addr16[1] == 0xc0 {
|
|
||||||
// fec0/10 IPv6 addresses are site local anycast DNS
|
// fec0/10 IPv6 addresses are site local anycast DNS
|
||||||
// addresses Microsoft sets by default if no other
|
// addresses Microsoft sets by default if no other
|
||||||
// IPv6 DNS address is set. Site local anycast is
|
// IPv6 DNS address is set. Site local anycast is
|
||||||
@ -58,14 +62,27 @@ func dnsReadConfig(_ string) *dnsConfig {
|
|||||||
// https://datatracker.ietf.org/doc/html/rfc3879
|
// https://datatracker.ietf.org/doc/html/rfc3879
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
ip = netip.AddrFrom16(addr16)
|
dnsServerAddr = netip.AddrFrom16(sockaddr.Addr)
|
||||||
default:
|
default:
|
||||||
// Unexpected type.
|
// Unexpected type.
|
||||||
continue
|
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
|
return conf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,6 +116,10 @@ func (s *platformInterfaceStub) FindProcessInfo(ctx context.Context, network str
|
|||||||
return nil, os.ErrInvalid
|
return nil, os.ErrInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *platformInterfaceStub) SendNotification(notification *platform.Notification) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type interfaceMonitorStub struct{}
|
type interfaceMonitorStub struct{}
|
||||||
|
|
||||||
func (s *interfaceMonitorStub) Start() error {
|
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 *interfaceMonitorStub) UnregisterCallback(element *list.Element[tun.DefaultInterfaceUpdateCallback]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *platformInterfaceStub) SendNotification(notification *platform.Notification) error {
|
func (s *interfaceMonitorStub) RegisterMyInterface(interfaceName string) {
|
||||||
return nil
|
}
|
||||||
|
|
||||||
|
func (s *interfaceMonitorStub) MyInterface() string {
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func FormatConfig(configContent string) (*StringBox, error) {
|
func FormatConfig(configContent string) (*StringBox, error) {
|
||||||
|
@ -15,9 +15,10 @@ var (
|
|||||||
|
|
||||||
type platformDefaultInterfaceMonitor struct {
|
type platformDefaultInterfaceMonitor struct {
|
||||||
*platformInterfaceWrapper
|
*platformInterfaceWrapper
|
||||||
element *list.Element[tun.NetworkUpdateCallback]
|
logger logger.Logger
|
||||||
callbacks list.List[tun.DefaultInterfaceUpdateCallback]
|
element *list.Element[tun.NetworkUpdateCallback]
|
||||||
logger logger.Logger
|
callbacks list.List[tun.DefaultInterfaceUpdateCallback]
|
||||||
|
myInterface string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *platformDefaultInterfaceMonitor) Start() error {
|
func (m *platformDefaultInterfaceMonitor) Start() error {
|
||||||
@ -102,3 +103,15 @@ func (m *platformDefaultInterfaceMonitor) updateDefaultInterface(interfaceName s
|
|||||||
callback(newInterface, 0)
|
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
|
||||||
|
}
|
||||||
|
@ -164,6 +164,7 @@ func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "query tun name")
|
return nil, E.Cause(err, "query tun name")
|
||||||
}
|
}
|
||||||
|
options.InterfaceMonitor.RegisterMyInterface(options.Name)
|
||||||
dupFd, err := dup(int(tunFd))
|
dupFd, err := dup(int(tunFd))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "dup tun file descriptor")
|
return nil, E.Cause(err, "dup tun file descriptor")
|
||||||
|
2
go.mod
2
go.mod
@ -32,7 +32,7 @@ require (
|
|||||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056
|
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/sing-vmess v0.2.1
|
||||||
github.com/sagernet/smux v1.5.34-mod.2
|
github.com/sagernet/smux v1.5.34-mod.2
|
||||||
github.com/sagernet/tailscale v1.80.3-mod.5
|
github.com/sagernet/tailscale v1.80.3-mod.5
|
||||||
|
4
go.sum
4
go.sum
@ -186,8 +186,8 @@ github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wK
|
|||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
github.com/sagernet/sing-shadowsocks2 v0.2.0/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 h1:GFNJQAHhSXqAfxAw1wDG/QWbdpGH5Na3k8qUynqWnEA=
|
||||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056/go.mod h1:HyacBPIFiKihJQR8LQp56FM4hBtd/7MZXnRxxQIOPsc=
|
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.6-0.20250428031943-0686f8c4f210 h1:6H4BZaTqKI3YcDMyTV3E576LuJM4S4wY99xoq2T1ECw=
|
||||||
github.com/sagernet/sing-tun v0.6.5/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
|
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 h1:6izHC2+B68aQCxTagki6eZZc+g5eh4dYwxOV5a2Lhug=
|
||||||
github.com/sagernet/sing-vmess v0.2.1/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA=
|
github.com/sagernet/sing-vmess v0.2.1/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 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=
|
||||||
|
Loading…
x
Reference in New Issue
Block a user