Using JH Software's Simple DNS Client Library For .NET In PowerShell
I recently had the opportunity to help out a good friend of mine, Nigel Boulton with a PowerShell issue he was experiencing. After some conversations and experimenting we came up with the following solution so we decided to cross-post the solution. Here’s the background from Nigel about why he had the issue in the first place and how we got around it:
Last week I had a requirement to perform a DNS lookup within a PowerShell script, using a DNS server other than the one that the machine in question was configured to use. This turned out to be quite a challenge and, having found a library that looked like it would do it, I ended up having to call on the expertise of my good friend and PowerShell MVP Jonathan Medd to help me out!
As I’m sure I can’t be the first person that has attempted this in PowerShell using the library in question (or something similar) I felt it was worth blogging the methodology…
I won’t bore you with too much of the background, but I was amending a script that I wrote a few years back which used the .NET class System.Net.Dns to perform a DNS lookup. This worked fine until my ISP changed the behaviour of their DNS servers to return a search page in the event that the host record in question was not found. Although this is probably helpful to a human being, I wanted my script to raise an error in the event that the host record wasn’t found - and this was no longer happening because my ISP’s DNS now returns the IP address of the server delivering the search page instead. Therefore, to my script, the DNS lookup was appearing to be successful!
The obvious answer to this is to use a DNS server that doesn’t behave in this way. Google’s DNS is an example of this. I didn’t want to change my entire infrastructure to use Google’s DNS servers, so the most sensible approach was to have just the script use them. This is where the next problem became apparent – System.Net.DNS does not allow the use of alternative DNS servers.
After some Googling, I came across the excellent and free ‘SimpleDNS DNS Client Library for .NET’ from JH Software. This allows you to specify DNS servers to perform lookups against. However (perhaps because I’m not a .NET programmer) I couldn’t work out for the life of me how to set the required property, and that is where Jonathan helped me out. The methodology is described below. I can’t take any credit for this – it’s all his work - JM: not quite true, but let’s carry on….
The first step is obviously to download the SimpleDNS library from here, and put at least the Dynamic Link Library ‘JHSoftware.DnsClient.dll’ into a suitable location on the local machine, say ‘C:\PowerShell\Libraries’. (There is a .CHM help file included in the download that is worth including too.)
Here is the entire code snippet used. An explanation of each line follows in the text below.
function Search-DNSRecordBySpecifedServer { <# .SYNOPSIS Search for a DNS record using a specified DNS server(s).
.DESCRIPTION Search for a DNS record using a specified DNS server(s).
.PARAMETER DllPath Path to the JH Software DNS dll.
.PARAMETER DNSServer DNS Server(s) to query, supplied as an IP address
.PARAMETER DomainName DNS Domain to query
.INPUTS IO.FileInfo Net.IPAddress System.String
.OUTPUTS System.String
.EXAMPLE PS> Search-DNSRecordBySpecifedServer -DllPath "C:\\PowerShell\\Libraries\\JHSoftware.DnsClient.dll" -DNSServer 8.8.8.8,8.8.4.4 -DomainName www.twitter.com
#> \[CmdletBinding()\]\[OutputTYpe('System.String')\]
Param ( \[parameter(Mandatory=$true)\] \[ValidateNotNullOrEmpty()\] \[IO.FileInfo\]$DllPath,
\[parameter(Mandatory=$true)\] \[ValidateNotNullOrEmpty()\] \[Net.IPAddress\[\]\]$DNSServer,
\[parameter(Mandatory=$true)\] \[ValidateNotNullOrEmpty()\] \[String\]$DomainName
)
try {
\# --- Test the path to $MediaPath exists if (!($DllPath.Exists)) {throw "Cannot continue. JHSoftware Dll does not exist"}
\# --- Load the JHSoftware Dll \[System.Reflection.Assembly\]::LoadFile($DllPath) | Out-Null
\# --- Create the RequestOptions object and populate with DNS servers $Options = New-Object JHSoftware.DnsClient+RequestOptions $Options.DnsServers = $DNSServer
\# --- Create the IPv4 IPVersion object $IPVersion = \[JHSoftware.DnsClient+IPVersion\]::IPv4
\# --- Carry out the DNS lookup and return the result $HostAddress = \[JHSoftware.DnsClient\]::LookupHost($DomainName,$IPVersion,$Options) $HostAddress\[0\].ToString()
} catch \[Exception\] {
throw "Unable to search for DNS record $DomainName"
} }
The DNSServer parameter turns strings in .NET IPAddress objects which are set to Google’s DNS server (8.8.8.8 and 8.8.4.4) in the example.
The line:
\[System.Reflection.Assembly\]::LoadFile("C:\\PowerShell\\Libraries\\JHSoftware.DnsClient.dll") | Out-Null
loads the library from the previously given location.
Next, we need to create a new .NET object to hold the request options, and set the ‘DnsServers’ property of that. For VB.NET and C# examples (but no PowerShell ), see the programming guide here.
$Options = New-Object JHSoftware.DnsClient+RequestOptions $Options.DnsServers = $ServerIPObj
The ‘LookupHost’ method requires an additional parameter because we are using ‘RequestOptions’ – an enumeration of the IP protocol version to be used, which is called DnsClient.IPVersion. The base class is System.Enum. To create this in PowerShell with the correct value you need use the method below, specifying the protocol version with the appropriate member name. There is a great post from Lincoln Atkinson on using enumerations in PowerShell here.
$IPVersion = \[JHSoftware.DnsClient+IPVersion\]::IPv4
Once this has been done, we have all the parameters we need to call the LookupHost method and return a result:
$HostAddress = \[JHSoftware.DnsClient\]::LookupHost($DomainName,$IPVersion,$Options)
Now let’s take a look at $HostAddress:
Address : 456734529
AddressFamily : InterNetwork
ScopeId :
IsIPv6Multicast : False
IsIPv6LinkLocal : False
IsIPv6SiteLocal : False
IPAddressToString : 65.55.57.27
I wanted just the IP address as a string in a variable for the script to process further, and how to do this eluded me initially until I realised that the $HostAddress object was in fact being returned by the library as System.Net.IPAddress object, and effectively as an array. The MSDN documentation for a similar method of the System.Net.Dns class, the GetHostAddresses method, helped me to work this out. So returning the IP address alone is simply a matter of:
$HostAddress\[0\].ToString()
…and that’s it!
You can view the same post over on Nigel’s blog.