Fix resolve using resolved

This commit is contained in:
世界 2025-08-21 16:23:14 +08:00
parent c54eb3381f
commit 1d72a6b30e
No known key found for this signature in database
GPG Key ID: CD109927C34A63C4

View File

@ -2,15 +2,19 @@ package local
import ( import (
"context" "context"
"errors"
"os" "os"
"sync" "sync"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/service/resolved" "github.com/sagernet/sing-box/service/resolved"
"github.com/sagernet/sing-tun" "github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/atomic" "github.com/sagernet/sing/common/atomic"
"github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger" "github.com/sagernet/sing/common/logger"
"github.com/sagernet/sing/common/x/list"
"github.com/sagernet/sing/service" "github.com/sagernet/sing/service"
"github.com/godbus/dbus/v5" "github.com/godbus/dbus/v5"
@ -18,13 +22,20 @@ import (
) )
type DBusResolvedResolver struct { type DBusResolvedResolver struct {
ctx context.Context
logger logger.ContextLogger logger logger.ContextLogger
interfaceMonitor tun.DefaultInterfaceMonitor interfaceMonitor tun.DefaultInterfaceMonitor
interfaceCallback *list.Element[tun.DefaultInterfaceUpdateCallback]
systemBus *dbus.Conn systemBus *dbus.Conn
resoledObject atomic.TypedValue[dbus.BusObject] resoledObject atomic.Pointer[ResolvedObject]
closeOnce sync.Once closeOnce sync.Once
} }
type ResolvedObject struct {
dbus.BusObject
InterfaceIndex int32
}
func NewResolvedResolver(ctx context.Context, logger logger.ContextLogger) (ResolvedResolver, error) { func NewResolvedResolver(ctx context.Context, logger logger.ContextLogger) (ResolvedResolver, error) {
interfaceMonitor := service.FromContext[adapter.NetworkManager](ctx).InterfaceMonitor() interfaceMonitor := service.FromContext[adapter.NetworkManager](ctx).InterfaceMonitor()
if interfaceMonitor == nil { if interfaceMonitor == nil {
@ -35,6 +46,7 @@ func NewResolvedResolver(ctx context.Context, logger logger.ContextLogger) (Reso
return nil, err return nil, err
} }
return &DBusResolvedResolver{ return &DBusResolvedResolver{
ctx: ctx,
logger: logger, logger: logger,
interfaceMonitor: interfaceMonitor, interfaceMonitor: interfaceMonitor,
systemBus: systemBus, systemBus: systemBus,
@ -43,6 +55,7 @@ func NewResolvedResolver(ctx context.Context, logger logger.ContextLogger) (Reso
func (t *DBusResolvedResolver) Start() error { func (t *DBusResolvedResolver) Start() error {
t.updateStatus() t.updateStatus()
t.interfaceCallback = t.interfaceMonitor.RegisterCallback(t.updateDefaultInterface)
err := t.systemBus.BusObject().AddMatchSignal( err := t.systemBus.BusObject().AddMatchSignal(
"org.freedesktop.DBus", "org.freedesktop.DBus",
"NameOwnerChanged", "NameOwnerChanged",
@ -58,6 +71,9 @@ func (t *DBusResolvedResolver) Start() error {
func (t *DBusResolvedResolver) Close() error { func (t *DBusResolvedResolver) Close() error {
t.closeOnce.Do(func() { t.closeOnce.Do(func() {
if t.interfaceCallback != nil {
t.interfaceMonitor.UnregisterCallback(t.interfaceCallback)
}
if t.systemBus != nil { if t.systemBus != nil {
_ = t.systemBus.Close() _ = t.systemBus.Close()
} }
@ -70,22 +86,23 @@ func (t *DBusResolvedResolver) Object() any {
} }
func (t *DBusResolvedResolver) Exchange(object any, ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { func (t *DBusResolvedResolver) Exchange(object any, ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
defaultInterface := t.interfaceMonitor.DefaultInterface()
if defaultInterface == nil {
return nil, E.New("missing default interface")
}
question := message.Question[0] question := message.Question[0]
call := object.(*dbus.Object).CallWithContext( resolvedObject := object.(*ResolvedObject)
call := resolvedObject.CallWithContext(
ctx, ctx,
"org.freedesktop.resolve1.Manager.ResolveRecord", "org.freedesktop.resolve1.Manager.ResolveRecord",
0, 0,
int32(defaultInterface.Index), resolvedObject.InterfaceIndex,
question.Name, question.Name,
question.Qclass, question.Qclass,
question.Qtype, question.Qtype,
uint64(0), uint64(0),
) )
if call.Err != nil { if call.Err != nil {
var dbusError dbus.Error
if errors.As(call.Err, &dbusError) && dbusError.Name == "org.freedesktop.resolve1.NoNameServers" {
t.updateStatus()
}
return nil, E.Cause(call.Err, " resolve record via resolved") return nil, E.Cause(call.Err, " resolve record via resolved")
} }
var ( var (
@ -137,14 +154,76 @@ func (t *DBusResolvedResolver) loopUpdateStatus() {
} }
func (t *DBusResolvedResolver) updateStatus() { func (t *DBusResolvedResolver) updateStatus() {
dbusObject := t.systemBus.Object("org.freedesktop.resolve1", "/org/freedesktop/resolve1") dbusObject, err := t.checkResolved(context.Background())
err := dbusObject.Call("org.freedesktop.DBus.Peer.Ping", 0).Err oldValue := t.resoledObject.Swap(dbusObject)
if err != nil { if err != nil {
if t.resoledObject.Swap(nil) != nil { var dbusErr dbus.Error
if !errors.As(err, &dbusErr) || dbusErr.Name != "org.freedesktop.DBus.Error.NameHasNoOwnerCould" {
t.logger.Debug(E.Cause(err, "systemd-resolved service unavailable"))
}
if oldValue != nil {
t.logger.Debug("systemd-resolved service is gone") t.logger.Debug("systemd-resolved service is gone")
} }
return return
} } else if oldValue == nil {
t.resoledObject.Store(dbusObject)
t.logger.Debug("using systemd-resolved service as resolver") t.logger.Debug("using systemd-resolved service as resolver")
}
}
func (t *DBusResolvedResolver) checkResolved(ctx context.Context) (*ResolvedObject, error) {
dbusObject := t.systemBus.Object("org.freedesktop.resolve1", "/org/freedesktop/resolve1")
err := dbusObject.Call("org.freedesktop.DBus.Peer.Ping", 0).Err
if err != nil {
return nil, err
}
defaultInterface := t.interfaceMonitor.DefaultInterface()
if defaultInterface == nil {
return nil, E.New("missing default interface")
}
call := dbusObject.(*dbus.Object).CallWithContext(
ctx,
"org.freedesktop.resolve1.Manager.GetLink",
0,
int32(defaultInterface.Index),
)
if call.Err != nil {
return nil, err
}
var linkPath dbus.ObjectPath
err = call.Store(&linkPath)
if err != nil {
return nil, err
}
linkObject := t.systemBus.Object("org.freedesktop.resolve1", linkPath)
if linkObject == nil {
return nil, E.New("missing link object for default interface")
}
dnsProp, err := linkObject.GetProperty("org.freedesktop.resolve1.Link.DNS")
if err != nil {
return nil, err
}
var linkDNS []resolved.LinkDNS
err = dnsProp.Store(&linkDNS)
if err != nil {
return nil, err
}
if len(linkDNS) == 0 {
for _, inbound := range service.FromContext[adapter.InboundManager](t.ctx).Inbounds() {
if inbound.Type() == C.TypeTun {
return nil, E.New("No appropriate name servers or networks for name found")
}
}
return &ResolvedObject{
BusObject: dbusObject,
}, nil
} else {
return &ResolvedObject{
BusObject: dbusObject,
InterfaceIndex: int32(defaultInterface.Index),
}, nil
}
}
func (t *DBusResolvedResolver) updateDefaultInterface(defaultInterface *control.Interface, flags int) {
t.updateStatus()
} }