1
0

client.go 18 KB

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