Skip to content

Commit

Permalink
decode dns class properly (#635)
Browse files Browse the repository at this point in the history
* decode dns class properly
dmachard authored Mar 4, 2024
1 parent 6371fa0 commit 06c9293
Showing 10 changed files with 125 additions and 71 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -114,7 +114,7 @@ The [`_integration`](./docs/_integration) folder contains DNS-collector `configu

## Performance

Tuning may be neccesary to deal with a large traffic loads.
Tuning may be necessary to deal with a large traffic loads.
Please refer to the [performance tuning](./docs/performance.md) guide if needed.

## Contributing
5 changes: 3 additions & 2 deletions config.yml
Original file line number Diff line number Diff line change
@@ -47,8 +47,9 @@ global:
# - protocol: protocol UDP, TCP
# - length: the length of the query or reply in bytes
# - length-unit: the length of the query or reply in bytes with unit
# - qtype: dns qtype
# - qname: dns qname
# - qtype: dns query type
# - qname: dns query name
# - qclass: dns query class
# - latency: computed latency between queries and replies
# - answercount: the number of answer
# - ttl: answer ttl, only the first one value
41 changes: 33 additions & 8 deletions dnsutils/dns_parser.go
Original file line number Diff line number Diff line change
@@ -14,6 +14,13 @@ const DNSLen = 12
const UNKNOWN = "UNKNOWN"

var (
Class = map[int]string{
1: "IN",
3: "CH",
4: "HS",
254: "NONE",
255: "ANY",
}
Rdatatypes = map[int]string{
0: "NONE",
1: "A",
@@ -124,6 +131,7 @@ var ErrDecodeDNSLabelTooShort = errors.New("malformed pkt, dns payload too short
var ErrDecodeQuestionQtypeTooShort = errors.New("malformed pkt, not enough data to decode qtype")
var ErrDecodeDNSAnswerTooShort = errors.New("malformed pkt, not enough data to decode answer")
var ErrDecodeDNSAnswerRdataTooShort = errors.New("malformed pkt, not enough data to decode rdata answer")
var ErrDecodeQuestionQclassTooShort = errors.New("malformed pkt, not enough data to decode qclass")

func RdatatypeToString(rrtype int) string {
if value, ok := Rdatatypes[rrtype]; ok {
@@ -139,6 +147,13 @@ func RcodeToString(rcode int) string {
return UNKNOWN
}

func ClassToString(class int) string {
if value, ok := Class[class]; ok {
return value
}
return UNKNOWN
}

// error returned if decoding of DNS packet payload fails.
type decodingError struct {
part string
@@ -273,14 +288,15 @@ func DecodePayload(dm *DNSMessage, header *DNSHeader, config *pkgconfig.Config)
var payloadOffset int
// decode DNS question
if header.Qdcount > 0 {
dnsQname, dnsRRtype, offsetrr, err := DecodeQuestion(header.Qdcount, dm.DNS.Payload)
dnsQname, dnsRRtype, dnsQclass, offsetrr, err := DecodeQuestion(header.Qdcount, dm.DNS.Payload)
if err != nil {
dm.DNS.MalformedPacket = true
return &decodingError{part: "query", err: err}
}

dm.DNS.Qname = dnsQname
dm.DNS.Qtype = RdatatypeToString(dnsRRtype)
dm.DNS.Qclass = ClassToString(dnsQclass)
payloadOffset = offsetrr
}

@@ -358,10 +374,11 @@ DNS QUESTION
| QCLASS |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*/
func DecodeQuestion(qdcount int, payload []byte) (string, int, int, error) {
func DecodeQuestion(qdcount int, payload []byte) (string, int, int, int, error) {
offset := DNSLen
var qname string
var qtype int
var qclass int

for i := 0; i < qdcount; i++ {
// the specification allows more than one query in DNS packet,
@@ -373,18 +390,26 @@ func DecodeQuestion(qdcount int, payload []byte) (string, int, int, error) {
// Decode QNAME
qname, offset, err = ParseLabels(offset, payload)
if err != nil {
return "", 0, 0, err
return "", 0, 0, 0, err
}

// decode QTYPE and support invalid packet, some abuser sends it...
if len(payload[offset:]) < 4 {
return "", 0, 0, ErrDecodeQuestionQtypeTooShort
if len(payload[offset:]) < 2 {
return "", 0, 0, 0, ErrDecodeQuestionQtypeTooShort
} else {
qtype = int(binary.BigEndian.Uint16(payload[offset : offset+2]))
offset += 4
offset += 2
}

// decode QCLASS
if len(payload[offset:]) < 2 {
return "", 0, 0, 0, ErrDecodeQuestionQclassTooShort
} else {
qclass = int(binary.BigEndian.Uint16(payload[offset : offset+2]))
offset += 2
}
}
return qname, qtype, offset, nil
return qname, qtype, qclass, offset, nil
}

/*
@@ -461,7 +486,7 @@ func DecodeAnswer(ancount int, startOffset int, payload []byte) ([]DNSAnswer, in
a := DNSAnswer{
Name: name,
Rdatatype: rdatatype,
Class: int(class),
Class: ClassToString(int(class)),
TTL: int(ttl),
Rdata: parsed,
}
89 changes: 48 additions & 41 deletions dnsutils/dns_parser_test.go
Original file line number Diff line number Diff line change
@@ -76,7 +76,11 @@ func TestDecodeQuestion(t *testing.T) {
dm.SetQuestion(fqdn, dns.TypeA)
payload, _ := dm.Pack()

qname, qtype, offsetRR, _ := DecodeQuestion(1, payload)
qname, qtype, qclass, offsetRR, _ := DecodeQuestion(1, payload)
if ClassToString(qclass) != "IN" {
t.Errorf("invalid qclass: %d", qclass)
}

if qname+"." != fqdn {
t.Errorf("invalid qname: %s", qname)
}
@@ -107,13 +111,16 @@ func TestDecodeQuestion_Multiple(t *testing.T) {
0x00, 0x1c, 0x00, 0x01,
}

qname, qtype, offset, err := DecodeQuestion(3, paylaod)
qname, qtype, qclass, offset, err := DecodeQuestion(3, paylaod)
if err != nil {
t.Errorf("unexpected error %v", err)
}
if qname != "c" || RdatatypeToString(qtype) != "AAAA" {
t.Errorf("expected qname=C, type=AAAA, got qname=%s, type=%s", qname, RdatatypeToString(qtype))
}
if ClassToString(qclass) != "IN" {
t.Errorf("expected qclass=IN %s", ClassToString(qclass))
}
if offset != 33 {
t.Errorf("expected resulting offset to be 33, got %d", offset)
}
@@ -137,7 +144,7 @@ func TestDecodeQuestion_Multiple_InvalidCount(t *testing.T) {
0x00, 0x1c, 0x00, 0x01,
}

_, _, _, err := DecodeQuestion(4, paylaod)
_, _, _, _, err := DecodeQuestion(4, paylaod)
if !errors.Is(err, ErrDecodeDNSLabelTooShort) {
t.Errorf("bad error received: %v", err)
}
@@ -159,7 +166,7 @@ func TestDecodeAnswer_Ns(t *testing.T) {
m.Ns = append(m.Ns, rrNs)

payload, _ := m.Pack()
_, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, _, offsetRR, _ := DecodeQuestion(1, payload)
_, offsetRRns, _ := DecodeAnswer(len(dm.Answer), offsetRR, payload)

nsAnswers, _, _ := DecodeAnswer(len(m.Ns), offsetRRns, payload)
@@ -180,7 +187,7 @@ func TestDecodeAnswer(t *testing.T) {

payload, _ := dm.Pack()

_, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, _, offsetRR, _ := DecodeQuestion(1, payload)
answer, _, _ := DecodeAnswer(len(dm.Answer), offsetRR, payload)

if len(answer) != len(dm.Answer) {
@@ -201,7 +208,7 @@ func TestDecodeRdataSVCB_alias(t *testing.T) {

payload, _ := dm.Pack()

_, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, _, offsetRR, _ := DecodeQuestion(1, payload)
answer, _, _ := DecodeAnswer(len(dm.Answer), offsetRR, payload)

if answer[0].Rdata != rdata {
@@ -229,7 +236,7 @@ func TestDecodeRdataSVCB_params(t *testing.T) {
rr1, _ := dns.NewRR(fmt.Sprintf("%s SVCB %s", fqdn, rdata))
dm.Answer = append(dm.Answer, rr1)
payload, _ := dm.Pack()
_, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, _, offsetRR, _ := DecodeQuestion(1, payload)
answer, _, _ := DecodeAnswer(len(dm.Answer), offsetRR, payload)
if answer[0].Rdata != rdata {
t.Errorf("invalid decode for rdata SVCB, want %s, got: %s", rdata, answer[0].Rdata)
@@ -251,7 +258,7 @@ func TestDecodeAnswer_QnameMinimized(t *testing.T) {
0xc0, 0x7e, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x04, 0x34, 0x71, 0xc3,
0x84, 0x00, 0x00, 0x29, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}

_, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, err := DecodeAnswer(4, offsetRR, payload)
if err != nil {
t.Errorf("failed to decode valid dns packet with minimization")
@@ -270,7 +277,7 @@ func TestDecodeRdataA(t *testing.T) {

payload, _ := dm.Pack()

_, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, _, offsetRR, _ := DecodeQuestion(1, payload)
answer, _, _ := DecodeAnswer(len(dm.Answer), offsetRR, payload)

if answer[0].Rdata != rdata {
@@ -301,7 +308,7 @@ func TestDecodeRdataA_Short(t *testing.T) {
// RDATA (1 byte too short for A record)
0x7f, 0x00, 0x00,
}
_, _, offsetrr, err := DecodeQuestion(1, payload)
_, _, _, offsetrr, err := DecodeQuestion(1, payload)
if err != nil {
t.Errorf("Unexpected error decoding question: %v", err)
}
@@ -324,7 +331,7 @@ func TestDecodeRdataAAAA(t *testing.T) {

payload, _ := dm.Pack()

_, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, _, offsetRR, _ := DecodeQuestion(1, payload)
answer, _, _ := DecodeAnswer(len(dm.Answer), offsetRR, payload)

if answer[0].Rdata != rdata {
@@ -358,7 +365,7 @@ func TestDecodeRdataAAAA_Short(t *testing.T) {
0x00, 0x00, 0x00,
}

_, _, offsetSetRR, err := DecodeQuestion(1, payload)
_, _, _, offsetSetRR, err := DecodeQuestion(1, payload)
if err != nil {
t.Errorf("unexpected error while decoding question: %v", err)
}
@@ -380,7 +387,7 @@ func TestDecodeRdataCNAME(t *testing.T) {

payload, _ := dm.Pack()

_, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, _, offsetRR, _ := DecodeQuestion(1, payload)
answer, _, _ := DecodeAnswer(len(dm.Answer), offsetRR, payload)

if answer[0].Rdata != rdata {
@@ -400,7 +407,7 @@ func TestDecodeRdataMX(t *testing.T) {

payload, _ := dm.Pack()

_, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, _, offsetRR, _ := DecodeQuestion(1, payload)
answer, _, _ := DecodeAnswer(len(dm.Answer), offsetRR, payload)

if answer[0].Rdata != rdata {
@@ -432,7 +439,7 @@ func TestDecodeRdataMX_Short(t *testing.T) {
// RDATA
0x00,
}
_, _, offsetRR, err := DecodeQuestion(1, payload)
_, _, _, offsetRR, err := DecodeQuestion(1, payload)
if err != nil {
t.Errorf("unexpected error while decoding question: %v", err)
}
@@ -467,7 +474,7 @@ func TestDecodeRdataMX_Minimal(t *testing.T) {
// RDATA
0x00, 0x00, 0x00,
}
_, _, offsetRR, err := DecodeQuestion(1, payload)
_, _, _, offsetRR, err := DecodeQuestion(1, payload)
if err != nil {
t.Errorf("unexpected error while decoding question: %v", err)
}
@@ -493,7 +500,7 @@ func TestDecodeRdataSRV(t *testing.T) {

payload, _ := dm.Pack()

_, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, _, offsetRR, _ := DecodeQuestion(1, payload)
answer, _, _ := DecodeAnswer(len(dm.Answer), offsetRR, payload)

if answer[0].Rdata != rdata {
@@ -530,7 +537,7 @@ func TestDecodeRdataSRV_Short(t *testing.T) {
// missing port and target
}

_, _, offsetRR, err := DecodeQuestion(1, payload)
_, _, _, offsetRR, err := DecodeQuestion(1, payload)
if err != nil {
t.Errorf("unexpected error while decoding question: %v", err)
}
@@ -572,7 +579,7 @@ func TestDecodeRdataSRV_Minimal(t *testing.T) {
0x00,
}

_, _, offsetRR, err := DecodeQuestion(1, payload)
_, _, _, offsetRR, err := DecodeQuestion(1, payload)
if err != nil {
t.Errorf("unexpected error while decoding question: %v", err)
}
@@ -597,7 +604,7 @@ func TestDecodeRdataNS(t *testing.T) {

payload, _ := dm.Pack()

_, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, _, offsetRR, _ := DecodeQuestion(1, payload)
answer, _, _ := DecodeAnswer(len(dm.Answer), offsetRR, payload)

if answer[0].Rdata != rdata {
@@ -617,7 +624,7 @@ func TestDecodeRdataTXT(t *testing.T) {

payload, _ := dm.Pack()

_, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, _, offsetRR, _ := DecodeQuestion(1, payload)
answer, _, _ := DecodeAnswer(len(dm.Answer), offsetRR, payload)

if answer[0].Rdata != rdata {
@@ -649,7 +656,7 @@ func TestDecodeRdataTXT_Empty(t *testing.T) {
// no data
}

_, _, offsetRR, err := DecodeQuestion(1, payload)
_, _, _, offsetRR, err := DecodeQuestion(1, payload)
if err != nil {
t.Errorf("unexpected error while decoding question: %v", err)
}
@@ -689,7 +696,7 @@ func TestDecodeRdataTXT_Short(t *testing.T) {
// missing two bytes
}

_, _, offsetRR, err := DecodeQuestion(1, payload)
_, _, _, offsetRR, err := DecodeQuestion(1, payload)
if err != nil {
t.Errorf("unexpected error while decoding question: %v", err)
}
@@ -726,7 +733,7 @@ func TestDecodeRdataTXT_NoTxt(t *testing.T) {
// no txt-data
}

_, _, offsetRR, err := DecodeQuestion(1, payload)
_, _, _, offsetRR, err := DecodeQuestion(1, payload)
if err != nil {
t.Errorf("unexpected error while decoding question: %v", err)
}
@@ -753,7 +760,7 @@ func TestDecodeRdataPTR(t *testing.T) {

payload, _ := dm.Pack()

_, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, _, offsetRR, _ := DecodeQuestion(1, payload)
answer, _, _ := DecodeAnswer(len(dm.Answer), offsetRR, payload)

if answer[0].Rdata != rdata {
@@ -773,7 +780,7 @@ func TestDecodeRdataSOA(t *testing.T) {

payload, _ := dm.Pack()

_, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, _, offsetRR, _ := DecodeQuestion(1, payload)
answer, _, _ := DecodeAnswer(len(dm.Answer), offsetRR, payload)

if answer[0].Rdata != rdata {
@@ -823,7 +830,7 @@ func TestDecodeRdataSOA_Short(t *testing.T) {
// minimum -field missing from the RDATA
}

_, _, offsetRR, err := DecodeQuestion(1, payload)
_, _, _, offsetRR, err := DecodeQuestion(1, payload)
if err != nil {
t.Errorf("Unable to decode question: %v", err)
}
@@ -840,7 +847,7 @@ func TestDecodeRdataSOA_Minimization(t *testing.T) {
51, 3, 111, 118, 104, 3, 110, 101, 116, 0, 4, 116, 101, 99, 104, 192, 53,
120, 119, 219, 34, 0, 1, 81, 128, 0, 0, 14, 16, 0, 54, 238, 128, 0, 0, 0, 60}

_, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, err := DecodeAnswer(1, offsetRR, payload)
if err != nil {
t.Errorf(" error returned: %v", err)
@@ -882,7 +889,7 @@ func TestDecodeQuestion_SkipOpt(t *testing.T) {
// RDATA
0x7f, 0x00, 0x00, 0x01,
}
_, _, offsetrr, err := DecodeQuestion(1, payload)
_, _, _, offsetrr, err := DecodeQuestion(1, payload)
if err != nil {
t.Errorf("Unexpected error decoding question: %v", err)
}
@@ -909,15 +916,15 @@ func TestDecodeDns_HeaderTooShort(t *testing.T) {

func TestDecodeDnsQuestion_InvalidOffset(t *testing.T) {
decoded := []byte{183, 59, 130, 217, 128, 16, 0, 51, 165, 67, 0, 0}
_, _, _, err := DecodeQuestion(1, decoded)
_, _, _, _, err := DecodeQuestion(1, decoded)
if !errors.Is(err, ErrDecodeDNSLabelTooShort) {
t.Errorf("bad error returned: %v", err)
}
}

func TestDecodeDnsQuestion_PacketTooShort(t *testing.T) {
decoded := []byte{183, 59, 130, 217, 128, 16, 0, 51, 165, 67, 0, 0, 1, 1, 8, 10, 23}
_, _, _, err := DecodeQuestion(1, decoded)
_, _, _, _, err := DecodeQuestion(1, decoded)
if !errors.Is(err, ErrDecodeDNSLabelTooShort) {
t.Errorf("bad error returned: %v", err)
}
@@ -926,15 +933,15 @@ func TestDecodeDnsQuestion_PacketTooShort(t *testing.T) {
func TestDecodeDnsQuestion_QtypeMissing(t *testing.T) {
decoded := []byte{88, 27, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 15, 100, 110, 115, 116, 97, 112,
99, 111, 108, 108, 101, 99, 116, 111, 114, 4, 116, 101, 115, 116, 0}
_, _, _, err := DecodeQuestion(1, decoded)
_, _, _, _, err := DecodeQuestion(1, decoded)
if !errors.Is(err, ErrDecodeQuestionQtypeTooShort) {
t.Errorf("bad error returned: %v", err)
}
}

func TestDecodeQuestion_InvalidPointer(t *testing.T) {
decoded := []byte{88, 27, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 202}
_, _, _, err := DecodeQuestion(1, decoded)
_, _, _, _, err := DecodeQuestion(1, decoded)
if !errors.Is(err, ErrDecodeDNSLabelTooShort) {
t.Errorf("bad error returned: %v", err)
}
@@ -945,7 +952,7 @@ func TestDecodeDnsAnswer_PacketTooShort(t *testing.T) {
111, 114, 4, 116, 101, 115, 116, 0, 0, 1, 0, 1, 15, 100, 110, 115, 116, 97, 112, 99, 111, 108, 108, 101, 99, 116,
111, 114, 4, 116, 101, 115, 116, 0, 0, 1, 0, 1, 0, 0, 14, 16, 0}

_, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, err := DecodeAnswer(1, offsetRR, payload)
if !errors.Is(err, ErrDecodeDNSAnswerTooShort) {
t.Errorf("bad error returned: %v", err)
@@ -1027,7 +1034,7 @@ func TestDecodeDnsAnswer_RdataTooShort(t *testing.T) {
111, 114, 4, 116, 101, 115, 116, 0, 0, 1, 0, 1, 15, 100, 110, 115, 116, 97, 112, 99, 111, 108, 108, 101, 99, 116,
111, 114, 4, 116, 101, 115, 116, 0, 0, 1, 0, 1, 0, 0, 14, 16, 0, 4, 127, 0}

_, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, err := DecodeAnswer(1, offsetRR, payload)
if !errors.Is(err, ErrDecodeDNSAnswerRdataTooShort) {
t.Errorf("bad error returned: %v", err)
@@ -1039,7 +1046,7 @@ func TestDecodeDnsAnswer_InvalidPtr(t *testing.T) {
109, 99, 104, 100, 2, 109, 101, 0, 0, 1, 0, 1, 192, 254, 0, 1, 0, 1, 0, 0,
14, 16, 0, 4, 83, 112, 146, 176}

_, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, err := DecodeAnswer(1, offsetRR, payload)
if !errors.Is(err, ErrDecodeDNSLabelInvalidPointer) {
t.Errorf("bad error returned: %v", err)
@@ -1052,7 +1059,7 @@ func TestDecodeDnsAnswer_InvalidPtr_Loop1(t *testing.T) {
109, 99, 104, 100, 2, 109, 101, 0, 0, 1, 0, 1, 192, 31, 0, 1, 0, 1, 0, 0,
14, 16, 0, 4, 83, 112, 146, 176}

_, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, err := DecodeAnswer(1, offsetRR, payload)
if !errors.Is(err, ErrDecodeDNSLabelInvalidPointer) {
t.Errorf("bad error returned: %v", err)
@@ -1066,7 +1073,7 @@ func TestDecodeDnsAnswer_InvalidPtr_Loop2(t *testing.T) {
14, 16, 0, 4, 83, 112, 146, 176, 192, 31, 0, 1, 0, 1, 0, 0,
14, 16, 0, 4, 83, 112, 146, 176}

_, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, err := DecodeAnswer(1, offsetRR, payload)
if !errors.Is(err, ErrDecodeDNSLabelInvalidPointer) {
t.Errorf("bad error returned: %v", err)
@@ -1541,7 +1548,7 @@ func TestDecodePayload_AnswerHappy(t *testing.T) {
expected := DNSAnswer{
Name: dm.DNS.Qname,
Rdatatype: RdatatypeToString(0x0001),
Class: 0x0001,
Class: "IN", // 0x0001,
TTL: 300,
Rdata: fmt.Sprintf("10.10.1.%d", i+1),
}
@@ -1662,7 +1669,7 @@ func TestDecodePayload_AnswerMultipleQueries(t *testing.T) {
expected := DNSAnswer{
Name: "s" + dm.DNS.Qname, // answers have qname from 1st query data, 2nd data is missing 's'
Rdatatype: RdatatypeToString(0x0001),
Class: 0x0001,
Class: "IN", // 0x0001,
TTL: 300,
Rdata: fmt.Sprintf("10.10.1.%d", i+1),
}
@@ -2049,7 +2056,7 @@ func TestDecodePayload_AnswerError(t *testing.T) {
expected := DNSAnswer{
Name: "google.com",
Rdatatype: RdatatypeToString(0x0006),
Class: 0x0001,
Class: "IN", // 0x0001,
TTL: 60,
Rdata: "ns1.google.com dns-admin.google.com 430000820 900 900 1800 60",
}
20 changes: 10 additions & 10 deletions dnsutils/edns_parser_test.go
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@ func TestDecodeQuery_EDNS(t *testing.T) {

payload, _ := dm.Pack()

_, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, err := DecodeEDNS(len(dm.Extra), offsetRR, payload)

if err != nil {
@@ -63,7 +63,7 @@ func TestDecodeReply_EDNS(t *testing.T) {
m.SetRcode(dm, 42) // 32(extended rcode) + 10(rcode)

payload, _ := m.Pack()
_, _, offsetRR, _ := DecodeQuestion(1, payload)
_, _, _, offsetRR, _ := DecodeQuestion(1, payload)
_, offsetRR, _ = DecodeAnswer(len(m.Answer), offsetRR, payload)

_, _, err := DecodeEDNS(len(m.Extra), offsetRR, payload)
@@ -109,7 +109,7 @@ func TestDecodeQuery_EdnsSubnet(t *testing.T) {
0xc0, 0xa8, 0x01,
}

_, _, offset, err := DecodeQuestion(1, payload)
_, _, _, offset, err := DecodeQuestion(1, payload)
if err != nil {
t.Errorf("unexpected error while decoding question: %v", err)
}
@@ -179,7 +179,7 @@ func TestDecodeQuery_EdnsSubnetV6(t *testing.T) {
0xfe, 0x80, 0x01,
}

_, _, offset, err := DecodeQuestion(1, payload)
_, _, _, offset, err := DecodeQuestion(1, payload)
if err != nil {
t.Errorf("unexpected error while decoding question: %v", err)
}
@@ -250,7 +250,7 @@ func TestDecodeQuery_EdnsSubnet_invalidFam(t *testing.T) {
0xfe, 0x80, 0x01,
}

_, _, offset, err := DecodeQuestion(1, payload)
_, _, _, offset, err := DecodeQuestion(1, payload)
if err != nil {
t.Errorf("unexpected error while decoding question: %v", err)
}
@@ -308,7 +308,7 @@ func TestDecodeQuery_EdnsSubnet_Short(t *testing.T) {
// 0xfe, 0x80, 0x01,
}

_, _, offset, err := DecodeQuestion(1, payload)
_, _, _, offset, err := DecodeQuestion(1, payload)
if err != nil {
t.Errorf("unexpected error while decoding question: %v", err)
}
@@ -366,7 +366,7 @@ func TestDecodeQuery_EdnsSubnet_NoAddr(t *testing.T) {
// 0xfe, 0x80, 0x01,
}

_, _, offset, err := DecodeQuestion(1, payload)
_, _, _, offset, err := DecodeQuestion(1, payload)
if err != nil {
t.Errorf("unexpected error while decoding question: %v", err)
}
@@ -429,7 +429,7 @@ func TestDecodeAnswer_EdnsError(t *testing.T) {
0x00, 0x17,
}

_, _, offset, err := DecodeQuestion(1, payload)
_, _, _, offset, err := DecodeQuestion(1, payload)
if err != nil {
t.Errorf("unexpected error while decoding question: %v", err)
}
@@ -485,7 +485,7 @@ func TestDecodeAnswer_EdnsErrorText(t *testing.T) {
0x62, 0x30, 0x72, 0x6b, 0x65, 0x6e,
}

_, _, offset, err := DecodeQuestion(1, payload)
_, _, _, offset, err := DecodeQuestion(1, payload)
if err != nil {
t.Errorf("unexpected error while decoding question: %v", err)
}
@@ -540,7 +540,7 @@ func TestDecodeAnswer_EdnsErrorShort(t *testing.T) {
0x00,
}

_, _, offset, err := DecodeQuestion(1, payload)
_, _, _, offset, err := DecodeQuestion(1, payload)
if err != nil {
t.Errorf("unexpected error while decoding question: %v", err)
}
10 changes: 9 additions & 1 deletion dnsutils/message.go
Original file line number Diff line number Diff line change
@@ -67,7 +67,7 @@ func GetIPPort(dm *DNSMessage) (string, int, string, int) {
type DNSAnswer struct {
Name string `json:"name"`
Rdatatype string `json:"rdatatype"`
Class int `json:"-"`
Class string `json:"class"`
TTL int `json:"ttl"`
Rdata string `json:"rdata"`
}
@@ -107,6 +107,7 @@ type DNS struct {
Opcode int `json:"opcode"`
Rcode string `json:"rcode"`
Qname string `json:"qname"`
Qclass string `json:"qclass"`

Qtype string `json:"qtype"`
Flags DNSFlags `json:"flags"`
@@ -277,6 +278,7 @@ func (dm *DNSMessage) Init() {
Rcode: "-",
Qtype: "-",
Qname: "-",
Qclass: "-",
DNSRRs: DNSRRs{Answers: []DNSAnswer{}, Nameservers: []DNSAnswer{}, Records: []DNSAnswer{}},
}

@@ -652,6 +654,8 @@ func (dm *DNSMessage) ToTextLine(format []string, fieldDelimiter string, fieldBo
s.WriteString(strconv.Itoa(dm.DNS.Length))
case directive == "qtype":
s.WriteString(dm.DNS.Qtype)
case directive == "qclass":
s.WriteString(dm.DNS.Qclass)
case directive == "latency":
s.WriteString(dm.DNSTap.LatencySec)
case directive == "malformed":
@@ -1061,6 +1065,7 @@ func (dm *DNSMessage) Flatten() (map[string]interface{}, error) {
"dns.opcode": dm.DNS.Opcode,
"dns.qname": dm.DNS.Qname,
"dns.qtype": dm.DNS.Qtype,
"dns.qclass": dm.DNS.Qclass,
"dns.rcode": dm.DNS.Rcode,
"dnstap.identity": dm.DNSTap.Identity,
"dnstap.latency": dm.DNSTap.LatencySec,
@@ -1111,20 +1116,23 @@ func (dm *DNSMessage) Flatten() (map[string]interface{}, error) {
dnsFields[prefixAn+".rdata"] = an.Rdata
dnsFields[prefixAn+".rdatatype"] = an.Rdatatype
dnsFields[prefixAn+".ttl"] = an.TTL
dnsFields[prefixAn+".class"] = an.Class
}
for i, ns := range dm.DNS.DNSRRs.Nameservers {
prefixNs := "dns.resource-records.ns." + strconv.Itoa(i)
dnsFields[prefixNs+".name"] = ns.Name
dnsFields[prefixNs+".rdata"] = ns.Rdata
dnsFields[prefixNs+".rdatatype"] = ns.Rdatatype
dnsFields[prefixNs+".ttl"] = ns.TTL
dnsFields[prefixNs+".class"] = ns.Class
}
for i, ar := range dm.DNS.DNSRRs.Records {
prefixAr := "dns.resource-records.ar." + strconv.Itoa(i)
dnsFields[prefixAr+".name"] = ar.Name
dnsFields[prefixAr+".rdata"] = ar.Rdata
dnsFields[prefixAr+".rdatatype"] = ar.Rdatatype
dnsFields[prefixAr+".ttl"] = ar.TTL
dnsFields[prefixAr+".class"] = ar.Class
}

// Add EDNSoptions fields: "edns.options.0.code": 10,
10 changes: 9 additions & 1 deletion dnsutils/message_test.go
Original file line number Diff line number Diff line change
@@ -201,6 +201,7 @@ func TestDnsMessage_Json_Reference(t *testing.T) {
"rcode": "-",
"qname": "-",
"qtype": "-",
"qclass": "-",
"flags": {
"qr": false,
"tc": false,
@@ -425,7 +426,7 @@ func TestDnsMessage_JsonFlatten_Reference(t *testing.T) {
dm.Init()

// add some items in slices field
dm.DNS.DNSRRs.Answers = append(dm.DNS.DNSRRs.Answers, DNSAnswer{Name: "google.nl", Rdata: "142.251.39.99", Rdatatype: "A", TTL: 300})
dm.DNS.DNSRRs.Answers = append(dm.DNS.DNSRRs.Answers, DNSAnswer{Name: "google.nl", Rdata: "142.251.39.99", Rdatatype: "A", TTL: 300, Class: "IN"})
dm.EDNS.Options = append(dm.EDNS.Options, DNSOption{Code: 10, Data: "aaaabbbbcccc", Name: "COOKIE"})

refJSON := `
@@ -444,10 +445,12 @@ func TestDnsMessage_JsonFlatten_Reference(t *testing.T) {
"dns.qname": "-",
"dns.qtype": "-",
"dns.rcode": "-",
"dns.qclass": "-",
"dns.resource-records.an.0.name": "google.nl",
"dns.resource-records.an.0.rdata": "142.251.39.99",
"dns.resource-records.an.0.rdatatype": "A",
"dns.resource-records.an.0.ttl": 300,
"dns.resource-records.an.0.class": "IN",
"dns.resource-records.ar": "-",
"dns.resource-records.ns": "-",
"dnstap.identity": "-",
@@ -869,6 +872,11 @@ func TestDnsMessage_TextFormat_DefaultDirectives(t *testing.T) {
dm: DNSMessage{DNS: DNS{Qname: "dnscollector.fr", Qtype: "AAAA", Opcode: 42}},
expected: "dnscollector.fr AAAA 42",
},
{
format: "qclass",
dm: DNSMessage{DNS: DNS{Qclass: "CH"}},
expected: "CH",
},
{
format: "operation",
dm: DNSMessage{DNSTap: DNSTap{Operation: "CLIENT_QUERY"}},
11 changes: 6 additions & 5 deletions docs/configuration.md
Original file line number Diff line number Diff line change
@@ -2,7 +2,9 @@

The configuration of DNS-collector is done through one yaml file named [`config.yml`](https://github.com/dmachard/go-dnscollector/blob/main/config.yml). When the DNS-collector starts, it will look for the config.yml from the current working directory.

A typically configuration in [multiplexer](./docs/running_mode.md) mode would have one or more collector to receive DNS traffic, and severals loggers to process the incoming traffics. You can take a look to the list of config [`examples`](examples.md).
A typically configuration in [multiplexer](./running_mode.md) mode would have one or more collector to receive DNS traffic, and severals loggers to process the incoming traffics. You can take a look to the list of config [`examples`](examples.md).

## Global

You can find the global settings below

@@ -11,8 +13,6 @@ You can find the global settings below
- [Custom text format](#custom-text-format)
- [Server identity](#server-identity)

## Global

### Trace

Logs can be enable to have more informations like debug, errors messages generated by the application
@@ -88,8 +88,9 @@ Default directives:
- `protocol`: protocol UDP, TCP
- `length`: the length of the query or reply in bytes
- `length-unit`: the length of the query or reply in bytes with unit (`b`)
- `qtype`: dns qtype
- `qname`: dns qname
- `qtype`: dns query type
- `qclass`: dns query class
- `qname`: dns query name
- `latency`: computed latency between queries and replies
- `answercount`: the number of answer
- `ttl`: answer ttl, only the first one
6 changes: 5 additions & 1 deletion docs/dnsjson.md
Original file line number Diff line number Diff line change
@@ -28,6 +28,7 @@ Example:
"qname": "eu.org",
"qtype": "A",
"id": 23455,
"qclass": "IN",
"flags": {
"qr": true,
"tc": false,
@@ -43,7 +44,8 @@ Example:
"name": "eu.org",
"rdatatype": "A",
"ttl": 2797,
"rdata": "78.194.169.74"
"rdata": "78.194.169.74",
"class": "IN"
}
],
"ns": [],
@@ -111,10 +113,12 @@ Here's a flat JSON output formatted using `jq`:
"dns.qname": "google.nl",
"dns.qtype": "A",
"dns.rcode": "NOERROR",
"dns.qclass": "IN",
"dns.resource-records.an.0.name": "google.nl",
"dns.resource-records.an.0.rdata": "142.251.39.99",
"dns.resource-records.an.0.rdatatype": "A",
"dns.resource-records.an.0.ttl": 300,
"dns.resource-records.an.0.class": "IN",
"dns.resource-records.ar": "-",
"dns.resource-records.ns": "-",
"dnstap.identity": "foo",
2 changes: 1 addition & 1 deletion processors/powerdns.go
Original file line number Diff line number Diff line change
@@ -270,7 +270,7 @@ RUN_LOOP:
rr := dnsutils.DNSAnswer{
Name: RRs[j].GetName(),
Rdatatype: dnsutils.RdatatypeToString(int(RRs[j].GetType())),
Class: int(RRs[j].GetClass()),
Class: dnsutils.ClassToString(int(RRs[j].GetClass())),
TTL: int(RRs[j].GetTtl()),
Rdata: rdata,
}

0 comments on commit 06c9293

Please sign in to comment.