forked from bogdanovich/dns_resolver
/
dns_resolver.go
79 lines (67 loc) · 2 KB
/
dns_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
// Package dns_resolver is a simple dns resolver
// based on miekg/dns
package dns_resolver
import (
"errors"
"math/rand"
"net"
"os"
"strings"
"time"
"github.com/miekg/dns"
)
type DnsResolver struct {
Servers []string
RetryTimes int
r *rand.Rand
}
// New initializes DnsResolver.
func New(servers []string) *DnsResolver {
for i, _ := range servers {
servers[i] += ":53"
}
return &DnsResolver{servers, len(servers) * 2, rand.New(rand.NewSource(time.Now().UnixNano()))}
}
// NewFromResolvConf initializes DnsResolver from resolv.conf like file.
func NewFromResolvConf(path string) (*DnsResolver, error) {
if _, err := os.Stat(path); os.IsNotExist(err) {
return &DnsResolver{}, errors.New("no such file or directory: " + path)
}
config, err := dns.ClientConfigFromFile(path)
servers := []string{}
for _, ipAddress := range config.Servers {
servers = append(servers, ipAddress+":53")
}
return &DnsResolver{servers, len(servers) * 2, rand.New(rand.NewSource(time.Now().UnixNano()))}, err
}
// LookupHost returns IP addresses of provied host.
// In case of timeout retries query RetryTimes times.
func (self *DnsResolver) LookupHost(host string) ([]net.IP, error) {
return self.lookupHost(host, self.RetryTimes)
}
func (self *DnsResolver) lookupHost(host string, triesLeft int) ([]net.IP, error) {
m1 := new(dns.Msg)
m1.Id = dns.Id()
m1.RecursionDesired = true
m1.Question = make([]dns.Question, 1)
m1.Question[0] = dns.Question{dns.Fqdn(host), dns.TypeA, dns.ClassINET}
in, err := dns.Exchange(m1, self.Servers[self.r.Intn(len(self.Servers))])
result := []net.IP{}
if err != nil {
if strings.HasSuffix(err.Error(), "i/o timeout") && triesLeft > 0 {
triesLeft -= 1
return self.lookupHost(host, triesLeft)
} else {
return result, err
}
}
if in != nil && in.Rcode != dns.RcodeSuccess {
return result, errors.New(dns.RcodeToString[in.Rcode])
}
for _, record := range in.Answer {
if t, ok := record.(*dns.A); ok {
result = append(result, t.A)
}
}
return result, err
}