forked from domainr/dnsr
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcache.go
More file actions
148 lines (134 loc) · 2.94 KB
/
cache.go
File metadata and controls
148 lines (134 loc) · 2.94 KB
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package dnsr
import (
"sync"
"time"
)
type cache struct {
capacity int
expire bool
m sync.RWMutex
entries map[string]entry
}
type entry map[RR]struct{}
const MinCacheCapacity = 1000
// newCache initializes and returns a new cache instance.
// Cache capacity defaults to MinCacheCapacity if <= 0.
func newCache(capacity int, expire bool) *cache {
if capacity <= 0 {
capacity = MinCacheCapacity
}
return &cache{
capacity: capacity,
entries: make(map[string]entry),
expire: expire,
}
}
// add adds 0 or more DNS records to the resolver cache for a specific
// domain name and record type. This ensures the cache entry exists, even
// if empty, for NXDOMAIN responses.
func (c *cache) add(qname string, rr RR) {
c.m.Lock()
defer c.m.Unlock()
c._add(qname, rr)
}
// addNX adds an NXDOMAIN to the cache.
// Safe for concurrent usage.
func (c *cache) addNX(qname string) {
c.m.Lock()
defer c.m.Unlock()
c._addEntry(qname)
}
// deleteNX removes an NXDOMAIN entry from the cache if it exists.
// This is used to remove NXDOMAIN entries from servers that are not authoritative for the queried domain.
// Safe for concurrent usage.
func (c *cache) deleteNX(qname string) {
c.m.Lock()
defer c.m.Unlock()
if e, ok := c.entries[qname]; ok && e == nil {
delete(c.entries, qname)
}
}
// _add does NOT lock the mutex so unsafe for concurrent usage.
func (c *cache) _add(qname string, rr RR) {
e, ok := c.entries[qname]
if !ok {
c._evict()
}
if e == nil {
c.entries[qname] = make(map[RR]struct{})
e = c.entries[qname]
}
e[rr] = struct{}{}
}
// _addEntry adds an entry for qname to c.
// Not safe for concurrent usage.
func (c *cache) _addEntry(qname string) {
_, ok := c.entries[qname]
if !ok {
c._evict()
// For NXDOMAIN responses,
// the cache entry is present, but nil.
c.entries[qname] = nil
}
}
// FIXME: better random cache eviction than Go’s random key guarantee?
// Not safe for concurrent usage.
func (c *cache) _evict() {
if len(c.entries) < c.capacity {
return
}
// First evict expired entries
if c.expire {
now := time.Now()
for k, e := range c.entries {
for rr := range e {
if !rr.Expiry.IsZero() && rr.Expiry.Before(now) {
delete(e, rr)
}
}
if len(e) == 0 {
delete(c.entries, k)
}
if len(c.entries) < c.capacity {
return
}
}
}
// Then randomly evict entries
for k := range c.entries {
delete(c.entries, k)
if len(c.entries) < c.capacity {
return
}
}
}
// get returns a randomly ordered slice of DNS records.
func (c *cache) get(qname string) RRs {
c.m.RLock()
defer c.m.RUnlock()
e, ok := c.entries[qname]
if !ok {
return nil
}
if len(e) == 0 {
return emptyRRs
}
if c.expire {
now := time.Now()
rrs := make(RRs, 0, len(e))
for rr := range e {
if rr.Expiry.IsZero() || rr.Expiry.After(now) {
rrs = append(rrs, rr)
}
}
return rrs
} else {
i := 0
rrs := make(RRs, len(e))
for rr := range e {
rrs[i] = rr
i++
}
return rrs
}
}