dns.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. // Copyright © 2021-2022 Ettore Di Giacinto <[email protected]>
  2. //
  3. // This program is free software; you can redistribute it and/or modify
  4. // it under the terms of the GNU General Public License as published by
  5. // the Free Software Foundation; either version 2 of the License, or
  6. // (at your option) any later version.
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License along
  14. // with this program; if not, see <http://www.gnu.org/licenses/>.
  15. package services
  16. import (
  17. "context"
  18. "fmt"
  19. "regexp"
  20. "time"
  21. lru "github.com/hashicorp/golang-lru"
  22. "github.com/ipfs/go-log"
  23. "github.com/miekg/dns"
  24. "github.com/mudler/edgevpn/pkg/blockchain"
  25. "github.com/mudler/edgevpn/pkg/node"
  26. "github.com/mudler/edgevpn/pkg/protocol"
  27. "github.com/mudler/edgevpn/pkg/types"
  28. "github.com/pkg/errors"
  29. )
  30. func DNSNetworkService(ll log.StandardLogger, listenAddr string, forwarder bool, forward []string, cacheSize int) node.NetworkService {
  31. return func(ctx context.Context, c node.Config, n *node.Node, b *blockchain.Ledger) error {
  32. server := &dns.Server{Addr: listenAddr, Net: "udp"}
  33. cache, err := lru.New(cacheSize)
  34. if err != nil {
  35. return err
  36. }
  37. go func() {
  38. dns.HandleFunc(".", dnsHandler{ctx, b, forwarder, forward, cache, ll}.handleDNSRequest())
  39. fmt.Println(server.ListenAndServe())
  40. }()
  41. go func() {
  42. <-ctx.Done()
  43. server.Shutdown()
  44. }()
  45. return nil
  46. }
  47. }
  48. // DNS returns a network service binding a dns blockchain resolver on listenAddr.
  49. // Takes an associated name for the addresses in the blockchain
  50. func DNS(ll log.StandardLogger, listenAddr string, forwarder bool, forward []string, cacheSize int) []node.Option {
  51. return []node.Option{
  52. node.WithNetworkService(DNSNetworkService(ll, listenAddr, forwarder, forward, cacheSize)),
  53. }
  54. }
  55. // PersistDNSRecord is syntatic sugar around the ledger
  56. // It persists a DNS record to the blockchain until it sees it reconciled.
  57. // It automatically stop announcing and it is not *guaranteed* to persist data.
  58. func PersistDNSRecord(ctx context.Context, b *blockchain.Ledger, announcetime, timeout time.Duration, regex string, record types.DNS) {
  59. b.Persist(ctx, announcetime, timeout, protocol.DNSKey, regex, record)
  60. }
  61. // AnnounceDNSRecord is syntatic sugar around the ledger
  62. // Announces a DNS record binding to the blockchain, and keeps announcing for the ctx lifecycle
  63. func AnnounceDNSRecord(ctx context.Context, b *blockchain.Ledger, announcetime time.Duration, regex string, record types.DNS) {
  64. b.AnnounceUpdate(ctx, announcetime, protocol.DNSKey, regex, record)
  65. }
  66. type dnsHandler struct {
  67. ctx context.Context
  68. b *blockchain.Ledger
  69. forwarder bool
  70. forward []string
  71. cache *lru.Cache
  72. ll log.StandardLogger
  73. }
  74. func (d dnsHandler) parseQuery(m *dns.Msg, forward bool) *dns.Msg {
  75. response := m.Copy()
  76. d.ll.Debug("Received DNS request", m)
  77. if len(m.Question) > 0 {
  78. q := m.Question[0]
  79. // Resolve the entry to an IP from the blockchain data
  80. for k, v := range d.b.CurrentData()[protocol.DNSKey] {
  81. r, err := regexp.Compile(k)
  82. if err == nil && r.MatchString(q.Name) {
  83. var res types.DNS
  84. v.Unmarshal(&res)
  85. if val, exists := res[dns.Type(q.Qtype)]; exists {
  86. rr, err := dns.NewRR(fmt.Sprintf("%s %s %s", q.Name, dns.TypeToString[q.Qtype], val))
  87. if err == nil {
  88. response.Answer = append(m.Answer, rr)
  89. d.ll.Debug("Response from blockchain", response)
  90. return response
  91. }
  92. }
  93. }
  94. }
  95. if forward {
  96. d.ll.Debug("Forwarding DNS request", m)
  97. r, err := d.forwardQuery(m)
  98. if err == nil {
  99. response.Answer = r.Answer
  100. }
  101. d.ll.Debug("Response from fw server", r)
  102. }
  103. }
  104. return response
  105. }
  106. func (d dnsHandler) handleDNSRequest() func(w dns.ResponseWriter, r *dns.Msg) {
  107. return func(w dns.ResponseWriter, r *dns.Msg) {
  108. var resp *dns.Msg
  109. switch r.Opcode {
  110. case dns.OpcodeQuery:
  111. resp = d.parseQuery(r, d.forwarder)
  112. }
  113. resp.SetReply(r)
  114. resp.Compress = false
  115. w.WriteMsg(resp)
  116. }
  117. }
  118. func (d dnsHandler) forwardQuery(dnsMessage *dns.Msg) (*dns.Msg, error) {
  119. reqCopy := dnsMessage.Copy()
  120. if len(reqCopy.Question) > 0 {
  121. if v, ok := d.cache.Get(reqCopy.Question[0].String()); ok {
  122. q := v.(*dns.Msg)
  123. q.Id = reqCopy.Id
  124. return q, nil
  125. }
  126. }
  127. for _, server := range d.forward {
  128. r, err := QueryDNS(d.ctx, reqCopy, server)
  129. if r != nil && len(r.Answer) == 0 && !r.MsgHdr.Truncated {
  130. continue
  131. }
  132. if err != nil {
  133. continue
  134. }
  135. if r.Rcode == dns.RcodeSuccess {
  136. d.cache.Add(reqCopy.Question[0].String(), r)
  137. }
  138. if r.Rcode == dns.RcodeNameError || r.Rcode == dns.RcodeSuccess || err == nil {
  139. return r, err
  140. }
  141. }
  142. return nil, errors.New("not available")
  143. }
  144. // QueryDNS queries a dns server with a dns message and return the answer
  145. // it is blocking.
  146. func QueryDNS(ctx context.Context, msg *dns.Msg, dnsServer string) (*dns.Msg, error) {
  147. client := &dns.Client{
  148. Net: "udp",
  149. Timeout: 30 * time.Second,
  150. SingleInflight: true}
  151. r, _, err := client.Exchange(msg, dnsServer)
  152. return r, err
  153. }