router.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. package dns
  2. import (
  3. "context"
  4. "errors"
  5. "net/netip"
  6. "strings"
  7. "time"
  8. "github.com/sagernet/sing-box/adapter"
  9. "github.com/sagernet/sing-box/common/taskmonitor"
  10. C "github.com/sagernet/sing-box/constant"
  11. "github.com/sagernet/sing-box/experimental/libbox/platform"
  12. "github.com/sagernet/sing-box/log"
  13. "github.com/sagernet/sing-box/option"
  14. R "github.com/sagernet/sing-box/route/rule"
  15. "github.com/sagernet/sing-tun"
  16. "github.com/sagernet/sing/common"
  17. E "github.com/sagernet/sing/common/exceptions"
  18. F "github.com/sagernet/sing/common/format"
  19. "github.com/sagernet/sing/common/logger"
  20. M "github.com/sagernet/sing/common/metadata"
  21. "github.com/sagernet/sing/contrab/freelru"
  22. "github.com/sagernet/sing/contrab/maphash"
  23. "github.com/sagernet/sing/service"
  24. mDNS "github.com/miekg/dns"
  25. )
  26. var _ adapter.DNSRouter = (*Router)(nil)
  27. type Router struct {
  28. ctx context.Context
  29. logger logger.ContextLogger
  30. transport adapter.DNSTransportManager
  31. outbound adapter.OutboundManager
  32. client adapter.DNSClient
  33. rules []adapter.DNSRule
  34. defaultDomainStrategy C.DomainStrategy
  35. dnsReverseMapping freelru.Cache[netip.Addr, string]
  36. platformInterface platform.Interface
  37. }
  38. func NewRouter(ctx context.Context, logFactory log.Factory, options option.DNSOptions) *Router {
  39. router := &Router{
  40. ctx: ctx,
  41. logger: logFactory.NewLogger("dns"),
  42. transport: service.FromContext[adapter.DNSTransportManager](ctx),
  43. outbound: service.FromContext[adapter.OutboundManager](ctx),
  44. rules: make([]adapter.DNSRule, 0, len(options.Rules)),
  45. defaultDomainStrategy: C.DomainStrategy(options.Strategy),
  46. }
  47. router.client = NewClient(ClientOptions{
  48. DisableCache: options.DNSClientOptions.DisableCache,
  49. DisableExpire: options.DNSClientOptions.DisableExpire,
  50. IndependentCache: options.DNSClientOptions.IndependentCache,
  51. CacheCapacity: options.DNSClientOptions.CacheCapacity,
  52. ClientSubnet: options.DNSClientOptions.ClientSubnet.Build(netip.Prefix{}),
  53. RDRC: func() adapter.RDRCStore {
  54. cacheFile := service.FromContext[adapter.CacheFile](ctx)
  55. if cacheFile == nil {
  56. return nil
  57. }
  58. if !cacheFile.StoreRDRC() {
  59. return nil
  60. }
  61. return cacheFile
  62. },
  63. Logger: router.logger,
  64. })
  65. if options.ReverseMapping {
  66. router.dnsReverseMapping = common.Must1(freelru.NewSharded[netip.Addr, string](1024, maphash.NewHasher[netip.Addr]().Hash32))
  67. }
  68. return router
  69. }
  70. func (r *Router) Initialize(rules []option.DNSRule) error {
  71. for i, ruleOptions := range rules {
  72. dnsRule, err := R.NewDNSRule(r.ctx, r.logger, ruleOptions, true)
  73. if err != nil {
  74. return E.Cause(err, "parse dns rule[", i, "]")
  75. }
  76. r.rules = append(r.rules, dnsRule)
  77. }
  78. return nil
  79. }
  80. func (r *Router) Start(stage adapter.StartStage) error {
  81. monitor := taskmonitor.New(r.logger, C.StartTimeout)
  82. switch stage {
  83. case adapter.StartStateStart:
  84. monitor.Start("initialize DNS client")
  85. r.client.Start()
  86. monitor.Finish()
  87. for i, rule := range r.rules {
  88. monitor.Start("initialize DNS rule[", i, "]")
  89. err := rule.Start()
  90. monitor.Finish()
  91. if err != nil {
  92. return E.Cause(err, "initialize DNS rule[", i, "]")
  93. }
  94. }
  95. }
  96. return nil
  97. }
  98. func (r *Router) Close() error {
  99. monitor := taskmonitor.New(r.logger, C.StopTimeout)
  100. var err error
  101. for i, rule := range r.rules {
  102. monitor.Start("close dns rule[", i, "]")
  103. err = E.Append(err, rule.Close(), func(err error) error {
  104. return E.Cause(err, "close dns rule[", i, "]")
  105. })
  106. monitor.Finish()
  107. }
  108. return err
  109. }
  110. func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, ruleIndex int, isAddressQuery bool, options *adapter.DNSQueryOptions) (adapter.DNSTransport, adapter.DNSRule, int) {
  111. metadata := adapter.ContextFrom(ctx)
  112. if metadata == nil {
  113. panic("no context")
  114. }
  115. var currentRuleIndex int
  116. if ruleIndex != -1 {
  117. currentRuleIndex = ruleIndex + 1
  118. }
  119. for ; currentRuleIndex < len(r.rules); currentRuleIndex++ {
  120. currentRule := r.rules[currentRuleIndex]
  121. if currentRule.WithAddressLimit() && !isAddressQuery {
  122. continue
  123. }
  124. metadata.ResetRuleCache()
  125. if currentRule.Match(metadata) {
  126. displayRuleIndex := currentRuleIndex
  127. if displayRuleIndex != -1 {
  128. displayRuleIndex += displayRuleIndex + 1
  129. }
  130. ruleDescription := currentRule.String()
  131. if ruleDescription != "" {
  132. r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] ", currentRule, " => ", currentRule.Action())
  133. } else {
  134. r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action())
  135. }
  136. switch action := currentRule.Action().(type) {
  137. case *R.RuleActionDNSRoute:
  138. transport, loaded := r.transport.Transport(action.Server)
  139. if !loaded {
  140. r.logger.ErrorContext(ctx, "transport not found: ", action.Server)
  141. continue
  142. }
  143. isFakeIP := transport.Type() == C.DNSTypeFakeIP
  144. if isFakeIP && !allowFakeIP {
  145. continue
  146. }
  147. if action.Strategy != C.DomainStrategyAsIS {
  148. options.Strategy = action.Strategy
  149. }
  150. if action.Timeout > 0 {
  151. options.Timeout = action.Timeout
  152. }
  153. if isFakeIP || action.DisableCache {
  154. options.DisableCache = true
  155. }
  156. if action.RewriteTTL != nil {
  157. options.RewriteTTL = action.RewriteTTL
  158. }
  159. if action.ClientSubnet.IsValid() {
  160. options.ClientSubnet = action.ClientSubnet
  161. }
  162. if legacyTransport, isLegacy := transport.(adapter.LegacyDNSTransport); isLegacy {
  163. if options.Strategy == C.DomainStrategyAsIS {
  164. options.Strategy = legacyTransport.LegacyStrategy()
  165. }
  166. if !options.ClientSubnet.IsValid() {
  167. options.ClientSubnet = legacyTransport.LegacyClientSubnet()
  168. }
  169. }
  170. return transport, currentRule, currentRuleIndex
  171. case *R.RuleActionDNSRouteOptions:
  172. if action.Strategy != C.DomainStrategyAsIS {
  173. options.Strategy = action.Strategy
  174. }
  175. if action.Timeout > 0 {
  176. options.Timeout = action.Timeout
  177. }
  178. if action.DisableCache {
  179. options.DisableCache = true
  180. }
  181. if action.RewriteTTL != nil {
  182. options.RewriteTTL = action.RewriteTTL
  183. }
  184. if action.ClientSubnet.IsValid() {
  185. options.ClientSubnet = action.ClientSubnet
  186. }
  187. case *R.RuleActionReject:
  188. return nil, currentRule, currentRuleIndex
  189. case *R.RuleActionPredefined:
  190. return nil, currentRule, currentRuleIndex
  191. }
  192. }
  193. }
  194. return r.transport.Default(), nil, -1
  195. }
  196. func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg, options adapter.DNSQueryOptions) (*mDNS.Msg, error) {
  197. if len(message.Question) != 1 {
  198. r.logger.WarnContext(ctx, "bad question size: ", len(message.Question))
  199. responseMessage := mDNS.Msg{
  200. MsgHdr: mDNS.MsgHdr{
  201. Id: message.Id,
  202. Response: true,
  203. Rcode: mDNS.RcodeFormatError,
  204. },
  205. Question: message.Question,
  206. }
  207. return &responseMessage, nil
  208. }
  209. r.logger.DebugContext(ctx, "exchange ", FormatQuestion(message.Question[0].String()))
  210. var (
  211. transport adapter.DNSTransport
  212. err error
  213. )
  214. response, cached := r.client.ExchangeCache(ctx, message)
  215. if !cached {
  216. var metadata *adapter.InboundContext
  217. ctx, metadata = adapter.ExtendContext(ctx)
  218. metadata.Destination = M.Socksaddr{}
  219. metadata.QueryType = message.Question[0].Qtype
  220. switch metadata.QueryType {
  221. case mDNS.TypeA:
  222. metadata.IPVersion = 4
  223. case mDNS.TypeAAAA:
  224. metadata.IPVersion = 6
  225. }
  226. metadata.Domain = FqdnToDomain(message.Question[0].Name)
  227. if options.Transport != nil {
  228. transport = options.Transport
  229. if legacyTransport, isLegacy := transport.(adapter.LegacyDNSTransport); isLegacy {
  230. if options.Strategy == C.DomainStrategyAsIS {
  231. options.Strategy = legacyTransport.LegacyStrategy()
  232. }
  233. if !options.ClientSubnet.IsValid() {
  234. options.ClientSubnet = legacyTransport.LegacyClientSubnet()
  235. }
  236. }
  237. if options.Strategy == C.DomainStrategyAsIS {
  238. options.Strategy = r.defaultDomainStrategy
  239. }
  240. response, err = r.client.Exchange(ctx, transport, message, options, nil)
  241. } else {
  242. var (
  243. rule adapter.DNSRule
  244. ruleIndex int
  245. )
  246. ruleIndex = -1
  247. for {
  248. dnsCtx := adapter.OverrideContext(ctx)
  249. dnsOptions := options
  250. transport, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex, isAddressQuery(message), &dnsOptions)
  251. if rule != nil {
  252. switch action := rule.Action().(type) {
  253. case *R.RuleActionReject:
  254. switch action.Method {
  255. case C.RuleActionRejectMethodDefault:
  256. return &mDNS.Msg{
  257. MsgHdr: mDNS.MsgHdr{
  258. Id: message.Id,
  259. Rcode: mDNS.RcodeRefused,
  260. Response: true,
  261. },
  262. Question: []mDNS.Question{message.Question[0]},
  263. }, nil
  264. case C.RuleActionRejectMethodDrop:
  265. return nil, tun.ErrDrop
  266. }
  267. case *R.RuleActionPredefined:
  268. return action.Response(message), nil
  269. }
  270. }
  271. var responseCheck func(responseAddrs []netip.Addr) bool
  272. if rule != nil && rule.WithAddressLimit() {
  273. responseCheck = func(responseAddrs []netip.Addr) bool {
  274. metadata.DestinationAddresses = responseAddrs
  275. return rule.MatchAddressLimit(metadata)
  276. }
  277. }
  278. if dnsOptions.Strategy == C.DomainStrategyAsIS {
  279. dnsOptions.Strategy = r.defaultDomainStrategy
  280. }
  281. response, err = r.client.Exchange(dnsCtx, transport, message, dnsOptions, responseCheck)
  282. var rejected bool
  283. if err != nil {
  284. if errors.Is(err, ErrResponseRejectedCached) {
  285. rejected = true
  286. r.logger.DebugContext(ctx, E.Cause(err, "response rejected for ", FormatQuestion(message.Question[0].String())), " (cached)")
  287. } else if errors.Is(err, ErrResponseRejected) {
  288. rejected = true
  289. r.logger.DebugContext(ctx, E.Cause(err, "response rejected for ", FormatQuestion(message.Question[0].String())))
  290. /*} else if responseCheck!= nil && errors.Is(err, RcodeError(mDNS.RcodeNameError)) {
  291. rejected = true
  292. r.logger.DebugContext(ctx, E.Cause(err, "response rejected for ", FormatQuestion(message.Question[0].String())))
  293. */
  294. } else if len(message.Question) > 0 {
  295. rejected = true
  296. r.logger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", FormatQuestion(message.Question[0].String())))
  297. } else {
  298. r.logger.ErrorContext(ctx, E.Cause(err, "exchange failed for <empty query>"))
  299. }
  300. }
  301. if responseCheck != nil && rejected {
  302. continue
  303. }
  304. break
  305. }
  306. }
  307. }
  308. if err != nil {
  309. return nil, err
  310. }
  311. if r.dnsReverseMapping != nil && len(message.Question) > 0 && response != nil && len(response.Answer) > 0 {
  312. if transport == nil || transport.Type() != C.DNSTypeFakeIP {
  313. for _, answer := range response.Answer {
  314. switch record := answer.(type) {
  315. case *mDNS.A:
  316. r.dnsReverseMapping.AddWithLifetime(M.AddrFromIP(record.A), FqdnToDomain(record.Hdr.Name), time.Duration(record.Hdr.Ttl)*time.Second)
  317. case *mDNS.AAAA:
  318. r.dnsReverseMapping.AddWithLifetime(M.AddrFromIP(record.AAAA), FqdnToDomain(record.Hdr.Name), time.Duration(record.Hdr.Ttl)*time.Second)
  319. }
  320. }
  321. }
  322. }
  323. return response, nil
  324. }
  325. func (r *Router) Lookup(ctx context.Context, domain string, options adapter.DNSQueryOptions) ([]netip.Addr, error) {
  326. var (
  327. responseAddrs []netip.Addr
  328. cached bool
  329. err error
  330. )
  331. printResult := func() {
  332. if err == nil && len(responseAddrs) == 0 {
  333. err = E.New("empty result")
  334. }
  335. if err != nil {
  336. if errors.Is(err, ErrResponseRejectedCached) {
  337. r.logger.DebugContext(ctx, "response rejected for ", domain, " (cached)")
  338. } else if errors.Is(err, ErrResponseRejected) {
  339. r.logger.DebugContext(ctx, "response rejected for ", domain)
  340. } else {
  341. r.logger.ErrorContext(ctx, E.Cause(err, "lookup failed for ", domain))
  342. }
  343. }
  344. if err != nil {
  345. err = E.Cause(err, "lookup ", domain)
  346. }
  347. }
  348. responseAddrs, cached = r.client.LookupCache(domain, options.Strategy)
  349. if cached {
  350. if len(responseAddrs) == 0 {
  351. return nil, E.New("lookup ", domain, ": empty result (cached)")
  352. }
  353. return responseAddrs, nil
  354. }
  355. r.logger.DebugContext(ctx, "lookup domain ", domain)
  356. ctx, metadata := adapter.ExtendContext(ctx)
  357. metadata.Destination = M.Socksaddr{}
  358. metadata.Domain = FqdnToDomain(domain)
  359. if options.Transport != nil {
  360. transport := options.Transport
  361. if legacyTransport, isLegacy := transport.(adapter.LegacyDNSTransport); isLegacy {
  362. if options.Strategy == C.DomainStrategyAsIS {
  363. options.Strategy = r.defaultDomainStrategy
  364. }
  365. if !options.ClientSubnet.IsValid() {
  366. options.ClientSubnet = legacyTransport.LegacyClientSubnet()
  367. }
  368. }
  369. if options.Strategy == C.DomainStrategyAsIS {
  370. options.Strategy = r.defaultDomainStrategy
  371. }
  372. responseAddrs, err = r.client.Lookup(ctx, transport, domain, options, nil)
  373. } else {
  374. var (
  375. transport adapter.DNSTransport
  376. rule adapter.DNSRule
  377. ruleIndex int
  378. )
  379. ruleIndex = -1
  380. for {
  381. dnsCtx := adapter.OverrideContext(ctx)
  382. dnsOptions := options
  383. transport, rule, ruleIndex = r.matchDNS(ctx, false, ruleIndex, true, &dnsOptions)
  384. if rule != nil {
  385. switch action := rule.Action().(type) {
  386. case *R.RuleActionReject:
  387. switch action.Method {
  388. case C.RuleActionRejectMethodDefault:
  389. return nil, nil
  390. case C.RuleActionRejectMethodDrop:
  391. return nil, tun.ErrDrop
  392. }
  393. case *R.RuleActionPredefined:
  394. if action.Rcode != mDNS.RcodeSuccess {
  395. err = RcodeError(action.Rcode)
  396. } else {
  397. for _, answer := range action.Answer {
  398. switch record := answer.(type) {
  399. case *mDNS.A:
  400. responseAddrs = append(responseAddrs, M.AddrFromIP(record.A))
  401. case *mDNS.AAAA:
  402. responseAddrs = append(responseAddrs, M.AddrFromIP(record.AAAA))
  403. }
  404. }
  405. }
  406. goto response
  407. }
  408. }
  409. var responseCheck func(responseAddrs []netip.Addr) bool
  410. if rule != nil && rule.WithAddressLimit() {
  411. responseCheck = func(responseAddrs []netip.Addr) bool {
  412. metadata.DestinationAddresses = responseAddrs
  413. return rule.MatchAddressLimit(metadata)
  414. }
  415. }
  416. if dnsOptions.Strategy == C.DomainStrategyAsIS {
  417. dnsOptions.Strategy = r.defaultDomainStrategy
  418. }
  419. responseAddrs, err = r.client.Lookup(dnsCtx, transport, domain, dnsOptions, responseCheck)
  420. if responseCheck == nil || err == nil {
  421. break
  422. }
  423. printResult()
  424. }
  425. }
  426. response:
  427. printResult()
  428. if len(responseAddrs) > 0 {
  429. r.logger.InfoContext(ctx, "lookup succeed for ", domain, ": ", strings.Join(F.MapToString(responseAddrs), " "))
  430. }
  431. return responseAddrs, err
  432. }
  433. func isAddressQuery(message *mDNS.Msg) bool {
  434. for _, question := range message.Question {
  435. if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA || question.Qtype == mDNS.TypeHTTPS {
  436. return true
  437. }
  438. }
  439. return false
  440. }
  441. func (r *Router) ClearCache() {
  442. r.client.ClearCache()
  443. if r.platformInterface != nil {
  444. r.platformInterface.ClearDNSCache()
  445. }
  446. }
  447. func (r *Router) LookupReverseMapping(ip netip.Addr) (string, bool) {
  448. if r.dnsReverseMapping == nil {
  449. return "", false
  450. }
  451. domain, loaded := r.dnsReverseMapping.Get(ip)
  452. return domain, loaded
  453. }
  454. func (r *Router) ResetNetwork() {
  455. r.ClearCache()
  456. for _, transport := range r.transport.Transports() {
  457. transport.Close()
  458. }
  459. }