Archive

Posts Tagged ‘dns’

Adding some beef: Multi server multi host DNS resolution testing

August 6, 2011 Leave a comment

When you’re hosting multiple web presences and you’re also being responsible for the DNS name resolution zones, you might find yourself in a situation where touching anything DNS-related is giving you a bad feeling even before you start. It is getting even worse if for some reason you also have to deal with a ‘split brain’ DNS setup (serving different IP addresses for the same host name depending on who is asking, either an external or internal client).

For helping me in that situation I started to build a script that allows me to run automated DNS resolution tests against groups of DNS servers. All configuration and test data is kept external to the script in an XML file. It’s astonishing how easy it is to work with XML in Powershell. Some introductory other examples show you just how to select nodes and manipulate XML to generate XML output again. This script uses the data structures built by reading an XML file to directly iterate over XML node subsets with the purpose to generate the DNS test command lines, for looking up the DNS servers to execute the test against and to check whether the results meet the expected IP addresses specified beforehand.

The actual DNS lookup is done wrapping a call to nslookup. This is because the alternative .NET class System.Net.Dns used in my last post doesn’t allow you to specify the DNS server to be used, it will always use the one from the local NIC configuration.

# Test-IPResoluton.ps1
# (c) Marcus Schommler, 2011
# Testing DNS resolution for multiple DNS server and multiple host names.
# The configuration and test data is being read from an XML file.

# ==========================================
# Open issues & possible enhancements:
# - It might be of interest whether the answer of a DNS server is non authoritative.
# - Also it might be helpful to specify for test cases whether an authoritative answer is expected.
# - XML could be used for output as well...

# assertEquals()
# inspired by: http://www.leeholmes.com/blog/2005/09/05/unit-testing-in-powershell-%E2%80%93-a-link-parser/
function assertEquals (
    $expected = "Please specify the expected object",
    $actual = "Please specify the actual object"
    )
{
    if(-not ($expected -eq $actual)) {
        $res = "FAILED. Expected: $expected.  Actual: $actual."
    } else{
        $res = "OK. $actual";
    }
    $res
}

# forward_lookup(): Do DNS forward lookups by using nslookup
# inspired by: http://powershellmasters.blogspot.com/2009/04/nslookup-and-powershell.html
Function forward_lookup ($hostname, $dns_server) {
    # Build command line including stderr redirection to null device:
    $cmd = "nslookup " + $hostname + " " + $dns_server + " 2>null"
    $Error.Clear()
    $global:nonauthAnswer = $false
    $global:controlladns = $false
    $result = Invoke-Expression ($cmd)
    trap {
        $global:controlladns = $true
        $solved_ip = "0.0.0.0"
        continue
    }
    if ($Error.Count -gt 0) {
        # nslookup does output to stderr if a name resolution result is not authoritative.
        # Simplified assumption here: This is the only reason for generating error output.
        # echo "answer not authoritative"
        $global:nonauthAnswer = $true
    }

    # Line 4 of the nslookup-Output contains the resolved IP address
    # -> check and extract by (simplified) pattern matching:
    if ($result.SyncRoot[4] -match "([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)") {
        $solved_ip = $matches[1]
    }
    $solved_ip
}

## main code section ##

# read all settings and test cases from an XML file:
$test_settings = [xml]( Get-Content .\dns-tests-2.xml )

# get start node for the dns server groups to use:
$server_groups = $test_settings.SelectNodes("ruth/dns_server_group")
# get start node for the test cases to check:
$test_sets = $test_settings.SelectNodes("ruth/dns_test_set")

# init array for storing information about failed tests:
$fails = @()

# iterate over test sets found:
foreach ($set in $test_sets) {
    # lookup up which group of DNS servers should be used for this test set:
    $sg_name = $set.getAttribute("dns_server_group")

    # get the individual dns server nodes for this dns server group:
    $sg = $test_settings.SelectNodes("ruth/dns_server_group[@id='$sg_name']/dns_server")

    Write-Host "Test-Set: " $set.GetAttribute("id")
    # get the actual tests to perform
    $tests = $set.SelectNodes("dns_test")
    # iterate over the servers in the sever group:
    foreach ($server in $sg) {
        # iterate over the test cases:
        foreach ($test in $tests) {
            # extract the needed information for test execution from various xml nodes:
            $hostname = $test.getAttribute("host")
            $expect_ip = $test.getAttribute("ip")
            $dns = $server.getAttribute("ip")
            # execute the test:
            $ip = forward_lookup $hostname $dns

            # check the result of the dns resolution against the expected IP address:
            $res = assertEquals $expect_ip $ip
            # generate output:
            $s1 = "DNS: $dns, Host: $hostname : "
            if ($res.contains("FAIL")) {
                $fails += $s1 + $res
            }
            Write-Host $($s1 + $res)
        }
    }
    Write-Host "===== End Test-Set: " $set.GetAttribute("id")
}

# Output of a Summary: To have only the failed tests all in one coherent block of text,
# we repeat the output just for them:
Write-Host "============"
Write-Host "All Failures"
foreach ($f in $fails) {
    Write-Host $f
}

And that’s how an XML file to be used with this script could look like:

<?xml version="1.0" standalone="yes"?>
<ruth>
	<dns_server_group id="Gesis external lookup">
		<dns_server note="dns2.gesis.org" ip="194.95.75.2" />
		<dns_server note="unix1" ip="134.95.45.3" />
	</dns_server_group>
	<dns_server_group id="Google public DNS servers">
		<dns_server note="goopub1" ip="8.8.8.8"/>
		<dns_server note="goopub2" ip="8.8.4.4"/>
	</dns_server_group>
	<dns_test_set id="gesis.org external" dns_server_group="Gesis external lookup">
		<dns_test host="www.gesis.org" ip="172.16.4.252" />
		<dns_test host="ftp.bonn.gesis.org" ip="193.175.238.3" />
		<dns_test host="jws.bonn.gesis.org" ip="194.95.75.5" />
		<dns_test host="listserv.bonn.gesis.org" ip="193.175.238.78" />
		<dns_test host="webmail.koeln.gesis.org" ip="134.95.45.6" />
		<dns_test host="download.za.gesis.org" ip="134.95.45.13" />
	</dns_test_set>
	<dns_test_set id="PartnerHosting external" dns_server_group="Gesis external lookup">
		<dns_test host="www.asi-ev.org" ip="193.175.238.210"/>
		<dns_test host="www.dgo-online.org" ip="193.175.238.92"/>
		<dns_test host="www.iconnecteu.org" ip="193.175.238.140"/>
		<dns_test host="www.soziologie.de" ip="194.95.75.36"/>
	</dns_test_set>
	<dns_test_set id="PartnerHosting external - google dns" dns_server_group="Google public DNS servers">
		<dns_test host="www.asi-ev.org" ip="193.175.238.210"/>
		<dns_test host="www.dgo-online.org" ip="193.175.238.92"/>
	</dns_test_set>
</ruth>

Categories: powershell Tags: , , , , ,

Humble beginnings: DNS Resolving for a list of host names

August 6, 2011 2 comments

Here’s my first small Powershell script to share. Tackling DNS cleanup and reorganization over multiple locations and DNS servers, one of my first tasks was to get a reference list of host names and IP Adresses for about twenty domains hosted by us. While ping or nslookup seemed still feasible to use, I anyhow decided to let a script do the tedious work. It takes just a few lines…

 # resolve-all.ps1
# (c) Marcus Schommler, 2011
# Reads in all lines of the specified file and tries DNS name resolution on every line.
# (Apparently the file should contain host names).
# The output is done with Write-Host in a 'XML-snippets' format for reuse
# in another script/project (testing DNS resolutions for multiple DNS servers and multiple host names).

$entries = Get-Content www-all-zones.txt
foreach ($e in $entries) {
   $ips = [System.Net.Dns]::GetHostAddresses($e)
   # A host can have multiple IP adresses, so we iterate over the result of GetHostAddresses():
   foreach ($ip in $ips) {
      # XML-snipped style output for reuse in another project (mass testing of DNS resolutions):
      Write-Host '<dns_test host="'$e'" ip="'$ip'"/>'
   }
}
Categories: powershell Tags: , , ,