-
Notifications
You must be signed in to change notification settings - Fork 1
/
query.go
133 lines (111 loc) · 2.7 KB
/
query.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
package main
import (
"net"
"strconv"
"strings"
"time"
"github.com/miekg/dns"
)
//
// QueryOptions - query options
//
type QueryOptions struct {
rdflag bool
adflag bool
cdflag bool
timeout time.Duration
retries int
tcp bool
}
//
// AddressString - compose address string for net functions
//
func AddressString(addr string, port int) string {
if !strings.Contains(addr, ":") {
return addr + ":" + strconv.Itoa(port)
}
return "[" + addr + "]" + ":" + strconv.Itoa(port)
}
//
// GetResolver - obtains system resolver addresses
//
func GetResolver(conffile string) (resolvers []net.IP, err error) {
if conffile == "" {
conffile = "/etc/resolv.conf"
}
config, err := dns.ClientConfigFromFile(conffile)
if err != nil {
return nil, err
}
for _, s := range config.Servers {
ip := net.ParseIP(s)
resolvers = append(resolvers, ip)
}
return resolvers, err
}
//
// MakeQuery - construct a DNS query MakeMessage
//
func MakeQuery(qname string, qtype uint16, qopts QueryOptions) *dns.Msg {
m := new(dns.Msg)
m.Id = dns.Id()
m.RecursionDesired = qopts.rdflag
m.AuthenticatedData = qopts.adflag
m.CheckingDisabled = qopts.cdflag
m.Question = make([]dns.Question, 1)
m.Question[0] = dns.Question{Name: qname, Qtype: qtype, Qclass: dns.ClassINET}
return m
}
//
// SendQueryUDP - send DNS query via UDP
//
func SendQueryUDP(query *dns.Msg, ipaddrs []net.IP, qopts QueryOptions) (response *dns.Msg, err error) {
var retries = qopts.retries
c := new(dns.Client)
c.Net = "udp"
c.Timeout = qopts.timeout
for retries > 0 {
for _, ipaddr := range ipaddrs {
destination := AddressString(ipaddr.String(), 53)
response, _, err = c.Exchange(query, destination)
if err == nil {
return response, err
}
if nerr, ok := err.(net.Error); ok && !nerr.Timeout() {
break
}
}
retries--
}
return response, err
}
//
// SendQueryTCP - send DNS query via TCP
//
func SendQueryTCP(query *dns.Msg, ipaddrs []net.IP, qopts QueryOptions) (response *dns.Msg, err error) {
c := new(dns.Client)
c.Net = "tcp"
c.Timeout = qopts.timeout
for _, ipaddr := range ipaddrs {
destination := AddressString(ipaddr.String(), 53)
response, _, err = c.Exchange(query, destination)
if err == nil {
return response, err
}
}
return response, err
}
//
// SendQuery - send DNS query via UDP with fallback to TCP upon truncation
//
func SendQuery(qname string, qtype uint16, ipaddrs []net.IP, qopts QueryOptions) (*dns.Msg, error) {
query := MakeQuery(qname, qtype, qopts)
if qopts.tcp {
return SendQueryTCP(query, ipaddrs, qopts)
}
response, err := SendQueryUDP(query, ipaddrs, qopts)
if err == nil && response.MsgHdr.Truncated {
return SendQueryTCP(query, ipaddrs, qopts)
}
return response, err
}