Skip to content

Commit 109a614

Browse files
committed
Fix resolve using resolved
1 parent 0ab00a4 commit 109a614

File tree

1 file changed

+97
-17
lines changed

1 file changed

+97
-17
lines changed

dns/transport/local/local_resolved_linux.go

Lines changed: 97 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,39 @@ package local
22

33
import (
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

2025
type 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

2840
func 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

4457
func (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

5973
func (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

6885
func (t *DBusResolvedResolver) Object() any {
69-
return t.resoledObject.Load()
86+
return common.PtrOrNil(t.resoledObject.Load())
7087
}
7188

7289
func (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

139157
func (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

Comments
 (0)