-
Notifications
You must be signed in to change notification settings - Fork 0
/
dns.go
97 lines (82 loc) · 1.96 KB
/
dns.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package dns
import (
"errors"
"strings"
"time"
"github.com/hashicorp/golang-lru"
"github.com/miekg/dns"
)
// SimpleDNSClient uses miekg/dns to look up TXT records, and supports falling
// back to TCP for big records.
//
// Exists because Go's built-in DNS client has problems with some TXT records.
type SimpleDNSClient struct {
Server string
}
// LookupTxt queries the underlying server for TXT records for the given
// hostname.
func (s *SimpleDNSClient) LookupTxt(hostname string) ([]string, error) {
// build the DNS query
m := new(dns.Msg)
m.SetQuestion(hostname, dns.TypeTXT)
// try getting over UDP
c := new(dns.Client)
r, _, e := c.Exchange(m, s.Server)
if e != nil {
return nil, e
}
if r.Truncated {
// try again with TCP for large messages
c.Net = "tcp"
r, _, e = c.Exchange(m, s.Server)
if e != nil {
return nil, e
}
}
// parse TXT answers into strings
var res []string
for _, answer := range r.Answer {
txt, ok := answer.(*dns.TXT)
if !ok {
return nil, errors.New("expected TXT")
}
// concatenate each multi-part answer into a single string
res = append(res, strings.Join(txt.Txt, ""))
}
return res, nil
}
type CachingDNSClient struct {
underlying *SimpleDNSClient
cache *lru.Cache
}
const cacheSize = 8192
const lifetime = 1 * time.Hour
type cacheItem struct {
result []string
expires time.Time
}
func NewCachingDNSClient(server string) *CachingDNSClient {
cache, _ := lru.New(cacheSize)
return &CachingDNSClient{
underlying: &SimpleDNSClient{Server: server},
cache: cache,
}
}
func (c *CachingDNSClient) LookupTxt(hostname string) ([]string, error) {
value, ok := c.cache.Get(hostname)
if ok {
item := value.(*cacheItem)
if time.Now().Before(item.expires) {
return item.result, nil
}
}
result, err := c.underlying.LookupTxt(hostname)
if err != nil {
return nil, err
}
c.cache.Add(hostname, &cacheItem{
result: result,
expires: time.Now().Add(lifetime),
})
return result, nil
}