error = new InvalidEmail(new LocalOrReservedDomain(), $host); return false; } return $this->checkDns($host); } public function getError() : ?InvalidEmail { return $this->error; } public function getWarnings() : array { return $this->warnings; } /** * @param string $host * * @return bool */ protected function checkDns($host) { $variant = INTL_IDNA_VARIANT_UTS46; $host = rtrim(idn_to_ascii($host, IDNA_DEFAULT, $variant), '.') . '.'; return $this->validateDnsRecords($host); } /** * Validate the DNS records for given host. * * @param string $host A set of DNS records in the format returned by dns_get_record. * * @return bool True on success. */ private function validateDnsRecords($host) : bool { // A workaround to fix https://bugs.php.net/bug.php?id=73149 /** @psalm-suppress InvalidArgument */ set_error_handler( static function (int $errorLevel, string $errorMessage): ?bool { throw new \RuntimeException("Unable to get DNS record for the host: $errorMessage"); } ); try { // Get all MX, A and AAAA DNS records for host $dnsRecords = dns_get_record($host, static::DNS_RECORD_TYPES_TO_CHECK); } catch (\RuntimeException $exception) { $this->error = new InvalidEmail(new UnableToGetDNSRecord(), ''); return false; } finally { restore_error_handler(); } // No MX, A or AAAA DNS records if ($dnsRecords === [] || $dnsRecords === false) { $this->error = new InvalidEmail(new ReasonNoDNSRecord(), ''); return false; } // For each DNS record foreach ($dnsRecords as $dnsRecord) { if (!$this->validateMXRecord($dnsRecord)) { // No MX records (fallback to A or AAAA records) if (empty($this->mxRecords)) { $this->warnings[NoDNSMXRecord::CODE] = new NoDNSMXRecord(); } return false; } } return true; } /** * Validate an MX record * * @param array $dnsRecord Given DNS record. * * @return bool True if valid. */ private function validateMxRecord($dnsRecord) : bool { if ($dnsRecord['type'] !== 'MX') { return true; } // "Null MX" record indicates the domain accepts no mail (https://tools.ietf.org/html/rfc7505) if (empty($dnsRecord['target']) || $dnsRecord['target'] === '.') { $this->error = new InvalidEmail(new DomainAcceptsNoMail(), ""); return false; } $this->mxRecords[] = $dnsRecord; return true; } }