Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add arcount, nscount and ancount #829

Merged
merged 3 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<p align="center">
<img src="https://goreportcard.com/badge/github.com/dmachard/go-dns-collector" alt="Go Report"/>
<img src="https://img.shields.io/badge/go%20version-min%201.21-green" alt="Go version"/>
<img src="https://img.shields.io/badge/go%20tests-501-green" alt="Go tests"/>
<img src="https://img.shields.io/badge/go%20tests-505-green" alt="Go tests"/>
<img src="https://img.shields.io/badge/go%20bench-21-green" alt="Go bench"/>
<img src="https://img.shields.io/badge/go%20lines-31528-green" alt="Go lines"/>
<img src="https://img.shields.io/badge/go%20lines-31634-green" alt="Go lines"/>
</p>

<p align="center">
Expand Down
31 changes: 22 additions & 9 deletions dnsutils/dnsmessage.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,10 @@ type DNS struct {
Qname string `json:"qname"`
Qclass string `json:"qclass"`

QuestionsCount int `json:"questions-count"`
QdCount int `json:"qdcount"`
AnCount int `json:"ancount"`
NsCount int `json:"nscount"`
ArCount int `json:"arcount"`

Qtype string `json:"qtype"`
Flags DNSFlags `json:"flags"`
Expand Down Expand Up @@ -611,7 +614,7 @@ func (dm *DNSMessage) String(format []string, fieldDelimiter string, fieldBounda
func (dm *DNSMessage) ToTextLine(format []string, fieldDelimiter string, fieldBoundary string) ([]byte, error) {
var s strings.Builder

answers := dm.DNS.DNSRRs.Answers
an := dm.DNS.DNSRRs.Answers
qname := dm.DNS.Qname
flags := dm.DNS.Flags

Expand Down Expand Up @@ -741,19 +744,26 @@ func (dm *DNSMessage) ToTextLine(format []string, fieldDelimiter string, fieldBo
s.WriteByte('-')
}
case directive == "ttl":
if len(answers) > 0 {
s.WriteString(strconv.Itoa(answers[0].TTL))
if len(an) > 0 {
s.WriteString(strconv.Itoa(an[0].TTL))
} else {
s.WriteByte('-')
}
case directive == "answer":
if len(answers) > 0 {
s.WriteString(answers[0].Rdata)
if len(an) > 0 {
s.WriteString(an[0].Rdata)
} else {
s.WriteByte('-')
}
case directive == "answercount":
s.WriteString(strconv.Itoa(len(answers)))

case directive == "questionscount" || directive == "qdcount":
s.WriteString(strconv.Itoa(dm.DNS.QdCount))
case directive == "answercount" || directive == "ancount":
s.WriteString(strconv.Itoa(dm.DNS.AnCount))
case directive == "nscount":
s.WriteString(strconv.Itoa(dm.DNS.NsCount))
case directive == "arcount":
s.WriteString(strconv.Itoa(dm.DNS.ArCount))

case directive == "edns-csubnet":
if len(dm.EDNS.Options) > 0 {
Expand Down Expand Up @@ -1130,7 +1140,10 @@ func (dm *DNSMessage) Flatten() (map[string]interface{}, error) {
"dns.qtype": dm.DNS.Qtype,
"dns.qclass": dm.DNS.Qclass,
"dns.rcode": dm.DNS.Rcode,
"dns.questions-count": dm.DNS.QuestionsCount,
"dns.qdcount": dm.DNS.QdCount,
"dns.ancount": dm.DNS.AnCount,
"dns.arcount": dm.DNS.ArCount,
"dns.nscount": dm.DNS.NsCount,
"dnstap.identity": dm.DNSTap.Identity,
"dnstap.latency": dm.DNSTap.Latency,
"dnstap.operation": dm.DNSTap.Operation,
Expand Down
10 changes: 8 additions & 2 deletions dnsutils/dnsmessage_json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ func TestDnsMessage_Json_Reference(t *testing.T) {
"qname": "-",
"qtype": "-",
"qclass": "-",
"questions-count": 0,
"qdcount": 0,
"ancount": 0,
"nscount": 0,
"arcount": 0,
"flags": {
"qr": false,
"tc": false,
Expand Down Expand Up @@ -287,7 +290,10 @@ func TestDnsMessage_JsonFlatten_Reference(t *testing.T) {
"dns.qtype": "-",
"dns.rcode": "-",
"dns.qclass": "-",
"dns.questions-count": 0,
"dns.qdcount": 0,
"dns.ancount": 0,
"dns.arcount": 0,
"dns.nscount": 0,
"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",
Expand Down
4 changes: 2 additions & 2 deletions dnsutils/dnsmessage_template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func TestDnsMessage_ToJinjaFormat(t *testing.T) {
template := `
;; Got {% if dm.DNS.Type == "QUERY" %}query{% else %}answer{% endif %} from {{ dm.NetworkInfo.QueryIP }}#{{ dm.NetworkInfo.QueryPort }}:
;; ->>HEADER<<- opcode: {{ dm.DNS.Opcode }}, status: {{ dm.DNS.Rcode }}, id: {{ dm.DNS.ID }}
;; flags: {{ dm.DNS.Flags.QR | yesno:"qr ," }}{{ dm.DNS.Flags.RD | yesno:"rd ," }}{{ dm.DNS.Flags.RA | yesno:"ra ," }}; QUERY: {{ dm.DNS.QuestionsCount }}, ANSWER: {{ dm.DNS.DNSRRs.Answers | length }}, AUTHORITY: {{ dm.DNS.DNSRRs.Nameservers | length }}, ADDITIONAL: {{ dm.DNS.DNSRRs.Records | length }}
;; flags: {{ dm.DNS.Flags.QR | yesno:"qr ," }}{{ dm.DNS.Flags.RD | yesno:"rd ," }}{{ dm.DNS.Flags.RA | yesno:"ra ," }}; QUERY: {{ dm.DNS.QdCount }}, ANSWER: {{ dm.DNS.DNSRRs.AnCount }}, AUTHORITY: {{ dm.DNS.DNSRRs.NsCount }}, ADDITIONAL: {{ dm.DNS.DNSRRs.ArCount }}

;; QUESTION SECTION:
;{{ dm.DNS.Qname }} {{ dm.DNS.Qclass }} {{ dm.DNS.Qtype }}
Expand Down Expand Up @@ -44,7 +44,7 @@ func BenchmarkDnsMessage_ToJinjaFormat(b *testing.B) {
template := `
;; Got {% if dm.DNS.Type == "QUERY" %}query{% else %}answer{% endif %} from {{ dm.NetworkInfo.QueryIP }}#{{ dm.NetworkInfo.QueryPort }}:
;; ->>HEADER<<- opcode: {{ dm.DNS.Opcode }}, status: {{ dm.DNS.Rcode }}, id: {{ dm.DNS.ID }}
;; flags: {{ dm.DNS.Flags.QR | yesno:"qr ," }}{{ dm.DNS.Flags.RD | yesno:"rd ," }}{{ dm.DNS.Flags.RA | yesno:"ra ," }}; QUERY: {{ dm.DNS.QuestionsCount }}, ANSWER: {{ dm.DNS.DNSRRs.Answers | length }}, AUTHORITY: {{ dm.DNS.DNSRRs.Nameservers | length }}, ADDITIONAL: {{ dm.DNS.DNSRRs.Records | length }}
;; flags: {{ dm.DNS.Flags.QR | yesno:"qr ," }}{{ dm.DNS.Flags.RD | yesno:"rd ," }}{{ dm.DNS.Flags.RA | yesno:"ra ," }}; QUERY: {{ dm.DNS.QdCount }}, ANSWER: {{ dm.DNS.DNSRRs.AnCount }}, AUTHORITY: {{ dm.DNS.DNSRRs.NsCount }}, ADDITIONAL: {{ dm.DNS.ArCount }}

;; QUESTION SECTION:
;{{ dm.DNS.Qname }} {{ dm.DNS.Qclass }} {{ dm.DNS.Qtype }}
Expand Down
10 changes: 10 additions & 0 deletions dnsutils/dnsmessage_text_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,16 @@ func TestDnsMessage_TextFormat_DefaultDirectives(t *testing.T) {
dm: DNSMessage{DNSTap: DNSTap{QueryZone: "queryzone.test"}},
expected: "queryzone.test",
},
{
format: "qdcount",
dm: DNSMessage{DNS: DNS{QdCount: 1}},
expected: "1",
},
{
format: "ancount nscount arcount",
dm: DNSMessage{DNS: DNS{AnCount: 1, ArCount: 2, NsCount: 3}},
expected: "1 3 2",
},
}

for _, tc := range testcases {
Expand Down
30 changes: 30 additions & 0 deletions dnsutils/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,36 @@ func GetFakeDNS() ([]byte, error) {
return dnsmsg.Pack()
}

func GetDNSResponsePacket() ([]byte, error) {
dnsmsg := new(dns.Msg)
dnsmsg.SetQuestion("dns.collector.", dns.TypeA)

// Build a fake response for the question
rr, err := dns.NewRR("dns.collector. 3600 IN A 192.168.1.1")
if err != nil {
return nil, err
}

// Add the resource record (the answer) to the message
dnsmsg.Answer = append(dnsmsg.Answer, rr)

// Build an authoritative NS record (Authoritative section)
nsRR, err := dns.NewRR("collector. 3600 IN NS ns1.collector.")
if err != nil {
return nil, err
}
dnsmsg.Ns = append(dnsmsg.Ns, nsRR)

// Build an additional A record for the authoritative NS (Additional section)
additionalRR, err := dns.NewRR("ns1.collector. 3600 IN A 192.168.2.1")
if err != nil {
return nil, err
}
dnsmsg.Extra = append(dnsmsg.Extra, additionalRR)

return dnsmsg.Pack()
}

func GetFakeDNSMessage() DNSMessage {
dm := DNSMessage{}
dm.Init()
Expand Down
2 changes: 1 addition & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ Example to enable output similiar to dig style:
text-jinja: |+
;; Got {% if dm.DNS.Type == "QUERY" %}query{% else %}answer{% endif %} from {{ dm.NetworkInfo.QueryIP }}#{{ dm.NetworkInfo.QueryPort }}:
;; ->>HEADER<<- opcode: {{ dm.DNS.Opcode }}, status: {{ dm.DNS.Rcode }}, id: {{ dm.DNS.ID }}
;; flags: {{ dm.DNS.Flags.QR | yesno:"qr ," }}{{ dm.DNS.Flags.RD | yesno:"rd ," }}{{ dm.DNS.Flags.RA | yesno:"ra ," }}; QUERY: {{ dm.DNS.QuestionsCount }}, ANSWER: {{ dm.DNS.DNSRRs.Answers | length }}, AUTHORITY: {{ dm.DNS.DNSRRs.Nameservers | length }}, ADDITIONAL: {{ dm.DNS.DNSRRs.Records | length }}
;; flags: {{ dm.DNS.Flags.QR | yesno:"qr ," }}{{ dm.DNS.Flags.RD | yesno:"rd ," }}{{ dm.DNS.Flags.RA | yesno:"ra ," }}; QUERY: {{ dm.DNS.QdCount }}, ANSWER: {{ dm.DNS.AnCount }}, AUTHORITY: {{ dm.DNS.NsCount }}, ADDITIONAL: {{ dm.DNS.ArCount }}

;; QUESTION SECTION:
;{{ dm.DNS.Qname }} {{ dm.DNS.Qclass }} {{ dm.DNS.Qtype }}
Expand Down
10 changes: 8 additions & 2 deletions docs/dnsjson.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ Example:
"qtype": "A",
"id": 23455,
"qclass": "IN",
"questions-count": 0,
"qdcount": 1,
"ancount": 1,
"nscount": 0,
"arcount": 0,
"flags": {
"qr": true,
"tc": false,
Expand Down Expand Up @@ -117,7 +120,10 @@ Here's a flat JSON output formatted using `jq`:
"dns.qtype": "A",
"dns.rcode": "NOERROR",
"dns.qclass": "IN",
"dns.questions-count": 0,
"dns.qdcount": 0,
"dns.ancount": 1,
"dns.arcount": 0,
"dns.nscount": 0,
"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",
Expand Down
7 changes: 5 additions & 2 deletions workers/dnsprocessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,11 @@ func (w *DNSProcessor) StartCollect() {
w.LogError("dns parser malformed packet: %s - %v+", err, dm)
}

// get number of questions
dm.DNS.QuestionsCount = dnsHeader.Qdcount
// get number of questions and answers
dm.DNS.QdCount = dnsHeader.Qdcount
dm.DNS.AnCount = dnsHeader.Ancount
dm.DNS.ArCount = dnsHeader.Arcount
dm.DNS.NsCount = dnsHeader.Nscount

// dns reply ?
if dnsHeader.Qr == 1 {
Expand Down
40 changes: 40 additions & 0 deletions workers/dnsprocessor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,46 @@ func Test_DnsProcessor(t *testing.T) {
}
}

func Test_DnsProcessor_DecodeCounters(t *testing.T) {
logger := logger.New(true)
var o bytes.Buffer
logger.SetOutput(&o)

// init and run the dns processor
fl := GetWorkerForTest(pkgconfig.DefaultBufferSize)

consumer := NewDNSProcessor(pkgconfig.GetDefaultConfig(), logger, "test", 512)
consumer.AddDefaultRoute(fl)
consumer.AddDroppedRoute(fl)
go consumer.StartCollect()

// get dns packet
responsePacket, _ := dnsutils.GetDNSResponsePacket()

// prepare dns message
dm := dnsutils.GetFakeDNSMessage()
dm.DNS.Payload = responsePacket
dm.DNS.Length = len(responsePacket)

// send dm to consumer
consumer.GetInputChannel() <- dm

// read dns message from dnstap consumer
dmOut := <-fl.GetInputChannel()
if dmOut.DNS.QdCount != 1 {
t.Errorf("invalid number of questions in dns message: got %d expect 1", dmOut.DNS.QdCount)
}
if dmOut.DNS.NsCount != 1 {
t.Errorf("invalid number of nscount in dns message: got %d expect 1", dmOut.DNS.NsCount)
}
if dmOut.DNS.AnCount != 1 {
t.Errorf("invalid number of ancount in dns message: got %d expect 1", dmOut.DNS.AnCount)
}
if dmOut.DNS.ArCount != 1 {
t.Errorf("invalid number of arcount in dns message: got %d expect 1", dmOut.DNS.ArCount)
}
}

func Test_DnsProcessor_BufferLoggerIsFull(t *testing.T) {
// redirect stdout output to bytes buffer
logsChan := make(chan logger.LogEntry, 10)
Expand Down
5 changes: 4 additions & 1 deletion workers/dnstapserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,10 @@ func (w *DNSTapProcessor) StartCollect() {
}

// get number of questions
dm.DNS.QuestionsCount = dnsHeader.Qdcount
dm.DNS.QdCount = dnsHeader.Qdcount
dm.DNS.AnCount = dnsHeader.Ancount
dm.DNS.ArCount = dnsHeader.Arcount
dm.DNS.NsCount = dnsHeader.Nscount

if err = dnsutils.DecodePayload(&dm, &dnsHeader, w.GetConfig()); err != nil {
dm.DNS.MalformedPacket = true
Expand Down
49 changes: 48 additions & 1 deletion workers/dnstapserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ func Test_DnstapCollector_CloseFrameStream(t *testing.T) {
c.Stop()
}

func Test_DnstapProcessor(t *testing.T) {
func Test_DnstapProcessor_toDNSMessage(t *testing.T) {
logger := logger.New(true)
var o bytes.Buffer
logger.SetOutput(&o)
Expand Down Expand Up @@ -240,6 +240,53 @@ func Test_DnstapProcessor(t *testing.T) {
}
}

func Test_DnstapProcessor_DecodeCounters(t *testing.T) {
logger := logger.New(true)
var o bytes.Buffer
logger.SetOutput(&o)

// run the consumer with a fake logger
fl := GetWorkerForTest(pkgconfig.DefaultBufferSize)

// init the dnstap consumer
consumer := NewDNSTapProcessor(0, "peertest", pkgconfig.GetDefaultConfig(), logger, "test", 512)
consumer.AddDefaultRoute(fl)
consumer.AddDroppedRoute(fl)

// get dns packet
responsePacket, _ := dnsutils.GetDNSResponsePacket()

// prepare dnstap
dt := &dnstap.Dnstap{}
dt.Type = dnstap.Dnstap_Type.Enum(1)

dt.Message = &dnstap.Message{}
dt.Message.Type = dnstap.Message_Type.Enum(6) // CLIENT_RESPONSE
dt.Message.ResponseMessage = responsePacket
data, _ := proto.Marshal(dt)

// start the consumer
go consumer.StartCollect()

// add packet to consumer
consumer.GetDataChannel() <- data

// read dns message from dnstap consumer
dm := <-fl.GetInputChannel()
if dm.DNS.QdCount != 1 {
t.Errorf("invalid number of questions in dns message: got %d expect 1", dm.DNS.QdCount)
}
if dm.DNS.NsCount != 1 {
t.Errorf("invalid number of nscount in dns message: got %d expect 1", dm.DNS.NsCount)
}
if dm.DNS.AnCount != 1 {
t.Errorf("invalid number of ancount in dns message: got %d expect 1", dm.DNS.AnCount)
}
if dm.DNS.ArCount != 1 {
t.Errorf("invalid number of arcount in dns message: got %d expect 1", dm.DNS.ArCount)
}
}

func Test_DnstapProcessor_MalformedDnsHeader(t *testing.T) {
// run the consumer with a fake logger
fl := GetWorkerForTest(pkgconfig.DefaultBufferSize)
Expand Down
Loading