@@ -2,27 +2,39 @@ package local
22
33import (
44 "context"
5+ "errors"
56 "os"
67 "sync"
8+ "sync/atomic"
79
810 "github.com/sagernet/sing-box/adapter"
11+ C "github.com/sagernet/sing-box/constant"
912 "github.com/sagernet/sing-box/service/resolved"
1013 "github.com/sagernet/sing-tun"
1114 "github.com/sagernet/sing/common"
15+ "github.com/sagernet/sing/common/control"
1216 E "github.com/sagernet/sing/common/exceptions"
1317 "github.com/sagernet/sing/common/logger"
18+ "github.com/sagernet/sing/common/x/list"
1419 "github.com/sagernet/sing/service"
1520
1621 "github.com/godbus/dbus/v5"
1722 mDNS "github.com/miekg/dns"
1823)
1924
2025type DBusResolvedResolver struct {
21- logger logger.ContextLogger
22- interfaceMonitor tun.DefaultInterfaceMonitor
23- systemBus * dbus.Conn
24- resoledObject common.TypedValue [dbus.BusObject ]
25- closeOnce sync.Once
26+ ctx context.Context
27+ logger logger.ContextLogger
28+ interfaceMonitor tun.DefaultInterfaceMonitor
29+ interfaceCallback * list.Element [tun.DefaultInterfaceUpdateCallback ]
30+ systemBus * dbus.Conn
31+ resoledObject atomic.Pointer [ResolvedObject ]
32+ closeOnce sync.Once
33+ }
34+
35+ type ResolvedObject struct {
36+ dbus.BusObject
37+ InterfaceIndex int32
2638}
2739
2840func NewResolvedResolver (ctx context.Context , logger logger.ContextLogger ) (ResolvedResolver , error ) {
@@ -35,6 +47,7 @@ func NewResolvedResolver(ctx context.Context, logger logger.ContextLogger) (Reso
3547 return nil , err
3648 }
3749 return & DBusResolvedResolver {
50+ ctx : ctx ,
3851 logger : logger ,
3952 interfaceMonitor : interfaceMonitor ,
4053 systemBus : systemBus ,
@@ -43,6 +56,7 @@ func NewResolvedResolver(ctx context.Context, logger logger.ContextLogger) (Reso
4356
4457func (t * DBusResolvedResolver ) Start () error {
4558 t .updateStatus ()
59+ t .interfaceCallback = t .interfaceMonitor .RegisterCallback (t .updateDefaultInterface )
4660 err := t .systemBus .BusObject ().AddMatchSignal (
4761 "org.freedesktop.DBus" ,
4862 "NameOwnerChanged" ,
@@ -58,6 +72,9 @@ func (t *DBusResolvedResolver) Start() error {
5872
5973func (t * DBusResolvedResolver ) Close () error {
6074 t .closeOnce .Do (func () {
75+ if t .interfaceCallback != nil {
76+ t .interfaceMonitor .UnregisterCallback (t .interfaceCallback )
77+ }
6178 if t .systemBus != nil {
6279 _ = t .systemBus .Close ()
6380 }
@@ -66,26 +83,27 @@ func (t *DBusResolvedResolver) Close() error {
6683}
6784
6885func (t * DBusResolvedResolver ) Object () any {
69- return t .resoledObject .Load ()
86+ return common . PtrOrNil ( t .resoledObject .Load () )
7087}
7188
7289func (t * DBusResolvedResolver ) Exchange (object any , ctx context.Context , message * mDNS.Msg ) (* mDNS.Msg , error ) {
73- defaultInterface := t .interfaceMonitor .DefaultInterface ()
74- if defaultInterface == nil {
75- return nil , E .New ("missing default interface" )
76- }
7790 question := message .Question [0 ]
78- call := object .(* dbus.Object ).CallWithContext (
91+ resolvedObject := object .(* ResolvedObject )
92+ call := resolvedObject .CallWithContext (
7993 ctx ,
8094 "org.freedesktop.resolve1.Manager.ResolveRecord" ,
8195 0 ,
82- int32 ( defaultInterface . Index ) ,
96+ resolvedObject . InterfaceIndex ,
8397 question .Name ,
8498 question .Qclass ,
8599 question .Qtype ,
86100 uint64 (0 ),
87101 )
88102 if call .Err != nil {
103+ var dbusError dbus.Error
104+ if errors .As (call .Err , & dbusError ) && dbusError .Name == "org.freedesktop.resolve1.NoNameServers" {
105+ t .updateStatus ()
106+ }
89107 return nil , E .Cause (call .Err , " resolve record via resolved" )
90108 }
91109 var (
@@ -137,14 +155,76 @@ func (t *DBusResolvedResolver) loopUpdateStatus() {
137155}
138156
139157func (t * DBusResolvedResolver ) updateStatus () {
140- dbusObject := t .systemBus . Object ( "org.freedesktop.resolve1" , "/org/freedesktop/resolve1" )
141- err := dbusObject . Call ( "org.freedesktop.DBus.Peer.Ping" , 0 ). Err
158+ dbusObject , err := t .checkResolved ( context . Background () )
159+ oldValue := t . resoledObject . Swap ( dbusObject )
142160 if err != nil {
143- if t .resoledObject .Swap (nil ) != nil {
161+ var dbusErr dbus.Error
162+ if ! errors .As (err , & dbusErr ) || dbusErr .Name != "org.freedesktop.DBus.Error.NameHasNoOwnerCould" {
163+ t .logger .Debug (E .Cause (err , "systemd-resolved service unavailable" ))
164+ }
165+ if oldValue != nil {
144166 t .logger .Debug ("systemd-resolved service is gone" )
145167 }
146168 return
169+ } else if oldValue == nil {
170+ t .logger .Debug ("using systemd-resolved service as resolver" )
171+ }
172+ }
173+
174+ func (t * DBusResolvedResolver ) checkResolved (ctx context.Context ) (* ResolvedObject , error ) {
175+ dbusObject := t .systemBus .Object ("org.freedesktop.resolve1" , "/org/freedesktop/resolve1" )
176+ err := dbusObject .Call ("org.freedesktop.DBus.Peer.Ping" , 0 ).Err
177+ if err != nil {
178+ return nil , err
179+ }
180+ defaultInterface := t .interfaceMonitor .DefaultInterface ()
181+ if defaultInterface == nil {
182+ return nil , E .New ("missing default interface" )
147183 }
148- t .resoledObject .Store (dbusObject )
149- t .logger .Debug ("using systemd-resolved service as resolver" )
184+ call := dbusObject .(* dbus.Object ).CallWithContext (
185+ ctx ,
186+ "org.freedesktop.resolve1.Manager.GetLink" ,
187+ 0 ,
188+ int32 (defaultInterface .Index ),
189+ )
190+ if call .Err != nil {
191+ return nil , err
192+ }
193+ var linkPath dbus.ObjectPath
194+ err = call .Store (& linkPath )
195+ if err != nil {
196+ return nil , err
197+ }
198+ linkObject := t .systemBus .Object ("org.freedesktop.resolve1" , linkPath )
199+ if linkObject == nil {
200+ return nil , E .New ("missing link object for default interface" )
201+ }
202+ dnsProp , err := linkObject .GetProperty ("org.freedesktop.resolve1.Link.DNS" )
203+ if err != nil {
204+ return nil , err
205+ }
206+ var linkDNS []resolved.LinkDNS
207+ err = dnsProp .Store (& linkDNS )
208+ if err != nil {
209+ return nil , err
210+ }
211+ if len (linkDNS ) == 0 {
212+ for _ , inbound := range service.FromContext [adapter.InboundManager ](t .ctx ).Inbounds () {
213+ if inbound .Type () == C .TypeTun {
214+ return nil , E .New ("No appropriate name servers or networks for name found" )
215+ }
216+ }
217+ return & ResolvedObject {
218+ BusObject : dbusObject ,
219+ }, nil
220+ } else {
221+ return & ResolvedObject {
222+ BusObject : dbusObject ,
223+ InterfaceIndex : int32 (defaultInterface .Index ),
224+ }, nil
225+ }
226+ }
227+
228+ func (t * DBusResolvedResolver ) updateDefaultInterface (defaultInterface * control.Interface , flags int ) {
229+ t .updateStatus ()
150230}
0 commit comments