5
5
"fmt"
6
6
"net/netip"
7
7
"strings"
8
- "unsafe"
9
8
)
10
9
11
10
const headerSizeDNS = 12
@@ -29,7 +28,6 @@ func (d *DNSMessage) String() string {
29
28
- Answer RRs: %d
30
29
- Authority RRs: %d
31
30
- Additional RRs: %d
32
- - Payload: %d bytes
33
31
%s
34
32
` ,
35
33
d .TransactionID ,
@@ -39,7 +37,6 @@ func (d *DNSMessage) String() string {
39
37
d .AnswerRRs ,
40
38
d .AuthorityRRs ,
41
39
d .AdditionalRRs ,
42
- len (d .payload ),
43
40
d .rrecords (),
44
41
)
45
42
}
@@ -64,9 +61,9 @@ func (d *DNSMessage) NextLayer() (string, []byte) {
64
61
}
65
62
66
63
func (d * DNSMessage ) flags () string {
67
- // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
68
64
var flags string
69
65
opcode := (d .Flags >> 11 ) & 15
66
+ // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-5
70
67
var opcodes string
71
68
switch opcode {
72
69
case 0 :
@@ -88,7 +85,7 @@ func (d *DNSMessage) flags() string {
88
85
rd := (d .Flags >> 8 ) & 1
89
86
z := (d .Flags >> 6 ) & 1
90
87
na := (d .Flags >> 4 ) & 1
91
- qr := ( d .Flags >> 15 ) & 1
88
+ qr := d .Flags >> 15
92
89
var qrs string
93
90
switch qr {
94
91
case 0 :
@@ -105,6 +102,7 @@ func (d *DNSMessage) flags() string {
105
102
ra := (d .Flags >> 7 ) & 1
106
103
aa := (d .Flags >> 5 ) & 1
107
104
rcode := d .Flags & 15
105
+ // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6
108
106
var rcodes string
109
107
switch rcode {
110
108
case 0 :
@@ -126,7 +124,27 @@ func (d *DNSMessage) flags() string {
126
124
case 8 :
127
125
rcodes = "Server not authoritative for the zone"
128
126
case 9 :
129
- rcodes = "Name not in zone"
127
+ rcodes = "Server Not Authoritative for zone"
128
+ case 10 :
129
+ rcodes = "Name not contained in zone"
130
+ case 11 :
131
+ rcodes = "DSO-TYPE Not Implemented"
132
+ case 16 :
133
+ rcodes = "Bad OPT Version/TSIG Signature Failure"
134
+ case 17 :
135
+ rcodes = "Key not recognizede"
136
+ case 18 :
137
+ rcodes = "Signature out of time window"
138
+ case 19 :
139
+ rcodes = "Bad TKEY Mode"
140
+ case 20 :
141
+ rcodes = "Duplicate key name"
142
+ case 21 :
143
+ rcodes = "Algorithm not supported"
144
+ case 22 :
145
+ rcodes = "Bad Truncation"
146
+ case 23 :
147
+ rcodes = "Bad/missing Server Cookie"
130
148
default :
131
149
rcodes = "Unknown"
132
150
}
@@ -144,17 +162,36 @@ func (d *DNSMessage) flags() string {
144
162
return flags
145
163
}
146
164
147
- func bytesToStr (myBytes []byte ) string {
148
- return unsafe .String (unsafe .SliceData (myBytes ), len (myBytes ))
165
+ // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2
166
+ func (d * DNSMessage ) className (cls uint16 ) string {
167
+ var cname string
168
+ switch cls {
169
+ case 0 :
170
+ cname = "Reserved"
171
+ case 1 :
172
+ cname = "IN"
173
+ case 3 :
174
+ cname = "CH"
175
+ case 4 :
176
+ cname = "HS"
177
+ default :
178
+ cname = "Unknown"
179
+ }
180
+ return cname
149
181
}
150
182
183
+ // extractDomain extracts the DNS domain name from the given byte slice.
184
+ //
185
+ // The domain name is parsed according to RFC 1035 section 4.1.
151
186
func (d * DNSMessage ) extractDomain (tail []byte ) (string , []byte ) {
187
+ // see https://brunoscheufler.com/blog/2024-05-12-building-a-dns-message-parser#domain-names
152
188
var domainName string
153
189
for {
154
190
blen := tail [0 ]
155
191
if blen >> 6 == 0b11 {
192
+ // compressed message offset is 14 bits according to RFC 1035 section 4.1.4
156
193
offset := binary .BigEndian .Uint16 (tail [0 :2 ])& (1 << 14 - 1 ) - headerSizeDNS
157
- part , _ := d .extractDomain (d .payload [offset :])
194
+ part , _ := d .extractDomain (d .payload [offset :]) // TODO: iterative approach
158
195
domainName += part
159
196
tail = tail [2 :]
160
197
break
@@ -171,24 +208,27 @@ func (d *DNSMessage) extractDomain(tail []byte) (string, []byte) {
171
208
return strings .TrimRight (domainName , "." ), tail
172
209
}
173
210
211
+ // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4
174
212
func (d * DNSMessage ) parseRData (typ uint16 , tail []byte , rdl int ) (string , string , []byte ) {
175
- var rdata string
176
- var typname string
213
+ var (
214
+ rdata string
215
+ typename string
216
+ )
177
217
switch typ {
178
218
case 1 :
179
- typname = "A"
219
+ typename = "A"
180
220
addr , _ := netip .AddrFromSlice (tail [0 :rdl ])
181
221
rdata = fmt .Sprintf ("Address: %s" , addr )
182
222
case 2 :
183
- typname = "NS"
223
+ typename = "NS"
184
224
domain , _ := d .extractDomain (tail )
185
- rdata = fmt .Sprintf ("%s: %s" , typname , domain )
225
+ rdata = fmt .Sprintf ("%s: %s" , typename , domain )
186
226
case 5 :
187
- typname = "CNAME"
227
+ typename = "CNAME"
188
228
domain , _ := d .extractDomain (tail )
189
- rdata = fmt .Sprintf ("%s: %s" , typname , domain )
229
+ rdata = fmt .Sprintf ("%s: %s" , typename , domain )
190
230
case 6 :
191
- typname = "SOA"
231
+ typename = "SOA"
192
232
var (
193
233
primary string
194
234
mailbox string
@@ -207,47 +247,52 @@ func (d *DNSMessage) parseRData(typ uint16, tail []byte, rdl int) (string, strin
207
247
- Refresh interval: %d
208
248
- Retry interval: %d
209
249
- Expire limit: %d
210
- - Minimum TTL: %d
211
- ` , primary , mailbox , serial , refresh , retry , expire , min )
250
+ - Minimum TTL: %d` ,
251
+ primary , mailbox , serial , refresh , retry , expire , min )
212
252
case 15 :
213
- typname = "MX"
253
+ typename = "MX"
214
254
preference := binary .BigEndian .Uint16 (tail [0 :2 ])
215
255
domain , _ := d .extractDomain (tail [2 :rdl ])
216
- rdata = fmt .Sprintf ("%s: preference %d %s" , typname , preference , domain )
256
+ rdata = fmt .Sprintf ("%s: preference %d %s" , typename , preference , domain )
217
257
case 16 :
218
- typname = "TXT"
219
- rdata = fmt .Sprintf ("%s: %s" , typname , tail [:rdl ])
258
+ typename = "TXT"
259
+ rdata = fmt .Sprintf ("%s: %s" , typename , tail [:rdl ])
220
260
case 28 :
221
- typname = "AAAA"
261
+ typename = "AAAA"
222
262
addr , _ := netip .AddrFromSlice (tail [0 :rdl ])
223
263
rdata = fmt .Sprintf ("Address: %s" , addr )
224
264
case 41 :
225
- typname = "OPT"
265
+ typename = "OPT"
266
+ case 65 :
267
+ typename = "HTTPS" // TODO: add proper parsing
268
+ rdata = fmt .Sprintf ("%s: %d bytes" , typename , rdl )
226
269
default :
227
270
rdata = fmt .Sprintf ("Unknown: %d bytes" , rdl )
228
271
}
229
- return typname , rdata , tail [rdl :]
272
+ return typename , rdata , tail [rdl :]
230
273
}
231
274
232
275
func (d * DNSMessage ) parseQuery (tail []byte ) (string , []byte ) {
233
276
var domain string
234
277
domain , tail = d .extractDomain (tail )
235
278
typ := binary .BigEndian .Uint16 (tail [0 :2 ])
279
+ typename , _ , _ := d .parseRData (typ , tail , 0 )
236
280
class := binary .BigEndian .Uint16 (tail [2 :4 ])
281
+ cname := d .className (class )
237
282
tail = tail [4 :]
238
- // TODO: add type and class description https://en.wikipedia.org/wiki/List_of_DNS_record_types
239
283
return fmt .Sprintf (` - %s:
240
284
- Name: %s
241
- - Type: (%d)
242
- - Class: %d
243
- ` , domain , domain , typ , class ), tail
285
+ - Type: %s (%d)
286
+ - Class: %s (%d)
287
+ ` , domain , domain , typename , typ , cname , class ), tail
244
288
}
245
289
246
290
func (d * DNSMessage ) parseRR (tail []byte ) (string , []byte ) {
247
291
var domain string
248
292
domain , tail = d .extractDomain (tail )
249
293
typ := binary .BigEndian .Uint16 (tail [0 :2 ])
250
294
class := binary .BigEndian .Uint16 (tail [2 :4 ])
295
+ cname := d .className (class )
251
296
ttl := binary .BigEndian .Uint32 (tail [4 :8 ])
252
297
rdl := int (binary .BigEndian .Uint16 (tail [8 :10 ]))
253
298
var (
@@ -258,11 +303,11 @@ func (d *DNSMessage) parseRR(tail []byte) (string, []byte) {
258
303
return fmt .Sprintf (` - %s:
259
304
- Name: %s
260
305
- Type: %s (%d)
261
- - Class: %d
306
+ - Class: %s (%d)
262
307
- TTL: %d
263
308
- Data Length: %d
264
309
- %s
265
- ` , domain , domain , typename , typ , class , ttl , rdl , rdata ), tail
310
+ ` , domain , domain , typename , typ , cname , class , ttl , rdl , rdata ), tail
266
311
}
267
312
268
313
func (d * DNSMessage ) parseRoot (tail []byte ) (string , []byte ) {
0 commit comments