diff --git a/pkg/dnscache/events b/pkg/dnscache/events new file mode 100644 index 000000000000..d4974ed057d0 --- /dev/null +++ b/pkg/dnscache/events @@ -0,0 +1,8 @@ +{"timestamp":3399319014234770100,"threadStartTime":3399319006836545726,"processorId":4,"processId":2546510,"cgroupId":145296,"threadId":2546511,"parentProcessId":2546328,"hostProcessId":2546510,"hostThreadId":2546511,"hostParentProcessId":2546328,"userId":1000,"mountNamespace":4026531841,"pidNamespace":4026531836,"processName":"isc-net-0000","executable":{"path":""},"hostName":"ip-172-31-75-17","containerId":"","container":{},"kubernetes":{},"eventId":"2006","eventName":"net_packet_dns","matchedPolicies":[""],"argsNum":5,"returnValue":0,"syscall":"sendmmsg","stackAddresses":[0],"contextFlags":{"containerStarted":false,"isCompat":false},"threadEntityId":458119841,"processEntityId":1413552547,"parentEntityId":3216664721,"args":[{"name":"src","type":"const char*","value":"127.0.0.1"},{"name":"dst","type":"const char*","value":"127.0.0.53"},{"name":"src_port","type":"u16","value":56900},{"name":"dst_port","type":"u16","value":53},{"name":"proto_dns","type":"trace.ProtoDNS","value":{"ID":20274,"QR":0,"opCode":"query","AA":0,"TC":0,"RD":1,"RA":0,"Z":0,"responseCode":"no error","QDCount":1,"ANCount":0,"NSCount":0,"ARCount":0,"questions":[{"name":"uol.com","type":"CNAME","class":"IN"}],"answers":[],"authorities":[],"additionals":[]}}]} +{"timestamp":3399319014363298501,"threadStartTime":3399319006836545726,"processorId":1,"processId":2546510,"cgroupId":145296,"threadId":2546511,"parentProcessId":2546328,"hostProcessId":2546510,"hostThreadId":2546511,"hostParentProcessId":2546328,"userId":1000,"mountNamespace":4026531841,"pidNamespace":4026531836,"processName":"isc-net-0000","executable":{"path":""},"hostName":"ip-172-31-75-17","containerId":"","container":{},"kubernetes":{},"eventId":"2006","eventName":"net_packet_dns","matchedPolicies":[""],"argsNum":5,"returnValue":0,"syscall":"","stackAddresses":[0],"contextFlags":{"containerStarted":false,"isCompat":false},"threadEntityId":458119841,"processEntityId":1413552547,"parentEntityId":3216664721,"args":[{"name":"src","type":"const char*","value":"127.0.0.53"},{"name":"dst","type":"const char*","value":"127.0.0.1"},{"name":"src_port","type":"u16","value":53},{"name":"dst_port","type":"u16","value":56900},{"name":"proto_dns","type":"trace.ProtoDNS","value":{"ID":20274,"QR":1,"opCode":"query","AA":0,"TC":0,"RD":1,"RA":1,"Z":0,"responseCode":"no error","QDCount":1,"ANCount":1,"NSCount":0,"ARCount":0,"questions":[{"name":"uol.com","type":"CNAME","class":"IN"}],"answers":[{"name":"uol.com","type":"CNAME","class":"IN","TTL":3600,"IP":"","NS":"","CNAME":"d3so3xb2gvtwlo.cloudfront.net","PTR":"","TXTs":[],"SOA":{"MName":"","RName":"","serial":0,"refresh":0,"retry":0,"expire":0,"minimum":0},"SRV":{"priority":0,"weight":0,"port":0,"name":""},"MX":{"preference":0,"name":""},"OPT":[],"URI":{"priority":0,"weight":0,"target":""},"TXT":""}],"authorities":[],"additionals":[]}}]} +{"timestamp":3399319021297982617,"threadStartTime":3399319006836545726,"processorId":5,"processId":2546510,"cgroupId":145296,"threadId":2546511,"parentProcessId":2546328,"hostProcessId":2546510,"hostThreadId":2546511,"hostParentProcessId":2546328,"userId":1000,"mountNamespace":4026531841,"pidNamespace":4026531836,"processName":"isc-net-0000","executable":{"path":""},"hostName":"ip-172-31-75-17","containerId":"","container":{},"kubernetes":{},"eventId":"2006","eventName":"net_packet_dns","matchedPolicies":[""],"argsNum":5,"returnValue":0,"syscall":"sendmmsg","stackAddresses":[0],"contextFlags":{"containerStarted":false,"isCompat":false},"threadEntityId":458119841,"processEntityId":1413552547,"parentEntityId":3216664721,"args":[{"name":"src","type":"const char*","value":"127.0.0.1"},{"name":"dst","type":"const char*","value":"127.0.0.53"},{"name":"src_port","type":"u16","value":57585},{"name":"dst_port","type":"u16","value":53},{"name":"proto_dns","type":"trace.ProtoDNS","value":{"ID":1707,"QR":0,"opCode":"query","AA":0,"TC":0,"RD":1,"RA":0,"Z":0,"responseCode":"no error","QDCount":1,"ANCount":0,"NSCount":0,"ARCount":0,"questions":[{"name":"d3so3xb2gvtwlo.cloudfront.net","type":"A","class":"IN"}],"answers":[],"authorities":[],"additionals":[]}}]} +{"timestamp":3399319021303322761,"threadStartTime":3399319006836545726,"processorId":1,"processId":2546510,"cgroupId":145296,"threadId":2546511,"parentProcessId":2546328,"hostProcessId":2546510,"hostThreadId":2546511,"hostParentProcessId":2546328,"userId":1000,"mountNamespace":4026531841,"pidNamespace":4026531836,"processName":"isc-net-0000","executable":{"path":""},"hostName":"ip-172-31-75-17","containerId":"","container":{},"kubernetes":{},"eventId":"2006","eventName":"net_packet_dns","matchedPolicies":[""],"argsNum":5,"returnValue":0,"syscall":"","stackAddresses":[0],"contextFlags":{"containerStarted":false,"isCompat":false},"threadEntityId":458119841,"processEntityId":1413552547,"parentEntityId":3216664721,"args":[{"name":"src","type":"const char*","value":"127.0.0.53"},{"name":"dst","type":"const char*","value":"127.0.0.1"},{"name":"src_port","type":"u16","value":53},{"name":"dst_port","type":"u16","value":57585},{"name":"proto_dns","type":"trace.ProtoDNS","value":{"ID":1707,"QR":1,"opCode":"query","AA":0,"TC":0,"RD":1,"RA":1,"Z":0,"responseCode":"no error","QDCount":1,"ANCount":4,"NSCount":0,"ARCount":0,"questions":[{"name":"d3so3xb2gvtwlo.cloudfront.net","type":"A","class":"IN"}],"answers":[{"name":"d3so3xb2gvtwlo.cloudfront.net","type":"A","class":"IN","TTL":60,"IP":"18.160.41.95","NS":"","CNAME":"","PTR":"","TXTs":[],"SOA":{"MName":"","RName":"","serial":0,"refresh":0,"retry":0,"expire":0,"minimum":0},"SRV":{"priority":0,"weight":0,"port":0,"name":""},"MX":{"preference":0,"name":""},"OPT":[],"URI":{"priority":0,"weight":0,"target":""},"TXT":""},{"name":"d3so3xb2gvtwlo.cloudfront.net","type":"A","class":"IN","TTL":60,"IP":"18.160.41.66","NS":"","CNAME":"","PTR":"","TXTs":[],"SOA":{"MName":"","RName":"","serial":0,"refresh":0,"retry":0,"expire":0,"minimum":0},"SRV":{"priority":0,"weight":0,"port":0,"name":""},"MX":{"preference":0,"name":""},"OPT":[],"URI":{"priority":0,"weight":0,"target":""},"TXT":""},{"name":"d3so3xb2gvtwlo.cloudfront.net","type":"A","class":"IN","TTL":60,"IP":"18.160.41.110","NS":"","CNAME":"","PTR":"","TXTs":[],"SOA":{"MName":"","RName":"","serial":0,"refresh":0,"retry":0,"expire":0,"minimum":0},"SRV":{"priority":0,"weight":0,"port":0,"name":""},"MX":{"preference":0,"name":""},"OPT":[],"URI":{"priority":0,"weight":0,"target":""},"TXT":""},{"name":"d3so3xb2gvtwlo.cloudfront.net","type":"A","class":"IN","TTL":60,"IP":"18.160.41.122","NS":"","CNAME":"","PTR":"","TXTs":[],"SOA":{"MName":"","RName":"","serial":0,"refresh":0,"retry":0,"expire":0,"minimum":0},"SRV":{"priority":0,"weight":0,"port":0,"name":""},"MX":{"preference":0,"name":""},"OPT":[],"URI":{"priority":0,"weight":0,"target":""},"TXT":""}],"authorities":[],"additionals":[]}}]} +{"timestamp":3399319024978841794,"threadStartTime":3399319006836545726,"processorId":3,"processId":2546510,"cgroupId":145296,"threadId":2546511,"parentProcessId":2546328,"hostProcessId":2546510,"hostThreadId":2546511,"hostParentProcessId":2546328,"userId":1000,"mountNamespace":4026531841,"pidNamespace":4026531836,"processName":"isc-net-0000","executable":{"path":""},"hostName":"ip-172-31-75-17","containerId":"","container":{},"kubernetes":{},"eventId":"2006","eventName":"net_packet_dns","matchedPolicies":[""],"argsNum":5,"returnValue":0,"syscall":"sendmmsg","stackAddresses":[0],"contextFlags":{"containerStarted":false,"isCompat":false},"threadEntityId":458119841,"processEntityId":1413552547,"parentEntityId":3216664721,"args":[{"name":"src","type":"const char*","value":"127.0.0.1"},{"name":"dst","type":"const char*","value":"127.0.0.53"},{"name":"src_port","type":"u16","value":60698},{"name":"dst_port","type":"u16","value":53},{"name":"proto_dns","type":"trace.ProtoDNS","value":{"ID":27028,"QR":0,"opCode":"query","AA":0,"TC":0,"RD":1,"RA":0,"Z":0,"responseCode":"no error","QDCount":1,"ANCount":0,"NSCount":0,"ARCount":0,"questions":[{"name":"d3so3xb2gvtwlo.cloudfront.net","type":"AAAA","class":"IN"}],"answers":[],"authorities":[],"additionals":[]}}]} +{"timestamp":3399319024983948654,"threadStartTime":3399319006836545726,"processorId":4,"processId":2546510,"cgroupId":145296,"threadId":2546511,"parentProcessId":2546328,"hostProcessId":2546510,"hostThreadId":2546511,"hostParentProcessId":2546328,"userId":1000,"mountNamespace":4026531841,"pidNamespace":4026531836,"processName":"isc-net-0000","executable":{"path":""},"hostName":"ip-172-31-75-17","containerId":"","container":{},"kubernetes":{},"eventId":"2006","eventName":"net_packet_dns","matchedPolicies":[""],"argsNum":5,"returnValue":0,"syscall":"","stackAddresses":[0],"contextFlags":{"containerStarted":false,"isCompat":false},"threadEntityId":458119841,"processEntityId":1413552547,"parentEntityId":3216664721,"args":[{"name":"src","type":"const char*","value":"127.0.0.53"},{"name":"dst","type":"const char*","value":"127.0.0.1"},{"name":"src_port","type":"u16","value":53},{"name":"dst_port","type":"u16","value":60698},{"name":"proto_dns","type":"trace.ProtoDNS","value":{"ID":27028,"QR":1,"opCode":"query","AA":0,"TC":0,"RD":1,"RA":1,"Z":0,"responseCode":"no error","QDCount":1,"ANCount":8,"NSCount":0,"ARCount":0,"questions":[{"name":"d3so3xb2gvtwlo.cloudfront.net","type":"AAAA","class":"IN"}],"answers":[{"name":"d3so3xb2gvtwlo.cloudfront.net","type":"AAAA","class":"IN","TTL":60,"IP":"2600:9000:24f2:fa00:b:8c7a:5300:93a1","NS":"","CNAME":"","PTR":"","TXTs":[],"SOA":{"MName":"","RName":"","serial":0,"refresh":0,"retry":0,"expire":0,"minimum":0},"SRV":{"priority":0,"weight":0,"port":0,"name":""},"MX":{"preference":0,"name":""},"OPT":[],"URI":{"priority":0,"weight":0,"target":""},"TXT":""},{"name":"d3so3xb2gvtwlo.cloudfront.net","type":"AAAA","class":"IN","TTL":60,"IP":"2600:9000:24f2:3c00:b:8c7a:5300:93a1","NS":"","CNAME":"","PTR":"","TXTs":[],"SOA":{"MName":"","RName":"","serial":0,"refresh":0,"retry":0,"expire":0,"minimum":0},"SRV":{"priority":0,"weight":0,"port":0,"name":""},"MX":{"preference":0,"name":""},"OPT":[],"URI":{"priority":0,"weight":0,"target":""},"TXT":""},{"name":"d3so3xb2gvtwlo.cloudfront.net","type":"AAAA","class":"IN","TTL":60,"IP":"2600:9000:24f2:6200:b:8c7a:5300:93a1","NS":"","CNAME":"","PTR":"","TXTs":[],"SOA":{"MName":"","RName":"","serial":0,"refresh":0,"retry":0,"expire":0,"minimum":0},"SRV":{"priority":0,"weight":0,"port":0,"name":""},"MX":{"preference":0,"name":""},"OPT":[],"URI":{"priority":0,"weight":0,"target":""},"TXT":""},{"name":"d3so3xb2gvtwlo.cloudfront.net","type":"AAAA","class":"IN","TTL":60,"IP":"2600:9000:24f2:a200:b:8c7a:5300:93a1","NS":"","CNAME":"","PTR":"","TXTs":[],"SOA":{"MName":"","RName":"","serial":0,"refresh":0,"retry":0,"expire":0,"minimum":0},"SRV":{"priority":0,"weight":0,"port":0,"name":""},"MX":{"preference":0,"name":""},"OPT":[],"URI":{"priority":0,"weight":0,"target":""},"TXT":""},{"name":"d3so3xb2gvtwlo.cloudfront.net","type":"AAAA","class":"IN","TTL":60,"IP":"2600:9000:24f2:9800:b:8c7a:5300:93a1","NS":"","CNAME":"","PTR":"","TXTs":[],"SOA":{"MName":"","RName":"","serial":0,"refresh":0,"retry":0,"expire":0,"minimum":0},"SRV":{"priority":0,"weight":0,"port":0,"name":""},"MX":{"preference":0,"name":""},"OPT":[],"URI":{"priority":0,"weight":0,"target":""},"TXT":""},{"name":"d3so3xb2gvtwlo.cloudfront.net","type":"AAAA","class":"IN","TTL":60,"IP":"2600:9000:24f2:4200:b:8c7a:5300:93a1","NS":"","CNAME":"","PTR":"","TXTs":[],"SOA":{"MName":"","RName":"","serial":0,"refresh":0,"retry":0,"expire":0,"minimum":0},"SRV":{"priority":0,"weight":0,"port":0,"name":""},"MX":{"preference":0,"name":""},"OPT":[],"URI":{"priority":0,"weight":0,"target":""},"TXT":""},{"name":"d3so3xb2gvtwlo.cloudfront.net","type":"AAAA","class":"IN","TTL":60,"IP":"2600:9000:24f2:3800:b:8c7a:5300:93a1","NS":"","CNAME":"","PTR":"","TXTs":[],"SOA":{"MName":"","RName":"","serial":0,"refresh":0,"retry":0,"expire":0,"minimum":0},"SRV":{"priority":0,"weight":0,"port":0,"name":""},"MX":{"preference":0,"name":""},"OPT":[],"URI":{"priority":0,"weight":0,"target":""},"TXT":""},{"name":"d3so3xb2gvtwlo.cloudfront.net","type":"AAAA","class":"IN","TTL":60,"IP":"2600:9000:24f2:e800:b:8c7a:5300:93a1","NS":"","CNAME":"","PTR":"","TXTs":[],"SOA":{"MName":"","RName":"","serial":0,"refresh":0,"retry":0,"expire":0,"minimum":0},"SRV":{"priority":0,"weight":0,"port":0,"name":""},"MX":{"preference":0,"name":""},"OPT":[],"URI":{"priority":0,"weight":0,"target":""},"TXT":""}],"authorities":[],"additionals":[]}}]} +{"timestamp":3399319035090102784,"threadStartTime":3399319006836545726,"processorId":5,"processId":2546510,"cgroupId":145296,"threadId":2546511,"parentProcessId":2546328,"hostProcessId":2546510,"hostThreadId":2546511,"hostParentProcessId":2546328,"userId":1000,"mountNamespace":4026531841,"pidNamespace":4026531836,"processName":"isc-net-0000","executable":{"path":""},"hostName":"ip-172-31-75-17","containerId":"","container":{},"kubernetes":{},"eventId":"2006","eventName":"net_packet_dns","matchedPolicies":[""],"argsNum":5,"returnValue":0,"syscall":"sendmmsg","stackAddresses":[0],"contextFlags":{"containerStarted":false,"isCompat":false},"threadEntityId":458119841,"processEntityId":1413552547,"parentEntityId":3216664721,"args":[{"name":"src","type":"const char*","value":"127.0.0.1"},{"name":"dst","type":"const char*","value":"127.0.0.53"},{"name":"src_port","type":"u16","value":45484},{"name":"dst_port","type":"u16","value":53},{"name":"proto_dns","type":"trace.ProtoDNS","value":{"ID":54587,"QR":0,"opCode":"query","AA":0,"TC":0,"RD":1,"RA":0,"Z":0,"responseCode":"no error","QDCount":1,"ANCount":0,"NSCount":0,"ARCount":0,"questions":[{"name":"google.com","type":"A","class":"IN"}],"answers":[],"authorities":[],"additionals":[]}}]} +{"timestamp":3399319035092776716,"threadStartTime":3399319006836545726,"processorId":6,"processId":2546510,"cgroupId":145296,"threadId":2546511,"parentProcessId":2546328,"hostProcessId":2546510,"hostThreadId":2546511,"hostParentProcessId":2546328,"userId":1000,"mountNamespace":4026531841,"pidNamespace":4026531836,"processName":"isc-net-0000","executable":{"path":""},"hostName":"ip-172-31-75-17","containerId":"","container":{},"kubernetes":{},"eventId":"2006","eventName":"net_packet_dns","matchedPolicies":[""],"argsNum":5,"returnValue":0,"syscall":"","stackAddresses":[0],"contextFlags":{"containerStarted":false,"isCompat":false},"threadEntityId":458119841,"processEntityId":1413552547,"parentEntityId":3216664721,"args":[{"name":"src","type":"const char*","value":"127.0.0.53"},{"name":"dst","type":"const char*","value":"127.0.0.1"},{"name":"src_port","type":"u16","value":53},{"name":"dst_port","type":"u16","value":45484},{"name":"proto_dns","type":"trace.ProtoDNS","value":{"ID":54587,"QR":1,"opCode":"query","AA":0,"TC":0,"RD":1,"RA":1,"Z":0,"responseCode":"no error","QDCount":1,"ANCount":6,"NSCount":0,"ARCount":0,"questions":[{"name":"google.com","type":"A","class":"IN"}],"answers":[{"name":"google.com","type":"A","class":"IN","TTL":111,"IP":"142.251.163.101","NS":"","CNAME":"","PTR":"","TXTs":[],"SOA":{"MName":"","RName":"","serial":0,"refresh":0,"retry":0,"expire":0,"minimum":0},"SRV":{"priority":0,"weight":0,"port":0,"name":""},"MX":{"preference":0,"name":""},"OPT":[],"URI":{"priority":0,"weight":0,"target":""},"TXT":""},{"name":"google.com","type":"A","class":"IN","TTL":111,"IP":"142.251.163.100","NS":"","CNAME":"","PTR":"","TXTs":[],"SOA":{"MName":"","RName":"","serial":0,"refresh":0,"retry":0,"expire":0,"minimum":0},"SRV":{"priority":0,"weight":0,"port":0,"name":""},"MX":{"preference":0,"name":""},"OPT":[],"URI":{"priority":0,"weight":0,"target":""},"TXT":""},{"name":"google.com","type":"A","class":"IN","TTL":111,"IP":"142.251.163.102","NS":"","CNAME":"","PTR":"","TXTs":[],"SOA":{"MName":"","RName":"","serial":0,"refresh":0,"retry":0,"expire":0,"minimum":0},"SRV":{"priority":0,"weight":0,"port":0,"name":""},"MX":{"preference":0,"name":""},"OPT":[],"URI":{"priority":0,"weight":0,"target":""},"TXT":""},{"name":"google.com","type":"A","class":"IN","TTL":111,"IP":"142.251.163.113","NS":"","CNAME":"","PTR":"","TXTs":[],"SOA":{"MName":"","RName":"","serial":0,"refresh":0,"retry":0,"expire":0,"minimum":0},"SRV":{"priority":0,"weight":0,"port":0,"name":""},"MX":{"preference":0,"name":""},"OPT":[],"URI":{"priority":0,"weight":0,"target":""},"TXT":""},{"name":"google.com","type":"A","class":"IN","TTL":111,"IP":"142.251.163.138","NS":"","CNAME":"","PTR":"","TXTs":[],"SOA":{"MName":"","RName":"","serial":0,"refresh":0,"retry":0,"expire":0,"minimum":0},"SRV":{"priority":0,"weight":0,"port":0,"name":""},"MX":{"preference":0,"name":""},"OPT":[],"URI":{"priority":0,"weight":0,"target":""},"TXT":""},{"name":"google.com","type":"A","class":"IN","TTL":111,"IP":"142.251.163.139","NS":"","CNAME":"","PTR":"","TXTs":[],"SOA":{"MName":"","RName":"","serial":0,"refresh":0,"retry":0,"expire":0,"minimum":0},"SRV":{"priority":0,"weight":0,"port":0,"name":""},"MX":{"preference":0,"name":""},"OPT":[],"URI":{"priority":0,"weight":0,"target":""},"TXT":""}],"authorities":[],"additionals":[]}}]} \ No newline at end of file diff --git a/pkg/dnscache/node.go b/pkg/dnscache/node.go new file mode 100644 index 000000000000..7b9ce59b6964 --- /dev/null +++ b/pkg/dnscache/node.go @@ -0,0 +1,79 @@ +package dnscache + +import ( + "fmt" + + "golang.org/x/exp/slices" + + "github.com/aquasecurity/tracee/types/trace" +) + +type nodeType int + +const ( + InvalidNode nodeType = iota + DNS + IP +) + +type dnsQueryNode struct { + value string + nodeType nodeType + previous *dnsQueryNode + next []*dnsQueryNode +} + +type nodeCacheQuery struct { + dnsResults []string + ipResults []string +} + +// made a cache node from a DNS answer. node may return nil, this case must be handled +func makeNodeFromAnswer(parent *dnsQueryNode, answer *trace.ProtoDNSResourceRecord) *dnsQueryNode { + nodeType := DNS + value := "" + switch answer.Type { + case "CNAME": + value = answer.CNAME + case "A", "AAAA": + value = answer.IP + nodeType = IP + case "MX": + value = answer.MX.Name + case "SRV": + value = answer.SRV.Name + default: + return nil + } + return &dnsQueryNode{ + value, nodeType, parent, nil, + } +} + +func appendOrPrepend[T any](slice *[]T, value T, isPrepend bool) { + if isPrepend { + *slice = slices.Insert(*slice, 0, value) + } else { + *slice = append(*slice, value) + } +} + +func addNodeToQuery(node *dnsQueryNode, query *nodeCacheQuery, isParent bool) error { + addNodeToQuerySingle(node, query, isParent) + for _, child := range node.next { + addNodeToQuery(child, query, isParent) + } + return nil +} + +func addNodeToQuerySingle(node *dnsQueryNode, query *nodeCacheQuery, isParent bool) error { + switch node.nodeType { + case DNS: + appendOrPrepend(&query.dnsResults, node.value, isParent) + case IP: + appendOrPrepend(&query.ipResults, node.value, isParent) + default: + return fmt.Errorf("invalid node type") + } + return nil +} diff --git a/pkg/dnscache/nodednscache.go b/pkg/dnscache/nodednscache.go new file mode 100644 index 000000000000..ebf6e319fd18 --- /dev/null +++ b/pkg/dnscache/nodednscache.go @@ -0,0 +1,132 @@ +package dnscache + +import ( + "fmt" + "sync" + "time" + + "github.com/hashicorp/golang-lru/v2/expirable" + + "github.com/aquasecurity/tracee/types/detect" + "github.com/aquasecurity/tracee/types/trace" +) + +type NodeCache struct { + queryRoots *expirable.LRU[string, *dnsQueryNode] + queryIndices map[string]*dnsQueryNode + + results *expirable.LRU[string, nodeCacheQuery] + + lock *sync.RWMutex +} + +func NewNodeDNSCache(config Config) *NodeCache { + ttl := time.Second * time.Duration(config.TTL) + nc := &NodeCache{} + nc.queryRoots = expirable.NewLRU[string, *dnsQueryNode](config.CacheSize, nc.evictQueryRoot, ttl) + nc.queryIndices = make(map[string]*dnsQueryNode) + nc.lock = new(sync.RWMutex) + return nc +} + +func (nc *NodeCache) Add(dns *trace.ProtoDNS) error { + // discover if it is a request or response + + if dns.QR != 1 { + return nil // not a dns response + } + + if len(dns.Questions) != 1 { + return fmt.Errorf("wrong number of requests found") + } + + nc.lock.Lock() + defer nc.lock.Unlock() + + question := dns.Questions[0].Name + questionNode, ok := nc.queryIndices[question] + // Check if question is indexed in the tree... + if !ok { + return nc.addRootNode(dns) + } + return nc.addNode(dns, questionNode) +} + +// Get returns all parent and child DNS records relative to the given record value. +// Note, that the query does not traverse the tree downwards from the parent nodes. +func (nc *NodeCache) Get(key string) (nodeCacheQuery, error) { + nc.lock.RLock() + defer nc.lock.RUnlock() + + node, ok := nc.queryIndices[key] + if !ok { + return nodeCacheQuery{}, detect.ErrDataNotFound + } + + queryResult := nodeCacheQuery{ + dnsResults: []string{}, + ipResults: []string{}, + } + + addNodeToQuery(node, &queryResult, false) + + for node != nil { + node = node.previous + if node != nil { + _ = addNodeToQuerySingle(node, &queryResult, true) + } + } + + return queryResult, nil +} + +func (nc *NodeCache) addRootNode(dns *trace.ProtoDNS) error { + nodeType := DNS + if dns.Questions[0].Type == "PTR" { + // Reverse Query + nodeType = IP + } + + node := &dnsQueryNode{ + value: dns.Questions[0].Name, + nodeType: nodeType, + next: make([]*dnsQueryNode, 0, len(dns.Answers)), + } + + nc.addNode(dns, node) + + nc.queryRoots.Add(node.value, node) + nc.queryIndices[node.value] = node + return nil +} + +func (nc *NodeCache) addNode(dns *trace.ProtoDNS, parent *dnsQueryNode) error { + for _, answer := range dns.Answers { + node := makeNodeFromAnswer(parent, &answer) + if node != nil { + parent.next = append(parent.next, node) + nc.queryIndices[node.value] = node + } + } + return nil +} + +func (nc *NodeCache) evictQueryRoot(addr string, node *dnsQueryNode) { + nc.lock.Lock() + defer nc.lock.Unlock() + + nc.queryIndices[addr] = nil + nc.clearNode(node) +} + +func (nc *NodeCache) clearNode(node *dnsQueryNode) { + node.previous = nil + if node.next == nil { + return + } + for _, node := range node.next { + nc.clearNode(node) + delete(nc.queryIndices, node.value) + } + node.next = nil +} diff --git a/pkg/dnscache/nodednscache_test.go b/pkg/dnscache/nodednscache_test.go new file mode 100644 index 000000000000..1e77c2ff0ff0 --- /dev/null +++ b/pkg/dnscache/nodednscache_test.go @@ -0,0 +1,68 @@ +package dnscache_test + +import ( + "bufio" + "encoding/json" + "os" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/aquasecurity/tracee/pkg/dnscache" + "github.com/aquasecurity/tracee/pkg/events/parse" + "github.com/aquasecurity/tracee/pkg/logger" + "github.com/aquasecurity/tracee/types/trace" +) + +var eventDump []trace.Event = []trace.Event{} + +func init() { + eventsJson, err := os.OpenFile("./events", os.O_RDONLY, os.ModeAppend) + if err != nil { + panic(err) + } + + scanner := bufio.NewScanner(eventsJson) + for scanner.Scan() { + event := scanner.Bytes() + var e trace.Event + err := json.Unmarshal(event, &e) + if err != nil { + logger.Errorw("Invalid json in " + string(event) + ": " + err.Error()) + } else { + eventDump = append(eventDump, e) + } + } +} + +func TestNodeDnsCache(t *testing.T) { + dnsCache := dnscache.NewNodeDNSCache(dnscache.Config{ + Enable: true, + CacheSize: 1000, + TTL: 5, + }) + + for _, evt := range eventDump { + dns, err := parse.ArgVal[trace.ProtoDNS](evt.Args, "proto_dns") + require.NoError(t, err) + err = dnsCache.Add(&dns) + require.NoError(t, err) + } + + // log from query root down + t.Log(dnsCache.Get("google.com")) + t.Log(dnsCache.Get("uol.com")) + + // log from index down + t.Log(dnsCache.Get("d3so3xb2gvtwlo.cloudfront.net")) + + // log from ip leaves up (other ips will not be included) + t.Log(dnsCache.Get("142.251.163.101")) + t.Log(dnsCache.Get("2600:9000:24f2:3c00:b:8c7a:5300:93a1")) + + time.Sleep(7 * time.Second) + + // cache is evicted - log an error + t.Log(dnsCache.Get("142.251.163.101")) +}