local_resolved_linux.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. package local
  2. import (
  3. "context"
  4. "os"
  5. "sync"
  6. "github.com/sagernet/sing-box/adapter"
  7. "github.com/sagernet/sing-box/service/resolved"
  8. "github.com/sagernet/sing-tun"
  9. "github.com/sagernet/sing/common"
  10. E "github.com/sagernet/sing/common/exceptions"
  11. "github.com/sagernet/sing/common/logger"
  12. "github.com/sagernet/sing/service"
  13. "github.com/godbus/dbus/v5"
  14. mDNS "github.com/miekg/dns"
  15. )
  16. type DBusResolvedResolver struct {
  17. logger logger.ContextLogger
  18. interfaceMonitor tun.DefaultInterfaceMonitor
  19. systemBus *dbus.Conn
  20. resoledObject common.TypedValue[dbus.BusObject]
  21. closeOnce sync.Once
  22. }
  23. func NewResolvedResolver(ctx context.Context, logger logger.ContextLogger) (ResolvedResolver, error) {
  24. interfaceMonitor := service.FromContext[adapter.NetworkManager](ctx).InterfaceMonitor()
  25. if interfaceMonitor == nil {
  26. return nil, os.ErrInvalid
  27. }
  28. systemBus, err := dbus.SystemBus()
  29. if err != nil {
  30. return nil, err
  31. }
  32. return &DBusResolvedResolver{
  33. logger: logger,
  34. interfaceMonitor: interfaceMonitor,
  35. systemBus: systemBus,
  36. }, nil
  37. }
  38. func (t *DBusResolvedResolver) Start() error {
  39. t.updateStatus()
  40. err := t.systemBus.BusObject().AddMatchSignal(
  41. "org.freedesktop.DBus",
  42. "NameOwnerChanged",
  43. dbus.WithMatchSender("org.freedesktop.DBus"),
  44. dbus.WithMatchArg(0, "org.freedesktop.resolve1.Manager"),
  45. ).Err
  46. if err != nil {
  47. return E.Cause(err, "configure resolved restart listener")
  48. }
  49. go t.loopUpdateStatus()
  50. return nil
  51. }
  52. func (t *DBusResolvedResolver) Close() error {
  53. t.closeOnce.Do(func() {
  54. if t.systemBus != nil {
  55. _ = t.systemBus.Close()
  56. }
  57. })
  58. return nil
  59. }
  60. func (t *DBusResolvedResolver) Object() any {
  61. return t.resoledObject.Load()
  62. }
  63. func (t *DBusResolvedResolver) Exchange(object any, ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
  64. defaultInterface := t.interfaceMonitor.DefaultInterface()
  65. if defaultInterface == nil {
  66. return nil, E.New("missing default interface")
  67. }
  68. question := message.Question[0]
  69. call := object.(*dbus.Object).CallWithContext(
  70. ctx,
  71. "org.freedesktop.resolve1.Manager.ResolveRecord",
  72. 0,
  73. int32(defaultInterface.Index),
  74. question.Name,
  75. question.Qclass,
  76. question.Qtype,
  77. uint64(0),
  78. )
  79. if call.Err != nil {
  80. return nil, E.Cause(call.Err, " resolve record via resolved")
  81. }
  82. var (
  83. records []resolved.ResourceRecord
  84. outflags uint64
  85. )
  86. err := call.Store(&records, &outflags)
  87. if err != nil {
  88. return nil, err
  89. }
  90. response := &mDNS.Msg{
  91. MsgHdr: mDNS.MsgHdr{
  92. Id: message.Id,
  93. Response: true,
  94. Authoritative: true,
  95. RecursionDesired: true,
  96. RecursionAvailable: true,
  97. Rcode: mDNS.RcodeSuccess,
  98. },
  99. Question: []mDNS.Question{question},
  100. }
  101. for _, record := range records {
  102. var rr mDNS.RR
  103. rr, _, err = mDNS.UnpackRR(record.Data, 0)
  104. if err != nil {
  105. return nil, E.Cause(err, "unpack resource record")
  106. }
  107. response.Answer = append(response.Answer, rr)
  108. }
  109. return response, nil
  110. }
  111. func (t *DBusResolvedResolver) loopUpdateStatus() {
  112. signalChan := make(chan *dbus.Signal, 1)
  113. t.systemBus.Signal(signalChan)
  114. for signal := range signalChan {
  115. var restarted bool
  116. if signal.Name == "org.freedesktop.DBus.NameOwnerChanged" {
  117. if len(signal.Body) != 3 || signal.Body[2].(string) == "" {
  118. continue
  119. } else {
  120. restarted = true
  121. }
  122. }
  123. if restarted {
  124. t.updateStatus()
  125. }
  126. }
  127. }
  128. func (t *DBusResolvedResolver) updateStatus() {
  129. dbusObject := t.systemBus.Object("org.freedesktop.resolve1", "/org/freedesktop/resolve1")
  130. err := dbusObject.Call("org.freedesktop.DBus.Peer.Ping", 0).Err
  131. if err != nil {
  132. if t.resoledObject.Swap(nil) != nil {
  133. t.logger.Debug("systemd-resolved service is gone")
  134. }
  135. return
  136. }
  137. t.resoledObject.Store(dbusObject)
  138. t.logger.Debug("using systemd-resolved service as resolver")
  139. }