Revision 148 | Blame | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed
<?phpnamespace Egulias\EmailValidator\Validation;use Egulias\EmailValidator\EmailLexer;use Egulias\EmailValidator\Result\InvalidEmail;use Egulias\EmailValidator\Result\Reason\DomainAcceptsNoMail;use Egulias\EmailValidator\Result\Reason\LocalOrReservedDomain;use Egulias\EmailValidator\Result\Reason\NoDNSRecord as ReasonNoDNSRecord;use Egulias\EmailValidator\Result\Reason\UnableToGetDNSRecord;use Egulias\EmailValidator\Warning\NoDNSMXRecord;use Egulias\EmailValidator\Warning\Warning;class DNSCheckValidation implements EmailValidation{/*** @var int*/protected const DNS_RECORD_TYPES_TO_CHECK = DNS_MX + DNS_A + DNS_AAAA;/*** Reserved Top Level DNS Names (https://tools.ietf.org/html/rfc2606#section-2),* mDNS and private DNS Namespaces (https://tools.ietf.org/html/rfc6762#appendix-G)** @var string[]*/public const RESERVED_DNS_TOP_LEVEL_NAMES = [// Reserved Top Level DNS Names'test','example','invalid','localhost',// mDNS'local',// Private DNS Namespaces'intranet','internal','private','corp','home','lan',];/*** @var Warning[]*/private $warnings = [];/*** @var InvalidEmail|null*/private $error;/*** @var array*/private $mxRecords = [];/*** @var DNSGetRecordWrapper*/private $dnsGetRecord;public function __construct(?DNSGetRecordWrapper $dnsGetRecord = null){if (!function_exists('idn_to_ascii')) {throw new \LogicException(sprintf('The %s class requires the Intl extension.', __CLASS__));}if ($dnsGetRecord == null) {$dnsGetRecord = new DNSGetRecordWrapper();}$this->dnsGetRecord = $dnsGetRecord;}public function isValid(string $email, EmailLexer $emailLexer): bool{// use the input to check DNS if we cannot extract something similar to a domain$host = $email;// Arguable pattern to extract the domain. Not aiming to validate the domain nor the emailif (false !== $lastAtPos = strrpos($email, '@')) {$host = substr($email, $lastAtPos + 1);}// Get the domain parts$hostParts = explode('.', $host);$isLocalDomain = count($hostParts) <= 1;$isReservedTopLevel = in_array($hostParts[(count($hostParts) - 1)], self::RESERVED_DNS_TOP_LEVEL_NAMES, true);// Exclude reserved top level DNS namesif ($isLocalDomain || $isReservedTopLevel) {$this->error = new InvalidEmail(new LocalOrReservedDomain(), $host);return false;}return $this->checkDns($host);}public function getError(): ?InvalidEmail{return $this->error;}/*** @return Warning[]*/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), '.');$hostParts = explode('.', $host);$host = array_pop($hostParts);while (count($hostParts) > 0) {$host = array_pop($hostParts) . '.' . $host;if ($this->validateDnsRecords($host)) {return true;}}return false;}/*** 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{$dnsRecordsResult = $this->dnsGetRecord->getRecords($host, static::DNS_RECORD_TYPES_TO_CHECK);if ($dnsRecordsResult->withError()) {$this->error = new InvalidEmail(new UnableToGetDNSRecord(), '');return false;}$dnsRecords = $dnsRecordsResult->getRecords();// No MX, A or AAAA DNS recordsif ($dnsRecords === []) {$this->error = new InvalidEmail(new ReasonNoDNSRecord(), '');return false;}// For each DNS recordforeach ($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 (!isset($dnsRecord['type'])) {$this->error = new InvalidEmail(new ReasonNoDNSRecord(), '');return false;}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;}}