Skip to content

Commit

Permalink
powerdns collector: replace go/dnsmessage
Browse files Browse the repository at this point in the history
by miekg/dns
  • Loading branch information
dmachard committed Nov 12, 2023
1 parent 9a586fb commit a25e951
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 76 deletions.
3 changes: 3 additions & 0 deletions config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ multiplexer:
dnstap:
listen-ip: 0.0.0.0
listen-port: 6000
add-dns-payload: false
transforms:
normalize:
qname-lowercase: false
Expand Down Expand Up @@ -199,6 +200,8 @@ multiplexer:
# reset-conn: true
# # Channel buffer size for incoming packets, number of packet before to drop it.
# chan-buffer-size: 65535
# # Reconstruct DNS payload
# add-dns-payload: false

# # tzsp (TaZmen Sniffer Protocol)
# tzsp:
Expand Down
10 changes: 6 additions & 4 deletions dnsutils/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import "crypto/tls"
const (
STR_UNKNOWN = "UNKNOWN"

PROG_NAME = "dnscollector"
LOCALHOST_IP = "127.0.0.1"
ANY_IP = "0.0.0.0"
HTTP_OK = "HTTP/1.1 200 OK\r\n\r\n"
PROG_NAME = "dnscollector"
LOCALHOST_IP = "127.0.0.1"
ANY_IP = "0.0.0.0"
HTTP_OK = "HTTP/1.1 200 OK\r\n\r\n"

VALID_DOMAIN = "dnscollector.dev."
BAD_LABEL_DOMAIN = "ultramegaverytoolonglabel-ultramegaverytoolonglabel-ultramegaverytoolonglabel.dnscollector.dev."
BAD_VERYLONG_DOMAIN = "ultramegaverytoolonglabel.dnscollector" +
"ultramegaverytoolonglabel-ultramegaverytoolonglabel-" +
Expand Down
112 changes: 49 additions & 63 deletions processors/powerdns.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/dmachard/go-dnscollector/transformers"
"github.com/dmachard/go-logger"
powerdns_protobuf "github.com/dmachard/go-powerdns-protobuf"
"golang.org/x/net/dns/dnsmessage"
"github.com/miekg/dns"
"google.golang.org/protobuf/proto"
)

Expand Down Expand Up @@ -292,76 +292,62 @@ RUN_LOOP:

if d.config.Collectors.PowerDNS.AddDnsPayload {

qname, err := dnsmessage.NewName(pbdm.Question.GetQName())
if err != nil {
dm.DNS.MalformedPacket = true
} else {
qname := dns.Fqdn(pbdm.Question.GetQName())
newDns := new(dns.Msg)
newDns.Id = uint16(pbdm.GetId())
newDns.Response = false

newDns := dnsmessage.Message{
Header: dnsmessage.Header{ID: uint16(pbdm.GetId()), Response: false},
Questions: []dnsmessage.Question{
{
Name: qname,
Type: dnsmessage.Type(pbdm.Question.GetQType()),
Class: dnsmessage.Class(pbdm.Question.GetQClass()),
},
},
}
if int(pbdm.Type.Number())%2 != 1 {
newDns.Header.Response = true
newDns.Header.RCode = dnsmessage.RCode(pbdm.Response.GetRcode())

newDns.Answers = []dnsmessage.Resource{}
// add RR only A, AAAA and CNAME are exported by PowerDNS
rrs := pbdm.GetResponse().GetRrs()

for j := range rrs {
rrname, err := dnsmessage.NewName(rrs[j].GetName())
if err != nil {
dm.DNS.MalformedPacket = true
continue
question := dns.Question{
Name: qname,
Qtype: uint16(pbdm.Question.GetQType()),
Qclass: uint16(pbdm.Question.GetQClass()),
}
newDns.Question = append(newDns.Question, question)

if int(pbdm.Type.Number())%2 != 1 {
newDns.Response = true
newDns.Rcode = int(pbdm.Response.GetRcode())

newDns.Answer = []dns.RR{}
rrs := pbdm.GetResponse().GetRrs()
for j := range rrs {
rrname := dns.Fqdn(rrs[j].GetName())
switch rrs[j].GetType() {
// A
case 1:
rdata := &dns.A{
Hdr: dns.RR_Header{Name: rrname, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: rrs[j].GetTtl()},
A: net.IP(rrs[j].GetRdata()),
}
r := dnsmessage.Resource{}
r.Header = dnsmessage.ResourceHeader{
Name: rrname,
Type: dnsmessage.Type(rrs[j].GetType()),
Class: dnsmessage.Class(rrs[j].GetClass()),
TTL: rrs[j].GetTtl(),
newDns.Answer = append(newDns.Answer, rdata)
// AAAA
case 28:
rdata := &dns.AAAA{
Hdr: dns.RR_Header{Name: rrname, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: rrs[j].GetTtl()},
AAAA: net.IP(rrs[j].GetRdata()),
}
switch {
// A
case RRs[j].GetType() == 1:
var rdata [4]byte
copy(rdata[:], rrs[j].GetRdata()[:4])
r.Body = &dnsmessage.AResource{A: rdata}
// AAAA
case RRs[j].GetType() == 28:
var rdata [16]byte
copy(rdata[:], rrs[j].GetRdata()[:16])
r.Body = &dnsmessage.AAAAResource{AAAA: rdata}
// CNAME
case RRs[j].GetType() == 5:
cname, err := dnsmessage.NewName(string(rrs[j].GetRdata()))
if err != nil {
dm.DNS.MalformedPacket = true
continue
}
r.Body = &dnsmessage.CNAMEResource{CNAME: cname}
newDns.Answer = append(newDns.Answer, rdata)
// CNAME
case 5:
rdata := &dns.CNAME{
Hdr: dns.RR_Header{Name: rrname, Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: rrs[j].GetTtl()},
Target: dns.Fqdn(string(rrs[j].GetRdata())),
}

newDns.Answers = append(newDns.Answers, r)

newDns.Answer = append(newDns.Answer, rdata)
}

}

if !dm.DNS.MalformedPacket {
pktWire, err := newDns.Pack()
if err == nil {
dm.DNS.Payload = pktWire
} else {
dm.DNS.MalformedPacket = true
}
}

pktWire, err := newDns.Pack()
if err == nil {
dm.DNS.Payload = pktWire
if dm.DNS.Length == 0 {
dm.DNS.Length = len(pktWire)
}
} else {
dm.DNS.MalformedPacket = true
}
}

Expand Down
75 changes: 66 additions & 9 deletions processors/powerdns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/dmachard/go-dnscollector/dnsutils"
"github.com/dmachard/go-logger"
powerdns_protobuf "github.com/dmachard/go-powerdns-protobuf"
"github.com/miekg/dns"
"google.golang.org/protobuf/proto"
)

Expand All @@ -14,8 +15,8 @@ func TestPowerDNS_Processor(t *testing.T) {
consumer := NewPdnsProcessor(0, dnsutils.GetFakeConfig(), logger.New(false), "test", 512)
chan_to := make(chan dnsutils.DnsMessage, 512)

// prepare dnstap
dnsQname := "test."
// init the powerdns processor
dnsQname := dnsutils.VALID_DOMAIN
dnsQuestion := powerdns_protobuf.PBDNSMessage_DNSQuestion{QName: &dnsQname}

dm := &powerdns_protobuf.PBDNSMessage{}
Expand All @@ -38,7 +39,54 @@ func TestPowerDNS_Processor(t *testing.T) {
}
}

func TestPowerDNS_Processor_InvalidLabelLength(t *testing.T) {
func TestPowerDNS_Processor_AddDNSPayload_Valid(t *testing.T) {
cfg := dnsutils.GetFakeConfig()
cfg.Collectors.PowerDNS.AddDnsPayload = true

// init the powerdns processor
consumer := NewPdnsProcessor(0, cfg, logger.New(false), "test", 512)
chan_to := make(chan dnsutils.DnsMessage, 1)

// prepare powerdns message
dnsQname := dnsutils.VALID_DOMAIN
dnsQuestion := powerdns_protobuf.PBDNSMessage_DNSQuestion{QName: &dnsQname}

dm := &powerdns_protobuf.PBDNSMessage{}
dm.ServerIdentity = []byte("powerdnspb")
dm.Type = powerdns_protobuf.PBDNSMessage_DNSQueryType.Enum()
dm.SocketProtocol = powerdns_protobuf.PBDNSMessage_DNSCryptUDP.Enum()
dm.SocketFamily = powerdns_protobuf.PBDNSMessage_INET.Enum()
dm.Question = &dnsQuestion

data, _ := proto.Marshal(dm)

// start the consumer and add packet
go consumer.Run([]chan dnsutils.DnsMessage{chan_to}, []string{"test"})
consumer.GetChannel() <- data

// read dns message
msg := <-chan_to

// checks
if msg.DNS.Length == 0 {
t.Errorf("invalid length got %d", msg.DNS.Length)
}
if len(msg.DNS.Payload) == 0 {
t.Errorf("invalid payload length %d", len(msg.DNS.Payload))
}

// valid dns payload ?
var decodedPayload dns.Msg
err := decodedPayload.Unpack(msg.DNS.Payload)
if err != nil {
t.Errorf("unpack error %s", err)
}
if decodedPayload.Question[0].Name != dnsutils.VALID_DOMAIN {
t.Errorf("invalid qname in payload: %s", decodedPayload.Question[0].Name)
}
}

func TestPowerDNS_Processor_AddDNSPayload_InvalidLabelLength(t *testing.T) {
cfg := dnsutils.GetFakeConfig()
cfg.Collectors.PowerDNS.AddDnsPayload = true

Expand All @@ -48,10 +96,11 @@ func TestPowerDNS_Processor_InvalidLabelLength(t *testing.T) {

// prepare dnstap
dnsQname := dnsutils.BAD_LABEL_DOMAIN
dnsQuestion := powerdns_protobuf.PBDNSMessage_DNSQuestion{QName: &dnsQname}
dnsQuestion := powerdns_protobuf.PBDNSMessage_DNSQuestion{QName: &dnsQname, QType: proto.Uint32(1), QClass: proto.Uint32(1)}

dm := &powerdns_protobuf.PBDNSMessage{}
dm.ServerIdentity = []byte("powerdnspb")
dm.Id = proto.Uint32(2000)
dm.Type = powerdns_protobuf.PBDNSMessage_DNSQueryType.Enum()
dm.SocketProtocol = powerdns_protobuf.PBDNSMessage_DNSCryptUDP.Enum()
dm.SocketFamily = powerdns_protobuf.PBDNSMessage_INET.Enum()
Expand All @@ -70,7 +119,7 @@ func TestPowerDNS_Processor_InvalidLabelLength(t *testing.T) {
}
}

func TestPowerDNS_Processor_Qname_TooLongDomain(t *testing.T) {
func TestPowerDNS_Processor_AddDNSPayload_QnameTooLongDomain(t *testing.T) {
cfg := dnsutils.GetFakeConfig()
cfg.Collectors.PowerDNS.AddDnsPayload = true

Expand Down Expand Up @@ -102,7 +151,7 @@ func TestPowerDNS_Processor_Qname_TooLongDomain(t *testing.T) {
}
}

func TestPowerDNS_Processor_Answers_TooLongDomain(t *testing.T) {
func TestPowerDNS_Processor_AddDNSPayload_AnswersTooLongDomain(t *testing.T) {
cfg := dnsutils.GetFakeConfig()
cfg.Collectors.PowerDNS.AddDnsPayload = true

Expand All @@ -111,12 +160,18 @@ func TestPowerDNS_Processor_Answers_TooLongDomain(t *testing.T) {
chan_to := make(chan dnsutils.DnsMessage, 512)

// prepare dnstap
dnsQname := "dnscollector.dev."
dnsQname := dnsutils.VALID_DOMAIN
dnsQuestion := powerdns_protobuf.PBDNSMessage_DNSQuestion{QName: &dnsQname}

dnsRR := dnsutils.BAD_VERYLONG_DOMAIN
rrQname := dnsutils.BAD_VERYLONG_DOMAIN
rrDns := powerdns_protobuf.PBDNSMessage_DNSResponse_DNSRR{
Name: &rrQname,
Class: proto.Uint32(1),
Type: proto.Uint32(1),
Rdata: []byte{0x01, 0x00, 0x00, 0x01},
}
dnsReply := powerdns_protobuf.PBDNSMessage_DNSResponse{}
dnsReply.Rrs = append(dnsReply.Rrs, &powerdns_protobuf.PBDNSMessage_DNSResponse_DNSRR{Name: &dnsRR})
dnsReply.Rrs = append(dnsReply.Rrs, &rrDns)

dm := &powerdns_protobuf.PBDNSMessage{}
dm.ServerIdentity = []byte("powerdnspb")
Expand All @@ -134,6 +189,8 @@ func TestPowerDNS_Processor_Answers_TooLongDomain(t *testing.T) {

// read dns message from dnstap consumer
msg := <-chan_to

// tests verifications
if !msg.DNS.MalformedPacket {
t.Errorf("DNS message is not malformed")
}
Expand Down

0 comments on commit a25e951

Please sign in to comment.