@@ -2,27 +2,38 @@ package local
22
33import (
44 "context"
5+ "errors"
56 "os"
67 "sync"
78
89 "github.com/sagernet/sing-box/adapter"
10+ C "github.com/sagernet/sing-box/constant"
911 "github.com/sagernet/sing-box/service/resolved"
1012 "github.com/sagernet/sing-tun"
1113 "github.com/sagernet/sing/common/atomic"
14+ "github.com/sagernet/sing/common/control"
1215 E "github.com/sagernet/sing/common/exceptions"
1316 "github.com/sagernet/sing/common/logger"
17+ "github.com/sagernet/sing/common/x/list"
1418 "github.com/sagernet/sing/service"
1519
1620 "github.com/godbus/dbus/v5"
1721 mDNS "github.com/miekg/dns"
1822)
1923
2024type DBusResolvedResolver struct {
21- logger logger.ContextLogger
22- interfaceMonitor tun.DefaultInterfaceMonitor
23- systemBus * dbus.Conn
24- resoledObject atomic.TypedValue [dbus.BusObject ]
25- closeOnce sync.Once
25+ ctx context.Context
26+ logger logger.ContextLogger
27+ interfaceMonitor tun.DefaultInterfaceMonitor
28+ interfaceCallback * list.Element [tun.DefaultInterfaceUpdateCallback ]
29+ systemBus * dbus.Conn
30+ resoledObject atomic.Pointer [ResolvedObject ]
31+ closeOnce sync.Once
32+ }
33+
34+ type ResolvedObject struct {
35+ dbus.BusObject
36+ InterfaceIndex int32
2637}
2738
2839func NewResolvedResolver (ctx context.Context , logger logger.ContextLogger ) (ResolvedResolver , error ) {
@@ -35,6 +46,7 @@ func NewResolvedResolver(ctx context.Context, logger logger.ContextLogger) (Reso
3546 return nil , err
3647 }
3748 return & DBusResolvedResolver {
49+ ctx : ctx ,
3850 logger : logger ,
3951 interfaceMonitor : interfaceMonitor ,
4052 systemBus : systemBus ,
@@ -43,6 +55,7 @@ func NewResolvedResolver(ctx context.Context, logger logger.ContextLogger) (Reso
4355
4456func (t * DBusResolvedResolver ) Start () error {
4557 t .updateStatus ()
58+ t .interfaceCallback = t .interfaceMonitor .RegisterCallback (t .updateDefaultInterface )
4659 err := t .systemBus .BusObject ().AddMatchSignal (
4760 "org.freedesktop.DBus" ,
4861 "NameOwnerChanged" ,
@@ -70,22 +83,23 @@ func (t *DBusResolvedResolver) Object() any {
7083}
7184
7285func (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- }
7786 question := message .Question [0 ]
78- call := object .(* dbus.Object ).CallWithContext (
87+ resolvedObject := object .(* ResolvedObject )
88+ call := resolvedObject .CallWithContext (
7989 ctx ,
8090 "org.freedesktop.resolve1.Manager.ResolveRecord" ,
8191 0 ,
82- int32 ( defaultInterface . Index ) ,
92+ resolvedObject . InterfaceIndex ,
8393 question .Name ,
8494 question .Qclass ,
8595 question .Qtype ,
8696 uint64 (0 ),
8797 )
8898 if call .Err != nil {
99+ var dbusError dbus.Error
100+ if errors .As (call .Err , & dbusError ) && dbusError .Name == "org.freedesktop.resolve1.NoNameServers" {
101+ t .updateStatus ()
102+ }
89103 return nil , E .Cause (call .Err , " resolve record via resolved" )
90104 }
91105 var (
@@ -137,14 +151,76 @@ func (t *DBusResolvedResolver) loopUpdateStatus() {
137151}
138152
139153func (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
154+ dbusObject , err := t .checkResolved ( context . Background () )
155+ oldValue := t . resoledObject . Swap ( dbusObject )
142156 if err != nil {
143- if t .resoledObject .Swap (nil ) != nil {
157+ var dbusErr dbus.Error
158+ if ! errors .As (err , & dbusErr ) || dbusErr .Name != "org.freedesktop.DBus.Error.NameHasNoOwnerCould" {
159+ t .logger .Debug (E .Cause (err , "systemd-resolved service unavailable" ))
160+ }
161+ if oldValue != nil {
144162 t .logger .Debug ("systemd-resolved service is gone" )
145163 }
146164 return
165+ } else if oldValue == nil {
166+ t .logger .Debug ("using systemd-resolved service as resolver" )
167+ }
168+ }
169+
170+ func (t * DBusResolvedResolver ) checkResolved (ctx context.Context ) (* ResolvedObject , error ) {
171+ dbusObject := t .systemBus .Object ("org.freedesktop.resolve1" , "/org/freedesktop/resolve1" )
172+ err := dbusObject .Call ("org.freedesktop.DBus.Peer.Ping" , 0 ).Err
173+ if err != nil {
174+ return nil , err
175+ }
176+ defaultInterface := t .interfaceMonitor .DefaultInterface ()
177+ if defaultInterface == nil {
178+ return nil , E .New ("missing default interface" )
147179 }
148- t .resoledObject .Store (dbusObject )
149- t .logger .Debug ("using systemd-resolved service as resolver" )
180+ call := dbusObject .(* dbus.Object ).CallWithContext (
181+ ctx ,
182+ "org.freedesktop.resolve1.Manager.GetLink" ,
183+ 0 ,
184+ int32 (defaultInterface .Index ),
185+ )
186+ if call .Err != nil {
187+ return nil , err
188+ }
189+ var linkPath dbus.ObjectPath
190+ err = call .Store (& linkPath )
191+ if err != nil {
192+ return nil , err
193+ }
194+ linkObject := t .systemBus .Object ("org.freedesktop.resolve1" , linkPath )
195+ if linkObject == nil {
196+ return nil , E .New ("missing link object for default interface" )
197+ }
198+ dnsProp , err := linkObject .GetProperty ("org.freedesktop.resolve1.Link.DNS" )
199+ if err != nil {
200+ return nil , err
201+ }
202+ var linkDNS []resolved.LinkDNS
203+ err = dnsProp .Store (& linkDNS )
204+ if err != nil {
205+ return nil , err
206+ }
207+ if len (linkDNS ) == 0 {
208+ for _ , inbound := range service.FromContext [adapter.InboundManager ](t .ctx ).Inbounds () {
209+ if inbound .Type () == C .TypeTun {
210+ return nil , E .New ("No appropriate name servers or networks for name found" )
211+ }
212+ }
213+ return & ResolvedObject {
214+ BusObject : dbusObject ,
215+ }, nil
216+ } else {
217+ return & ResolvedObject {
218+ BusObject : dbusObject ,
219+ InterfaceIndex : int32 (defaultInterface .Index ),
220+ }, nil
221+ }
222+ }
223+
224+ func (t * DBusResolvedResolver ) updateDefaultInterface (defaultInterface * control.Interface , flags int ) {
225+ t .updateStatus ()
150226}
0 commit comments