-
Notifications
You must be signed in to change notification settings - Fork 12
/
dns_cache.go
68 lines (58 loc) · 1.66 KB
/
dns_cache.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
package main
import (
"context"
"net"
"time"
"github.com/rs/dnscache"
)
// How often should we check for successful updates to cached entries
const dnsCacheRefreshInterval = 5 * time.Minute
// Local DNS cache because in this world things are ephemeral
type cachedDNS struct {
resolver *dnscache.Resolver
refresher *time.Ticker
}
func newCachedDNS(refreshInterval time.Duration) *cachedDNS {
cache := &cachedDNS{
resolver: &dnscache.Resolver{},
refresher: time.NewTicker(refreshInterval),
}
// Configure DNS cache to not remove stale records to protect gateway from
// catastrophic failures like https://github.com/ipfs/bifrost-gateway/issues/34
options := dnscache.ResolverRefreshOptions{}
options.ClearUnused = false
options.PersistOnFailure = true
// Every refreshInterval we check for updates, but if there is
// none, or if domain disappears, we keep the last cached version
go func(cdns *cachedDNS) {
defer cdns.refresher.Stop()
for range cdns.refresher.C {
cdns.resolver.RefreshWithOptions(options)
}
}(cache)
return cache
}
// dialWithCachedDNS implements DialContext that uses cachedDNS
func (cdns *cachedDNS) dialWithCachedDNS(ctx context.Context, network string, addr string) (conn net.Conn, err error) {
host, port, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
}
ips, err := cdns.resolver.LookupHost(ctx, host)
if err != nil {
return nil, err
}
// Try all IPs returned by DNS
for _, ip := range ips {
var dialer net.Dialer
conn, err = dialer.DialContext(ctx, network, net.JoinHostPort(ip, port))
if err == nil {
break
}
}
return
}
func (cdns *cachedDNS) Close() error {
cdns.refresher.Stop()
return nil
}