commit 6b190a2e2ffda601fd71835bb8f49fd23b0d5bc7 Author: maierch Date: Thu Jul 2 08:16:05 2026 +0200 dig.ps1: dig-artiger DNS-Lookup fuer PowerShell Skript und README hinzugefuegt. Co-Authored-By: Claude Opus 4.8 diff --git a/README.md b/README.md new file mode 100644 index 0000000..c080879 --- /dev/null +++ b/README.md @@ -0,0 +1,87 @@ +# dig.ps1 — dig-artiger DNS-Lookup für PowerShell + +Ein PowerShell-Skript, das die wichtigsten `dig`-Aufrufe unter Windows nachbildet. +Intern wird `Resolve-DnsName` verwendet. + +## Aufrufe + +```powershell +.\dig.ps1 example.com # A-Record (Default) +.\dig.ps1 MX example.com # type name +.\dig.ps1 example.com MX # name type (Reihenfolge egal) +.\dig.ps1 example.com AAAA @1.1.1.1 # gegen bestimmten Server +.\dig.ps1 -x 8.8.8.8 # Reverse-Lookup (PTR) +.\dig.ps1 example.com TXT +short # nur die Werte ausgeben +``` + +Unterstützt: Record-`type`, `name`, `@server`, die Option `+short` sowie `-x` für Reverse-Lookups. + +## `dig`-Funktion anlegen + +Damit sich das Skript wie unter Linux einfach mit `dig …` (statt `.\dig.ps1 …`) aufrufen lässt, +wird eine Funktion im PowerShell-Profil angelegt. + +### 1. Skript ablegen + +Kopiere `dig.ps1` an einen festen Ort, z. B.: + +```powershell +C:\Tools\dig.ps1 +``` + +### 2. Profil öffnen (bei Bedarf anlegen) + +```powershell +# Existiert das Profil schon? +Test-Path $PROFILE + +# Falls nicht: anlegen +if (-not (Test-Path $PROFILE)) { + New-Item -ItemType File -Path $PROFILE -Force +} + +# Profil im Editor öffnen +notepad $PROFILE +``` + +### 3. Funktion eintragen + +Füge folgende Zeilen in die Profildatei ein und passe den Pfad an: + +```powershell +function dig { + & "C:\Tools\dig.ps1" @args +} +``` + +`@args` reicht alle Argumente unverändert an das Skript weiter, sodass die dig-Syntax +1:1 erhalten bleibt. + +### 4. Profil neu laden + +```powershell +. $PROFILE +``` + +Danach steht `dig` in jeder neuen PowerShell-Sitzung zur Verfügung: + +```powershell +dig example.com MX @1.1.1.1 +dig -x 8.8.8.8 +short +``` + +## Ausführungsrichtlinie (ExecutionPolicy) + +Wird die Ausführung von Skripten blockiert, kann sie für den aktuellen Benutzer erlaubt werden: + +```powershell +Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned +``` + +`RemoteSigned` erlaubt lokal erstellte Skripte; aus dem Internet geladene Skripte müssen signiert sein. + +## Hinweise + +- Das Skript benötigt das Modul `DnsClient` (`Resolve-DnsName`), das unter Windows standardmäßig vorhanden ist. +- Die Standardausgabe orientiert sich am `dig`-Format mit `QUESTION`- und `ANSWER SECTION`. +- Mit `+short` werden nur die reinen Antwortwerte ausgegeben (eine pro Zeile). diff --git a/dig.ps1 b/dig.ps1 new file mode 100644 index 0000000..122c702 --- /dev/null +++ b/dig.ps1 @@ -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 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 [+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 "" +}