PowerShell und DNSClient

Die meisten Commandlets arbeiten auch mit dem Parameter "Computer", "DomainController" o.ä. Und akzeptieren dort natürlich auch Hostnamen. Aber was macht man, wenn man wirklich mal selbst eine DNS-Abfrage stellen will? Das NET-Framework stellt dazu eine Klasse bereit, die aber eher rudimentär ist. Gerade für Lync hingegen ist z.B. die Abfrage eines SRV-Records interessant und auch die Überprüfung, ob ein Name auf mehrere IP-Adressen auflöst. Und nicht immer ist NSLOOKUP der passende Weg.

NSLOOOKUP Fallback

Über viel Jahre habe ich wirklich meine "Tests" über den Umweg mit NSLookup gemacht. Aus PowerShell heraus wurde NSLOOKUP mit den passenden Parametern aufgerufen und die Rückgabe als Textstring geparsed. Hier z.B. ein Codeteil aus einer VBScript-Routine

Hier der gesamte Code als Download
psdns-ocsdns.1.2.vbs.txt

Das gleiche geht natürlich auch per PowerShell aber es ist nicht elegant und zudem auch abhängig von der Sprache, da hier der Rückgabetext analysiert werden muss.

DNS bis PowerShell 2

Wer auf PowerShell 2 aufbaut, der kann neben NSLOOKUP auch WMI-Anfragen oder eben das .NET Framework nutzen, z.B.

[System.Net.Dns]::GetHostAddresses("www.msxfaq.de")

Das Ergebnis ist dann eine .NET Objekt mit folgende Informationen (Stand Jul 2014)

   TypeName: System.Net.IPAddress

Name               MemberType     Definition
----               ----------     ----------
Equals             Method         bool Equals(System.Object comparand)
GetAddressBytes    Method         byte[] GetAddressBytes()
GetHashCode        Method         int GetHashCode()
GetType            Method         type GetType()
MapToIPv4          Method         ipaddress MapToIPv4()
MapToIPv6          Method         ipaddress MapToIPv6()
ToString           Method         string ToString()
Address            Property       long Address {get;set;}
AddressFamily      Property       System.Net.Sockets.AddressFamily AddressFa
IsIPv4MappedToIPv6 Property       bool IsIPv4MappedToIPv6 {get;}
IsIPv6LinkLocal    Property       bool IsIPv6LinkLocal {get;}
IsIPv6Multicast    Property       bool IsIPv6Multicast {get;}
IsIPv6SiteLocal    Property       bool IsIPv6SiteLocal {get;}
IsIPv6Teredo       Property       bool IsIPv6Teredo {get;}
ScopeId            Property       long ScopeId {get;set;}
IPAddressToString  ScriptProperty System.Object IPAddressToString {get=$this

In meinem Fall ergibt dies:

c:PS C:\> [System.Net.Dns]::GetHostAddresses("www.msxfaq.de") | fl

Address            : 3947144530
AddressFamily      : InterNetwork
ScopeId            :
IsIPv6Multicast    : False
IsIPv6LinkLocal    : False
IsIPv6SiteLocal    : False
IsIPv6Teredo       : False
IsIPv4MappedToIPv6 : False
IPAddressToString  : 82.165.68.235

Auf dem gleichen Weg kann eine IP-Adresse rückwärts auf einen Host aufgelöst werden. Allerdings funktioniert dies nur für A, AAAA und PTR-Records. Selbst ein MX-Record kann so nicht aufgelöst werden.

Resolve-DNSName mit PS3+

Seit Windows 8/Windows 2012 und PowerShell 3.0 gibt es endlich ein eigenes Commandlet für die Auflösung von DNS-Namen, welches fast alle Wünsche abdeckt:

Damit lassen sich nicht nur "Hostnamen" auflösen sondern auch MX-Records, SRV-Records und einiges mehr. Die Microsoft Hilfe beschreibt aber schön die einzelnen Optionen. Voraussetzung ist natürlich die Installation von PowerShell 3 oder 4. Sie finden den Download bei der Suche nach "Windows Management Framework 4.0"

Achtung: Das .Net Framework 4.5 muss vorher installiert sein. Das WMF4 Setup weist darauf NICHT hin und stoppt auch nicht, wenn das Framework nicht installiert ist.

Windows Management Framework 4.0
http://www.microsoft.com/de-de/download/details.aspx?id=40855

Microsoft .NET Framework 4.5
http://www.microsoft.com/en-us/download/details.aspx?id=30653

Die PowerShell 4 funktioniert auch auf Windows 7 SP1 und anderen Plattformen. Prüfen Sie aber unbedingt die Systemvoraussetzungen. Bestimmte ältere Software, z.B. Exchange 2007) ist nicht damit kompatibel.

Leider ist das Commandlet per Default NICHT auf Windows 7 damit schon installiert. Das entsprechende Modul, welches in C:\Windows\System32\WindowsPowerShell\v1.0\Modules\DnsClient liegt, wird bei Windows 7 nicht mit installiert.
Tipp: Es funktioniert aber, wenn Sie das komplette Verzeichnis von einem Windows 2012 Server oder Windows 8 Client einfach kopieren. Eine entsprechende Lizenz benötigen Sie natürlich.

Erst dann steht ihnen der "Resolve-DNSName" zur Verfügung. Hier ein paar Beispiele:

PS C:\> Resolve-DnsName _sipfederationtls._tcp.netatwork.de -Type SRV | fl

Name       : _sipfederationtls._tcp.netatwork.de
Type       : SRV
TTL        : 86392
NameTarget : sip.netatwork.de
Priority   : 0
Weight     : 0
Port       : 5061

Nun kann man per PowerShell sehr einfach endlich auch SRV-Records auslesen

Wenn ein Anbieter die Last auf mehrere Server verteilt, dann bekommen Sie auch mehrere Records in einem Objekt

PS C:\> Resolve-DnsName www.google.de | ft -AutoSize

Name          Type TTL Section IPAddress
----          ---- --- ------- ---------
www.google.de AAAA 178 Answer  2a00:1450:4008:800::100f
www.google.de A    139 Answer  173.194.32.223
www.google.de A    139 Answer  173.194.32.216
www.google.de A    139 Answer  173.194.32.215
www.google.de A    139 Answer  173.194.32.207

Bei Microsoft hingegen sieht es (Jun 2014) etwas anders aus.

PS C:\> Resolve-DnsName www.microsoft.com

Name                           Type   TTL   Section    NameHost
----                           ----   ---   -------    --------
www.microsoft.com              CNAME  241   Answer     toggle.www.ms.akadns.net
toggle.www.ms.akadns.net       CNAME  241   Answer     g.www.ms.akadns.net
g.www.ms.akadns.net            CNAME  241   Answer     lb1.www.ms.akadns.net

Name       : lb1.www.ms.akadns.net
QueryType  : A
TTL        : 31
Section    : Answer
IP4Address : 65.55.57.27


Name                   : akadns.net
QueryType              : SOA
TTL                    : 22
Section                : Authority
NameAdministrator      : hostmaster.akamai.com
SerialNumber           : 1403167478
TimeToZoneRefresh      : 90000
TimeToZoneFailureRetry : 90000
TimeToExpiration       : 90000
DefaultTTL             : 180

Das sind genau genommen fünf Einträge, von denen die ersten drei auf CNAME verweisen, der vierte Eintrag ein "A-Record" und der Eintrag 5 der SOA ist. Selbst wenn man den Type mit angibt, bekommt man die CNAME Einträge mit:

PS C:\> Resolve-DnsName www.microsoft.com -Type a

Name                           Type   TTL   Section    NameHost
----                           ----   ---   -------    --------
www.microsoft.com              CNAME  105   Answer     toggle.www.ms.akadns.net
toggle.www.ms.akadns.net       CNAME  105   Answer     g.www.ms.akadns.net
g.www.ms.akadns.net            CNAME  105   Answer     lb1.www.ms.akadns.net

Name       : lb1.www.ms.akadns.net
QueryType  : A
TTL        : 159
Section    : Answer
IP4Address : 65.55.57.27

Hier sollten Sie also eventuell noch etwas weiter filtern und prüfen, z.B. mit

PS C:\> Resolve-DnsName www.microsoft.com | where {$_.type -eq "a"}

Name                                           Type   TTL   Section    IPAddres
                                                                       s
----                                           ----   ---   -------    --------
lb1.www.ms.akadns.net                          A      86    Answer     65.55.57
                                                                       .27

Bild sollten Sie sich also nicht auf die Antworten verlassen, sondern zumindest damit umgehen, das die Rückgabe mehrere Records enthält. Ist ein Name nicht auflösbar, wird natürlich eine Exception geworfen.

PS C:\> Resolve-DnsName www.microsoft.egal
Resolve-DnsName : www.microsoft.egal : Der DNS-Name ist nicht vorhanden In Zeile:1 Zeichen:1
+ Resolve-DnsName www.microsoft.egal
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + CategoryInfo          : ResourceUnavailable: (www.microsoft.egal:String)[Resolve-DnsName], Win32Exception
 + FullyQualifiedErrorId : DNS_ERROR_RCODE_NAME_ERROR,Microsoft.DnsClient.Commands.ResolveDnsName

Der Umgang mit Try/Catch ist hier also ratsam.

DNS_ANY Probleme

Auf eine andere Besonderheit sollten Sie achten, wenn Sie möglichst alle DNS-Einträge suchen und daher nach "ANY" suchen. Nicht alle DNS-Server erlauben ein ANY.

Per Wireshark ist auch zu sehen, dass hier keine Antwort kommt.

Eine Abfrage mit "A" oder "A_AAAA" führt aber zu einen Erfolg. Ich habe dann natürlich verschiedene DNS-Server angefragt aber alle haben auf einen "ANY" nichts mehr geliefert. Resolve-DNS nutzt by Default übrigens "A_AAA" als Default, womit aber auch CNAME-Einträge aufgelöst werden.

Die Reaktion auf ANY-Anfragen müssen DNS-Server aber nicht mehr beantworten, da Sie über UDP zu leicht für DDoS-Attacken missbraucht werden können. Als Angreifer stellen Sie eine DNS ANY-Abfrage per UDP mit gefälschter IP-Adresse an einen DNS-Server, der seine auch längere Antwort an das Ziel sendet. Aus einer kleinen DNS-Abfrage wird so eine große Antwort an ein drittes System.

Resolve-DNSName mit CNAME

Interessant ist auch anderes Verhalten, was ich habe von "Invoke-Webrequest" schon ähnlich kenne. Sowohl bei DNS als auch HTTP gibt es die Möglichkeit einer Weiterleitung. Was bei HTTP ein Code 302/301 ist, ist bei DNS der CNAME. Das kann zu durchaus interessanten Ergebnissen führen, wenn Sie z.B. mit DKIM Abfragen machen. Um z.B. die Funktion DKIM mit Office 365 zu nutzen, legt Microsoft einen TXT-Record mit dem öffentlichen RSA-Token an und der Domaininhaber muss einen CNAME anlegen. Das kann ich per Resolve-DNS einfach abfragen.

PS C:\> Resolve-DnsName -Type CNAME -Name selector1._domainkey.msxfaq.com |fl

Name     : selector1._domainkey.msxfaq.com
Type     : CNAME
TTL      : 86400
Section  : Answer
NameHost : selector1-msxfaq-com._domainkey.msxfaq.onmicrosoft.com

In einem zweiten Schritte kann ich dann den TXT-Record abfragen:

PS C:\> Resolve-DnsName -Type txt -Name selector1-msxfaq-com._domainkey.msxfaq.onmicrosoft.com | fl

Name    : selector1-msxfaq-com._domainkey.msxfaq.onmicrosoft.com
Type    : TXT
TTL     : 3579
Strings : {v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCY2i1orZ9zezwp/+xOcq2xc31F099nXV
          SuDJxeXEVUr9FP788r/fr5FumyaBf/y1LAPz2hRouIvJdSx6OCVHcCvzJorbWfixFxIHL6p2PN0XmVngQM2doEyg1
          YZhAQDUIegT1YwvKzo2SS2pHikTFhIlLL+4IqZVnF1kM3mzGHuQIDAQAB; n=1024,1450894448,1}

Interessant wird es aber, wenn ich nicht den CNAME sondern den TXT-Record abfrage. Ein DKIM-Verifier muss ja nicht zwingend mit dem CNAME rechnen sondern erwartet erst einmal einen TXT-Record.

PS C:\group\Technik\Skripte\Get-O365Tenantinfo> Resolve-DnsName -Type txt -Name selector1._domainkey.msxfaq.com | fl

Name     : selector1._domainkey.msxfaq.com
Type     : CNAME
TTL      : 86346
Section  : Answer
NameHost : selector1-msxfaq-com._domainkey.msxfaq.onmicrosoft.com


Name      : selector1-msxfaq-com._domainkey.msxfaq.onmicrosoft.com
QueryType : TXT
TTL       : 3546
Section   : Answer
Strings   : {v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCY2i1orZ9zezwp/+xOcq2xc31F099n
            XVSuDJxeXEVUr9FP788r/fr5FumyaBf/y1LAPz2hRouIvJdSx6OCVHcCvzJorbWfixFxIHL6p2PN0XmVngQM2do
            Eyg1YZhAQDUIegT1YwvKzo2SS2pHikTFhIlLL+4IqZVnF1kM3mzGHuQIDAQAB; n=1024,1450894448,1}

Hier sehen Sie dann, dass ich zwei Ergebnisse erhalte. Resolve-DNS liefert mir natürlich den CNAME aber passende dazu auch gleich die Abfrage zum Zielsystem. Als PowerShell-Entwickler müssen Sie natürlich auf sowas gefasst sein, dass Sie mehrere Ergebnisse mit mehreren Rückgaben erhalten, die sie dann auch noch filtern müssen. Das ist in PowerShell natürlich einfach

PS C:\> Resolve-DnsName -Type txt -Name selector1._domainkey.msxfaq.com | ?{$_.Querytype -eq "TXT"} | fl

Name    : selector1-msxfaq-com._domainkey.msxfaq.onmicrosoft.com
Type    : TXT
TTL     : 3277
Strings : {v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCY2i1orZ9zezwp/+xOcq2xc31F099nXV
          SuDJxeXEVUr9FP788r/fr5FumyaBf/y1LAPz2hRouIvJdSx6OCVHcCvzJorbWfixFxIHL6p2PN0XmVngQM2doEyg1
          YZhAQDUIegT1YwvKzo2SS2pHikTFhIlLL+4IqZVnF1kM3mzGHuQIDAQAB; n=1024,1450894448,1}

Win32 API

Für die C++-Entwickler ist der Weg zu .NET und PowerShell eher ein Umweg und Sie werden vermutlich die Win32 API nutzen. Das ist gar nicht mal so verwerflich, da die Win32-API auch in Zeiten von .NET noch alle nicht abgeschrieben ist. Zugegeben würde so ein Programm vermutlich nicht auf eine "anderen Plattform" laufen, aber bislang habe ich noch keinen ernsthaften Einsatz von .NET auf fremden Plattformen gesehen. Sicher gibt es das Projekt Mono, was das .NET Framework auf Linux bereit stellt, aber dort gibt es meines Wissens noch keine PowerShell. Daher hier nur zur Vollständigkeit ein paar Links:

Weitere Links