-
Notifications
You must be signed in to change notification settings - Fork 0
/
resolver.go
269 lines (223 loc) · 5.9 KB
/
resolver.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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
package resolver
import (
"fmt"
"io"
"net"
"sort"
"sync"
"time"
logApi "github.com/ndmsystems/go/api/log"
)
const (
V4key = "ipv4"
V6key = "ipv6"
)
// Resolver ...
type Resolver struct {
mu sync.RWMutex
// tag - the string to identify this instance of resolver among the others
tag string
// hosts - a map with maintained hosts
hosts map[string]*host
// dnsClient - a network client that can use a list of nameservers to lookup hosts and retrieve its ip addresses with ttl
dnsClient *dnsClient
// logger - a logger which used in this package
logger logApi.Logger
// stopCh ...
stopCh chan struct{}
}
// New returns ResolverService instance
func New(tag string, logger logApi.Logger) *Resolver {
r := &Resolver{
tag: tag,
hosts: make(map[string]*host),
dnsClient: newDnsClient(logger),
logger: logger,
stopCh: make(chan struct{}),
}
go r.oldHostsDeleteLoop()
return r
}
// WithNameservers - sets nameservers to resolve hosts
func (r *Resolver) WithNameservers(nameServers ...string) *Resolver {
r.dnsClient.setNameServers(nameServers)
return r
}
// AddHost adds a host to maintaining
func (r *Resolver) AddHost(hostName string) {
r.mu.RLock()
_, ok := r.hosts[hostName]
r.mu.RUnlock()
if ok {
return
}
r.mu.Lock()
defer r.mu.Unlock()
if _, ok := r.hosts[hostName]; !ok {
r.hosts[hostName] = newHost(r.tag, hostName, true, r.dnsClient, r.logger)
}
}
// DelHost deletes a host with name hostName from maintaining
func (r *Resolver) DelHost(hostName string) {
r.delHosts([]string{hostName})
}
// Stop - stops maintaining for all hosts
func (r *Resolver) Stop() {
close(r.stopCh)
}
// GetNextIP returns next IPv4 for host with name hostName
func (r *Resolver) GetNextIP(hostName string) string {
ip, _ := r.GetNextIPWithIdx(hostName)
return ip
}
// GetNextIPWithIdx returns next IPv4 and index for host with name hostName
func (r *Resolver) GetNextIPWithIdx(hostName string) (string, int) {
r.mu.RLock()
h, ok := r.hosts[hostName]
r.mu.RUnlock()
if ok {
ip, idx := h.getNextIP4WithIndex()
return ipStrIdx(ip, idx)
}
r.mu.Lock()
if h, ok = r.hosts[hostName]; !ok {
h = newHost(r.tag, hostName, false, r.dnsClient, r.logger)
r.hosts[hostName] = h
}
r.mu.Unlock()
ip, idx := h.getNextIP4WithIndex()
return ipStrIdx(ip, idx)
}
// GetNextIP6 returns next IPv6 for host with name hostName
func (r *Resolver) GetNextIP6(hostName string) string {
ip, _ := r.GetNextIP6WithIdx(hostName)
return ip
}
// GetNextIP6WithIdx returns next IPv6 and index for host with name hostName
func (r *Resolver) GetNextIP6WithIdx(hostName string) (string, int) {
r.mu.RLock()
h, ok := r.hosts[hostName]
r.mu.RUnlock()
if ok {
ip, idx := h.getNextIP6WithIndex()
return ipStrIdx(ip, idx)
}
r.mu.Lock()
if h, ok = r.hosts[hostName]; !ok {
h = newHost(r.tag, hostName, false, r.dnsClient, r.logger)
r.hosts[hostName] = h
}
r.mu.Unlock()
ip, idx := h.getNextIP6WithIndex()
return ipStrIdx(ip, idx)
}
// GetIPs returns a list of IPv4 and IPv6
func (r *Resolver) GetIPs(hostName string) ([]net.IP, []net.IP) {
r.mu.RLock()
h := r.hosts[hostName]
r.mu.RUnlock()
if h == nil {
return nil, nil
}
return h.getIPs()
}
// GetIPsStr returns a string list of IPv4 and IPv6
func (r *Resolver) GetIPsStr(hostName string) ([]string, []string) {
ip4, ip6 := r.GetIPs(hostName)
var ip4Str, ip6Str []string
for _, ip := range ip4 {
ip4Str = append(ip4Str, ip.String())
}
for _, ip := range ip6 {
ip6Str = append(ip6Str, ip.String())
}
return ip4Str, ip6Str
}
// LookupSRV makes LookupSRV request to one of nameserver passed to WithNameservers
func (r *Resolver) LookupSRV(service, proto, name string) (string, []*net.SRV, error) {
return r.dnsClient.lookupSRV(service, proto, name)
}
// Dump dumps into writer all hosts with theirs ips
func (r *Resolver) Dump(w io.Writer) {
r.DumpPrefix(w, "")
}
// DumpPrefix dumps with prefix into writer all hosts with theirs ips
func (r *Resolver) DumpPrefix(w io.Writer, prefix string) {
r.mu.RLock()
defer r.mu.RUnlock()
hosts := make([]string, 0, len(r.hosts))
for host := range r.hosts {
hosts = append(hosts, host)
}
sort.Strings(hosts)
for _, hostName := range hosts {
ip4, ip6 := r.GetIPsStr(hostName)
sort.Strings(ip4)
sort.Strings(ip6)
for idx, ip := range ip4 {
fmt.Fprintf(w, "%sresolver.v4.%s.%d: %s\n", prefix, hostName, idx, ip)
}
for idx, ip := range ip6 {
fmt.Fprintf(w, "%sresolver.v6.%s.%d: %s\n", prefix, hostName, idx, ip)
}
}
}
// oldHostsDeleteLoop runs a loop that deletes old hosts that were added non-explicitly
func (r *Resolver) oldHostsDeleteLoop() {
ticker := time.NewTicker(time.Minute)
defer ticker.Stop()
for {
select {
case <-r.stopCh:
r.logger.Info().Println(r.tag, "Stop resolving hosts")
r.emptyHosts()
return
case <-ticker.C:
hostsToDel := make([]string, 0)
r.mu.RLock()
for hostName := range r.hosts {
if r.hosts[hostName].isOld() && !r.hosts[hostName].isExplicitlyAdded() && !r.hosts[hostName].static {
hostsToDel = append(hostsToDel, hostName)
}
}
r.mu.RUnlock()
if len(hostsToDel) > 0 {
r.delHosts(hostsToDel)
r.logger.Info().Println(r.tag, "Deleted old hosts:", hostsToDel)
}
}
}
}
// delHosts deletes hosts from maintaining
func (r *Resolver) delHosts(hosts []string) {
r.mu.Lock()
defer r.mu.Unlock()
for _, h := range hosts {
delete(r.hosts, h)
}
}
// emptyHosts deletes all hosts from maintaining
func (r *Resolver) emptyHosts() {
r.mu.Lock()
defer r.mu.Unlock()
for hostName := range r.hosts {
r.hosts[hostName].stop()
}
r.hosts = make(map[string]*host)
}
func ipStrIdx(ip net.IP, idx int) (string, int) {
if ip == nil {
return "", -1
}
return ip.String(), idx
}
func (r *Resolver) UpdateHostsFromMaping(mapping map[string]map[string][]string) {
for k, v := range mapping {
r.mu.Lock()
if _, ok := r.hosts[k]; ok {
delete(r.hosts, k)
}
r.hosts[k] = newStaticHost(r.tag, k, true, v, r.logger)
r.mu.Unlock()
}
}