$this->dnsRecord->id, 'domain' => $this->dnsRecord->domain, 'record_type' => $this->dnsRecord->record_type, ]); // Perform DNS lookup $result = $this->performDnsLookup(); $wasValid = $this->dnsRecord->is_valid; $isValid = $result['is_valid']; $actualValue = $result['actual_value']; $errorMessage = $result['error_message']; // Update DNS record $this->dnsRecord->update([ 'last_checked_at' => now(), 'is_valid' => $isValid, 'actual_value' => $actualValue, 'error_message' => $errorMessage, ]); Log::info('CheckDnsRecord: Check completed', [ 'dns_record_id' => $this->dnsRecord->id, 'is_valid' => $isValid, 'actual_value' => $actualValue, ]); // Send notification if status changed from valid to invalid if ($wasValid === true && $isValid === false) { $this->dnsRecord->site->user->notify(new DnsRecordAlert( $this->dnsRecord, 'invalid', $actualValue, $errorMessage )); Log::warning('CheckDnsRecord: Invalid alert sent', [ 'dns_record_id' => $this->dnsRecord->id, 'error_message' => $errorMessage, ]); } // Send notification when DNS is restored if ($wasValid === false && $isValid === true) { $this->dnsRecord->site->user->notify(new DnsRecordAlert( $this->dnsRecord, 'valid', $actualValue )); Log::info('CheckDnsRecord: Valid alert sent', [ 'dns_record_id' => $this->dnsRecord->id, ]); } } catch (\Exception $e) { Log::error('CheckDnsRecord: Unexpected error', [ 'dns_record_id' => $this->dnsRecord->id, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), ]); $this->dnsRecord->update([ 'last_checked_at' => now(), 'is_valid' => false, 'error_message' => 'Unexpected error: ' . $e->getMessage(), ]); throw $e; } } /** * Perform DNS lookup and validation. */ protected function performDnsLookup(): array { try { // Map record type to DNS_* constant $dnsType = $this->getDnsTypeConstant($this->dnsRecord->record_type); if ($dnsType === null) { return [ 'is_valid' => false, 'actual_value' => null, 'error_message' => "Unsupported DNS record type: {$this->dnsRecord->record_type}", ]; } // Perform DNS lookup $records = @dns_get_record($this->dnsRecord->domain, $dnsType); if ($records === false) { $error = error_get_last(); return [ 'is_valid' => false, 'actual_value' => null, 'error_message' => 'DNS lookup failed: ' . ($error['message'] ?? 'Unknown error'), ]; } if (empty($records)) { return [ 'is_valid' => false, 'actual_value' => null, 'error_message' => "No {$this->dnsRecord->record_type} records found for {$this->dnsRecord->domain}", ]; } // Extract values based on record type $actualValues = $this->extractDnsValues($records, $this->dnsRecord->record_type); // If no expected value, just return that records exist if (empty($this->dnsRecord->expected_value)) { return [ 'is_valid' => true, 'actual_value' => $actualValues, 'error_message' => null, ]; } // Validate against expected value $isValid = $this->validateDnsValue($actualValues, $this->dnsRecord->expected_value); return [ 'is_valid' => $isValid, 'actual_value' => $actualValues, 'error_message' => $isValid ? null : 'DNS record does not match expected value', ]; } catch (\Exception $e) { return [ 'is_valid' => false, 'actual_value' => null, 'error_message' => 'DNS lookup error: ' . $e->getMessage(), ]; } } /** * Get DNS type constant for dns_get_record(). */ protected function getDnsTypeConstant(string $recordType): ?int { return match (strtoupper($recordType)) { 'A' => DNS_A, 'AAAA' => DNS_AAAA, 'CNAME' => DNS_CNAME, 'MX' => DNS_MX, 'TXT' => DNS_TXT, 'NS' => DNS_NS, 'SOA' => DNS_SOA, 'PTR' => DNS_PTR, 'SRV' => DNS_SRV, default => null, }; } /** * Extract DNS values from records. */ protected function extractDnsValues(array $records, string $recordType): array { $values = []; foreach ($records as $record) { $value = match (strtoupper($recordType)) { 'A' => $record['ip'] ?? null, 'AAAA' => $record['ipv6'] ?? null, 'CNAME' => $record['target'] ?? null, 'MX' => isset($record['target']) ? "{$record['pri']} {$record['target']}" : null, 'TXT' => $record['txt'] ?? null, 'NS' => $record['target'] ?? null, 'SOA' => isset($record['mname']) ? $record['mname'] : null, 'PTR' => $record['target'] ?? null, 'SRV' => isset($record['target']) ? "{$record['pri']} {$record['weight']} {$record['port']} {$record['target']}" : null, default => null, }; if ($value !== null) { $values[] = $value; } } return $values; } /** * Validate DNS value against expected value. */ protected function validateDnsValue(array $actualValues, string $expectedValue): bool { // Normalize expected value $expectedValue = trim($expectedValue); // Check if any actual value matches expected foreach ($actualValues as $actualValue) { // Exact match if (strcasecmp($actualValue, $expectedValue) === 0) { return true; } // Partial match for MX and SRV records (check target only) if (stripos($actualValue, $expectedValue) !== false) { return true; } } return false; } /** * Handle a job failure. */ public function failed(\Throwable $exception): void { Log::error('CheckDnsRecord: Job failed after all retries', [ 'dns_record_id' => $this->dnsRecord->id, 'exception' => $exception->getMessage(), ]); } }