forked from jvns/dns-weekend
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
367 lines (319 loc) · 9.43 KB
/
main.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
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
package main
import (
"encoding/binary"
"errors"
"fmt"
"math/rand"
"net"
"strings"
)
type DNSHeader struct {
ID uint16
Flags uint16
NumbQuestions uint16
NumbAnswers uint16
NumAuthority uint16
NumAdditional uint16
}
type DNSQuestion struct {
QName []byte
QType uint16
QClass uint16
}
type DNSRecord struct {
Name []byte
Type uint16
Class uint16
TTL uint32
Data []byte
}
type DNSPacket struct {
Header DNSHeader
questions []DNSQuestion
Answers []DNSRecord
Authorities []DNSRecord
Additional []DNSRecord
}
func encodeDNSName(name string) []byte {
var encoded []byte
for _, part := range strings.Split(name, ".") {
encoded = append(encoded, byte(len(part)))
encoded = append(encoded, []byte(part)...)
}
encoded = append(encoded, 0)
return encoded
}
var dnsTypeMap = map[string]uint16{
"A": 1,
"NS": 2,
"CNAME": 5,
"SOA": 6,
"PTR": 12,
"MX": 15,
"TXT": 16,
"AAAA": 28,
"SRV": 33,
"ANY": 255,
}
func headerToBytes(header DNSHeader) []byte {
var bytes []byte
temp := make([]byte, 2)
binary.BigEndian.PutUint16(temp, header.ID)
bytes = append(bytes, temp...)
binary.BigEndian.PutUint16(temp, header.Flags)
bytes = append(bytes, temp...)
binary.BigEndian.PutUint16(temp, header.NumbQuestions)
bytes = append(bytes, temp...)
binary.BigEndian.PutUint16(temp, header.NumbAnswers)
bytes = append(bytes, temp...)
binary.BigEndian.PutUint16(temp, header.NumAuthority)
bytes = append(bytes, temp...)
binary.BigEndian.PutUint16(temp, header.NumAdditional)
bytes = append(bytes, temp...)
return bytes
}
func ipToString(ip []byte) string {
return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3])
}
func questionToBytes(question DNSQuestion) []byte {
var bytes []byte
bytes = append(bytes, question.QName...)
temp := make([]byte, 2)
binary.BigEndian.PutUint16(temp, question.QType)
bytes = append(bytes, temp...)
binary.BigEndian.PutUint16(temp, question.QClass)
bytes = append(bytes, temp...)
return bytes
}
func buildQuery(domainName string, recordType string) []byte {
name := encodeDNSName(domainName)
id := rand.Intn(65535)
//recursionDesired := 1 << 8
//header := DNSHeader{uint16(id), uint16(recursionDesired), 1, 0, 0, 0} // Asking Server to resolve domain name with recursion
header := DNSHeader{uint16(id), 0, 1, 0, 0, 0} // Asking Server to resolve domain name without recursion
question := DNSQuestion{name, dnsTypeMap[recordType], 1}
result := append(headerToBytes(header), questionToBytes(question)...)
return result
}
func parseHeader(buffer []byte) DNSHeader {
//fmt.Println("Received for parseHeader: ", buffer)
var header DNSHeader
header.ID = binary.BigEndian.Uint16(buffer[0:2])
header.Flags = binary.BigEndian.Uint16(buffer[2:4])
header.NumbQuestions = binary.BigEndian.Uint16(buffer[4:6])
header.NumbAnswers = binary.BigEndian.Uint16(buffer[6:8])
header.NumAuthority = binary.BigEndian.Uint16(buffer[8:10])
header.NumAdditional = binary.BigEndian.Uint16(buffer[10:12])
return header
}
// Simple DNS Name decoder
func decodeDNSNameSimple(buffer []byte, currentLocation int) ([]byte, int) {
var name []byte
var dnsEndMarker int
i := currentLocation
for i < len(buffer) {
length := int(buffer[i])
if length == 0 {
break
}
name = append(name, buffer[i+1:i+1+length]...)
name = append(name, '.')
i += length + 1
dnsEndMarker = i
}
// dnsEndMarker + 1 to account for the 0x00 at the end of the name, which is not included in the length due to break
return name, dnsEndMarker + 1
}
func decodeQuestionData(buffer []byte, currentLocation int) (uint16, uint16) {
var qType uint16
var qClass uint16
qType = binary.BigEndian.Uint16(buffer[currentLocation : currentLocation+2])
qClass = binary.BigEndian.Uint16(buffer[currentLocation+2 : currentLocation+4])
return qType, qClass
}
func parseQuestion(buffer []byte, currentLocation int) (DNSQuestion, int) {
//fmt.Println("Received for parseQuestion: ", buffer)
var question DNSQuestion
var dnsEndMarker int
question.QName, dnsEndMarker = decodeDNSNameSimple(buffer, currentLocation)
question.QType, question.QClass = decodeQuestionData(buffer, dnsEndMarker)
return question, dnsEndMarker + 4
}
func decodeDNSName(buffer []byte, location int) ([]byte, int) {
var name []byte
var dnsEndMarker int
i := location
for i < len(buffer) {
length := int(buffer[i])
if length == 0 {
break
} else if length >= 192 {
name = append(name, decodeCompressedName(length, buffer, i)...)
i += 2
return name, i
} else {
name = append(name, buffer[i+1:i+1+length]...)
name = append(name, '.')
}
i += length + 1
dnsEndMarker = i
}
return name, dnsEndMarker
}
func decodeCompressedName(length int, buffer []byte, currentPosition int) []byte {
offset := int(byte(length&0x3f) + buffer[currentPosition+1])
result, _ := decodeDNSName(buffer, offset)
return []byte(result)
}
func decodeRecordData(buffer []byte) (uint16, uint16, uint32, int) {
var rType uint16
var rClass uint16
var rTTL uint32
var rDataLength int
rType = binary.BigEndian.Uint16(buffer[0:2])
rClass = binary.BigEndian.Uint16(buffer[2:4])
rTTL = binary.BigEndian.Uint32(buffer[4:8])
rDataLength = int(binary.BigEndian.Uint16(buffer[8:10]))
return rType, rClass, rTTL, rDataLength
}
func parseRecord(buffer []byte, currentLocation int) (DNSRecord, int) {
var record DNSRecord
var dataLength int
var dnsEndMarker int
record.Name, dnsEndMarker = decodeDNSName(buffer, currentLocation)
record.Type, record.Class, record.TTL, dataLength = decodeRecordData(buffer[dnsEndMarker : dnsEndMarker+10])
if record.Type == dnsTypeMap["NS"] {
record.Data, _ = decodeDNSName(buffer, dnsEndMarker+10)
} else {
record.Data = buffer[dnsEndMarker+10 : dnsEndMarker+10+dataLength]
}
return record, dnsEndMarker + 10 + dataLength
}
func parseDNSPacket(buffer []byte) DNSPacket {
currentLocation := 12 // Skip the header as header size is fixed to 12 bytes
var questions []DNSQuestion
var answers []DNSRecord
var authorities []DNSRecord
var additionals []DNSRecord
header := parseHeader(buffer[0:12])
for i := 0; i < int(header.NumbQuestions); i++ {
var question DNSQuestion
question, currentLocation = parseQuestion(buffer, currentLocation)
questions = append(questions, question)
//currentLocation += length
}
for i := 0; i < int(header.NumbAnswers); i++ {
answer, length := parseRecord(buffer, currentLocation)
answers = append(answers, answer)
currentLocation += length + 1
}
for i := 0; i < int(header.NumAuthority); i++ {
var authority DNSRecord
authority, currentLocation = parseRecord(buffer, currentLocation)
authorities = append(authorities, authority)
//currentLocation += length
}
for i := 0; i < int(header.NumAdditional); i++ {
var additional DNSRecord
additional, currentLocation = parseRecord(buffer, currentLocation)
additionals = append(additionals, additional)
//currentLocation += length
}
return DNSPacket{header, questions, answers, authorities, additionals}
}
// func lookupDomain(domain string) (string, error) {
// query := buildQuery(domain, "A")
// conn, err := net.Dial("udp", "8.8.8.8:53")
// if err != nil {
// fmt.Println(err)
// return "", err
// }
// defer conn.Close()
// _, err = conn.Write(query)
// if err != nil {
// fmt.Println(err)
// return "", err
// }
// buffer := make([]byte, 1024)
// _, err = conn.Read(buffer)
// if err != nil {
// fmt.Println(err)
// return "", err
// }
// dnsPacket := parseDNSPacket(buffer)
// return ipToString(dnsPacket.Answers[0].Data), nil
// }
func sendQuery(ipAddress string, domain string, recordType string) (DNSPacket, error) {
query := buildQuery(domain, recordType)
conn, err := net.Dial("udp", ipAddress+":53")
if err != nil {
fmt.Println(err)
return DNSPacket{}, err
}
defer conn.Close()
_, err = conn.Write(query)
if err != nil {
fmt.Println(err)
return DNSPacket{}, err
}
buffer := make([]byte, 1024)
_, err = conn.Read(buffer)
if err != nil {
fmt.Println(err)
return DNSPacket{}, err
}
dnsPacket := parseDNSPacket(buffer)
return dnsPacket, nil
}
func getAnswer(dnsPacket DNSPacket) string {
for i := range dnsPacket.Answers {
if dnsPacket.Answers[i].Type == dnsTypeMap["A"] {
return ipToString(dnsPacket.Answers[i].Data)
}
}
return ""
}
func getNameServerIP(dnsPacket DNSPacket) string {
for i := range dnsPacket.Additional {
if dnsPacket.Additional[i].Type == dnsTypeMap["A"] {
return ipToString(dnsPacket.Additional[i].Data)
}
}
return ""
}
func getNameServer(dnsPacket DNSPacket) string {
for i := range dnsPacket.Authorities {
if dnsPacket.Authorities[i].Type == dnsTypeMap["NS"] {
return string(dnsPacket.Authorities[i].Data)
}
}
return ""
}
func resolve(domainName string, recordType string) (string, error) {
nameServer := "198.41.0.4"
for {
fmt.Printf("Querying %s for %s\n", nameServer, domainName)
dnsPacket, err := sendQuery(nameServer, domainName, recordType)
if err != nil {
fmt.Println(err)
return "", err
}
if ip := getAnswer(dnsPacket); ip != "" {
return ip, nil
} else if nameServerIP := getNameServerIP(dnsPacket); nameServerIP != "" {
nameServer = nameServerIP
} else if nameServerDomain := getNameServer(dnsPacket); nameServerDomain != "" {
nameServerDomain = strings.TrimSuffix(nameServerDomain, ".")
nameServer, _ = resolve(nameServerDomain, "A")
} else {
return "", errors.New("no answer found")
}
}
}
func main() {
result, err := resolve("twitter.com", "A")
if err != nil {
fmt.Println(err)
}
fmt.Println(result)
}