Skip to content

Commit c38b68b

Browse files
author
shadowy-pycoder
committed
Added basic parsing of DNS queries and answers
1 parent 2de1437 commit c38b68b

File tree

2 files changed

+85
-16
lines changed

2 files changed

+85
-16
lines changed

layers/dns.go

Lines changed: 82 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package layers
33
import (
44
"encoding/binary"
55
"fmt"
6+
"strings"
7+
"unsafe"
68
)
79

8-
const dnsHeaderSize = 12
10+
const headerSizeDNS = 12
911

1012
type DNSMessage struct {
1113
TransactionID uint16 // Used for matching response to queries.
@@ -27,6 +29,7 @@ func (d *DNSMessage) String() string {
2729
- Authority RRs: %d
2830
- Additional RRs: %d
2931
- Payload: %d bytes
32+
%s
3033
`,
3134
d.TransactionID,
3235
d.Flags,
@@ -36,21 +39,22 @@ func (d *DNSMessage) String() string {
3639
d.AuthorityRRs,
3740
d.AdditionalRRs,
3841
len(d.payload),
42+
d.queries(),
3943
)
4044
}
4145

4246
// Parse parses the given byte data into a DNSMessage struct.
4347
func (d *DNSMessage) Parse(data []byte) error {
44-
if len(data) < dnsHeaderSize {
45-
return fmt.Errorf("minimum header size for DNS is %d bytes, got %d bytes", dnsHeaderSize, len(data))
48+
if len(data) < headerSizeDNS {
49+
return fmt.Errorf("minimum header size for DNS is %d bytes, got %d bytes", headerSizeDNS, len(data))
4650
}
4751
d.TransactionID = binary.BigEndian.Uint16(data[0:2])
4852
d.Flags = binary.BigEndian.Uint16(data[2:4])
4953
d.Questions = binary.BigEndian.Uint16(data[4:6])
5054
d.AnswerRRs = binary.BigEndian.Uint16(data[6:8])
5155
d.AuthorityRRs = binary.BigEndian.Uint16(data[8:10])
52-
d.AdditionalRRs = binary.BigEndian.Uint16(data[10:dnsHeaderSize])
53-
d.payload = data[dnsHeaderSize:]
56+
d.AdditionalRRs = binary.BigEndian.Uint16(data[10:headerSizeDNS])
57+
d.payload = data[headerSizeDNS:]
5458
return nil
5559
}
5660

@@ -79,15 +83,15 @@ func (d *DNSMessage) flags() string {
7983
default:
8084
opcodes = "Unknown"
8185
}
86+
tc := (d.Flags >> 9) & 1
87+
rd := (d.Flags >> 8) & 1
88+
z := (d.Flags >> 6) & 1
89+
na := (d.Flags >> 4) & 1
8290
qr := (d.Flags >> 15) & 1
8391
var qrs string
8492
switch qr {
8593
case 0:
8694
qrs = "query"
87-
tc := (d.Flags >> 9) & 1
88-
rd := (d.Flags >> 8) & 1
89-
z := (d.Flags >> 6) & 1
90-
na := (d.Flags >> 4) & 1
9195
flags = fmt.Sprintf(` - Response: Message is a %s (%d)
9296
- Opcode: %s (%d)
9397
- Truncated: %d
@@ -97,12 +101,8 @@ func (d *DNSMessage) flags() string {
97101
case 1:
98102
qrs = "reply"
99103
a := (d.Flags >> 10) & 1
100-
tc := (d.Flags >> 9) & 1
101-
rd := (d.Flags >> 8) & 1
102104
ra := (d.Flags >> 7) & 1
103-
z := (d.Flags >> 6) & 1
104105
aa := (d.Flags >> 5) & 1
105-
na := (d.Flags >> 4) & 1
106106
rcode := d.Flags & 15
107107
var rcodes string
108108
switch rcode {
@@ -142,3 +142,72 @@ func (d *DNSMessage) flags() string {
142142
}
143143
return flags
144144
}
145+
146+
func bytesToStr(myBytes []byte) string {
147+
return unsafe.String(unsafe.SliceData(myBytes), len(myBytes))
148+
}
149+
150+
func extractDomain(data []byte, offset int) (string, int) {
151+
var parts []string
152+
for {
153+
blen := int(data[offset])
154+
offset++
155+
if blen == 0 {
156+
break
157+
}
158+
if blen == 0xc0 {
159+
offset = int(data[offset]) - headerSizeDNS
160+
blen = int(data[offset])
161+
offset++
162+
}
163+
parts = append(parts, bytesToStr(data[offset:offset+blen]))
164+
offset += blen
165+
}
166+
return strings.Join(parts, "."), offset
167+
}
168+
169+
func (d *DNSMessage) queries() string {
170+
var (
171+
sb strings.Builder
172+
offset int
173+
)
174+
if d.Questions > 0 {
175+
sb.WriteString("- Queries:\n")
176+
for range d.Questions {
177+
var domain string
178+
domain, offset = extractDomain(d.payload, offset)
179+
typ := binary.BigEndian.Uint16(d.payload[offset : offset+2])
180+
offset += 2
181+
class := binary.BigEndian.Uint16(d.payload[offset : offset+2])
182+
offset += 2
183+
// TODO: add type and class description https://en.wikipedia.org/wiki/List_of_DNS_record_types
184+
sb.WriteString(fmt.Sprintf(" %s: type %d class %d\n", domain, typ, class))
185+
}
186+
}
187+
if d.AnswerRRs > 0 {
188+
sb.WriteString("- Answers:\n")
189+
for range d.AnswerRRs {
190+
no := binary.BigEndian.Uint16(d.payload[offset : offset+2])
191+
offset += 2
192+
domain, _ := extractDomain(d.payload, int(no&0xFF)-headerSizeDNS)
193+
typ := binary.BigEndian.Uint16(d.payload[offset : offset+2])
194+
offset += 2
195+
class := binary.BigEndian.Uint16(d.payload[offset : offset+2])
196+
offset += 2
197+
ttl := binary.BigEndian.Uint32(d.payload[offset : offset+4])
198+
offset += 4
199+
rdl := int(binary.BigEndian.Uint16(d.payload[offset : offset+2]))
200+
offset += 2
201+
//rdata := d.payload[offset : offset+rdl]
202+
offset += rdl
203+
sb.WriteString(fmt.Sprintf(" %s: type %d class %d ttl %d rdl %d\n", domain, typ, class, ttl, rdl))
204+
}
205+
}
206+
if d.AuthorityRRs > 0 {
207+
208+
}
209+
if d.AdditionalRRs > 0 {
210+
211+
}
212+
return sb.String()
213+
}

layers/ethernet.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"net"
88
)
99

10-
const ethernetHeaderSize = 14
10+
const headerSizeEthernet = 14
1111

1212
// An Ethernet frame is a data link layer protocol data unit.
1313
type EthernetFrame struct {
@@ -35,13 +35,13 @@ func (ef *EthernetFrame) String() string {
3535

3636
// Parse parses the given byte data into an Ethernet frame.
3737
func (ef *EthernetFrame) Parse(data []byte) error {
38-
if len(data) < ethernetHeaderSize {
38+
if len(data) < headerSizeEthernet {
3939
return fmt.Errorf("did not read a complete Ethernet frame, only %d bytes read", len(data))
4040
}
4141
ef.DstMAC = net.HardwareAddr(data[0:6])
4242
ef.SrcMAC = net.HardwareAddr(data[6:12])
4343
ef.EtherType = binary.BigEndian.Uint16(data[12:14])
44-
ef.payload = data[ethernetHeaderSize:]
44+
ef.payload = data[headerSizeEthernet:]
4545
return nil
4646
}
4747

0 commit comments

Comments
 (0)