Windows DNS Optimization / Troubleshooting

This article has not been completed yet. However, it may already contain   helpful Information and therefore it has been published at this stage.

Recently, I had to find out why surfing is so slow at a customer's site. After some digging it occurred to me that this may be DNS related, so I used a Power Shell - Script I found in the internet.

But let's start with the basics.

There are already built in Power Shell functions that you can use.

Measure-Command {Resolve-DnsName google.com -DnsOnly -Type A -NoHostsFile -server x.x.x.x}

However, this does not exactly meet my needs. So I did some research and found a script with the following functionality.

Resource: Link

Functional Description:

The function resolves a few records from locally defined DNS servers and some publicly available DNS servers (or a custom list if you want) and measures the average latency (in milliseconds). The results are put in a list and sorted by latency.

I adapted the script a little bit for my own need.

Script:

function Measure-DNSLatency
{
    [CmdletBinding()] 
        Param (
            [string[]]$CustomDNSServer
            )

    try {
        Get-DnsClientServerAddress -ErrorAction Stop | Out-Null
        }
    catch [System.Management.Automation.CommandNotFoundException] {
        Throw "You need at least PowerShell v4.0, your version is: $(($PSVersionTable.psversion).Major)"
        }
    
    $report = @()
    
    if ($CustomDNSServer.count -eq "0") {    
        Write-Verbose "Grabbing a list of locally defined dns servers"
        $LocalDNSServers = (Get-DnsClientServerAddress).serveraddresses | Select-Object -Unique
        $LocalDNSServers = $LocalDNSServers | Where-Object {$_ -ne "fec0:0:0:ffff::1"}
        $LocalDNSServers = $LocalDNSServers | Where-Object {$_ -ne "fec0:0:0:ffff::2"}
        $LocalDNSServers = $LocalDNSServers | Where-Object {$_ -ne "fec0:0:0:ffff::3"}

        Write-Verbose "Measuring locally defined dns servers"
        $progress = 1
        foreach ($DNSServer in $LocalDNSServers) {
            Write-Progress -ErrorAction SilentlyContinue -Activity "Measuring Latency on locally defined servers" -Status "Measuring $((Resolve-DnsName $DNSServer -Server $DNSServer).NameHost)" -PercentComplete ($progress/$LocalDNSServers.count*100) -Id 1
            $results = @()
            $results += (Measure-Command {Resolve-DnsName a.root-servers.net -DnsOnly -Type A -NoHostsFile -server $DNSServer -ErrorAction SilentlyContinue}).TotalMilliSeconds
            $results += (Measure-Command {Resolve-DnsName b.root-servers.net -DnsOnly -Type A -NoHostsFile -server $DNSServer -ErrorAction SilentlyContinue}).TotalMilliSeconds
            $results += (Measure-Command {Resolve-DnsName c.root-servers.net -DnsOnly -Type A -NoHostsFile -server $DNSServer -ErrorAction SilentlyContinue}).TotalMilliSeconds
            $results += (Measure-Command {Resolve-DnsName d.root-servers.net -DnsOnly -Type A -NoHostsFile -server $DNSServer -ErrorAction SilentlyContinue}).TotalMilliSeconds
            $results += (Measure-Command {Resolve-DnsName e.root-servers.net -DnsOnly -Type A -NoHostsFile -server $DNSServer -ErrorAction SilentlyContinue}).TotalMilliSeconds
            $object = New-Object PSObject
            Add-Member -InputObject $object NoteProperty DNSServerName (Resolve-DnsName $DNSServer -Server $DNSServer).NameHost
            Add-Member -InputObject $object NoteProperty DNSServerIP $DNSServer
            Add-Member -InputObject $object NoteProperty LatencyInMS ($results | Measure-Object -Average).Average
            $report += $object
            $progress++
            }
        Write-Progress -ErrorAction SilentlyContinue -Activity "Measuring Latency on locally defined servers" -Status "Done" -Id 1 -Completed


        Write-Verbose "Measuring well known and publicly open dns servers"
        $PublicDNSServers = New-Object System.Collections.ArrayList
        $PublicDNSServers.Add("8.8.8.8") | Out-Null
        $PublicDNSServers.Add("8.8.4.4") | Out-Null
        $PublicDNSServers.Add("1.1.1.1") | Out-Null
        $PublicDNSServers.Add("1.0.0.1") | Out-Null

        $progress = 1
        foreach ($DNSServer in $PublicDNSServers) {
            Write-Progress -ErrorAction SilentlyContinue -Activity "Measuring Latency on publicly available servers" -Status "Measuring $((Resolve-DnsName $DNSServer -Server $DNSServer).NameHost)" -PercentComplete ($progress/$PublicDNSServers.count*100) -Id 1
            $results = @()
            $results += (Measure-Command {Resolve-DnsName a.root-servers.net -DnsOnly -Type A -NoHostsFile -server $DNSServer -ErrorAction SilentlyContinue}).TotalMilliSeconds
            $results += (Measure-Command {Resolve-DnsName b.root-servers.net -DnsOnly -Type A -NoHostsFile -server $DNSServer -ErrorAction SilentlyContinue}).TotalMilliSeconds
            $results += (Measure-Command {Resolve-DnsName c.root-servers.net -DnsOnly -Type A -NoHostsFile -server $DNSServer -ErrorAction SilentlyContinue}).TotalMilliSeconds
            $results += (Measure-Command {Resolve-DnsName d.root-servers.net -DnsOnly -Type A -NoHostsFile -server $DNSServer -ErrorAction SilentlyContinue}).TotalMilliSeconds
            $results += (Measure-Command {Resolve-DnsName e.root-servers.net -DnsOnly -Type A -NoHostsFile -server $DNSServer -ErrorAction SilentlyContinue}).TotalMilliSeconds
            $object = New-Object PSObject
            Add-Member -InputObject $object NoteProperty DNSServerName (Resolve-DnsName $DNSServer -Server $DNSServer).NameHost
            Add-Member -InputObject $object NoteProperty DNSServerIP $DNSServer
            Add-Member -InputObject $object NoteProperty LatencyInMS ($results | Measure-Object -Average).Average
            $report += $object
            $progress++
            }
        Write-Progress -ErrorAction SilentlyContinue -Activity "Measuring Latency on publicly available servers" -Status "Done" -Id 1 -Completed
        }
    
    else {
        Write-Verbose "Measuring custom dns servers"
        $progress = 1
        foreach ($DNSServer in $CustomDNSServer) {
            Write-Progress -ErrorAction SilentlyContinue -Activity "Measuring Latency on custom servers" -Status "Measuring $((Resolve-DnsName $DNSServer -Server $DNSServer).NameHost)" -PercentComplete ($progress/$CustomDNSServer.count*100) -Id 1
            $results = @()
            $results += (Measure-Command {Resolve-DnsName a.root-servers.net -DnsOnly -Type A -NoHostsFile -server $DNSServer -ErrorAction SilentlyContinue}).TotalMilliSeconds
            $results += (Measure-Command {Resolve-DnsName b.root-servers.net -DnsOnly -Type A -NoHostsFile -server $DNSServer -ErrorAction SilentlyContinue}).TotalMilliSeconds
            $results += (Measure-Command {Resolve-DnsName c.root-servers.net -DnsOnly -Type A -NoHostsFile -server $DNSServer -ErrorAction SilentlyContinue}).TotalMilliSeconds
            $results += (Measure-Command {Resolve-DnsName d.root-servers.net -DnsOnly -Type A -NoHostsFile -server $DNSServer -ErrorAction SilentlyContinue}).TotalMilliSeconds
            $results += (Measure-Command {Resolve-DnsName e.root-servers.net -DnsOnly -Type A -NoHostsFile -server $DNSServer -ErrorAction SilentlyContinue}).TotalMilliSeconds
            $object = New-Object PSObject
            Add-Member -InputObject $object NoteProperty DNSServerName (Resolve-DnsName $DNSServer -Server $DNSServer).NameHost
            Add-Member -InputObject $object NoteProperty DNSServerIP $DNSServer
            Add-Member -InputObject $object NoteProperty LatencyInMS ($results | Measure-Object -Average).Average
            $report += $object
            $progress++
            }
    }
    
    $report | Sort-Object LatencyInMS
        
<#
 .Synopsis
  Measures DNS Latency
 .Description
  Grabs locally defined dns servers and some publicly available dns servers and measures the Latency (in milliseconds) on DNS queries from your computer to them
 .Parameter CustomDNSServer
  Specifies a custom list of DNS servers to measure, or it could be a single server
 .Example
  Measure-DNSLatency
  Measures DNS latency on locally defined DNS servers og some public available DNS servers
 .Example
  Measure-DNSLatency 8.8.8.8
  Measures the DNS latency only on the DNS server 8.8.8.8
 .Example
  Measure-DNSLatency 8.8.8.8,8.8.4.4
  Measures the DNS latency only on the DNS servers 8.8.8.8 and 8.8.4.4
 #>
}

As a result, I was able to find out that the customer was using an old DNS server based on Windows Server 2003, which appeared to be ten times slower than a relatively new server running Windows Server 2019.

The intended solution is the decommissioning of this old server. In the meantime, however, I checked several settings that I took from this article (see link).

First I checked the network settings:

  • a fixed IP address is required (no DHCDP)
  • unnecessary network cards need to be deactivated
  • your own IP address should be entered as the first preferred DNS server (and not 127.0.0.1)

Next, I checked the forwarders:

If there are still old entries present that might point to an invalid target, then you have to deal with a time delay until the request runs into a timeout.

I recommend using the Google - or the Cloudflare - DNS - servers.

Google:

  • 8.8.8.8
  • 8.8.4.4

Cloudflare:

  • 1.1.1.1
  • 1.0.0.1

The last thing I did was to check the functionality:

Switch to the "Monitoring" tab. Check the boxes as shown and test the forwarding by clicking "Test now". A successful test should then be displayed.

References:

Powershell: Measuring latency on DNS queries

https://support.schulportal.hessen.de/knowledgebase.php?article=54