dig.ps1: dig-artiger DNS-Lookup fuer PowerShell

Skript und README hinzugefuegt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
maierch
2026-07-02 08:16:05 +02:00
commit 6b190a2e2f
2 changed files with 285 additions and 0 deletions
+198
View File
@@ -0,0 +1,198 @@
<#
.SYNOPSIS
dig-artiger DNS-Lookup fuer Windows PowerShell, basierend auf Resolve-DnsName.
.DESCRIPTION
Bildet die wichtigsten dig-Aufrufe nach, damit sich unter Windows die gleiche
Syntax wie mit dig unter Linux verwenden laesst. Argumente koennen wie bei dig
in beliebiger Reihenfolge stehen.
.PARAMETER Args
Freie dig-artige Argumente:
name abzufragender Name (z.B. example.com)
type Record-Typ (A, AAAA, MX, TXT, NS, CNAME, SOA, SRV, PTR, ...)
@server zu verwendender DNS-Server (z.B. @8.8.8.8)
-x <ip> Reverse-Lookup (PTR) fuer eine IPv4-/IPv6-Adresse
+short nur die reinen Antwortwerte ausgeben
.EXAMPLE
.\dig.ps1 example.com
.\dig.ps1 MX example.com
.\dig.ps1 example.com MX @1.1.1.1
.\dig.ps1 -x 8.8.8.8
.\dig.ps1 AAAA example.com +short
#>
# Alle Argumente einsammeln (bewusst als freies Array, dig-Stil).
$rest = $args
$name = $null
$type = $null
$server = $null
$short = $false
$reverse = $false
# Bekannte DNS-Record-Typen, die Resolve-DnsName unterstuetzt.
$knownTypes = @(
'A','AAAA','A_AAAA','CNAME','MX','NS','PTR','SOA','SRV','TXT',
'CAA','DNSKEY','DS','NAPTR','NSEC','NSEC3','RRSIG','ANY','ALL'
)
# Argumente parsen (Reihenfolge egal).
for ($i = 0; $i -lt $rest.Count; $i++) {
$arg = [string]$rest[$i]
if ($arg -eq '-x') {
$reverse = $true
# naechstes Argument ist die IP-Adresse
if ($i + 1 -lt $rest.Count) {
$name = [string]$rest[$i + 1]
$i++
}
continue
}
if ($arg.StartsWith('@')) {
$server = $arg.Substring(1)
continue
}
if ($arg -eq '+short') {
$short = $true
continue
}
if ($arg.StartsWith('+')) {
# weitere +optionen aktuell ignorieren
continue
}
if ($knownTypes -contains $arg.ToUpper()) {
$type = $arg.ToUpper()
continue
}
# alles andere ist der Name
if (-not $name) {
$name = $arg
}
}
if (-not $name) {
Write-Host "Verwendung: dig.ps1 [type] name [@server] [+short] | dig.ps1 -x <ip> [+short]"
exit 1
}
# Reverse-Lookup: IP in .in-addr.arpa / .ip6.arpa umwandeln.
function ConvertTo-PtrName([string]$ip) {
$parsed = $null
if (-not [System.Net.IPAddress]::TryParse($ip, [ref]$parsed)) {
return $ip # kein gueltiges IP-Literal, unveraendert zurueck
}
if ($parsed.AddressFamily -eq 'InterNetwork') {
# IPv4: Oktette umdrehen
$octets = $ip.Split('.')
[array]::Reverse($octets)
return ($octets -join '.') + '.in-addr.arpa'
}
else {
# IPv6: vollstaendige Nibble-Darstellung, umgedreht
$bytes = $parsed.GetAddressBytes()
$nibbles = foreach ($b in $bytes) {
'{0:x2}' -f $b
}
$hex = ($nibbles -join '')
$chars = $hex.ToCharArray()
[array]::Reverse($chars)
return (($chars -join '.') + '.ip6.arpa')
}
}
if ($reverse) {
$name = ConvertTo-PtrName $name
$type = 'PTR'
}
if (-not $type) {
$type = 'A'
}
# Resolve-DnsName-Parameter zusammenbauen.
$params = @{
Name = $name
Type = $type
ErrorAction = 'Stop'
}
if ($server) {
$params['Server'] = $server
}
try {
$records = Resolve-DnsName @params
}
catch {
Write-Host ";; Fehler bei der Aufloesung von '$name' ($type): $($_.Exception.Message)"
exit 1
}
# Nur echte Antwort-Records (keine Query-Hinweiszeilen).
$answers = $records | Where-Object { $_.Section -ne 'Question' }
# Wert eines Records typgerecht als String formatieren.
function Get-RecordData($r) {
switch -Wildcard ($r.Type) {
'A' { return $r.IPAddress }
'AAAA' { return $r.IPAddress }
'CNAME' { return $r.NameHost }
'NS' { return $r.NameHost }
'PTR' { return $r.NameHost }
'MX' { return "$($r.Preference) $($r.NameExchange)" }
'SOA' { return "$($r.PrimaryServer) $($r.NameAdministrator) $($r.SerialNumber) $($r.TimeToZoneRefresh) $($r.TimeToZoneFailureRetry) $($r.TimeToExpiration) $($r.DefaultTTL)" }
'SRV' { return "$($r.Priority) $($r.Weight) $($r.Port) $($r.NameTarget)" }
'TXT' { return '"' + ($r.Strings -join '" "') + '"' }
'CAA' { return "$($r.Flags) $($r.Tag) `"$($r.Value)`"" }
default {
# Fallback: sinnvolles Feld suchen
foreach ($p in 'NameHost','IPAddress','NameTarget','Strings') {
if ($r.PSObject.Properties[$p] -and $r.$p) {
return ($r.$p -join ' ')
}
}
return ($r | Out-String).Trim()
}
}
}
if ($short) {
# +short: nur die Werte, eine pro Zeile.
foreach ($r in $answers) {
Get-RecordData $r
}
exit 0
}
# dig-artige Ausgabe.
$srvText = if ($server) { $server } else { '(System-Resolver)' }
Write-Host ""
Write-Host "; <<>> dig.ps1 <<>> $name $type" -ForegroundColor DarkGray
Write-Host ";; Server: $srvText"
Write-Host ""
Write-Host ";; QUESTION SECTION:"
Write-Host (";{0}`t`tIN`t{1}" -f $name, $type)
Write-Host ""
if ($answers) {
Write-Host ";; ANSWER SECTION:"
foreach ($r in $answers) {
$ttl = if ($r.PSObject.Properties['TTL']) { $r.TTL } else { 0 }
$data = Get-RecordData $r
Write-Host ("{0}`t{1}`tIN`t{2}`t{3}" -f $r.Name, $ttl, $r.Type, $data)
}
Write-Host ""
}
else {
Write-Host ";; keine Antworten (ANSWER SECTION leer)"
Write-Host ""
}