load(); } public function render() { return view('livewire.ui.mail.dns-health-card'); } public function refresh(): void { $this->load(); } public function openDnsModal(int $domainId): void { $this->dispatch('openModal', component: 'ui.domain.modal.domain-dns-modal', arguments: ['domainId' => $domainId]); } // protected function load(bool $force = false): void // { // [$this->mtaHost, $this->tlsa, $this->rows] = Cache::remember('dash.dnshealth.v2', $force ? 1 : 600, function () { // // $base = trim((string) env('BASE_DOMAIN', '')); // $mtaSub = trim((string) env('MTA_SUB', 'mx')); // $mtaHost = $base !== '' ? "{$mtaSub}.{$base}" : $mtaSub; // z.B. mx.nexlab.at // // // ▼ gewünschter Filter: // $domains = Domain::query() // ->where('is_active', true) // ->where('is_server', false) // <<< Server-Domain sauber ausschließen // ->orderBy('domain') // ->get(['id', 'domain']); // // $rows = []; // foreach ($domains as $d) { // $dom = $d->domain; // // // DKIM-Selector ermitteln: .env > DB > Fallback null // $selector = trim((string) env('DKIM_SELECTOR', '')); // if ($selector === '') { // $selector = (string) DB::table('dkim_keys') // ->where('domain_id', $d->id) // ->where('is_active', 1) // ->orderByDesc('id') // ->value('selector') ?? ''; // } // // $missing = []; // // if (!$this->mxPointsTo($dom, [$mtaHost])) $missing[] = 'MX'; // if (!$this->hasSpf($dom)) $missing[] = 'SPF'; // if (!$this->hasDkim($dom, $selector)) $missing[] = 'DKIM'; // if (!$this->hasTxt("_dmarc.$dom")) $missing[] = 'DMARC'; // // $rows[] = [ // 'id' => (int) $d->id, // 'name' => $dom, // 'ok' => empty($missing), // 'missing' => $missing, // ]; // } // // // Hostweites TLSA (nur Hinweis) // $tlsa = $this->hasTlsa("_25._tcp.$mtaHost") || $this->hasTlsa("_465._tcp.$mtaHost") || $this->hasTlsa("_587._tcp.$mtaHost"); // // return [$mtaHost, $tlsa, $rows]; // }); // } protected function load(): void { $base = trim((string) env('BASE_DOMAIN', '')); $mtaSub = trim((string) env('MTA_SUB', 'mx')); $mtaHost = $base !== '' ? "{$mtaSub}.{$base}" : $mtaSub; // z.B. mx.nexlab.at // nur aktive, NICHT-Server-Domains (System + Custom, solange is_server = false) $domains = Domain::query() ->where('is_active', true) ->where('is_server', false) ->orderBy('domain') ->get(['id','domain']); $rows = []; foreach ($domains as $d) { $dom = $d->domain; // DKIM-Selector: .env > DB > leer $selector = trim((string) env('DKIM_SELECTOR', '')); if ($selector === '') { $selector = (string) DB::table('dkim_keys') ->where('domain_id', $d->id) ->where('is_active', 1) ->orderByDesc('id') ->value('selector') ?? ''; } $missing = []; if (!$this->mxPointsTo($dom, [$mtaHost])) $missing[] = 'MX'; if (!$this->hasSpf($dom)) $missing[] = 'SPF'; if (!$this->hasDkim($dom, $selector)) $missing[] = 'DKIM'; if (!$this->hasTxt("_dmarc.$dom")) $missing[] = 'DMARC'; $rows[] = [ 'id' => (int) $d->id, 'name' => $dom, 'ok' => empty($missing), 'missing' => $missing, ]; } // Hostweites TLSA (nur Info) $tlsa = $this->hasTlsa("_25._tcp.$mtaHost") || $this->hasTlsa("_465._tcp.$mtaHost") || $this->hasTlsa("_587._tcp.$mtaHost"); $this->mtaHost = $mtaHost; $this->tlsa = $tlsa; $this->rows = $rows; } /* ── DNS Helpers ───────────────────────────────────────────────────── */ protected function digShort(string $type, string $name): string { $cmd = "timeout 2 dig +short " . escapeshellarg($name) . ' ' . escapeshellarg(strtoupper($type)) . " 2>/dev/null"; return (string) @shell_exec($cmd) ?: ''; } protected function hasTxt(string $name): bool { return trim($this->digShort('TXT', $name)) !== ''; } protected function hasTlsa(string $name): bool { return trim($this->digShort('TLSA', $name)) !== ''; } protected function hasSpf(string $domain): bool { $out = $this->digShort('TXT', $domain); foreach (preg_split('/\R+/', trim($out)) as $line) { if (stripos($line, 'v=spf1') !== false) return true; } return false; } // ▼ DKIM: bevorzugt konkreten Selector prüfen; wenn leer, versuche Policy (_domainkey) protected function hasDkim(string $domain, string $selector = ''): bool { if ($selector !== '' && $this->hasTxt("{$selector}._domainkey.$domain")) { return true; } // Fallback: irgendein _domainkey-TXT vorhanden return $this->hasTxt("_domainkey.$domain"); } // protected function hasDkim(string $domain, string $selector = ''): bool // { // if ($selector !== '') { // return $this->hasTxt("{$selector}._domainkey.$domain"); // } // // Manche Betreiber veröffentlichen eine Policy auf _domainkey. // return $this->hasTxt("_domainkey.$domain"); // } protected function mxPointsTo(string $domain, array $allowedHosts): bool { $out = $this->digShort('MX', $domain); if ($out === '') return false; $targets = []; foreach (preg_split('/\R+/', trim($out)) as $line) { if (preg_match('~\s+([A-Za-z0-9\.\-]+)\.?$~', trim($line), $m)) { $targets[] = strtolower($m[1]); } } if (!$targets) return false; $allowed = array_map(fn ($h) => strtolower(rtrim($h, '.')), $allowedHosts); foreach ($targets as $t) { $t = rtrim($t, '.'); if (in_array($t, $allowed, true)) return true; } return false; } } //namespace App\Livewire\Ui\Mail; // //use Livewire\Attributes\On; //use Livewire\Component; //use App\Models\Domain; //use Illuminate\Support\Facades\Cache; // //class DnsHealthCard extends Component //{ // public array $rows = []; // [{id,name,ok,missing:[...]}] // public string $mtaHost = ''; // z.B. mx.nexlab.at // public bool $tlsa = false; // hostweit // // public function mount(): void // { // $this->load(); // } // // public function render() // { // return view('livewire.ui.mail.dns-health-card'); // } // // public function refresh(): void // { // $this->load(true); // } // // public function openDnsModal(int $domainId): void // { // $this->dispatch('openModal', component: 'ui.domain.modal.domain-dns-modal', arguments: ['domainId' => $domainId]); // } // // protected function load(bool $force = false): void // { // [$this->mtaHost, $this->tlsa, $this->rows] = Cache::remember('dash.dnshealth.v1', $force ? 1 : 600, function () { // // $base = trim((string)env('BASE_DOMAIN', '')); // $mtaSub = trim((string)env('MTA_SUB', 'mx')); // $mtaHost = $base !== '' ? "{$mtaSub}.{$base}" : $mtaSub; // // $rows = []; // $domains = Domain::query() // ->where('is_system', false) // ->where('is_active', true) // ->orderBy('domain') // ->get(['id', 'domain']); // // foreach ($domains as $d) { // $dom = $d->domain; // // $missing = []; // // // Pflicht-Checks // if (!$this->mxPointsTo($dom, [$mtaHost])) $missing[] = 'MX'; // if (!$this->hasSpf($dom)) $missing[] = 'SPF'; // if (!$this->hasDkim($dom)) $missing[] = 'DKIM'; // if (!$this->hasTxt("_dmarc.$dom")) $missing[] = 'DMARC'; // // $rows[] = [ // 'id' => (int)$d->id, // 'name' => $dom, // 'ok' => empty($missing), // 'missing' => $missing, // ]; // } // // // TLSA (hostweit, nur Info) // $tlsa = $this->hasTlsa("_25._tcp.$mtaHost") || $this->hasTlsa("_465._tcp.$mtaHost") || $this->hasTlsa("_587._tcp.$mtaHost"); // // return [$mtaHost, $tlsa, $rows]; // }); // } // // /* ── DNS Helpers (mit Timeout, damit UI nicht hängt) ───────────────── */ // // protected function digShort(string $type, string $name): string // { // $cmd = "timeout 2 dig +short " . escapeshellarg($name) . " " . escapeshellarg(strtoupper($type)) . " 2>/dev/null"; // return (string)@shell_exec($cmd) ?: ''; // } // // protected function hasTxt(string $name): bool // { // return trim($this->digShort('TXT', $name)) !== ''; // } // // protected function hasTlsa(string $name): bool // { // return trim($this->digShort('TLSA', $name)) !== ''; // } // // protected function hasSpf(string $domain): bool // { // $out = $this->digShort('TXT', $domain); // foreach (preg_split('/\R+/', trim($out)) as $line) { // if (stripos($line, 'v=spf1') !== false) return true; // } // return false; // } // // // DKIM: wenn spezifischer Selector vorhanden → prüfe den, sonst akzeptiere _domainkey-Policy als “vorhanden” // protected function hasDkim(string $domain): bool // { // $sel = trim((string)env('DKIM_SELECTOR', '')); // if ($sel !== '' && $this->hasTxt("{$sel}._domainkey.$domain")) return true; // return $this->hasTxt("_domainkey.$domain"); // } // // protected function mxPointsTo(string $domain, array $allowedHosts): bool // { // $out = $this->digShort('MX', $domain); // if ($out === '') return false; // // $targets = []; // foreach (preg_split('/\R+/', trim($out)) as $line) { // // Format: "10 mx.example.com." // if (preg_match('~\s+([A-Za-z0-9\.\-]+)\.?$~', trim($line), $m)) { // $targets[] = strtolower($m[1]); // } // } // if (!$targets) return false; // // $allowed = array_map('strtolower', $allowedHosts); // foreach ($targets as $t) { // if (in_array($t, $allowed, true)) return true; // } // return false; // } //} //namespace App\Livewire\Ui\Mail; // //use Livewire\Component; //use App\Models\Domain; //use Illuminate\Support\Facades\Cache; // //class DnsHealthCard extends Component //{ // public array $domains = []; // [['name'=>..., 'dkim'=>bool, 'dmarc'=>bool], ...] // public string $host = ''; // z.B. mx.nexlab.at // public bool $tlsa = false; // hostbasiert (einmalig) // public ?string $ipv4 = null; // public ?string $ipv6 = null; // // public function mount(): void // { // $this->load(); // } // // public function render() // { // return view('livewire.ui.mail.dns-health-card'); // } // // public function refresh(): void // { // $this->load(true); // } // // protected function load(bool $force = false): void // { // [$this->host, $this->tlsa, $this->domains, $this->ipv4, $this->ipv6] = // Cache::remember('dash.dnshealth', $force ? 1 : 900, function () { // // // ── ENV lesen ──────────────────────────────────────────────── // $base = trim((string)env('BASE_DOMAIN', '')); // $mtaSub = trim((string)env('MTA_SUB', 'mx')); // $host = $base !== '' ? "{$mtaSub}.{$base}" : $mtaSub; // // $ipv4 = trim((string)env('SERVER_PUBLIC_IPV4', '')) ?: null; // $ipv6 = trim((string)env('SERVER_PUBLIC_IPV6', '')) ?: null; // // // ── Domains laden (nur aktive, nicht-system) ──────────────── // $rows = []; // $domains = Domain::query() // ->where('is_system', false) // ->where('is_active', true) // ->orderBy('domain') // ->get(['domain']); // // foreach ($domains as $d) { // $dom = $d->domain; // $rows[] = [ // 'name' => $dom, // 'dkim' => $this->hasTxt("_domainkey.$dom"), // 'dmarc' => $this->hasTxt("_dmarc.$dom"), // ]; // } // // // ── TLSA nur hostbasiert prüfen (25/465/587) ──────────────── // $tlsa = $this->hasTlsa("_25._tcp.$host") // || $this->hasTlsa("_465._tcp.$host") // || $this->hasTlsa("_587._tcp.$host"); // // return [$host, $tlsa, $rows, $ipv4, $ipv6]; // }); // } // // /* ───────────────────────── DNS Helpers ───────────────────────── */ // // protected function hasTxt(string $name): bool // { // $out = @shell_exec("timeout 2 dig +short TXT " . escapeshellarg($name) . " 2>/dev/null"); // return is_string($out) && trim($out) !== ''; // } // // protected function hasTlsa(string $name): bool // { // $out = @shell_exec("timeout 2 dig +short TLSA " . escapeshellarg($name) . " 2>/dev/null"); // return is_string($out) && trim($out) !== ''; // } //} //namespace App\Livewire\Ui\Mail; // //use Livewire\Component; //use App\Models\Domain; //use Illuminate\Support\Facades\Cache; // //class DnsHealthCard extends Component //{ // public array $rows = []; // pro Domain: ['dom','dkim','dmarc'] // public string $host = ''; // z.B. mx.nexlab.at // public bool $tlsa = false; // TLSA-Status für den Host // public ?string $ipv4 = null; // public ?string $ipv6 = null; // // public function mount(): void { $this->load(); } // public function render() { return view('livewire.ui.mail.dns-health-card'); } // public function refresh(): void { $this->load(true); } // // protected function load(bool $force = false): void // { // // Werte aus .env // $this->ipv4 = trim((string) env('SERVER_PUBLIC_IPV4', '')) ?: null; // $this->ipv6 = trim((string) env('SERVER_PUBLIC_IPV6', '')) ?: null; // // $base = trim((string) env('BASE_DOMAIN', '')); // $mta = trim((string) env('MTA_SUB', 'mx')); // $host = $base ? "{$mta}.{$base}" : $mta; // z.B. mx.nexlab.at // $this->host = $host; // // // Neuer Cache-Key, damit altes Format keinen Crash verursacht // [$calcHost, $calcTlsa, $calcRows] = Cache::remember( // 'dash.dnshealth.v2', // $force ? 1 : 900, // function () use ($host) { // // TLSA: nur 1× pro Host prüfen // $tlsa = $this->hasTlsa("_25._tcp.{$host}") // || $this->hasTlsa("_465._tcp.{$host}") // || $this->hasTlsa("_587._tcp.{$host}"); // // // Domains: nur DKIM/DMARC // $rows = []; // $domains = Domain::query() // ->where('is_system', false) // ->where('is_active', true) // ->get(['domain']); // // foreach ($domains as $d) { // $dom = $d->domain; // $dkim = $this->hasTxt("_domainkey.{$dom}"); // $dmarc = $this->hasTxt("_dmarc.{$dom}"); // $rows[] = compact('dom','dkim','dmarc'); // } // // return [$host, $tlsa, $rows]; // } // ); // // // Defensive: falls mal falsches Datenformat im Cache war // if (!is_string($calcHost) || !is_bool($calcTlsa) || !is_array($calcRows)) { // Cache::forget('dash.dnshealth.v2'); // $this->load(true); // return; // } // // $this->host = $calcHost; // $this->tlsa = $calcTlsa; // $this->rows = $calcRows; // } // // protected function hasTxt(string $name): bool // { // $out = @shell_exec("dig +short TXT " . escapeshellarg($name) . " 2>/dev/null"); // return is_string($out) && trim($out) !== ''; // } // // protected function hasTlsa(string $name): bool // { // $out = @shell_exec("dig +short TLSA " . escapeshellarg($name) . " 2>/dev/null"); // return is_string($out) && trim($out) !== ''; // } //} // //namespace App\Livewire\Ui\Mail; // //use Livewire\Component; //use App\Models\Domain; //use Illuminate\Support\Facades\Cache; // //class DnsHealthCard extends Component //{ // public array $rows = []; // [ ['domain'=>..., 'dkim'=>bool, 'dmarc'=>bool, 'tlsa'=>bool], ... ] // // public function mount(): void { $this->load(); } // public function render() { return view('livewire.ui.mail.dns-health-card'); } // public function refresh(): void { $this->load(true); } // // protected function load(bool $force=false): void // { // $this->rows = Cache::remember('dash.dnshealth', $force ? 1 : 600, function () { // $rows = []; // $domains = Domain::query()->where('is_system', false)->where('is_active', true)->get(['domain']); // foreach ($domains as $d) { // $dom = $d->domain; // $dkim = $this->hasTxt("_domainkey.$dom"); // rough: just any dkim TXT exists // $dmarc = $this->hasTxt("_dmarc.$dom"); // $tlsa = $this->hasTlsa("_25._tcp.$dom") || $this->hasTlsa("_465._tcp.$dom") || $this->hasTlsa("_587._tcp.$dom"); // $rows[] = compact('dom','dkim','dmarc','tlsa'); // } // return $rows; // }); // } // // protected function hasTxt(string $name): bool // { // $out = @shell_exec("dig +short TXT ".escapeshellarg($name)." 2>/dev/null"); // return is_string($out) && trim($out) !== ''; // } // protected function hasTlsa(string $name): bool // { // $out = @shell_exec("dig +short TLSA ".escapeshellarg($name)." 2>/dev/null"); // return is_string($out) && trim($out) !== ''; // } //}