1
0
mirror of https://github.com/taigrr/godns synced 2025-01-18 04:03:25 -08:00
godns/resolver/resolver.go
2020-08-10 23:49:12 +08:00

111 lines
2.7 KiB
Go

// Package resolver is a simple dns resolver
// based on miekg/dns
package resolver
import (
"errors"
"math/rand"
"net"
"os"
"strings"
"time"
"github.com/miekg/dns"
)
// DNSResolver represents a dns resolver
type DNSResolver struct {
Servers []string
RetryTimes int
r *rand.Rand
}
// New initializes DnsResolver.
func New(servers []string) *DNSResolver {
for i := range servers {
servers[i] = net.JoinHostPort(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)
if err != nil {
return &DNSResolver{}, err
}
var servers []string
for _, ipAddress := range config.Servers {
servers = append(servers, net.JoinHostPort(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 (r *DNSResolver) LookupHost(host string, dnsType uint16) ([]net.IP, error) {
return r.lookupHost(host, dnsType, r.RetryTimes)
}
func (r *DNSResolver) lookupHost(host string, dnsType uint16, triesLeft int) ([]net.IP, error) {
m1 := new(dns.Msg)
m1.Id = dns.Id()
m1.RecursionDesired = true
m1.Question = make([]dns.Question, 1)
switch dnsType {
case dns.TypeA:
m1.Question[0] = dns.Question{Name: dns.Fqdn(host), Qtype: dns.TypeA, Qclass: dns.ClassINET}
case dns.TypeAAAA:
m1.Question[0] = dns.Question{Name: dns.Fqdn(host), Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}
}
in, err := dns.Exchange(m1, r.Servers[r.r.Intn(len(r.Servers))])
var result []net.IP
if err != nil {
if strings.HasSuffix(err.Error(), "i/o timeout") && triesLeft > 0 {
triesLeft--
return r.lookupHost(host, dnsType, triesLeft)
}
return result, err
}
if in != nil && in.Rcode != dns.RcodeSuccess {
return result, errors.New(dns.RcodeToString[in.Rcode])
}
if dnsType == dns.TypeA {
if len(in.Answer) > 0 {
for _, record := range in.Answer {
if t, ok := record.(*dns.A); ok {
result = append(result, t.A)
}
}
} else {
return result, errors.New("empty result")
}
}
if dnsType == dns.TypeAAAA {
if len(in.Answer) > 0 {
for _, record := range in.Answer {
if t, ok := record.(*dns.AAAA); ok {
result = append(result, t.AAAA)
}
}
} else {
return result, errors.New("Cannot resolve this domain, please make sure the IP type is right")
}
}
return result, err
}