Subversion-Projekte lars-tiefland.laravel_shop

Revision

Zur aktuellen Revision | Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Egulias\EmailValidator\Validation;
4
 
5
use Egulias\EmailValidator\EmailLexer;
6
use Egulias\EmailValidator\Result\InvalidEmail;
7
use Egulias\EmailValidator\Result\Reason\DomainAcceptsNoMail;
8
use Egulias\EmailValidator\Result\Reason\LocalOrReservedDomain;
9
use Egulias\EmailValidator\Result\Reason\NoDNSRecord as ReasonNoDNSRecord;
10
use Egulias\EmailValidator\Result\Reason\UnableToGetDNSRecord;
11
use Egulias\EmailValidator\Warning\NoDNSMXRecord;
12
 
13
class DNSCheckValidation implements EmailValidation
14
{
15
    /**
16
     * @var int
17
     */
18
    protected const DNS_RECORD_TYPES_TO_CHECK = DNS_MX + DNS_A + DNS_AAAA;
19
 
20
    /**
21
     * Reserved Top Level DNS Names (https://tools.ietf.org/html/rfc2606#section-2),
22
     * mDNS and private DNS Namespaces (https://tools.ietf.org/html/rfc6762#appendix-G)
23
     */
24
    public const RESERVED_DNS_TOP_LEVEL_NAMES = [
25
        // Reserved Top Level DNS Names
26
        'test',
27
        'example',
28
        'invalid',
29
        'localhost',
30
 
31
        // mDNS
32
        'local',
33
 
34
        // Private DNS Namespaces
35
        'intranet',
36
        'internal',
37
        'private',
38
        'corp',
39
        'home',
40
        'lan',
41
    ];
42
 
43
    /**
44
     * @var array
45
     */
46
    private $warnings = [];
47
 
48
    /**
49
     * @var InvalidEmail|null
50
     */
51
    private $error;
52
 
53
    /**
54
     * @var array
55
     */
56
    private $mxRecords = [];
57
 
58
    /**
59
     * @var DNSGetRecordWrapper
60
     */
61
    private $dnsGetRecord;
62
 
63
    public function __construct(?DNSGetRecordWrapper $dnsGetRecord = null)
64
    {
65
        if (!function_exists('idn_to_ascii')) {
66
            throw new \LogicException(sprintf('The %s class requires the Intl extension.', __CLASS__));
67
        }
68
 
69
        if ($dnsGetRecord == null) {
70
            $dnsGetRecord = new DNSGetRecordWrapper();
71
        }
72
 
73
        $this->dnsGetRecord = $dnsGetRecord;
74
    }
75
 
76
    public function isValid(string $email, EmailLexer $emailLexer) : bool
77
    {
78
        // use the input to check DNS if we cannot extract something similar to a domain
79
        $host = $email;
80
 
81
        // Arguable pattern to extract the domain. Not aiming to validate the domain nor the email
82
        if (false !== $lastAtPos = strrpos($email, '@')) {
83
            $host = substr($email, $lastAtPos + 1);
84
        }
85
 
86
        // Get the domain parts
87
        $hostParts = explode('.', $host);
88
 
89
        $isLocalDomain = count($hostParts) <= 1;
90
        $isReservedTopLevel = in_array($hostParts[(count($hostParts) - 1)], self::RESERVED_DNS_TOP_LEVEL_NAMES, true);
91
 
92
        // Exclude reserved top level DNS names
93
        if ($isLocalDomain || $isReservedTopLevel) {
94
            $this->error = new InvalidEmail(new LocalOrReservedDomain(), $host);
95
            return false;
96
        }
97
 
98
        return $this->checkDns($host);
99
    }
100
 
101
    public function getError() : ?InvalidEmail
102
    {
103
        return $this->error;
104
    }
105
 
106
    public function getWarnings() : array
107
    {
108
        return $this->warnings;
109
    }
110
 
111
    /**
112
     * @param string $host
113
     *
114
     * @return bool
115
     */
116
    protected function checkDns($host)
117
    {
118
        $variant = INTL_IDNA_VARIANT_UTS46;
119
 
120
        $host = rtrim(idn_to_ascii($host, IDNA_DEFAULT, $variant), '.') . '.';
121
 
122
        return $this->validateDnsRecords($host);
123
    }
124
 
125
 
126
    /**
127
     * Validate the DNS records for given host.
128
     *
129
     * @param string $host A set of DNS records in the format returned by dns_get_record.
130
     *
131
     * @return bool True on success.
132
     */
133
    private function validateDnsRecords($host) : bool
134
    {
135
        $dnsRecordsResult = $this->dnsGetRecord->getRecords($host, static::DNS_RECORD_TYPES_TO_CHECK);
136
 
137
        if ($dnsRecordsResult->withError()) {
138
            $this->error = new InvalidEmail(new UnableToGetDNSRecord(), '');
139
            return false;
140
        }
141
 
142
        $dnsRecords = $dnsRecordsResult->getRecords();
143
 
144
        // No MX, A or AAAA DNS records
145
        if ($dnsRecords === []) {
146
            $this->error = new InvalidEmail(new ReasonNoDNSRecord(), '');
147
            return false;
148
        }
149
 
150
        // For each DNS record
151
        foreach ($dnsRecords as $dnsRecord) {
152
            if (!$this->validateMXRecord($dnsRecord)) {
153
                // No MX records (fallback to A or AAAA records)
154
                if (empty($this->mxRecords)) {
155
                    $this->warnings[NoDNSMXRecord::CODE] = new NoDNSMXRecord();
156
                }
157
                return false;
158
            }
159
        }
160
        return true;
161
    }
162
 
163
    /**
164
     * Validate an MX record
165
     *
166
     * @param array $dnsRecord Given DNS record.
167
     *
168
     * @return bool True if valid.
169
     */
170
    private function validateMxRecord($dnsRecord) : bool
171
    {
172
        if (!isset($dnsRecord['type'])) {
173
            $this->error = new InvalidEmail(new ReasonNoDNSRecord(), '');
174
            return false;
175
        }
176
 
177
        if ($dnsRecord['type'] !== 'MX') {
178
            return true;
179
        }
180
 
181
        // "Null MX" record indicates the domain accepts no mail (https://tools.ietf.org/html/rfc7505)
182
        if (empty($dnsRecord['target']) || $dnsRecord['target'] === '.') {
183
            $this->error = new InvalidEmail(new DomainAcceptsNoMail(), "");
184
            return false;
185
        }
186
 
187
        $this->mxRecords[] = $dnsRecord;
188
 
189
        return true;
190
    }
191
}