Skip to content

Commit

Permalink
Fix bug: internet intents discovered with CNAME instead of the actual…
Browse files Browse the repository at this point in the history
… domain name (#223)
  • Loading branch information
NetanelBollag authored Jun 20, 2024
1 parent e06a975 commit 1348643
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 2 deletions.
32 changes: 30 additions & 2 deletions src/sniffer/pkg/collectors/dnssniffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/otterize/network-mapper/src/sniffer/pkg/config"
"github.com/otterize/network-mapper/src/sniffer/pkg/ipresolver"
"github.com/otterize/nilable"
"github.com/samber/lo"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
"net"
Expand Down Expand Up @@ -136,13 +137,21 @@ func (s *DNSSniffer) HandlePacket(packet gopacket.Packet) {
ip, _ := ipLayer.(*layers.IPv4)
dns, _ := dnsLayer.(*layers.DNS)
if dns.OpCode == layers.DNSOpCodeQuery && dns.ResponseCode == layers.DNSResponseCodeNoErr {
cnameToA := getCNameTranslation(dns)

for _, answer := range dns.Answers {
// This is the DNS Answer, so the Dst IP is the pod IP
if answer.Type != layers.DNSTypeA && answer.Type != layers.DNSTypeAAAA {
continue
}
hostName := string(answer.Name)
if nameFromCNAME, ok := cnameToA[hostName]; ok {
logrus.Debugf("Found CNAME record for %s: %s", hostName, nameFromCNAME)
hostName = nameFromCNAME
}

if !s.isRunningOnAWS {
s.addCapturedRequest(ip.DstIP.String(), "", string(answer.Name), answer.IP.String(), captureTime, nilable.From(int(answer.TTL)), nil)
s.addCapturedRequest(ip.DstIP.String(), "", hostName, answer.IP.String(), captureTime, nilable.From(int(answer.TTL)), nil)
continue
}
hostname, err := s.resolver.ResolveIP(ip.DstIP.String())
Expand All @@ -153,7 +162,7 @@ func (s *DNSSniffer) HandlePacket(packet gopacket.Packet) {
s.pending = append(s.pending, pendingCapture{
srcIp: ip.DstIP.String(),
srcHostname: hostname,
destHostnameOrIP: string(answer.Name),
destHostnameOrIP: hostName,
destIPFromDNS: answer.IP.String(),
time: captureTime,
ttl: nilable.From(int(answer.TTL)),
Expand All @@ -164,6 +173,25 @@ func (s *DNSSniffer) HandlePacket(packet gopacket.Packet) {
}
}

func getCNameTranslation(dns *layers.DNS) map[string]string {
// Probably there is one CNAME record per DNS packet, so we could just use the first one we find
// But, since the implementation uses a list of answers, we will support multiple CNAME records with one
// caveat: it won't work with multiple domains for the same CNAME which is really unlikely in the same packet
cnameAnswer := lo.Filter(dns.Answers, func(answer layers.DNSResourceRecord, _ int) bool {
return answer.Type == layers.DNSTypeCNAME && len(answer.CNAME) > 0 && len(answer.Name) > 0
})

cnameToA := make(map[string]string)
for _, answer := range cnameAnswer {
existing, found := cnameToA[string(answer.CNAME)]
if found && existing != string(answer.Name) {
logrus.Debugf("Multiple CNAME records for the same CNAME, overwriting %s with %s", existing, string(answer.Name))
}
cnameToA[string(answer.CNAME)] = string(answer.Name)
}
return cnameToA
}

func (s *DNSSniffer) RefreshHostsMapping() error {
if !s.isRunningOnAWS {
return nil
Expand Down
28 changes: 28 additions & 0 deletions src/sniffer/pkg/collectors/dnssniffer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,34 @@ func (s *SnifferTestSuite) TestHandlePacket() {
}, sniffer.CollectResults())
}

func (s *SnifferTestSuite) TestHandlePacketWithCNAME() {
sniffer := NewDNSSniffer(&ipresolver.MockIPResolver{}, false)

rawDnsResponse, err := hex.DecodeString("92e72b05f87b02af9e5f513c0800450000b1443940004011e0100af400020af4000900359c2b009d16a123e085800001000200000001036170690c6f74746572697a652d64657603636f6d0000010001036170690c6f74746572697a652d64657603636f6d000005000100000006001b08696e7465726e616c0c6f74746572697a652d64657603636f6d0008696e7465726e616c0c6f74746572697a652d64657603636f6d00000100010000000600040bdc020000002904d0000000000000")
if err != nil {
s.Require().NoError(err)
}
packet := gopacket.NewPacket(rawDnsResponse, layers.LayerTypeEthernet, gopacket.Default)
timestamp := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
packet.Metadata().CaptureInfo.Timestamp = timestamp
sniffer.HandlePacket(packet)
_ = sniffer.RefreshHostsMapping()

s.Require().Equal([]mapperclient.RecordedDestinationsForSrc{
{
SrcIp: "10.244.0.9",
Destinations: []mapperclient.Destination{
{
Destination: "api.otterize-dev.com",
DestinationIP: nilable.From("11.220.2.0"),
LastSeen: timestamp,
TTL: nilable.From(6),
},
},
},
}, sniffer.CollectResults())
}

func TestDNSSnifferSuite(t *testing.T) {
suite.Run(t, new(SnifferTestSuite))
}

0 comments on commit 1348643

Please sign in to comment.