1
1
mirror of https://github.com/wader/fq.git synced 2024-11-23 18:56:52 +03:00
fq/format/dns/dns.go
2021-09-12 13:08:50 +02:00

200 lines
4.6 KiB
Go

package dns
// TODO: https://github.com/Forescout/namewreck/blob/main/rfc/draft-dashevskyi-dnsrr-antipatterns-00.txt
import (
"fmt"
"strings"
"github.com/wader/fq/format"
"github.com/wader/fq/format/registry"
"github.com/wader/fq/pkg/decode"
)
func init() {
registry.MustRegister(&decode.Format{
Name: format.DNS,
Description: "DNS packet",
DecodeFn: dnsDecode,
})
}
// TODO: type consts
// TODO: aaaa,a rddata
var classNames = map[[2]uint64]string{
{0x0000, 0x0000}: "Reserved",
{0x0001, 0x0001}: "IN",
{0x0002, 0x0002}: "Unassigned",
{0x0003, 0x0003}: "Chaos",
{0x0004, 0x0004}: "Hesiod",
{0x0005, 0x00fd}: "Unassigned",
{0x00fe, 0x00fe}: "QCLASS NONE",
{0x00ff, 0x00ff}: "QCLASS ANY",
{0x0100, 0xfeff}: "Unassigned",
{0xff00, 0xfffe}: "Reserved for Private Use",
{0xffff, 0xffff}: "Reserved",
}
const (
typeCNAME = 5
)
var typeNames = map[uint64]string{
1: "A",
28: "AAAA",
18: "AFSDB",
42: "APL",
257: "CAA",
60: "CDNSKEY",
59: "CDS",
37: "CERT",
typeCNAME: "CNAME",
62: "CSYNC",
49: "DHCID",
32769: "DLV",
39: "DNAME",
48: "DNSKEY",
43: "DS",
108: "EUI48",
109: "EUI64",
13: "HINFO",
55: "HIP",
45: "IPSECKEY",
25: "KEY",
36: "KX",
29: "LOC",
15: "MX",
35: "NAPTR",
2: "NS",
47: "NSEC",
50: "NSEC3",
51: "NSEC3PARAM",
61: "OPENPGPKEY",
12: "PTR",
46: "RRSIG",
17: "RP",
24: "SIG",
53: "SMIMEA",
6: "SOA",
33: "SRV",
44: "SSHFP",
32768: "TA",
249: "TKEY",
52: "TLSA",
250: "TSIG",
16: "TXT",
256: "URI",
63: "ZONEMD",
64: "SVCB",
65: "HTTPS",
}
var rcodeNames = map[uint64]string{
0: "No error",
1: "Format error",
2: "Server failure",
3: "Name error",
4: "Not implemented",
5: "Refused",
}
func FieldFormatLabel(d *decode.D, name string) {
var endPos int64
const maxJumps = 1000
jumpCount := 0
d.FieldStructFn(name, func(d *decode.D) {
var ls []string
d.FieldArrayFn("labels", func(d *decode.D) {
seenTermintor := false
for !seenTermintor {
d.FieldStructFn("label", func(d *decode.D) {
if d.PeekBits(2) == 0b11 {
d.FieldU2("is_pointer")
pointer := d.FieldU14("pointer")
if endPos == 0 {
endPos = d.Pos()
}
jumpCount++
if jumpCount > maxJumps {
d.Invalid(fmt.Sprintf("label has more than %d jumps", maxJumps))
}
d.SeekAbs(int64(pointer * 8))
}
l := d.FieldU8("length")
if l == 0 {
seenTermintor = true
return
}
ls = append(ls, d.FieldUTF8("value", int(l)))
})
}
})
d.FieldValueStr("value", strings.Join(ls, "."), "")
})
if endPos != 0 {
d.SeekAbs(endPos)
}
}
func FieldFormatRR(d *decode.D, count uint64, name string, structName string) {
d.FieldArrayFn(name, func(d *decode.D) {
for i := uint64(0); i < count; i++ {
d.FieldStructFn(structName, func(d *decode.D) {
FieldFormatLabel(d, "name")
typ, _ := d.FieldStringMapFn("type", typeNames, "Unknown", d.U16, decode.NumberDecimal)
d.FieldStringRangeMapFn("class", classNames, "Unknown", d.U16, decode.NumberDecimal)
d.FieldU32("ttl")
// TODO: pointer?
rdLength := d.FieldU16("rd_length")
switch typ {
case typeCNAME:
FieldFormatLabel(d, "cname")
default:
d.FieldUTF8("rddata", int(rdLength))
}
})
}
})
}
func dnsDecode(d *decode.D, in interface{}) interface{} {
d.FieldStructFn("header", func(d *decode.D) {
d.FieldU16("id")
d.FieldBool("query")
d.FieldU4("opcode")
d.FieldBool("authoritative_answer")
d.FieldBool("truncation")
d.FieldBool("recursion_desired")
d.FieldBool("recursion_available")
d.FieldU3("z")
d.FieldStringMapFn("rcode", rcodeNames, "Unknown", d.U4, decode.NumberDecimal)
})
qdCount := d.FieldU16("qd_count")
anCount := d.FieldU16("an_count")
nsCount := d.FieldU16("ns_count")
arCount := d.FieldU16("ar_count")
d.FieldArrayFn("questions", func(d *decode.D) {
for i := uint64(0); i < qdCount; i++ {
d.FieldStructFn("question", func(d *decode.D) {
FieldFormatLabel(d, "name")
d.FieldStringMapFn("type", typeNames, "Unknown", d.U16, decode.NumberDecimal)
d.FieldStringRangeMapFn("class", classNames, "Unknown", d.U16, decode.NumberDecimal)
})
}
})
FieldFormatRR(d, anCount, "answers", "answer")
FieldFormatRR(d, nsCount, "nameservers", "nameserver")
FieldFormatRR(d, arCount, "additionals", "additional")
return nil
}