Verifying DNS configurations using PowerShell
Originally published July 26, 2007
One of the most common reasons for e-mail delivery failures is improper DNS configuration. There are several things that are important:
1) Having an MX record (while not strictly required by RFC, most anti-spam servers now require this)
2) Having a valid rDNS (reverse DNS) record for the IP address pointed to by the MX record
3) Having a TXT record (SPFv1 or SPFv2) that is accurate (http://spf.pobox.com)
There are, of course, other things that you might want to know about a given domain. I've written a PowerShell script (dns.ps1) that interrogates everything that I want to know about a domain. It does require an additional .Net library from Lumisoft. Thankfully, this library is also free: http://www.lumisoft.ee/lswww/download/downloads/Net/Lumisoft.Net.zip
This library includes far more than the features used by this script. If you are doing TCP programming using PowerShell (or any .Net capable language) you should really investigate what this library can do for you...
I put the library in C:\Temp on my computer. If you want to place it elsewhere, update the
[Reflection.Assembly]::LoadFile("c:\temp\LumiSoft.Net.dll") | out-null
line in the script to point to the proper location of the DLL. Also, plese update the DNS servers to your local DNS servers on this line:
[Lumisoft.Net.Dns.Client.Dns_Client]::DnsServers = "216.30.183.13", "216.30.183.14"
Otherwise, this script should simply “work” for you. For example:
[PS C:\Scripts]> ./dns briworks.com
Information for briworks.com
IP 216.30.183.90, TTL 3600
Information for www.briworks.com
CNAME briworks.com, TTL 3600
IP 216.30.183.90, TTL 3599
Information for mail.briworks.com
CNAME mail.brnets.com, TTL 3600
IP 216.30.183.81, TTL 3040
MX Information for briworks.com
Priority 10, Host nospam.brnets.com, TTL 3600
Information for nospam.brnets.com
IP 216.30.183.67, TTL 3599
NS Information for briworks.com
NS ns1.briworks.net, TTL 3600
NS ns2.briworks.net, TTL 3600
TXT records for briworks.com
(no text (TXT) records)
SOA record for briworks.com
Nameserver : ns1.briworks.net
AdminEmail : domains@briworks.net
Serial : 94
Refresh : 900
Retry : 600
Expire : 86400
Minimum : 3600
TTL : 3600
[PS C:\Scripts]>
So, does this do anything that you can't get out of nslookup? Nope. But it does it all in one command and in a lot more compact output format.
Enjoy!
Here is the script:
#
# ./dns.ps1
#
# This PowerShell script makes the queries that tell you whether a
# domain is setup and/or ready for doing web-hosting and/or e-mail
# hosting.
#
# Some small assumption here that the MX host and the mail host are
# separate (which is true in basic setup for anti-spam provisioning
# at Blue Ridge Internetworks.
#
# This code is provided "as-is" with no warranty nor guarantee of
# suitability for any particular use or purpose. Use at your own
# risk.
#
# Use it however you wish. If you put it in a derivative work or
# further distribute it, provide me with named credit.
#
# Michael B. Smith
# July 2007
#
#
# result codes from resolver library
#
$rcode_no_error = 0
$rcode_format_error = 1
$rcode_server_failure = 2
$rcode_name_error = 3
$rcode_not_implemented = 4
$rcode_refused = 5
#
# query types for DNS client
#
$qtype_a = 1
$qtype_ns = 2
$qtype_cname = 5
$qtype_soa = 6
$qtype_ptr = 12
$qtype_hinfo = 13
$qtype_mx = 15
$qtype_txt = 16
$qtype_aaaa = 28
function tab($count)
{
$str = $null
for ($i = 0; $i -lt $count; $i++)
{
$str += "`t"
}
return $str
}
function decodeAnswer($tabCount, $answer)
{
$t = tab $tabCount
switch ($answer.RecordType.Value__)
{
$qtype_a
{
($t + "IP " + $answer.IP + ", TTL " + $answer.TTL)
}
$qtype_ns
{
($t + "NS " + $answer.NameServer + ", TTL " + $answer.TTL)
}
$qtype_cname
{
($t + "CNAME " + $answer.Alias + ", TTL " + $answer.TTL)
}
$qtype_soa
{
($t + "Nameserver : " + $answer.NameServer)
($t + "AdminEmail : " + $answer.AdminEmail)
($t + "Serial : " + $answer.Serial)
($t + "Refresh : " + $answer.Refresh)
($t + "Retry : " + $answer.Retry)
($t + "Expire : " + $answer.Expire)
($t + "Minimum : " + $answer.Minimum)
($t + "TTL : " + $answer.TTL)
}
$qtype_mx
{
($t + "Priority " + $answer.Preference.ToString() + ", Host " + $answer.Host + `
", TTL " + $answer.TTL)
}
$qtype_txt
{
($t + "TXT '" + $answer.Text + "', TTL " + $answer.TTL)
}
}
}
function decodeAnswers($tabCount, $answers, $finalStr)
{
foreach ($answer in $answers)
{
decodeAnswer $tabCount $answer
}
if ($answers.Length -eq 0)
{
((tab $tabCount) + $finalstr)
}
" "
}
function doQuery($queryObject, $queryString)
{
[LumiSoft.Net.Dns.Client.DnsServerResponse]$re = $dns.Query($queryObject, $queryString)
if ($re -and ($re.ConnectionOK -eq $true) -and ($re.ResponseCode.value__ -eq $rcode_no_error))
{
return $re
}
else
{
$re = $null
write-host ("Error on '" + $queryString + "' query for " + $queryObject)
return $null
}
}
[Reflection.Assembly]::LoadFile("c:\temp\LumiSoft.Net.dll") | out-null
foreach ($arg in $args)
{
[string]$domain = $arg
" "
[Lumisoft.Net.Dns.Client.Dns_Client]::DnsServers = "216.30.183.13", "216.30.183.14"
$dns = new-object LumiSoft.Net.Dns.Client.Dns_Client
$re = doQuery $domain "A"
if ($re)
{
"Information for $domain"
decodeAnswers 1 $re.Answers "(no address (A) record)"
}
$re = doQuery ("www." + $domain) "A"
if ($re)
{
"Information for www.$domain"
decodeAnswers 1 $re.Answers "(no address (A) record)"
}
$re = doQuery ("mail." + $domain) "A"
if ($re)
{
"Information for mail.$domain"
decodeAnswers 1 $re.Answers "(no address (A) record)"
}
$re = doQuery $domain "MX"
if ($re)
{
"MX Information for $domain"
foreach ($answer in $re.Answers)
{
decodeAnswer 1 $answer
$re2 = doQuery $answer.Host "A"
if ($re2)
{
((tab 1) + "Information for " + $answer.Host)
decodeAnswers 2 $re2.Answers "(no address record for MX record)"
}
}
if ($re.Answers.Length -eq 0)
{
((tab 1) + "(no mail exchanger (MX) records)")
" "
}
}
$re = doQuery $domain "NS"
if ($re)
{
"NS Information for $domain"
decodeAnswers 1 $re.Answers "(no name server (NS) records)"
}
$re = doQuery $domain "TXT"
if ($re)
{
"TXT records for $domain"
decodeAnswers 1 $re.Answers "(no text (TXT) records)"
}
$re = doQuery $domain "SOA"
if ($re)
{
"SOA record for $domain"
decodeAnswers 1 $re.Answers "(no start of authority (SOA) record)"
}
}