-
Notifications
You must be signed in to change notification settings - Fork 14
/
resolver.go
255 lines (213 loc) · 7.12 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
// Package doh implements client operations for DoH (DNS over HTTPS) lookups.
package doh
import "net/http"
// Resolver handles lookups.
type Resolver struct {
// The host to send DoH requests to.
Host string
// The DNS class to lookup with, must be one of IN, CS, CH, HS or ANYCLASS.
// As a hint, the most used class nowadays is IN (Internet).
Class DNSClass
// HttpClient is a http.Client used to connect to DoH server
HTTPClient *http.Client
}
// lookup encodes a DNS query, sends it over HTTPS then parses the response.
// Returns an error if something went wrong at the network level, or when
// parsing the response headers.
func (r *Resolver) lookup(fqdn string, t DNSType, c DNSClass) ([]answer, error) {
q := encodeQuery(fqdn, t, c)
res, err := r.exchangeHTTPS(q)
if err != nil {
return nil, err
}
return parseResponse(res)
}
// LookupA performs a DoH lookup on A records for the given FQDN.
// Returns records and TTLs such that ttls[0] is the TTL for recs[0], and so on.
// Returns an error if something went wrong at the network level, or when
// parsing the response headers, or if the resolver's class isn't IN.
func (r *Resolver) LookupA(fqdn string) (recs []*ARecord, ttls []uint32, err error) {
if r.Class != IN && r.Class != ANYCLASS {
err = ErrNotIN
return
}
answers, err := r.lookup(fqdn, A, IN)
if err != nil {
return
}
recs = make([]*ARecord, 0)
ttls = make([]uint32, 0)
for _, a := range answers {
if a.t == A {
recs = append(recs, a.parsed.(*ARecord))
ttls = append(ttls, a.ttl)
}
}
return
}
// LookupAAAA performs a DoH lookup on AAAA records for the given FQDN.
// Returns records and TTLs such that ttls[0] is the TTL for recs[0], and so on.
// Returns an error if something went wrong at the network level, or when
// parsing the response headers, or if the resolver's class isn't IN.
func (r *Resolver) LookupAAAA(fqdn string) (recs []*AAAARecord, ttls []uint32, err error) {
if r.Class != IN && r.Class != ANYCLASS {
err = ErrNotIN
return
}
answers, err := r.lookup(fqdn, AAAA, IN)
if err != nil {
return
}
recs = make([]*AAAARecord, 0)
ttls = make([]uint32, 0)
for _, a := range answers {
if a.t == AAAA {
recs = append(recs, a.parsed.(*AAAARecord))
ttls = append(ttls, a.ttl)
}
}
return
}
// LookupCNAME performs a DoH lookup on CNAME records for the given FQDN.
// Returns records and TTLs such that ttls[0] is the TTL for recs[0], and so on.
// Returns an error if something went wrong at the network level, or when
// parsing the response headers.
func (r *Resolver) LookupCNAME(fqdn string) (recs []*CNAMERecord, ttls []uint32, err error) {
answers, err := r.lookup(fqdn, CNAME, IN)
if err != nil {
return
}
recs = make([]*CNAMERecord, 0)
ttls = make([]uint32, 0)
for _, a := range answers {
if a.t == CNAME {
recs = append(recs, a.parsed.(*CNAMERecord))
ttls = append(ttls, a.ttl)
}
}
return
}
// LookupMX performs a DoH lookup on CNAME records for the given FQDN.
// Returns records and TTLs such that ttls[0] is the TTL for recs[0], and so on.
// Returns an error if something went wrong at the network level, or when
// parsing the response headers.
func (r *Resolver) LookupMX(fqdn string) (recs []*MXRecord, ttls []uint32, err error) {
answers, err := r.lookup(fqdn, MX, IN)
if err != nil {
return
}
recs = make([]*MXRecord, 0)
ttls = make([]uint32, 0)
for _, a := range answers {
if a.t == MX {
recs = append(recs, a.parsed.(*MXRecord))
ttls = append(ttls, a.ttl)
}
}
return
}
// LookupNS performs a DoH lookup on CNAME records for the given FQDN.
// Returns records and TTLs such that ttls[0] is the TTL for recs[0], and so on.
// Returns an error if something went wrong at the network level, or when
// parsing the response headers.
func (r *Resolver) LookupNS(fqdn string) (recs []*NSRecord, ttls []uint32, err error) {
answers, err := r.lookup(fqdn, NS, IN)
if err != nil {
return
}
recs = make([]*NSRecord, 0)
ttls = make([]uint32, 0)
for _, a := range answers {
if a.t == NS {
recs = append(recs, a.parsed.(*NSRecord))
ttls = append(ttls, a.ttl)
}
}
return
}
// LookupTXT performs a DoH lookup on TXT records for the given FQDN.
// Returns records and TTLs such that ttls[0] is the TTL for recs[0], and so on.
// Returns an error if something went wrong at the network level, or when
// parsing the response headers.
func (r *Resolver) LookupTXT(fqdn string) (recs []*TXTRecord, ttls []uint32, err error) {
answers, err := r.lookup(fqdn, TXT, IN)
if err != nil {
return
}
recs = make([]*TXTRecord, 0)
ttls = make([]uint32, 0)
for _, a := range answers {
if a.t == TXT {
recs = append(recs, a.parsed.(*TXTRecord))
ttls = append(ttls, a.ttl)
}
}
return
}
// LookupSRV performs a DoH lookup on SRV records for the given FQDN.
// Returns records and TTLs such that ttls[0] is the TTL for recs[0], and so on.
// Returns an error if something went wrong at the network level, or when
// parsing the response headers.
func (r *Resolver) LookupSRV(fqdn string) (recs []*SRVRecord, ttls []uint32, err error) {
answers, err := r.lookup(fqdn, SRV, IN)
if err != nil {
return
}
recs = make([]*SRVRecord, 0)
ttls = make([]uint32, 0)
for _, a := range answers {
if a.t == SRV {
recs = append(recs, a.parsed.(*SRVRecord))
ttls = append(ttls, a.ttl)
}
}
return
}
// LookupService performs a DoH lookup on SRV records for the given service,
// network and domain. network's value is expected to be in the likes of "udp",
// "tcp" and so on. Under the hood, it builds a FQDN of the form
// _service._network.domain and calls r.LookupSRV with it.
// Returns records and TTLs such that ttls[0] is the TTL for recs[0], and so on.
// Returns an error if something went wrong at the network level, or when
// parsing the response headers.
func (r *Resolver) LookupService(service, network, domain string) (recs []*SRVRecord, ttls []uint32, err error) {
return r.LookupSRV("_" + service + "._" + network + "." + domain)
}
// LookupSOA performs a DoH lookup on SOA records for the given FQDN.
// Returns records and TTLs such that ttls[0] is the TTL for recs[0], and so on.
// Returns an error if something went wrong at the network level, or when
// parsing the response headers.
func (r *Resolver) LookupSOA(fqdn string) (recs []*SOARecord, ttls []uint32, err error) {
answers, err := r.lookup(fqdn, SOA, IN)
if err != nil {
return
}
recs = make([]*SOARecord, 0)
ttls = make([]uint32, 0)
for _, a := range answers {
if a.t == SOA {
recs = append(recs, a.parsed.(*SOARecord))
ttls = append(ttls, a.ttl)
}
}
return
}
// LookupPTR performs a DoH lookup on PTR records for the given FQDN.
// Returns records and TTLs such that ttls[0] is the TTL for recs[0], and so on.
// Returns an error if something went wrong at the network level, or when
// parsing the response headers.
func (r *Resolver) LookupPTR(fqdn string) (recs []*PTRRecord, ttls []uint32, err error) {
answers, err := r.lookup(fqdn, PTR, IN)
if err != nil {
return
}
recs = make([]*PTRRecord, 0)
ttls = make([]uint32, 0)
for _, a := range answers {
if a.t == PTR {
recs = append(recs, a.parsed.(*PTRRecord))
ttls = append(ttls, a.ttl)
}
}
return
}