| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166 |
- <?php
- namespace Egulias\EmailValidator\Validation;
- use Egulias\EmailValidator\EmailLexer;
- use Egulias\EmailValidator\Exception\InvalidEmail;
- use Egulias\EmailValidator\Exception\LocalOrReservedDomain;
- use Egulias\EmailValidator\Exception\DomainAcceptsNoMail;
- use Egulias\EmailValidator\Warning\NoDNSMXRecord;
- use Egulias\EmailValidator\Exception\NoDNSRecord;
- class DNSCheckValidation implements EmailValidation
- {
- /**
- * @var array
- */
- private $warnings = [];
- /**
- * @var InvalidEmail|null
- */
- private $error;
- /**
- * @var array
- */
- private $mxRecords = [];
- public function __construct()
- {
- if (!function_exists('idn_to_ascii')) {
- throw new \LogicException(sprintf('The %s class requires the Intl extension.', __CLASS__));
- }
- }
- public function isValid($email, EmailLexer $emailLexer)
- {
- // 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 email
- if (false !== $lastAtPos = strrpos($email, '@')) {
- $host = substr($email, $lastAtPos + 1);
- }
- // Get the domain parts
- $hostParts = explode('.', $host);
- // 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)
- $reservedTopLevelDnsNames = [
- // Reserved Top Level DNS Names
- 'test',
- 'example',
- 'invalid',
- 'localhost',
- // mDNS
- 'local',
- // Private DNS Namespaces
- 'intranet',
- 'internal',
- 'private',
- 'corp',
- 'home',
- 'lan',
- ];
- $isLocalDomain = count($hostParts) <= 1;
- $isReservedTopLevel = in_array($hostParts[(count($hostParts) - 1)], $reservedTopLevelDnsNames, true);
- // Exclude reserved top level DNS names
- if ($isLocalDomain || $isReservedTopLevel) {
- $this->error = new LocalOrReservedDomain();
- return false;
- }
- return $this->checkDns($host);
- }
- public function getError()
- {
- return $this->error;
- }
- public function getWarnings()
- {
- 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)
- {
- // Get all MX, A and AAAA DNS records for host
- // Using @ as workaround to fix https://bugs.php.net/bug.php?id=73149
- $dnsRecords = @dns_get_record($host, DNS_MX + DNS_A + DNS_AAAA);
- // No MX, A or AAAA DNS records
- if (empty($dnsRecords)) {
- $this->error = new NoDNSRecord();
- return false;
- }
- // For each DNS record
- foreach ($dnsRecords as $dnsRecord) {
- if (!$this->validateMXRecord($dnsRecord)) {
- return false;
- }
- }
- // No MX records (fallback to A or AAAA records)
- if (empty($this->mxRecords)) {
- $this->warnings[NoDNSMXRecord::CODE] = new NoDNSMXRecord();
- }
- return true;
- }
- /**
- * Validate an MX record
- *
- * @param array $dnsRecord Given DNS record.
- *
- * @return bool True if valid.
- */
- private function validateMxRecord($dnsRecord)
- {
- 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 DomainAcceptsNoMail();
- return false;
- }
- $this->mxRecords[] = $dnsRecord;
- return true;
- }
- }
|