Visualising your DNS cache with PSGraph

Exploring how to create a visual with Powershell

James Montgomery

5 minute read

TL; DR

In this post, I’d like to introduce you to how I’ve used PSGraph to create visuals from code. I’ll show you how to represent the Windows DNS cache graphically. Here is an example output (click for a full size image):

Below I cover how to arrive at that outcome. You can find all scripts used at my GitHub page.

Why DNS

The hierarchical nature of DNS (subdomain.topleveldomain.rootdomain) lends itself to graphing. Head over to dnsdumpster if you want to see an excellent example of that.

If you would like a primer on the topic, the most accessible introduction to DNS is probably this comic by DNSimple:

The DNS Cache

One aspect of the protocol is the distributed caching involved. The intention is to decentralise recent lookups and reduce the load on the authoritative origin. The thinking behind this is that the answer for a given query is likely to be the same if recently evaluated. The time for which to cache these answers is known as the time to live or TTL.

One such place to view the DNS cache is in the client operating system.

Accessing the DNS cache from PowerShell

The Get-DnsClientCache cmdlet was introduced some years ago providing an alternative to the venerable ipconfig /displaydns. It is by this means that we will get access to the Windows OS DNS cache.

Getting started with PSGraph

There is good quality documentation on getting started in minutes. If you are the type of person who learns through examples - you have several examples provided here.

Additionally, Kevin Marquette produced a module with some ready-made examples in PSGraphPlus.

Creating a graph from the DNS cache

Data review

The first thing I considered was the data returned.

Sample of cmdlet output

Here are some fields that could be useful as an input:

  • Entry
  • Record name
  • Record type
  • data

I elected to omit the following from consideration:

  • CNAME results (orange box)
  • PTR results (yellow box)
Sample of cmdlet output

The PTR results I filtered out due to a desire to focus on forward name resolution. I ignored CNAMEs for much the same reason. While adding them would add some richness to the story of how an answer was determined - its exclusion simplified the problem and maintained focus on the original record request.

Therefore it became clear that I should focus solely on the content of the Entry field ignoring only those relating to PTR as a value in Record Type.

The resulting graph would provide a breakdown by domain based on the original DNS request.

Prototyping a graph

In a perfect world, I would have arrived at the above conclusion before prototyping. The reality is that I experimented with PSGraph and supplied mock input to see how it might look. Here is an example prototype:

An early example

The exercise was useful as I had a clear understanding of how I needed to process the data. One particular lesson was that I should test for existing edges for the same relationship. Otherwise, the effect is additive. Here is an example if I restrict the depth to root domains only with and without unique edge testing:

This is what happens if I applied edges without considering uniqueness
An clean example when testing for present edges

Prototyping parsing the DNS cache data

Working solely with the Entry field data returned was the first choice. I decided to ignore PTR records during result processing rather than a filter with the query.

I achieved this with the following conditional statement on the value of the Entry field data:

if($result.Entry -like "*.in-addr.arpa")
{#Reverse lookup processing}
else
{#Forward lookup processing}

The second challenge was arriving at the values required for the node values and labels in PSGraph. Let’s consider the following record www.google.com.

First of all, I split them into an array of strings based on ..

$subs = @('www','google','com')

I then want to walk the results backwards to construct the following:

  • ID = com, Label = com
  • ID = google.com, Label = google
  • ID =www.google.com, Label = www

The label will be the array item current indexed. However, the node ID is a string based on a join of this item and every one after it until the array ends.

As the labels are what you see in the resulting diagram we achieve the ability to have more than one node with the same visible value (“www” is a prime example) yet retain its relationship with the parent domain.

The code for this prototype is available in WalkDNSCache-loop-proto.ps1 file on my GitHub repository.

Putting it together

TLD processing

I made a conscious choice to process the TLDs (e.g. com/net/io) separately rather than process them in the array walk. The intention was to simplify the loop.

Uniqueness testing

To track unique node IDs, I’m using a hash table. The value element is static at “1” - the thinking was a future version could using the counts of IDs in some fashion.

The code for this prototype is available in WalkDNSCache-psgraph-entry.ps1 file on my GitHub repository.

Conclusion

In this experience, I felt the need to separately prototype to assert my understanding of both the problem and the solution. With two unknowns (how to parse the data and how to utilise the visualisation library), it was a time-efficient means of arriving at a solution.

I’ve left the prototypes in the same repository for transparency in the method applied.

It has been very satisfying creating a visual from code. I’ve tried to keep this example focused and accessible.

Relevant posts

Acknowledgements