Skip to content

Commit ad0ffe4

Browse files
author
shadowy-pycoder
committed
Kinda finished DNS part
1 parent c8d6d83 commit ad0ffe4

File tree

2 files changed

+85
-33
lines changed

2 files changed

+85
-33
lines changed

layers/dns.go

Lines changed: 77 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"fmt"
66
"net/netip"
77
"strings"
8-
"unsafe"
98
)
109

1110
const headerSizeDNS = 12
@@ -29,7 +28,6 @@ func (d *DNSMessage) String() string {
2928
- Answer RRs: %d
3029
- Authority RRs: %d
3130
- Additional RRs: %d
32-
- Payload: %d bytes
3331
%s
3432
`,
3533
d.TransactionID,
@@ -39,7 +37,6 @@ func (d *DNSMessage) String() string {
3937
d.AnswerRRs,
4038
d.AuthorityRRs,
4139
d.AdditionalRRs,
42-
len(d.payload),
4340
d.rrecords(),
4441
)
4542
}
@@ -64,9 +61,9 @@ func (d *DNSMessage) NextLayer() (string, []byte) {
6461
}
6562

6663
func (d *DNSMessage) flags() string {
67-
// https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
6864
var flags string
6965
opcode := (d.Flags >> 11) & 15
66+
// https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-5
7067
var opcodes string
7168
switch opcode {
7269
case 0:
@@ -88,7 +85,7 @@ func (d *DNSMessage) flags() string {
8885
rd := (d.Flags >> 8) & 1
8986
z := (d.Flags >> 6) & 1
9087
na := (d.Flags >> 4) & 1
91-
qr := (d.Flags >> 15) & 1
88+
qr := d.Flags >> 15
9289
var qrs string
9390
switch qr {
9491
case 0:
@@ -105,6 +102,7 @@ func (d *DNSMessage) flags() string {
105102
ra := (d.Flags >> 7) & 1
106103
aa := (d.Flags >> 5) & 1
107104
rcode := d.Flags & 15
105+
// https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6
108106
var rcodes string
109107
switch rcode {
110108
case 0:
@@ -126,7 +124,27 @@ func (d *DNSMessage) flags() string {
126124
case 8:
127125
rcodes = "Server not authoritative for the zone"
128126
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"
130148
default:
131149
rcodes = "Unknown"
132150
}
@@ -144,17 +162,36 @@ func (d *DNSMessage) flags() string {
144162
return flags
145163
}
146164

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
149181
}
150182

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.
151186
func (d *DNSMessage) extractDomain(tail []byte) (string, []byte) {
187+
// see https://brunoscheufler.com/blog/2024-05-12-building-a-dns-message-parser#domain-names
152188
var domainName string
153189
for {
154190
blen := tail[0]
155191
if blen>>6 == 0b11 {
192+
// compressed message offset is 14 bits according to RFC 1035 section 4.1.4
156193
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
158195
domainName += part
159196
tail = tail[2:]
160197
break
@@ -171,24 +208,27 @@ func (d *DNSMessage) extractDomain(tail []byte) (string, []byte) {
171208
return strings.TrimRight(domainName, "."), tail
172209
}
173210

211+
// https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4
174212
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+
)
177217
switch typ {
178218
case 1:
179-
typname = "A"
219+
typename = "A"
180220
addr, _ := netip.AddrFromSlice(tail[0:rdl])
181221
rdata = fmt.Sprintf("Address: %s", addr)
182222
case 2:
183-
typname = "NS"
223+
typename = "NS"
184224
domain, _ := d.extractDomain(tail)
185-
rdata = fmt.Sprintf("%s: %s", typname, domain)
225+
rdata = fmt.Sprintf("%s: %s", typename, domain)
186226
case 5:
187-
typname = "CNAME"
227+
typename = "CNAME"
188228
domain, _ := d.extractDomain(tail)
189-
rdata = fmt.Sprintf("%s: %s", typname, domain)
229+
rdata = fmt.Sprintf("%s: %s", typename, domain)
190230
case 6:
191-
typname = "SOA"
231+
typename = "SOA"
192232
var (
193233
primary string
194234
mailbox string
@@ -207,47 +247,52 @@ func (d *DNSMessage) parseRData(typ uint16, tail []byte, rdl int) (string, strin
207247
- Refresh interval: %d
208248
- Retry interval: %d
209249
- 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)
212252
case 15:
213-
typname = "MX"
253+
typename = "MX"
214254
preference := binary.BigEndian.Uint16(tail[0:2])
215255
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)
217257
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])
220260
case 28:
221-
typname = "AAAA"
261+
typename = "AAAA"
222262
addr, _ := netip.AddrFromSlice(tail[0:rdl])
223263
rdata = fmt.Sprintf("Address: %s", addr)
224264
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)
226269
default:
227270
rdata = fmt.Sprintf("Unknown: %d bytes", rdl)
228271
}
229-
return typname, rdata, tail[rdl:]
272+
return typename, rdata, tail[rdl:]
230273
}
231274

232275
func (d *DNSMessage) parseQuery(tail []byte) (string, []byte) {
233276
var domain string
234277
domain, tail = d.extractDomain(tail)
235278
typ := binary.BigEndian.Uint16(tail[0:2])
279+
typename, _, _ := d.parseRData(typ, tail, 0)
236280
class := binary.BigEndian.Uint16(tail[2:4])
281+
cname := d.className(class)
237282
tail = tail[4:]
238-
// TODO: add type and class description https://en.wikipedia.org/wiki/List_of_DNS_record_types
239283
return fmt.Sprintf(` - %s:
240284
- 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
244288
}
245289

246290
func (d *DNSMessage) parseRR(tail []byte) (string, []byte) {
247291
var domain string
248292
domain, tail = d.extractDomain(tail)
249293
typ := binary.BigEndian.Uint16(tail[0:2])
250294
class := binary.BigEndian.Uint16(tail[2:4])
295+
cname := d.className(class)
251296
ttl := binary.BigEndian.Uint32(tail[4:8])
252297
rdl := int(binary.BigEndian.Uint16(tail[8:10]))
253298
var (
@@ -258,11 +303,11 @@ func (d *DNSMessage) parseRR(tail []byte) (string, []byte) {
258303
return fmt.Sprintf(` - %s:
259304
- Name: %s
260305
- Type: %s (%d)
261-
- Class: %d
306+
- Class: %s (%d)
262307
- TTL: %d
263308
- Data Length: %d
264309
- %s
265-
`, domain, domain, typename, typ, class, ttl, rdl, rdata), tail
310+
`, domain, domain, typename, typ, cname, class, ttl, rdl, rdata), tail
266311
}
267312

268313
func (d *DNSMessage) parseRoot(tail []byte) (string, []byte) {

layers/layers.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package layers
22

3-
import "fmt"
3+
import (
4+
"fmt"
5+
"unsafe"
6+
)
47

58
var LayerMap = map[string]Layer{
69
"ETH": &EthernetFrame{},
@@ -45,3 +48,7 @@ func nextAppLayer(src, dst uint16) string {
4548
}
4649
return layer
4750
}
51+
52+
func bytesToStr(myBytes []byte) string {
53+
return unsafe.String(unsafe.SliceData(myBytes), len(myBytes))
54+
}

0 commit comments

Comments
 (0)