local_resolved_linux.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. package local
  2. import (
  3. "bufio"
  4. "context"
  5. "errors"
  6. "os"
  7. "strings"
  8. "sync"
  9. "sync/atomic"
  10. "github.com/sagernet/sing-box/adapter"
  11. C "github.com/sagernet/sing-box/constant"
  12. "github.com/sagernet/sing-box/service/resolved"
  13. "github.com/sagernet/sing-tun"
  14. "github.com/sagernet/sing/common"
  15. "github.com/sagernet/sing/common/control"
  16. E "github.com/sagernet/sing/common/exceptions"
  17. "github.com/sagernet/sing/common/logger"
  18. "github.com/sagernet/sing/common/x/list"
  19. "github.com/sagernet/sing/service"
  20. "github.com/godbus/dbus/v5"
  21. mDNS "github.com/miekg/dns"
  22. )
  23. func isSystemdResolvedManaged() bool {
  24. resolvContent, err := os.Open("/etc/resolv.conf")
  25. if err != nil {
  26. return false
  27. }
  28. defer resolvContent.Close()
  29. scanner := bufio.NewScanner(resolvContent)
  30. for scanner.Scan() {
  31. line := strings.TrimSpace(scanner.Text())
  32. if line == "" || line[0] != '#' {
  33. return false
  34. }
  35. if strings.Contains(line, "systemd-resolved") {
  36. return true
  37. }
  38. }
  39. return false
  40. }
  41. type DBusResolvedResolver struct {
  42. ctx context.Context
  43. logger logger.ContextLogger
  44. interfaceMonitor tun.DefaultInterfaceMonitor
  45. interfaceCallback *list.Element[tun.DefaultInterfaceUpdateCallback]
  46. systemBus *dbus.Conn
  47. resoledObject atomic.Pointer[ResolvedObject]
  48. closeOnce sync.Once
  49. }
  50. type ResolvedObject struct {
  51. dbus.BusObject
  52. InterfaceIndex int32
  53. }
  54. func NewResolvedResolver(ctx context.Context, logger logger.ContextLogger) (ResolvedResolver, error) {
  55. interfaceMonitor := service.FromContext[adapter.NetworkManager](ctx).InterfaceMonitor()
  56. if interfaceMonitor == nil {
  57. return nil, os.ErrInvalid
  58. }
  59. systemBus, err := dbus.SystemBus()
  60. if err != nil {
  61. return nil, err
  62. }
  63. return &DBusResolvedResolver{
  64. ctx: ctx,
  65. logger: logger,
  66. interfaceMonitor: interfaceMonitor,
  67. systemBus: systemBus,
  68. }, nil
  69. }
  70. func (t *DBusResolvedResolver) Start() error {
  71. t.updateStatus()
  72. t.interfaceCallback = t.interfaceMonitor.RegisterCallback(t.updateDefaultInterface)
  73. err := t.systemBus.BusObject().AddMatchSignal(
  74. "org.freedesktop.DBus",
  75. "NameOwnerChanged",
  76. dbus.WithMatchSender("org.freedesktop.DBus"),
  77. dbus.WithMatchArg(0, "org.freedesktop.resolve1.Manager"),
  78. ).Err
  79. if err != nil {
  80. return E.Cause(err, "configure resolved restart listener")
  81. }
  82. go t.loopUpdateStatus()
  83. return nil
  84. }
  85. func (t *DBusResolvedResolver) Close() error {
  86. t.closeOnce.Do(func() {
  87. if t.interfaceCallback != nil {
  88. t.interfaceMonitor.UnregisterCallback(t.interfaceCallback)
  89. }
  90. if t.systemBus != nil {
  91. _ = t.systemBus.Close()
  92. }
  93. })
  94. return nil
  95. }
  96. func (t *DBusResolvedResolver) Object() any {
  97. return common.PtrOrNil(t.resoledObject.Load())
  98. }
  99. func (t *DBusResolvedResolver) Exchange(object any, ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
  100. question := message.Question[0]
  101. resolvedObject := object.(*ResolvedObject)
  102. call := resolvedObject.CallWithContext(
  103. ctx,
  104. "org.freedesktop.resolve1.Manager.ResolveRecord",
  105. 0,
  106. resolvedObject.InterfaceIndex,
  107. question.Name,
  108. question.Qclass,
  109. question.Qtype,
  110. uint64(0),
  111. )
  112. if call.Err != nil {
  113. var dbusError dbus.Error
  114. if errors.As(call.Err, &dbusError) && dbusError.Name == "org.freedesktop.resolve1.NoNameServers" {
  115. t.updateStatus()
  116. }
  117. return nil, E.Cause(call.Err, " resolve record via resolved")
  118. }
  119. var (
  120. records []resolved.ResourceRecord
  121. outflags uint64
  122. )
  123. err := call.Store(&records, &outflags)
  124. if err != nil {
  125. return nil, err
  126. }
  127. response := &mDNS.Msg{
  128. MsgHdr: mDNS.MsgHdr{
  129. Id: message.Id,
  130. Response: true,
  131. Authoritative: true,
  132. RecursionDesired: true,
  133. RecursionAvailable: true,
  134. Rcode: mDNS.RcodeSuccess,
  135. },
  136. Question: []mDNS.Question{question},
  137. }
  138. for _, record := range records {
  139. var rr mDNS.RR
  140. rr, _, err = mDNS.UnpackRR(record.Data, 0)
  141. if err != nil {
  142. return nil, E.Cause(err, "unpack resource record")
  143. }
  144. response.Answer = append(response.Answer, rr)
  145. }
  146. return response, nil
  147. }
  148. func (t *DBusResolvedResolver) loopUpdateStatus() {
  149. signalChan := make(chan *dbus.Signal, 1)
  150. t.systemBus.Signal(signalChan)
  151. for signal := range signalChan {
  152. var restarted bool
  153. if signal.Name == "org.freedesktop.DBus.NameOwnerChanged" {
  154. if len(signal.Body) != 3 || signal.Body[2].(string) == "" {
  155. continue
  156. } else {
  157. restarted = true
  158. }
  159. }
  160. if restarted {
  161. t.updateStatus()
  162. }
  163. }
  164. }
  165. func (t *DBusResolvedResolver) updateStatus() {
  166. dbusObject, err := t.checkResolved(context.Background())
  167. oldValue := t.resoledObject.Swap(dbusObject)
  168. if err != nil {
  169. var dbusErr dbus.Error
  170. if !errors.As(err, &dbusErr) || dbusErr.Name != "org.freedesktop.DBus.Error.NameHasNoOwnerCould" {
  171. t.logger.Debug(E.Cause(err, "systemd-resolved service unavailable"))
  172. }
  173. if oldValue != nil {
  174. t.logger.Debug("systemd-resolved service is gone")
  175. }
  176. return
  177. } else if oldValue == nil {
  178. t.logger.Debug("using systemd-resolved service as resolver")
  179. }
  180. }
  181. func (t *DBusResolvedResolver) checkResolved(ctx context.Context) (*ResolvedObject, error) {
  182. dbusObject := t.systemBus.Object("org.freedesktop.resolve1", "/org/freedesktop/resolve1")
  183. err := dbusObject.Call("org.freedesktop.DBus.Peer.Ping", 0).Err
  184. if err != nil {
  185. return nil, err
  186. }
  187. defaultInterface := t.interfaceMonitor.DefaultInterface()
  188. if defaultInterface == nil {
  189. return nil, E.New("missing default interface")
  190. }
  191. call := dbusObject.(*dbus.Object).CallWithContext(
  192. ctx,
  193. "org.freedesktop.resolve1.Manager.GetLink",
  194. 0,
  195. int32(defaultInterface.Index),
  196. )
  197. if call.Err != nil {
  198. return nil, call.Err
  199. }
  200. var linkPath dbus.ObjectPath
  201. err = call.Store(&linkPath)
  202. if err != nil {
  203. return nil, err
  204. }
  205. linkObject := t.systemBus.Object("org.freedesktop.resolve1", linkPath)
  206. if linkObject == nil {
  207. return nil, E.New("missing link object for default interface")
  208. }
  209. dnsProp, err := linkObject.GetProperty("org.freedesktop.resolve1.Link.DNS")
  210. if err != nil {
  211. return nil, err
  212. }
  213. var linkDNS []resolved.LinkDNS
  214. err = dnsProp.Store(&linkDNS)
  215. if err != nil {
  216. return nil, err
  217. }
  218. if len(linkDNS) == 0 {
  219. for _, inbound := range service.FromContext[adapter.InboundManager](t.ctx).Inbounds() {
  220. if inbound.Type() == C.TypeTun {
  221. return nil, E.New("No appropriate name servers or networks for name found")
  222. }
  223. }
  224. return nil, E.New("link has no DNS servers configured")
  225. }
  226. return &ResolvedObject{
  227. BusObject: dbusObject,
  228. InterfaceIndex: int32(defaultInterface.Index),
  229. }, nil
  230. }
  231. func (t *DBusResolvedResolver) updateDefaultInterface(defaultInterface *control.Interface, flags int) {
  232. t.updateStatus()
  233. }