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)"
 }
}

Published Tuesday, November 13, 2007 9:03 PM by michael
Filed under:

Comments

No Comments