-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathwatches.go
209 lines (172 loc) · 4 KB
/
watches.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
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
package dnsproxy
import (
"sync"
"time"
"github.com/miekg/dns"
)
const (
// tempWatchCleanInterval clean interval for temporary watches
// in seconds.
tempWatchCleanInterval = 10
)
// tempWatch is information about a temporary watch domain.
type tempWatch struct {
ttl uint32
updated bool
}
// Watches contains a list of domains to watch for A and AAAA updates.
type Watches struct {
sync.RWMutex
m map[string]bool
// temporary CNAMEs
c map[string]*tempWatch
// temporary DNAMEs
d map[string]*tempWatch
done chan struct{}
closed chan struct{}
}
// Add adds domain to the watch list.
func (w *Watches) Add(domain string) {
w.Lock()
defer w.Unlock()
w.m[domain] = true
}
// AddTempCNAME adds a temporary CNAME domain to the watch list with a ttl.
func (w *Watches) AddTempCNAME(domain string, ttl uint32) {
w.Lock()
defer w.Unlock()
w.c[domain] = &tempWatch{
ttl: ttl,
updated: true,
}
}
// AddTempDNAME adds a temporary DNAME domain to the watch list with a ttl.
func (w *Watches) AddTempDNAME(domain string, ttl uint32) {
w.Lock()
defer w.Unlock()
w.d[domain] = &tempWatch{
ttl: ttl,
updated: true,
}
}
// Remove removes domain from the watch list.
func (w *Watches) Remove(domain string) {
w.Lock()
defer w.Unlock()
delete(w.m, domain)
delete(w.c, domain)
delete(w.d, domain)
}
// cleanTemp removes expired temporary entries from the watch list and reduces
// the ttl of all other entries by interval seconds; this is meant to be called
// once every interval seconds to actually implement cleaning of the cache.
func (w *Watches) cleanTemp(interval uint32) {
w.Lock()
defer w.Unlock()
for _, temps := range []map[string]*tempWatch{w.c, w.d} {
for d, t := range temps {
if t.updated {
// mark new entries as old
t.updated = false
continue
}
if t.ttl < interval {
// delete expired entry
delete(temps, d)
continue
}
// reduce ttl
t.ttl -= interval
}
}
}
// cleanTempWatches cleans temporary watches.
func (w *Watches) cleanTempWatches() {
defer close(w.closed)
timer := time.NewTimer(tempWatchCleanInterval * time.Second)
for {
select {
case <-timer.C:
// reset timer
timer.Reset(tempWatchCleanInterval * time.Second)
// clean temporary watches
w.cleanTemp(tempWatchCleanInterval)
case <-w.done:
// stop timer
if !timer.Stop() {
<-timer.C
}
return
}
}
}
// Flush removes all entries from the watch list.
func (w *Watches) Flush() {
w.Lock()
defer w.Unlock()
w.m = make(map[string]bool)
w.c = make(map[string]*tempWatch)
w.d = make(map[string]*tempWatch)
}
// Contains returns whether the domain is in the watch list.
func (w *Watches) Contains(domain string) bool {
w.RLock()
defer w.RUnlock()
// only handle domain names
if _, ok := dns.IsDomainName(domain); !ok {
return false
}
// get label indexes and find matching domains
labels := dns.Split(domain)
if labels == nil {
// root domain, not supported in watch list
return false
}
// try finding exact domain name in temporary CNAMEs
if w.c[domain] != nil {
return true
}
// try finding longest matching domain name in watched domains and
// temporary DNAMEs
for _, i := range labels {
d := domain[i:]
if w.m[d] || w.d[d] != nil {
return true
}
}
// did not find anything
return false
}
// List returns the list of watches and temporary watches.
func (w *Watches) List() (watches, tempWatches []string) {
w.RLock()
defer w.RUnlock()
for k := range w.m {
watches = append(watches, k)
}
for k := range w.c {
tempWatches = append(tempWatches, k)
}
for k := range w.d {
tempWatches = append(tempWatches, k)
}
return
}
// Close closes the watches
func (w *Watches) Close() {
close(w.done)
<-w.closed
}
// NewWatches returns a new Watches.
func NewWatches() *Watches {
w := &Watches{
m: make(map[string]bool),
c: make(map[string]*tempWatch),
d: make(map[string]*tempWatch),
done: make(chan struct{}),
closed: make(chan struct{}),
}
// start cleaning goroutine
go w.cleanTempWatches()
return w
}