-
Notifications
You must be signed in to change notification settings - Fork 14
/
response.go
166 lines (145 loc) · 5.05 KB
/
response.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
package doh
import (
"encoding/binary"
)
// answer describes a parsed answer from the response message.
type answer struct {
name string
t DNSType
class DNSClass
ttl uint32
parsed interface{}
}
// parseResponse parses the message the resolver responded with.
// Returns all of the answers included in the message.
// Returns an error if the message isn't a response, if the message includes
// header values that are not currently supported, or if the message includes an
// error code.
func parseResponse(res []byte) ([]answer, error) {
p := new(parser)
p.res = res
if len(res) < DNSMsgHeaderLen {
return nil, ErrCorrupted
}
/*
DNS HEADER
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ID |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QR| Opcode |AA|TC|RD|RA| Z|AD|CD| RCODE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QDCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ANCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| NSCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ARCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*/
// Check QR == 1 (is response)
qr := res[2] >> 7
if qr != 1 {
return nil, ErrNotAResponse
}
// Check Opcode == 0 (is standard query)
// TODO: Support other values.
opcode := res[2] >> 3 & 15
if opcode != 0 {
return nil, ErrNotStandardQuery
}
// Check TC == 0 (not truncated)
// TODO: Support truncated messages.
tc := res[2] >> 1 & 1
if tc != 0 {
return nil, ErrTruncated
}
// Check RCODE == 0 (no error)
rcode := res[3] & 15
if rcode != 0 {
return nil, dnsErrors[rcode]
}
qdcount := binary.BigEndian.Uint16(res[4:6])
ancount := binary.BigEndian.Uint16(res[6:8])
// Get to the very first byte after decoding headers.
buf := res[DNSMsgHeaderLen:]
var i uint16
for i = 0; i < qdcount; i++ {
/*
Parse queries
We only process them in order to reach the answers section.
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| |
/ QNAME /
/ /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QTYPE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QCLASS |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*/
if len(buf) == 0 {
return nil, ErrCorrupted
}
_, offset := p.parseName(buf)
buf = buf[offset+4:]
}
// Now buf should be at the first byte of the first answer.
answers := make([]answer, 0)
for i = 0; i < ancount; i++ {
/*
Parse answers
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| |
/ /
/ NAME /
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| TYPE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| CLASS |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| TTL |
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| RDLENGTH |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
/ RDATA /
/ /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
NAME (or some labels) can be compressed as:
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| 1 1| OFFSET |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*/
if len(buf) == 0 {
return nil, ErrCorrupted
}
name, offset := p.parseName(buf)
t := DNSType(binary.BigEndian.Uint16(buf[offset : offset+2]))
class := DNSClass(binary.BigEndian.Uint16(buf[offset+2 : offset+4]))
ttl := binary.BigEndian.Uint32(buf[offset+4 : offset+8])
rdlength := binary.BigEndian.Uint16(buf[offset+8 : offset+10])
rdata := buf[offset+10 : offset+10+int(rdlength)]
// Set buffer value for next occurrence.
buf = buf[offset+10+int(rdlength):]
// Parse the answer.
parsed := p.parse(t, class, rdata)
answers = append(answers, answer{
name: name,
t: t,
class: class,
ttl: ttl,
parsed: parsed,
})
}
return answers, nil
}