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) !== ''; // } //}