Showing posts with label DNS. Show all posts
Showing posts with label DNS. Show all posts

17 January, 2016

Search host name based on IP from Forward Lookup zone - DNS

There are times when the need calls for very ugly but practical workarounds. This time, we want to know the DNS name of a host, we only know the IP address and we don't have reverse lookup zones.

In this case, we can list all the A records from the forward lookup zone and search for the IP address we know.


Use WMI

If you have admin rights on the DNS server, you can use WMI:
gwmi -Namespace root/microsoftDNS -q "select * from MicrosoftDNS_AType where recorddata='10.1.1.123'" | select Ownername,recordData


Use dnscmd

However, if you don't have any sort of permissions, you can try to use dnscmd to enumerate all records from the given zone and then use powershell to search for the IP, then do some text parsing to get a proper output:



A bit of explanation:
  • $zoneContent = dnscmd $dnsserver /enumrecords $dnsDomain . /continue
    Get the full list of records from the given zone
  • if($item -match "$ip"){...
    Go through each line in the output and if the given line contains the IP you are looking for, start processing the data
  • if($item -match "^  "){
    If the line starts with spaces, that means it will have an IP which belongs to a host with multiple IPs, so we will need to list the previous line as well
  • $aging = $($tmp=$zoneContent[$k-1] -match "aging:(?<number>[^\]]+)"; $matches.number)
    $timestamp = (Get-Date ("1601/01/01 00:00")).addhours($aging)
    Calculate the time stamp of the record from the Aging number (which is the number of hours from 1st Jan 1601
  • New-Object -TypeName psobject -Property @{"IP"=$ip; Host=($zoneContent[$k-1].split(" ")[0]); timestamp=$timestamp}
    Put the data into an object and throw it to the std out
The sample script in full:








 $ip = "10.1.1.122"  
 $dnsServer = "c3podc1"  
 $dnsDomain = "tatooine.com"  
   
 $zoneContent = dnscmd $dnsserver /enumrecords $dnsDomain . /continue  
 $k = 0  
   
 Foreach($item in $zoneContent){  
    if($item -match "$ip"){  
       # if the host has 2 IPs and we searched for the 2nd one, we will need the previous line from the output  
       if($item -match "^ "){  
          $aging = $($tmp=$zoneContent[$k-1] -match "aging:(?<number>[^\]]+)"; $matches.number)  
          $timestamp = (Get-Date ("1601/01/01 00:00")).addhours($aging)  
          New-Object -TypeName psobject -Property @{"IP"=$ip; Host=($zoneContent[$k-1].split(" ")[0]); timestamp=$timestamp}  
            
          $aging = $($tmp=$item -match "aging:(?<number>[^\]]+)"; $matches.number)  
          $timestamp = (Get-Date ("1601/01/01 00:00")).addhours($aging)  
          New-Object -TypeName psobject -Property @{"IP"=$ip; Host=($zoneContent[$k-1].split(" ")[0]); timestamp=$timestamp}  
       }  
       else{  
          $aging = $($tmp=$item -match "aging:(?<number>[^\]]+)"; $matches.number)  
          $timestamp = (Get-Date ("1601/01/01 00:00")).addhours($aging)  
          New-Object -TypeName psobject -Property @{"IP"=$ip; Host=($item.split(" ")[0]); timestamp=$timestamp}  
       }  
    }  
    $k++  
 }  
   

23 November, 2015

Which DNS records would be scavenged - AD

In connection with a previous post on listing DNS scavenging settings, I thought I'd post those couple of lines of codes which gave me confidence before turning on or modifying scavenging settings - before I turn on any automation which only exists to delete stuff from production environment I always have a second thought when I put my head on the pillow, "did I really set the right values or will the phone ring in 2 hours waking me up and telling me there's a DNS outage?"

To make it a bit more scientific than "close your eyes and click OK", here is a couple of lines of PS which can help you identify all records from a DNS zone which would be deleted based on your thresholds.
  • Set parameters, DNS server name, the DNS zone and the age threshold which specifies how many days older records should be deleted. Scavenging has a 7 + 7 days "No-refresh" + "Refresh" interval, so records older than 14 days will potentially be deleted when scavenging process runs:
    #set parameters
    $server = "c3podc1"
    $domain = "tatooine.com"
    $agetreshold = 14
  • Threshold in hours from Microsoft's beginning of time definition (1st Jan 1601):
    # calculate how many hours is the age which will be the threshold
    $minimumTimeStamp = [int] (New-TimeSpan -Start $(Get-Date ("01/01/1601 00:00")) -End $((Get-Date).AddDays(-$agetreshold))).TotalHours
  • Enumerate all records older than the time threshold
    # get all records from the zone whose age is more than our threshold $records = Get-WmiObject -ComputerName $dnsServer -Namespace "root\MicrosoftDNS" -Query "select * from MicrosoftDNS_AType where Containername='$domain' AND TimeStamp<$minimumTimeStamp AND TimeStamp<>0 "
  • List the records and the time stamps
    # list the name and the calculated last update time stamp
    $records | Select Ownername, @{n="timestamp";e={([datetime]"1.1.1601").AddHours($_.Timestamp)}}
The output should look like this:
DNS records with time stamps


The full script:
 #set parameters  
 $dnsServer = "c3podc1"  
 $domain = "tatooine.com"  
 $agetreshold = 14  
   
 # calculate how many hours is the age which will be the threshold  
 $minimumTimeStamp = [int] (New-TimeSpan -Start $(Get-Date ("01/01/1601 00:00")) -End $((Get-Date).AddDays(-$agetreshold))).TotalHours  
   
 # get all records from the zone whose age is more than our threshold   
 $records = Get-WmiObject -ComputerName $dnsServer -Namespace "root\MicrosoftDNS" -Query "select * from MicrosoftDNS_AType where Containername='$domain' AND TimeStamp<$minimumTimeStamp AND TimeStamp<>0 "  
   
 # list the name and the calculated last update time stamp  
 $records | Select Ownername, @{n="timestamp";e={([datetime]"1.1.1601").AddHours($_.Timestamp)}}  
   



t


06 April, 2015

Verify forward and reverse DNS records - OS

DNS is a fairly simple and usually reliable service - when it works. People don't really think about it unless there are weird issues with an application and they spent 2 days troubleshooting it and just because they want to make sure they looked at everything, they check DNS and it turns out that those weird issues are in fact caused by e.g. missing reverse records.

These issues manifest in applications behaving strangely often providing uselessly generic errors messages or even worse, some misleading ones. After a very tedious issue over a weekend which required repeated checkouts across a globally distributed AD integrated DNS zone, I thought I wouldn't do this manually all the time with excel magic but via a script.

Steps needed to be made:
  • Take a list of server names (ideally FQDNs)
  • Perform a forward lookup to get the IP address(es) of each server
  • Take those IPs and perform a reverse lookup on them
  • Mark each host and IP with error if either the forward or reverse lookups fail or the reverse lookup provides a different hostname than the server name provided

Forward lookup (filtering on IPv4 only):
[array]$IPAddresses = [System.Net.Dns]::GetHostAddresses($obj.ComputerName) | ?{$_.AddressFamily -eq "InterNetwork"} | %{$_.IPAddressToString}


Reverse lookup:
$tmpreverse = [System.Net.Dns]::GetHostByAddress($_).HostName

Output:






The full script (simplified version):

 $hostlist = @($input)  
   
 # running through the list of hosts  
 $hostlist | %{  
      $obj = "" | Select ComputerName,Ping,IPNumber,ForwardLookup,ReverseLookup,Result  
      $obj.ComputerName = $_  
   
      # ping each host  
      if(Test-Connection $_ -quiet){  
           $obj.Ping = "OK"  
     $obj.Result = "OK"  
      }  
      else{  
           $obj.Ping = "Error"  
     $obj.Result = "Error"  
      }  
        
      # lookup IP addresses of the given host  
      [array]$IPAddresses = [System.Net.Dns]::GetHostAddresses($obj.ComputerName) | ?{$_.AddressFamily -eq "InterNetwork"} | %{$_.IPAddressToString}  
   
      # caputer count of IPs  
      $obj.IPNumber = ($IPAddresses | measure).count  
        
      # if there were IPs returned from DNS, go through each IP  
   if($IPAddresses){  
     $obj.ForwardLookup = "OK"  
   
        $IPAddresses | %{  
             $tmpreverse = $null  
                  
                # perform reverse lookup on the given IP  
             $tmpreverse = [System.Net.Dns]::GetHostByAddress($_).HostName  
             if($tmpreverse){  
                  
                     # if the returned host name is the same as the name being processed from the input, the result is OK  
                  if($tmpreverse -ieq $obj.ComputerName){  
                       $obj.ReverseLookup += "$_ : OK `n"  
                  }  
                  else{  
                       $obj.ReverseLookup += "$_ different hostname: $tmpreverse `n"  
                       $obj.Result = "Error"  
                  }  
             }  
             else{  
                  $obj.ReverseLookup = "No host found"  
                  $obj.Result = "Error"  
             }  
     }  
      }  
      else{  
           $obj.ForwardLookup = "No IP found"  
           $obj.Result = "Error"  
      }  
        
      # return the output object  
      $obj  
 }  


t


14 July, 2014

Check DNS server IPs on NICs remotely - OS

This will be a quick post. I've come across an issue - while you assume that configuration management on loads of hosts is important and usually managed well, you can never be sure. Let's say I have some DNS servers and I'm replacing them with new ones, hopefully all other servers - in one way or another - will pick up the new DNS IPs and drop the old ones on all of their network interfaces. I just had a bad feeling about this...felt tremor in The Force.

I wanted a quick report on a list of servers and see what DNS IPs they used. At the same time - if I was writing some PowerShell code already why not check if those DNS IPs were alive.

So here is what needed to be done:

Enumerate DNS IPs of all interfaces

This is the easy bit, just use WMI and take all data from all adapters which are IP enabled and have a default gateway defined.
[array]$InterfaceList = gwmi win32_networkadapterconfiguration -ComputerName $srv | ?{$_.ipenabled -ieq "true" -and $_.DefaultIPGateway}

Read DNS IPs from each interface

This will be in the DNSServerSearchOrder property and you can assume it will be an array with as many IPs as many DNS servers the interface has. In my case, I know I have 3 everywhere so I might just hard code it into a loop to look at 3 elements:
for ($i = 1; $i -le 3; $i++){
$script:obj.("DNS_" + $i) = $interface.DNSServerSearchOrder[$i]
}

Verify the given DNS IP to see if it's a live DNS server

This is the tricky bit. Not impossible though.. there are ugly solutions and some uglier ones. Why? The nice native method would be to perform a DNS lookup against the DNS server by using the System.Net.Dns class. The problem is that this class will always use the DNS server set on your local host and you can't specify one like with nslookup.

So an ugly solution would be to run nslookup against the given DNS server and parse its text output. Or you could also change your DNS server on the local host on the fly and use System.Net.Dns. Another one is to see if UDP port 53 is open, it's at least 30 lines of code because as UDP is not a handshaking protocol, you need to setup a System.Net.Sockets.Udpclient, set encoding, initiate connection, handle errors, false positives...etc.
I decided to go with an easier one. By default, all DNS servers have the TCP port 53 open as well for large queries and zone transfers.  That's much easier to test:
$Socket = New-Object System.Net.Sockets.TCPClient
$Connect = $Socket.BeginConnect($srv,53,$null,$null)
$Wait = $Connect.AsyncWaitHandle.WaitOne(3000,$false)

The below script takes a list of hosts from the pipe, enumerates 3 DNS IPs of each interface and checks if those DNS servers are alive and then lists the output in an object collection.

 param ( [string] $log = "",  
  [string] $hosts = "")  
   
   
   
 #### Function for checking if a TCP port is opened on a host  
 function TCPportCheck ([string]$fsrv, [int]$port) {  
  $Socket = New-Object System.Net.Sockets.TCPClient  
  $Connect = $Socket.BeginConnect($fsrv,$port,$null,$null)  
  Start-Sleep 1  
  if($Connect.IsCompleted){  
  $Wait = $Connect.AsyncWaitHandle.WaitOne(3000,$false)    
  if(!$Wait){  
   $Socket.Close()   
   return $false   
  }   
  else{  
   $Socket.EndConnect($Connect)  
   $Socket.Close()  
   return $true  
  }  
  }  
  else{  
  return $false  
  }  
 }  
   
   
 $script:objColl = @()  
   
 #### Collate the host list.  
 $hostlist = @($Input)  
 if ($hosts) {  
  if($hosts -imatch " "){  
  $hostsArr = @($hosts.split(" "))  
  $hostlist += $hostsArr  
  }  
  else{  
  $hostlist += $hosts  
  }  
 }  
   
 foreach ($srv in $hostlist) {  
    
  # read list of interfaces  
  [array]$InterfaceList = gwmi win32_networkadapterconfiguration -ComputerName $srv | ?{$_.ipenabled -ieq "true" -and $_.DefaultIPGateway}  
    
  # go through each interface and read DNS IPS  
  if($InterfaceList.Count -gt 0){  
  foreach ($interface in $interfaceList){  
     
   #then check the DNS accessibility and lookup the DNS server name.  
   $script:obj = "" | select ComputerName,Access,InterfaceName,DNS_1,DNS_1_check,DNS_2,DNS_2_check,DNS_3,DNS_3_check,Result  
   $script:obj.ComputerName = $srv  
   $script:obj.Access = "OK"  
   $script:obj.InterfaceName = $interface.Description  
   $tmpResult = $null  
     
   for ($i = 0; $i -le 2; $i++){  
   $tmpIP = $tmpName = $null  
   $tmpIP = $interface.DNSServerSearchOrder[$i]  
     
   $tmpName = [System.Net.Dns]::GetHostByAddress($tmpIP).HostName  
   $script:obj.("DNS_"+($i+1)) = $tmpIP + " ($tmpName)"  
     
   if((TCPportCheck $tmpName 53)){  
    $script:obj.("DNS_"+($i+1)+"_Check") = "DNS port open"  
   }  
   else{  
    $script:obj.("DNS_"+($i+1)+"_Check") = "DNS port closed"  
    $tmpResult = "One or more DNS servers are not accessible"  
   }  
   }  
   if($tmpResult) {$script:obj.Result = $tmpResult}  
   else  {$script:obj.Result = "OK"}  
   $script:objColl += $script:obj  
  }  
  }  
 }  
   
 $script:objColl  
   


t



24 October, 2012

Cleanup DNS SRV records - Active Directory

As I mentioned in the previous post, when you perform DC metadata cleanup at 2 AM (after working all day with a hangover from the previous night) in a large IT environment, you don't want to walk through all SRV records and "folders" on the dnsmgmt.msc console and hit delete and then think about it 3 times, sweat a bit before clicking on Yes for the "Are you sure?" question.

What you need is a semi-automated way which creates the dnscmd commands for you to review on the fly and run - I wouldn't just run the dnscmd command blindly even if I'm about to fall asleep and my eyes are like arrowslits. The reason for this paranoia is that each SRV record has a so called RRData (Resource Record Data) which contains the DC name, weight, priority. If you specify only the SRV record for dnscmd and you don't specify the RRData, it wipes out the whole SRV record for ALL DCs not just for the one you wanted to clean up. Believe me, I've been there... or test it in a QA environment, you'll see.

Red circle shows the RRData, without it, dnsmcd will wipe out the record for ALL DCs!

Although restoring SRV records is fairly easy in a small environment: just restart the NETLOGON service on all DCs and they re-register all of them. However, if you have a global environment, with 100+ DCs in different parts of the world, it's a bit more difficult and can take several hours... and your weekend has gone and it's Monday again...

When you Think Big - think about large IT infrastructure with multiple 1000s of servers spread across 100+ locations - just take into account:
  • There's no such thing that "out of business hours" - when it's at night in EU for you, it's business hours in America. If it's Sunday for you, doesn't matter, people may work still in different part of the world 24x7.
  • You have to go through all 100+ DCs probably with a quick script but not just to restart the NETLOGON service, you will have to perform checkouts on all of the DCs to make sure all dependent services are running and are functional.
  • If you want to recover in a multi-site environment, it can take several hours to just replicate the SRV records you registered to all DCs/DNS servers.
  • Restarting the netlogon service may break some applications during business hours
  • etc.


Essentially, you want to make sure you delete what needs to be deleted and only that. Here is the main part of the script (without proper error handling, parameter handling, logging...etc.) which composes the dnscmd commands for deleting SRV records of a given domain controller:

$dcname = "c3poDC1"
$domain = "tatooine.com"
$dnsServer = "MyDNSServer"
$dnscommands = @()

# list all srv records containing the given DC name
$listSRVs = gwmi -Namespace root/microsoftdns -q "select * from MicrosoftDNS_SRvtype" -ComputerName $dnsServer | ?{($_.domainname -imatch $dcname) -or ($_.srvdomainname -imatch $dcname)}

#generate dnscmd syntax for each
$listSRVs | %{
   $tmpstr = $srvrecordname = $null

  
# if the textrepresentation contains our DC name,
   # then we can proceed
   if($_.textrepresentation -imatch $dcname){

     
# the srvrecord name is in the ownername property,
      # we need to delete the domain FQDN from it
      $srvrecordname = $_.ownername -replace $domain, ""
      # delete the domain FQDN from the SRV record name
      # and delete the text 'IN '
      $srvrecordNameandData = $_.textrepresentation -replace "((?<!.$domain.*).$domain)|IN ",""

      # store the command in a variable and in an array
      $tmpstr = "dnscmd $dnsServer /recorddelete $domain $srvrecordNameandData "
      $dnscommands += $tmpstr
   }
   else{
      write-host -ForegroundColor 'red' "Could not enumerate RRData from $srvrecordname"
   }
}
$dnscommands

Explanation:
  • The script enumerates all SRV records of a given DC
  • Picks up the 'textrepresentation' of each record
  • Creates the syntax of the dnscmd command for deleting that particular record

Clipboard friendly code:


 $dcname = "c3poDC1"  
 $domain = "tatooine.com"  
 $dnsServer = "MyDNSServer"  
 $dnscommands = @()  
   
 # list all srv records containing the given DC name  
 $listSRVs = gwmi -Namespace root/microsoftdns -q "select * from MicrosoftDNS_SRvtype" -ComputerName $dnsServer | ?{($_.domainname -imatch $dcname) -or ($_.srvdomainname -imatch $dcname)}   
 
 #generate dnscmd syntax for each  
 $listSRVs | %{  
      $tmpstr = $srvrecordname = $null  

      # if the textrepresentation contains our DC name, then we can proceed  
      if($_.textrepresentation -imatch $dcname){  

           # the srvrecord name is in the ownername property, we need to delete the domain FQDN from it  
           $srvrecordname = $_.ownername -replace $domain, ""  
  
           # delete the domain FQDN from the SRV record name and delete the text IN  
           $srvrecordNameandData = $_.textrepresentation -replace "((?<!.$domain.*).$domain)|IN ",""  

           # store the command in a variable and in an array  
           $tmpstr = "dnscmd $dnsServer /recorddelete $domain $srvrecordNameandData "  
           $dnscommands += $tmpstr  
      }  
      else{  
           write-host -ForegroundColor 'red' "Could not enumerate RRData from $srvrecordname"  
      }  
 }  
 $dnscommands  

May The Force...

19 October, 2012

List DC SRV records with PowerShell - Active Directory

To start off this blog, one of my friend suggested me posting a simple problem/solution and then continuing with deep dive stuff later. I'll take his advice.

Because this blog is supposed to show how real life problems make you think and how you can perform tasks in a large IT environment - which are easy to do on 10 servers but a bit more difficult on a large scale with 1000+ servers - let me start with the following use case which unfortunately happened with me too many times:

A domain controller dies, dies hard and you have to perform a metadata cleanup - this means you have to delete all references of the dead DC in the AD database and DNS. The last time this happened to me is because someone replaced the wrong disk in the only RAID 1 array of the server (one disk was broken and the other one wasn't until it was pulled out). The OS came back up but unfortunately the NTDS database was corrupt.
You can read more about metadata cleanup here. I'd like to focus on a specific step in this process, namely: DNS SRV record cleanup.

So when you do a metadata cleanup on a production AD at 2 AM, it's bad enough already. When you have 100+ domain controllers and you need to go through all DNS SRV records on dnsmgmt.msc to find out if the DC was truly removed and delete the lingering records that doesn't make it more fun.
I decided to decrease my stress level, so have a command that lists all srv records for a particular DC, here it is:

PS C:\> gwmi -Namespace root/microsoftdns -q "select * from MicrosoftDNS_SRvtype" -ComputerName MyDNSServer | ?{($_.domainname -imatch "c3poDC1") -or ($_.srvdomainname -imatch "c3poDC1")} | ft OwnerName,DomainName,srvdomainname -auto


OwnerNameDomainNamesrvdomainname
_kerberos._tcp.Mos_Eisley._sites.dc._msdcs.tatooine.comc3poDC1.tatooine.com.
_ldap._tcp.Mos_Eisley._sites.dc._msdcs.tatooine.comc3poDC1.tatooine.com.
_kerberos._tcp.dc._msdcs.tatooine.comc3poDC1.tatooine.com.
_ldap._tcp.dc._msdcs.tatooine.comc3poDC1.tatooine.com.
_kerberos._tcp.Mos_Eisley._sites.tatooine.comc3poDC1.tatooine.com.
_ldap._tcp.Mos_Eisley._sites.tatooine.comc3poDC1.tatooine.com.
_kerberos._tcp.tatooine.comc3poDC1.tatooine.com.
_kpasswd._tcp.tatooine.comc3poDC1.tatooine.com.
_ldap._tcp.tatooine.comc3poDC1.tatooine.com.
_kerberos._udp.tatooine.comc3poDC1.tatooine.com.
_kpasswd._udp.tatooine.comc3poDC1.tatooine.com.
_ldap._tcp.Mos_Eisley._sites.DomainDnsZones.tatooine.comc3poDC1.tatooine.com.
_ldap._tcp.DomainDnsZones.tatooine.comc3poDC1.tatooine.com.


Explanation:
  • MyDNSServer could be a domain controller if the DNS zone is AD integrated or it sits on domain controllers as a separate DNS zone
  • This oneliner filters for DomainName and for SRVDomainName properties as well ($_.domainname and $_.srvdomainname), this is because depending on what kind of DNS zone you have, different fields will contain the domain controller name in the WMI class:
    • If you have the DNS zone in a separate AD partition, the SRV records will be in a separate zone called _msdcs.domainfqdn, this will make the WMI class show the DC name in the SRVDomainName property
    • If you have a Windows 2000 AD compatible DNS zone, which means the zone is in the domain partition in AD, then the WMI class will have the DC name in the DomainName property

Prerequisites:
  • I assume you have Windows DNS in your environment
  • You are admin on your DNS server

Clipboard friendly code:
 PS C:\> gwmi -Namespace root/microsoftdns -q "select * from MicrosoftDNS_SRvtype" -ComputerName MyDNSServer | ?{($_.domainname -imatch "c3poDC1") -or ($_.srvdomainname -imatch "c3poDC1")} | ft OwnerName,DomainName,srvdomainname -auto  


In the next post, I'll share a way how to compose dnscmd command syntax out of this output to perform SRV record deletion quickly - instead of walking through all records on dnsmgmt.msc.

Until then, May the Force be you all.

t