client.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. package dns
  2. import (
  3. "context"
  4. "net"
  5. "net/netip"
  6. "strings"
  7. "time"
  8. "github.com/sagernet/sing-box/adapter"
  9. C "github.com/sagernet/sing-box/constant"
  10. "github.com/sagernet/sing/common"
  11. E "github.com/sagernet/sing/common/exceptions"
  12. "github.com/sagernet/sing/common/logger"
  13. M "github.com/sagernet/sing/common/metadata"
  14. "github.com/sagernet/sing/common/task"
  15. "github.com/sagernet/sing/contrab/freelru"
  16. "github.com/sagernet/sing/contrab/maphash"
  17. dns "github.com/miekg/dns"
  18. )
  19. var (
  20. ErrNoRawSupport = E.New("no raw query support by current transport")
  21. ErrNotCached = E.New("not cached")
  22. ErrResponseRejected = E.New("response rejected")
  23. ErrResponseRejectedCached = E.Extend(ErrResponseRejected, "cached")
  24. )
  25. var _ adapter.DNSClient = (*Client)(nil)
  26. type Client struct {
  27. disableCache bool
  28. disableExpire bool
  29. independentCache bool
  30. clientSubnet netip.Prefix
  31. rdrc adapter.RDRCStore
  32. initRDRCFunc func() adapter.RDRCStore
  33. logger logger.ContextLogger
  34. cache freelru.Cache[dns.Question, *dns.Msg]
  35. transportCache freelru.Cache[transportCacheKey, *dns.Msg]
  36. }
  37. type ClientOptions struct {
  38. DisableCache bool
  39. DisableExpire bool
  40. IndependentCache bool
  41. CacheCapacity uint32
  42. ClientSubnet netip.Prefix
  43. RDRC func() adapter.RDRCStore
  44. Logger logger.ContextLogger
  45. }
  46. func NewClient(options ClientOptions) *Client {
  47. client := &Client{
  48. disableCache: options.DisableCache,
  49. disableExpire: options.DisableExpire,
  50. independentCache: options.IndependentCache,
  51. clientSubnet: options.ClientSubnet,
  52. initRDRCFunc: options.RDRC,
  53. logger: options.Logger,
  54. }
  55. cacheCapacity := options.CacheCapacity
  56. if cacheCapacity < 1024 {
  57. cacheCapacity = 1024
  58. }
  59. if !client.disableCache {
  60. if !client.independentCache {
  61. client.cache = common.Must1(freelru.NewSharded[dns.Question, *dns.Msg](cacheCapacity, maphash.NewHasher[dns.Question]().Hash32))
  62. } else {
  63. client.transportCache = common.Must1(freelru.NewSharded[transportCacheKey, *dns.Msg](cacheCapacity, maphash.NewHasher[transportCacheKey]().Hash32))
  64. }
  65. }
  66. return client
  67. }
  68. type transportCacheKey struct {
  69. dns.Question
  70. transportTag string
  71. }
  72. func (c *Client) Start() {
  73. if c.initRDRCFunc != nil {
  74. c.rdrc = c.initRDRCFunc()
  75. }
  76. }
  77. func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, message *dns.Msg, options adapter.DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) (*dns.Msg, error) {
  78. if len(message.Question) == 0 {
  79. if c.logger != nil {
  80. c.logger.WarnContext(ctx, "bad question size: ", len(message.Question))
  81. }
  82. responseMessage := dns.Msg{
  83. MsgHdr: dns.MsgHdr{
  84. Id: message.Id,
  85. Response: true,
  86. Rcode: dns.RcodeFormatError,
  87. },
  88. Question: message.Question,
  89. }
  90. return &responseMessage, nil
  91. }
  92. question := message.Question[0]
  93. clientSubnet := options.ClientSubnet
  94. if !clientSubnet.IsValid() {
  95. clientSubnet = c.clientSubnet
  96. }
  97. if clientSubnet.IsValid() {
  98. message = SetClientSubnet(message, clientSubnet)
  99. }
  100. isSimpleRequest := len(message.Question) == 1 &&
  101. len(message.Ns) == 0 &&
  102. len(message.Extra) == 0 &&
  103. !options.ClientSubnet.IsValid()
  104. disableCache := !isSimpleRequest || c.disableCache || options.DisableCache
  105. if !disableCache {
  106. response, ttl := c.loadResponse(question, transport)
  107. if response != nil {
  108. logCachedResponse(c.logger, ctx, response, ttl)
  109. response.Id = message.Id
  110. return response, nil
  111. }
  112. }
  113. if question.Qtype == dns.TypeA && options.Strategy == C.DomainStrategyIPv6Only || question.Qtype == dns.TypeAAAA && options.Strategy == C.DomainStrategyIPv4Only {
  114. responseMessage := dns.Msg{
  115. MsgHdr: dns.MsgHdr{
  116. Id: message.Id,
  117. Response: true,
  118. Rcode: dns.RcodeSuccess,
  119. },
  120. Question: []dns.Question{question},
  121. }
  122. if c.logger != nil {
  123. c.logger.DebugContext(ctx, "strategy rejected")
  124. }
  125. return &responseMessage, nil
  126. }
  127. messageId := message.Id
  128. contextTransport, clientSubnetLoaded := transportTagFromContext(ctx)
  129. if clientSubnetLoaded && transport.Tag() == contextTransport {
  130. return nil, E.New("DNS query loopback in transport[", contextTransport, "]")
  131. }
  132. ctx = contextWithTransportTag(ctx, transport.Tag())
  133. if responseChecker != nil && c.rdrc != nil {
  134. rejected := c.rdrc.LoadRDRC(transport.Tag(), question.Name, question.Qtype)
  135. if rejected {
  136. return nil, ErrResponseRejectedCached
  137. }
  138. }
  139. timeout := options.Timeout
  140. if timeout == 0 {
  141. if transport.HasDetour() {
  142. timeout = C.DNSTimeout
  143. } else {
  144. timeout = C.DirectDNSTimeout
  145. }
  146. }
  147. ctx, cancel := context.WithTimeout(ctx, timeout)
  148. response, err := transport.Exchange(ctx, message)
  149. cancel()
  150. if err != nil {
  151. return nil, err
  152. }
  153. /*if question.Qtype == dns.TypeA || question.Qtype == dns.TypeAAAA {
  154. validResponse := response
  155. loop:
  156. for {
  157. var (
  158. addresses int
  159. queryCNAME string
  160. )
  161. for _, rawRR := range validResponse.Answer {
  162. switch rr := rawRR.(type) {
  163. case *dns.A:
  164. break loop
  165. case *dns.AAAA:
  166. break loop
  167. case *dns.CNAME:
  168. queryCNAME = rr.Target
  169. }
  170. }
  171. if queryCNAME == "" {
  172. break
  173. }
  174. exMessage := *message
  175. exMessage.Question = []dns.Question{{
  176. Name: queryCNAME,
  177. Qtype: question.Qtype,
  178. }}
  179. validResponse, err = c.Exchange(ctx, transport, &exMessage, options, responseChecker)
  180. if err != nil {
  181. return nil, err
  182. }
  183. }
  184. if validResponse != response {
  185. response.Answer = append(response.Answer, validResponse.Answer...)
  186. }
  187. }*/
  188. if responseChecker != nil {
  189. addr, addrErr := MessageToAddresses(response)
  190. if addrErr != nil || !responseChecker(addr) {
  191. if c.rdrc != nil {
  192. c.rdrc.SaveRDRCAsync(transport.Tag(), question.Name, question.Qtype, c.logger)
  193. }
  194. logRejectedResponse(c.logger, ctx, response)
  195. return response, ErrResponseRejected
  196. }
  197. }
  198. if question.Qtype == dns.TypeHTTPS {
  199. if options.Strategy == C.DomainStrategyIPv4Only || options.Strategy == C.DomainStrategyIPv6Only {
  200. for _, rr := range response.Answer {
  201. https, isHTTPS := rr.(*dns.HTTPS)
  202. if !isHTTPS {
  203. continue
  204. }
  205. content := https.SVCB
  206. content.Value = common.Filter(content.Value, func(it dns.SVCBKeyValue) bool {
  207. if options.Strategy == C.DomainStrategyIPv4Only {
  208. return it.Key() != dns.SVCB_IPV6HINT
  209. } else {
  210. return it.Key() != dns.SVCB_IPV4HINT
  211. }
  212. })
  213. https.SVCB = content
  214. }
  215. }
  216. }
  217. var timeToLive uint32
  218. for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} {
  219. for _, record := range recordList {
  220. if timeToLive == 0 || record.Header().Ttl > 0 && record.Header().Ttl < timeToLive {
  221. timeToLive = record.Header().Ttl
  222. }
  223. }
  224. }
  225. if options.RewriteTTL != nil {
  226. timeToLive = *options.RewriteTTL
  227. }
  228. for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} {
  229. for _, record := range recordList {
  230. record.Header().Ttl = timeToLive
  231. }
  232. }
  233. if !disableCache {
  234. c.storeCache(transport, question, response, timeToLive)
  235. }
  236. response.Id = messageId
  237. requestEDNSOpt := message.IsEdns0()
  238. responseEDNSOpt := response.IsEdns0()
  239. if responseEDNSOpt != nil && (requestEDNSOpt == nil || requestEDNSOpt.Version() < responseEDNSOpt.Version()) {
  240. response.Extra = common.Filter(response.Extra, func(it dns.RR) bool {
  241. return it.Header().Rrtype != dns.TypeOPT
  242. })
  243. if requestEDNSOpt != nil {
  244. response.SetEdns0(responseEDNSOpt.UDPSize(), responseEDNSOpt.Do())
  245. }
  246. }
  247. logExchangedResponse(c.logger, ctx, response, timeToLive)
  248. return response, err
  249. }
  250. func (c *Client) Lookup(ctx context.Context, transport adapter.DNSTransport, domain string, options adapter.DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) ([]netip.Addr, error) {
  251. domain = FqdnToDomain(domain)
  252. dnsName := dns.Fqdn(domain)
  253. var strategy C.DomainStrategy
  254. if options.LookupStrategy != C.DomainStrategyAsIS {
  255. strategy = options.LookupStrategy
  256. } else {
  257. strategy = options.Strategy
  258. }
  259. if strategy == C.DomainStrategyIPv4Only {
  260. return c.lookupToExchange(ctx, transport, dnsName, dns.TypeA, options, responseChecker)
  261. } else if strategy == C.DomainStrategyIPv6Only {
  262. return c.lookupToExchange(ctx, transport, dnsName, dns.TypeAAAA, options, responseChecker)
  263. }
  264. var response4 []netip.Addr
  265. var response6 []netip.Addr
  266. var group task.Group
  267. group.Append("exchange4", func(ctx context.Context) error {
  268. response, err := c.lookupToExchange(ctx, transport, dnsName, dns.TypeA, options, responseChecker)
  269. if err != nil {
  270. return err
  271. }
  272. response4 = response
  273. return nil
  274. })
  275. group.Append("exchange6", func(ctx context.Context) error {
  276. response, err := c.lookupToExchange(ctx, transport, dnsName, dns.TypeAAAA, options, responseChecker)
  277. if err != nil {
  278. return err
  279. }
  280. response6 = response
  281. return nil
  282. })
  283. err := group.Run(ctx)
  284. if len(response4) == 0 && len(response6) == 0 {
  285. return nil, err
  286. }
  287. return sortAddresses(response4, response6, strategy), nil
  288. }
  289. func (c *Client) ClearCache() {
  290. if c.cache != nil {
  291. c.cache.Purge()
  292. }
  293. if c.transportCache != nil {
  294. c.transportCache.Purge()
  295. }
  296. }
  297. func (c *Client) LookupCache(domain string, strategy C.DomainStrategy) ([]netip.Addr, bool) {
  298. if c.disableCache || c.independentCache {
  299. return nil, false
  300. }
  301. if dns.IsFqdn(domain) {
  302. domain = domain[:len(domain)-1]
  303. }
  304. dnsName := dns.Fqdn(domain)
  305. if strategy == C.DomainStrategyIPv4Only {
  306. response, err := c.questionCache(dns.Question{
  307. Name: dnsName,
  308. Qtype: dns.TypeA,
  309. Qclass: dns.ClassINET,
  310. }, nil)
  311. if err != ErrNotCached {
  312. return response, true
  313. }
  314. } else if strategy == C.DomainStrategyIPv6Only {
  315. response, err := c.questionCache(dns.Question{
  316. Name: dnsName,
  317. Qtype: dns.TypeAAAA,
  318. Qclass: dns.ClassINET,
  319. }, nil)
  320. if err != ErrNotCached {
  321. return response, true
  322. }
  323. } else {
  324. response4, _ := c.questionCache(dns.Question{
  325. Name: dnsName,
  326. Qtype: dns.TypeA,
  327. Qclass: dns.ClassINET,
  328. }, nil)
  329. response6, _ := c.questionCache(dns.Question{
  330. Name: dnsName,
  331. Qtype: dns.TypeAAAA,
  332. Qclass: dns.ClassINET,
  333. }, nil)
  334. if len(response4) > 0 || len(response6) > 0 {
  335. return sortAddresses(response4, response6, strategy), true
  336. }
  337. }
  338. return nil, false
  339. }
  340. func (c *Client) ExchangeCache(ctx context.Context, message *dns.Msg) (*dns.Msg, bool) {
  341. if c.disableCache || c.independentCache || len(message.Question) != 1 {
  342. return nil, false
  343. }
  344. question := message.Question[0]
  345. response, ttl := c.loadResponse(question, nil)
  346. if response == nil {
  347. return nil, false
  348. }
  349. logCachedResponse(c.logger, ctx, response, ttl)
  350. response.Id = message.Id
  351. return response, true
  352. }
  353. func sortAddresses(response4 []netip.Addr, response6 []netip.Addr, strategy C.DomainStrategy) []netip.Addr {
  354. if strategy == C.DomainStrategyPreferIPv6 {
  355. return append(response6, response4...)
  356. } else {
  357. return append(response4, response6...)
  358. }
  359. }
  360. func (c *Client) storeCache(transport adapter.DNSTransport, question dns.Question, message *dns.Msg, timeToLive uint32) {
  361. if timeToLive == 0 {
  362. return
  363. }
  364. if c.disableExpire {
  365. if !c.independentCache {
  366. c.cache.Add(question, message)
  367. } else {
  368. c.transportCache.Add(transportCacheKey{
  369. Question: question,
  370. transportTag: transport.Tag(),
  371. }, message)
  372. }
  373. return
  374. }
  375. if !c.independentCache {
  376. c.cache.AddWithLifetime(question, message, time.Second*time.Duration(timeToLive))
  377. } else {
  378. c.transportCache.AddWithLifetime(transportCacheKey{
  379. Question: question,
  380. transportTag: transport.Tag(),
  381. }, message, time.Second*time.Duration(timeToLive))
  382. }
  383. }
  384. func (c *Client) lookupToExchange(ctx context.Context, transport adapter.DNSTransport, name string, qType uint16, options adapter.DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) ([]netip.Addr, error) {
  385. question := dns.Question{
  386. Name: name,
  387. Qtype: qType,
  388. Qclass: dns.ClassINET,
  389. }
  390. disableCache := c.disableCache || options.DisableCache
  391. if !disableCache {
  392. cachedAddresses, err := c.questionCache(question, transport)
  393. if err != ErrNotCached {
  394. return cachedAddresses, err
  395. }
  396. }
  397. message := dns.Msg{
  398. MsgHdr: dns.MsgHdr{
  399. RecursionDesired: true,
  400. },
  401. Question: []dns.Question{question},
  402. }
  403. response, err := c.Exchange(ctx, transport, &message, options, responseChecker)
  404. if err != nil {
  405. return nil, err
  406. }
  407. return MessageToAddresses(response)
  408. }
  409. func (c *Client) questionCache(question dns.Question, transport adapter.DNSTransport) ([]netip.Addr, error) {
  410. response, _ := c.loadResponse(question, transport)
  411. if response == nil {
  412. return nil, ErrNotCached
  413. }
  414. return MessageToAddresses(response)
  415. }
  416. func (c *Client) loadResponse(question dns.Question, transport adapter.DNSTransport) (*dns.Msg, int) {
  417. var (
  418. response *dns.Msg
  419. loaded bool
  420. )
  421. if c.disableExpire {
  422. if !c.independentCache {
  423. response, loaded = c.cache.Get(question)
  424. } else {
  425. response, loaded = c.transportCache.Get(transportCacheKey{
  426. Question: question,
  427. transportTag: transport.Tag(),
  428. })
  429. }
  430. if !loaded {
  431. return nil, 0
  432. }
  433. return response.Copy(), 0
  434. } else {
  435. var expireAt time.Time
  436. if !c.independentCache {
  437. response, expireAt, loaded = c.cache.GetWithLifetime(question)
  438. } else {
  439. response, expireAt, loaded = c.transportCache.GetWithLifetime(transportCacheKey{
  440. Question: question,
  441. transportTag: transport.Tag(),
  442. })
  443. }
  444. if !loaded {
  445. return nil, 0
  446. }
  447. timeNow := time.Now()
  448. if timeNow.After(expireAt) {
  449. if !c.independentCache {
  450. c.cache.Remove(question)
  451. } else {
  452. c.transportCache.Remove(transportCacheKey{
  453. Question: question,
  454. transportTag: transport.Tag(),
  455. })
  456. }
  457. return nil, 0
  458. }
  459. var originTTL int
  460. for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} {
  461. for _, record := range recordList {
  462. if originTTL == 0 || record.Header().Ttl > 0 && int(record.Header().Ttl) < originTTL {
  463. originTTL = int(record.Header().Ttl)
  464. }
  465. }
  466. }
  467. nowTTL := int(expireAt.Sub(timeNow).Seconds())
  468. if nowTTL < 0 {
  469. nowTTL = 0
  470. }
  471. response = response.Copy()
  472. if originTTL > 0 {
  473. duration := uint32(originTTL - nowTTL)
  474. for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} {
  475. for _, record := range recordList {
  476. record.Header().Ttl = record.Header().Ttl - duration
  477. }
  478. }
  479. } else {
  480. for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} {
  481. for _, record := range recordList {
  482. record.Header().Ttl = uint32(nowTTL)
  483. }
  484. }
  485. }
  486. return response, nowTTL
  487. }
  488. }
  489. func MessageToAddresses(response *dns.Msg) ([]netip.Addr, error) {
  490. if response.Rcode != dns.RcodeSuccess {
  491. return nil, RcodeError(response.Rcode)
  492. }
  493. addresses := make([]netip.Addr, 0, len(response.Answer))
  494. for _, rawAnswer := range response.Answer {
  495. switch answer := rawAnswer.(type) {
  496. case *dns.A:
  497. addresses = append(addresses, M.AddrFromIP(answer.A))
  498. case *dns.AAAA:
  499. addresses = append(addresses, M.AddrFromIP(answer.AAAA))
  500. case *dns.HTTPS:
  501. for _, value := range answer.SVCB.Value {
  502. if value.Key() == dns.SVCB_IPV4HINT || value.Key() == dns.SVCB_IPV6HINT {
  503. addresses = append(addresses, common.Map(strings.Split(value.String(), ","), M.ParseAddr)...)
  504. }
  505. }
  506. }
  507. }
  508. return addresses, nil
  509. }
  510. func wrapError(err error) error {
  511. switch dnsErr := err.(type) {
  512. case *net.DNSError:
  513. if dnsErr.IsNotFound {
  514. return RcodeNameError
  515. }
  516. case *net.AddrError:
  517. return RcodeNameError
  518. }
  519. return err
  520. }
  521. type transportKey struct{}
  522. func contextWithTransportTag(ctx context.Context, transportTag string) context.Context {
  523. return context.WithValue(ctx, transportKey{}, transportTag)
  524. }
  525. func transportTagFromContext(ctx context.Context) (string, bool) {
  526. value, loaded := ctx.Value(transportKey{}).(string)
  527. return value, loaded
  528. }
  529. func FixedResponseStatus(message *dns.Msg, rcode int) *dns.Msg {
  530. return &dns.Msg{
  531. MsgHdr: dns.MsgHdr{
  532. Id: message.Id,
  533. Rcode: rcode,
  534. Response: true,
  535. },
  536. Question: message.Question,
  537. }
  538. }
  539. func FixedResponse(id uint16, question dns.Question, addresses []netip.Addr, timeToLive uint32) *dns.Msg {
  540. response := dns.Msg{
  541. MsgHdr: dns.MsgHdr{
  542. Id: id,
  543. Response: true,
  544. Authoritative: true,
  545. RecursionDesired: true,
  546. RecursionAvailable: true,
  547. Rcode: dns.RcodeSuccess,
  548. },
  549. Question: []dns.Question{question},
  550. }
  551. for _, address := range addresses {
  552. if address.Is4() && question.Qtype == dns.TypeA {
  553. response.Answer = append(response.Answer, &dns.A{
  554. Hdr: dns.RR_Header{
  555. Name: question.Name,
  556. Rrtype: dns.TypeA,
  557. Class: dns.ClassINET,
  558. Ttl: timeToLive,
  559. },
  560. A: address.AsSlice(),
  561. })
  562. } else if address.Is6() && question.Qtype == dns.TypeAAAA {
  563. response.Answer = append(response.Answer, &dns.AAAA{
  564. Hdr: dns.RR_Header{
  565. Name: question.Name,
  566. Rrtype: dns.TypeAAAA,
  567. Class: dns.ClassINET,
  568. Ttl: timeToLive,
  569. },
  570. AAAA: address.AsSlice(),
  571. })
  572. }
  573. }
  574. return &response
  575. }
  576. func FixedResponseCNAME(id uint16, question dns.Question, record string, timeToLive uint32) *dns.Msg {
  577. response := dns.Msg{
  578. MsgHdr: dns.MsgHdr{
  579. Id: id,
  580. Response: true,
  581. Authoritative: true,
  582. RecursionDesired: true,
  583. RecursionAvailable: true,
  584. Rcode: dns.RcodeSuccess,
  585. },
  586. Question: []dns.Question{question},
  587. Answer: []dns.RR{
  588. &dns.CNAME{
  589. Hdr: dns.RR_Header{
  590. Name: question.Name,
  591. Rrtype: dns.TypeCNAME,
  592. Class: dns.ClassINET,
  593. Ttl: timeToLive,
  594. },
  595. Target: record,
  596. },
  597. },
  598. }
  599. return &response
  600. }
  601. func FixedResponseTXT(id uint16, question dns.Question, records []string, timeToLive uint32) *dns.Msg {
  602. response := dns.Msg{
  603. MsgHdr: dns.MsgHdr{
  604. Id: id,
  605. Response: true,
  606. Authoritative: true,
  607. RecursionDesired: true,
  608. RecursionAvailable: true,
  609. Rcode: dns.RcodeSuccess,
  610. },
  611. Question: []dns.Question{question},
  612. Answer: []dns.RR{
  613. &dns.TXT{
  614. Hdr: dns.RR_Header{
  615. Name: question.Name,
  616. Rrtype: dns.TypeA,
  617. Class: dns.ClassINET,
  618. Ttl: timeToLive,
  619. },
  620. Txt: records,
  621. },
  622. },
  623. }
  624. return &response
  625. }
  626. func FixedResponseMX(id uint16, question dns.Question, records []*net.MX, timeToLive uint32) *dns.Msg {
  627. response := dns.Msg{
  628. MsgHdr: dns.MsgHdr{
  629. Id: id,
  630. Response: true,
  631. Authoritative: true,
  632. RecursionDesired: true,
  633. RecursionAvailable: true,
  634. Rcode: dns.RcodeSuccess,
  635. },
  636. Question: []dns.Question{question},
  637. }
  638. for _, record := range records {
  639. response.Answer = append(response.Answer, &dns.MX{
  640. Hdr: dns.RR_Header{
  641. Name: question.Name,
  642. Rrtype: dns.TypeA,
  643. Class: dns.ClassINET,
  644. Ttl: timeToLive,
  645. },
  646. Preference: record.Pref,
  647. Mx: record.Host,
  648. })
  649. }
  650. return &response
  651. }