Interaktiver TUI-Viewer für dns-watcher SQLite Datenbanken. Unterstützt sowohl DnsClientWatcher als auch DnsServerWatcher Datenbanken.
- Auto-Detection: Erkennt automatisch Client- oder Server-Datenbank
- Live-Ansicht: Auto-Refresh mit Follow-Modus
- Mächtiges Filter-System: Vollständiger Expression Parser mit AND, OR, NOT, Klammern
- Lazy Loading: Effizientes Laden großer Datenbanken
- Maus-Unterstützung: Doppelklick für Details, Shift+Maus zum Markieren
- Peer-to-Peer: Daten von mehreren DNS-Servern aggregieren
- TTL: Gespeicherte TTL aus der Server-DB, per
F5live nachschlagbar
- Go 1.21+
- GCC (für CGO/sqlite3) - unter Windows z.B. via mingw-w64
go mod tidy
go build -o dns-viewer.exe ./cmd/dns-viewer# PowerShell (einmalig)
dns-viewer completion powershell | Out-String | Invoke-Expression
# PowerShell (permanent - zu $PROFILE hinzufügen)
dns-viewer completion powershell >> $PROFILE# Bash
source <(dns-viewer completion bash)
# Zsh
dns-viewer completion zsh > "${fpath[1]}/_dns-viewer"# Nur lokal
./dns-viewer.exe --db <path-to-database.db>
# Als Server für andere Peers
./dns-viewer.exe --db dns.db --listen :8847 --secret "geheim"
# Mit Peer-Abfrage
./dns-viewer.exe --db dns.db --peer dns-srv2:8847 --secret "geheim"
# Headless Server-Mode (ohne TUI)
./dns-viewer.exe --db dns.db --listen :8847 --secret "geheim" --headless| Option | Beschreibung |
|---|---|
--db |
Pfad zur SQLite Datenbank |
--listen |
Adresse für Peer-Verbindungen (z.B. :8847) |
--port |
Port für Peer-Verbindungen (default: 8847) |
--peer |
Peer-Adresse (kann mehrfach angegeben werden) |
--secret |
Shared Secret für Authentifizierung |
--headless |
Server-Mode ohne TUI |
--no-mouse |
Maus-Unterstützung deaktivieren (RDP/Remote) |
--resolver |
DNS-Server für die TTL-Live-Abfrage (F5) |
--version |
Version anzeigen |
| Variable | Beschreibung |
|---|---|
DNS_VIEWER_SECRET |
Shared Secret für Peer-Authentifizierung |
Der Viewer kann Daten von mehreren DNS-Servern aggregieren:
┌─────────────────┐ ┌─────────────────┐
│ DNS-Server A │ │ DNS-Server B │
│ dns-viewer │◄───►│ dns-viewer │
│ -listen :8847 │ │ -listen :8847 │
└────────┬────────┘ └────────┬────────┘
│ │
└───────────┬───────────┘
│
┌──────▼──────┐
│ Workstation │
│ dns-viewer │
│ -peer A │
│ -peer B │
└─────────────┘
- Authentifizierung: Alle Peers müssen das gleiche Secret verwenden
- Source-Filter:
source:hostnamefiltert nach Server-Herkunft - Statusbar: Zeigt verbundene Peers (z.B.
PEERS:2/2)
| Taste | Funktion |
|---|---|
↑/↓ oder j/k |
Navigation |
g/G |
Zum Anfang/Ende |
Enter |
Details anzeigen |
Esc |
Details schließen |
f oder / |
Filter-Eingabe |
c |
Filter löschen |
s |
Statistik-Ansicht |
t |
RTT-Check (Log-Latenz messen) |
F5 |
TTL live abfragen |
Space |
Pause/Resume |
n |
Mehr Events laden |
? |
Hilfe |
q |
Beenden |
| Feld | Beschreibung | Beispiel |
|---|---|---|
type: |
Event-Typ | type:QUERY |
qtype: |
Query-Typ | qtype:A |
status: |
Status | status:OK |
process: |
Prozess (Client) | process:chrome |
client: |
Client-IP (Server) | client:10.0.0 |
result: |
Aufgelöste IP | result:142.250 |
zone: |
DNS-Zone (Server) | zone:lab.local |
source: |
Server-Herkunft (Peer) | source:dns-srv1 |
error: |
Fehler-Kategorie | error:CONFIG |
ttl: |
TTL in Sekunden (Server ab v6) | ttl:<60 |
negttl: |
Negative-Caching-TTL (Server ab v7) | negttl:>300 |
flags: |
DNS-Header-Bit (Server ab v7) | flags:AA |
ecs: |
EDNS Client Subnet (Server ab v7) | ecs:10.0.12 |
edns: |
EDNS-Puffergröße (Server ab v7) | edns:<1232 |
Der ttl:-Filter kennt die Operatoren <, <=, >, >= und = sowie
ttl:null (kein Wert gespeichert) und ttl:set (Wert vorhanden). Ein fehlender
Wert wird nie in Vergleiche einbezogen: ttl:<60 trifft keine Events ohne TTL.
| Operator | Beschreibung | Beispiel |
|---|---|---|
Leerzeichen oder & |
AND | qtype:A status:OK |
| oder , |
OR | qtype:A,AAAA |
! oder - |
NOT | !status:Cached |
() |
Klammern | (qtype:A | qtype:AAAA) & status:OK |
| Filter | Beschreibung |
|---|---|
from:10:30 |
Ab 10:30 heute |
to:14:00 |
Bis 14:00 heute |
from:-1h |
Letzte Stunde |
from:-30m |
Letzte 30 Minuten |
from:2025-12-06 |
Ab Datum |
# Google-Anfragen der letzten Stunde
from:-1h google
# A oder AAAA Records mit OK Status
(qtype:A | qtype:AAAA) & status:OK
# Alles außer Cached von svchost
!status:Cached & !process:svchost
# Chrome DNS-Anfragen zu Microsoft
process:chrome microsoft
# Kandidaten für Traffic-Steering: kurzlebige Einträge (Server-DB ab v6)
ttl:<=60
Server-DB ab Schema v6 speichert in der Spalte ttl die Original-TTL aus dem
DNS-Antwortpaket (Minimum über alle Answer-Records, befüllt bei RESPONSE und
RECURSE). Der Viewer zeigt sie in der TTL-Spalte an und hebt Werte ≤ 60 s hervor.
NULL bedeutet: keine Answer-Records, z.B. bei NXDomain — das Feld bleibt leer.
0 ist ein gültiger Wert und bedeutet „nicht cachen".
Client-DB hat keine TTL und wird auch nie eine haben. Der ETW-Provider
Microsoft-Windows-DNS-Client gibt sie nicht aus. In der TTL-Spalte steht dort
ein ausgegrautes -.
In der zugeklappten Summary-Zeile einer Event-Gruppe steht die TTL der RESPONSE —
also der Wert, den der Client tatsächlich bekommen hat. Tragen die Events der
Gruppe unterschiedliche TTLs (z.B. RECURSE gegen RESPONSE), wird der Wert mit
einem ? gekennzeichnet: 15?. Enter klappt die Gruppe auf und zeigt die
Einzelwerte.
F5 fragt die TTL des markierten Eintrags direkt per DNS ab — der Viewer sendet
dafür eigene UDP-Pakete und umgeht damit den Windows-Cache. Abgefragt werden zwei
Werte, die zusammen erst aussagekräftig sind:
| Abfrage | Ergebnis |
|---|---|
| gegen den Resolver | Restlaufzeit im Cache — zählt herunter |
| gegen den autoritativen Nameserver | Original-TTL |
Die Differenz ist das Alter des Cache-Eintrags. Der Viewer meldet zusätzlich, wenn Resolver und autoritativer Server verschiedene Antworten liefern — wechselnde Antworten bei konstant niedriger TTL sind die Signatur von Traffic-Steering, Round-Robin und Fast-Flux.
Der befragte Resolver wird in dieser Reihenfolge bestimmt: --resolver, sonst
dns_server des Events (Client-DB), sonst der System-Resolver.
Bei NXDomain und NODATA gibt es keine Answer-Records und damit keine ttl. Wie
lange das Fehlen der Antwort gecacht wird, steht stattdessen in neg_ttl — nach
RFC 2308 das Minimum aus dem MINIMUM-Feld des SOA und der TTL des SOA-Records.
Das sind bewusst zwei getrennte Spalten: ttl sagt, wie lange eine Antwort gilt,
neg_ttl, wie lange ihr Fehlen gilt. In einer Spalte vermengt wäre eine
Auswertung wie „alle TTLs unter 60" nicht mehr eindeutig. Der Viewer zeigt beide
in derselben Spalte, kennzeichnet die negative aber mit einem n: 900n. Die
Hervorhebung ab 60 s greift dort nicht — sie ist auf positive TTLs gemünzt.
Filter: negttl: statt ttl:, mit denselben Operatoren.
Aus dem rohen DNS-Paket werden drei weitere Angaben gelesen, sichtbar im Detail-View:
| Feld | Bedeutung |
|---|---|
flags |
Gesetzte Header-Bits: AA, TC, RD, RA, AD, CD, DO |
edns_udp_size |
EDNS-Puffergröße aus dem OPT-Record |
ecs_subnet |
EDNS Client Subnet, z.B. 10.0.12.0/24 scope 24 |
Zwei davon lohnen besondere Beachtung: AA ist die einzige verlässliche
Auskunft darüber, ob eine Antwort autoritativ war oder aus dem Cache kam.
ecs_subnet erklärt wechselnde Antworten für denselben Namen — EDNS Client
Subnet ist der Standardmechanismus, mit dem Antworten je nach Client-Netz
variieren.
Befüllt sind die Felder bei Event 256 (nur Flags, EDNS, ECS), 257 und 261.
Live-Werte beschreiben den Zustand jetzt, nicht den Zeitpunkt des Events. Sie werden getrennt dargestellt und niemals in die Datenbank zurückgeschrieben.
- FOLLOW (grün): Live-Modus aktiv
- PAUSED (rot): Manuell pausiert
- ↑X neue: Neue Events während Scroll
- ↓mehr: Weitere Events verfügbar
dns-viewer/
├── cmd/dns-viewer/ # Entry point
├── internal/
│ ├── db/ # Datenbank-Layer
│ │ ├── models.go # Datenmodelle
│ │ ├── reader.go # DB-Reader
│ │ └── filter_parser.go # Expression Parser
│ ├── dnsquery/ # DNS-Live-Abfrage für die TTL (rohe UDP-Pakete)
│ │ ├── lookup.go # Resolver- und autoritative Abfrage
│ │ └── resolver_*.go # Ermittlung des System-Resolvers
│ └── ui/ # Terminal UI
│ ├── tui.go # Haupt-TUI
│ └── peer.go # Peer-to-Peer Kommunikation
└── go.mod
| Farbe | Bedeutung |
|---|---|
| Weiß | QUERY (normale Anfrage) |
| Grün | OK, NOERROR (Erfolg) |
| Gelb | NXDOMAIN (nicht gefunden) |
| Rot | FAILURE, TIMEOUT, SERVFAIL, error_category |
| Grau | CACHED |
MIT License