initial
This commit is contained in:
commit
e8e4a5706b
21
LICENSE.md
Normal file
21
LICENSE.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2024 D3 Data Development (Inh. Thomas Dartsch)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
38
composer.json
Normal file
38
composer.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "d3/mailauthenticationcheck",
|
||||||
|
"description": "Checks for configured mail athentication methods like SPF, DKIM and DMARC",
|
||||||
|
"type": "library",
|
||||||
|
"keywords": [
|
||||||
|
"SPF",
|
||||||
|
"DKIM",
|
||||||
|
"DMARC",
|
||||||
|
"e-mail",
|
||||||
|
"dns",
|
||||||
|
"record",
|
||||||
|
"authentication"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "D3 Data Development (Inh. Thomas Dartsch)",
|
||||||
|
"email": "info@shopmodule.com",
|
||||||
|
"homepage": "https://www.d3data.de",
|
||||||
|
"role": "Owner"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"email": "support@shopmodule.com"
|
||||||
|
},
|
||||||
|
"homepage": "https://www.oxidmodule.com/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.4",
|
||||||
|
"mika56/spfcheck": "^2.1.1"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"D3\\MailAuthenticationCheck\\": "src"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
111
src/DMARCCheck.php
Normal file
111
src/DMARCCheck.php
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* https://www.d3data.de
|
||||||
|
*
|
||||||
|
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
||||||
|
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
|
||||||
|
* @link https://www.oxidmodule.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace D3\MailAuthenticationCheck;
|
||||||
|
|
||||||
|
use D3\MailAuthenticationCheck\Mechanism\DMARC\RejectPolicy;
|
||||||
|
use D3\MailAuthenticationCheck\Model\DMARCRecord as Record;
|
||||||
|
use D3\MailAuthenticationCheck\Model\DMARCResult as Result;
|
||||||
|
use Mika56\SPFCheck\DNS\DNSRecordGetterInterface;
|
||||||
|
use Mika56\SPFCheck\Exception\DNSLookupException;
|
||||||
|
use Mika56\SPFCheck\Model\Query;
|
||||||
|
|
||||||
|
class DMARCCheck
|
||||||
|
{
|
||||||
|
protected DNSRecordGetterInterface $DNSRecordGetter;
|
||||||
|
|
||||||
|
public function __construct(DNSRecordGetterInterface $DNSRecordGetter)
|
||||||
|
{
|
||||||
|
$this->DNSRecordGetter = $DNSRecordGetter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $domainName
|
||||||
|
* @return Record[]
|
||||||
|
* @throws DNSLookupException
|
||||||
|
*/
|
||||||
|
public function getDomainDMARCRecords(string $domainName): array
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
if (!str_starts_with($domainName, '_dmarc.')) {
|
||||||
|
$domainName = '_dmarc.'.$domainName;
|
||||||
|
}
|
||||||
|
|
||||||
|
$records = $this->DNSRecordGetter->resolveTXT($domainName);
|
||||||
|
|
||||||
|
foreach ($records as $record) {
|
||||||
|
$txt = strtolower($record);
|
||||||
|
// An DMARC record can be empty (default policy)
|
||||||
|
if ($txt == 'v=dmarc1;' || str_starts_with($txt, 'v=dmarc1; ')) {
|
||||||
|
$result[] = new Record($record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResult(Query $query): Result
|
||||||
|
{
|
||||||
|
return $this->doGetResult($query);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function doGetResult(Query $query): Result
|
||||||
|
{
|
||||||
|
$domainName = $query->getDomainName();
|
||||||
|
|
||||||
|
$result??= new Result();
|
||||||
|
|
||||||
|
if(empty($domainName)) {
|
||||||
|
$result->setResult(Result::NONE);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$records = $this->getDomainDMARCRecords( $domainName);
|
||||||
|
} catch (DNSLookupException $e) {
|
||||||
|
$result->setResult(Result::TEMPERROR);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($records) == 0) {
|
||||||
|
$result->setResult(Result::NONE);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
if (count($records) > 1) {
|
||||||
|
$result->setResult(Result::PERMERROR);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$record = $records[0];
|
||||||
|
$result->setRecord($record);
|
||||||
|
if (!$record->isValid()) {
|
||||||
|
$result->setResult(Result::PERMERROR);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($record->getTerms() as $term) {
|
||||||
|
if ( $term instanceof RejectPolicy) {
|
||||||
|
$result->setResult($term->getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
30
src/Enum/DMARCMechanism.php
Normal file
30
src/Enum/DMARCMechanism.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* https://www.d3data.de
|
||||||
|
*
|
||||||
|
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
||||||
|
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
|
||||||
|
* @link https://www.oxidmodule.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace D3\MailAuthenticationCheck\Enum;
|
||||||
|
|
||||||
|
abstract class DMARCMechanism
|
||||||
|
{
|
||||||
|
public const REJECT_POLICY = 'p';
|
||||||
|
public const SUB_REJECT_POLICY = 'sp';
|
||||||
|
public const REPORT_URI_AGGREGATE = 'rua';
|
||||||
|
public const REPORT_URI_FORENSIC = 'ruf';
|
||||||
|
public const SPF_ALIGNMENT = 'aspf';
|
||||||
|
public const DKIM_ALIGNMENT = 'adkim';
|
||||||
|
public const REPORTING_INTERVAL = 'ri';
|
||||||
|
public const REPORTING_FORMAT = 'rf';
|
||||||
|
public const FORENSIC_REPORT_OPTIONS = 'fo';
|
||||||
|
public const PERCENTAGE = 'pct';
|
||||||
|
}
|
49
src/Mechanism/AbstractMechanism.php
Normal file
49
src/Mechanism/AbstractMechanism.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* https://www.d3data.de
|
||||||
|
*
|
||||||
|
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
||||||
|
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
|
||||||
|
* @link https://www.oxidmodule.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace D3\MailAuthenticationCheck\Mechanism;
|
||||||
|
|
||||||
|
use Mika56\SPFCheck\Mechanism\AbstractMechanism as SpfAbstractMechanism;
|
||||||
|
|
||||||
|
abstract class AbstractMechanism extends SpfAbstractMechanism
|
||||||
|
{
|
||||||
|
private string $qualifier;
|
||||||
|
private string $content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $rawTerm
|
||||||
|
* @param string $qualifier
|
||||||
|
* @param string $termContent
|
||||||
|
*/
|
||||||
|
public function __construct(string $rawTerm, string $qualifier, string $termContent)
|
||||||
|
{
|
||||||
|
parent::__construct($rawTerm, $qualifier);
|
||||||
|
$this->qualifier = $qualifier;
|
||||||
|
$this->content = $termContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getQualifier(): string
|
||||||
|
{
|
||||||
|
return $this->qualifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return $this->content;
|
||||||
|
}
|
||||||
|
}
|
33
src/Mechanism/DMARC/DkimAlignment.php
Normal file
33
src/Mechanism/DMARC/DkimAlignment.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* https://www.d3data.de
|
||||||
|
*
|
||||||
|
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
||||||
|
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
|
||||||
|
* @link https://www.oxidmodule.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace D3\MailAuthenticationCheck\Mechanism\DMARC;
|
||||||
|
|
||||||
|
use D3\MailAuthenticationCheck\Mechanism\AbstractMechanism;
|
||||||
|
|
||||||
|
class DkimAlignment extends AbstractMechanism
|
||||||
|
{
|
||||||
|
public const DEFAULT = 'r';
|
||||||
|
|
||||||
|
public function isStrict(): bool
|
||||||
|
{
|
||||||
|
return strtolower((string) $this) === 's';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isRelaxed(): bool
|
||||||
|
{
|
||||||
|
return !$this->isStrict();
|
||||||
|
}
|
||||||
|
}
|
48
src/Mechanism/DMARC/ForensicReportOptions.php
Normal file
48
src/Mechanism/DMARC/ForensicReportOptions.php
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* https://www.d3data.de
|
||||||
|
*
|
||||||
|
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
||||||
|
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
|
||||||
|
* @link https://www.oxidmodule.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace D3\MailAuthenticationCheck\Mechanism\DMARC;
|
||||||
|
|
||||||
|
use D3\MailAuthenticationCheck\Mechanism\AbstractMechanism;
|
||||||
|
|
||||||
|
class ForensicReportOptions extends AbstractMechanism
|
||||||
|
{
|
||||||
|
public const DEFAULT = '0';
|
||||||
|
|
||||||
|
public function getList(): array
|
||||||
|
{
|
||||||
|
return explode(':', trim(strtolower((string) $this)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function SpfAndDkimAlignmentFailed(): bool
|
||||||
|
{
|
||||||
|
return in_array('0', $this->getList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function SpfOrDkimAlignmentFailed(): bool
|
||||||
|
{
|
||||||
|
return in_array('1', $this->getList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function DkimFailed(): bool
|
||||||
|
{
|
||||||
|
return in_array('d', $this->getList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function SpfFailed(): bool
|
||||||
|
{
|
||||||
|
return in_array('s', $this->getList());
|
||||||
|
}
|
||||||
|
}
|
23
src/Mechanism/DMARC/Percentage.php
Normal file
23
src/Mechanism/DMARC/Percentage.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* https://www.d3data.de
|
||||||
|
*
|
||||||
|
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
||||||
|
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
|
||||||
|
* @link https://www.oxidmodule.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace D3\MailAuthenticationCheck\Mechanism\DMARC;
|
||||||
|
|
||||||
|
use D3\MailAuthenticationCheck\Mechanism\AbstractMechanism;
|
||||||
|
|
||||||
|
class Percentage extends AbstractMechanism
|
||||||
|
{
|
||||||
|
public const DEFAULT = 100;
|
||||||
|
}
|
35
src/Mechanism/DMARC/RejectPolicy.php
Normal file
35
src/Mechanism/DMARC/RejectPolicy.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* https://www.d3data.de
|
||||||
|
*
|
||||||
|
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
||||||
|
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
|
||||||
|
* @link https://www.oxidmodule.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace D3\MailAuthenticationCheck\Mechanism\DMARC;
|
||||||
|
|
||||||
|
use D3\MailAuthenticationCheck\Mechanism\AbstractMechanism;
|
||||||
|
use D3\MailAuthenticationCheck\Model\DMARCResult;
|
||||||
|
|
||||||
|
class RejectPolicy extends AbstractMechanism
|
||||||
|
{
|
||||||
|
public function getValue(): string
|
||||||
|
{
|
||||||
|
switch (strtolower((string) $this)) {
|
||||||
|
case 'quarantine':
|
||||||
|
return DMARCResult::REJECT_QUARANTINE;
|
||||||
|
case 'reject':
|
||||||
|
return DMARCResult::REJECT_REJECT;
|
||||||
|
case 'none':
|
||||||
|
default:
|
||||||
|
return DMARCResult::REJECT_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
src/Mechanism/DMARC/ReportUriAggregateData.php
Normal file
31
src/Mechanism/DMARC/ReportUriAggregateData.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* https://www.d3data.de
|
||||||
|
*
|
||||||
|
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
||||||
|
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
|
||||||
|
* @link https://www.oxidmodule.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace D3\MailAuthenticationCheck\Mechanism\DMARC;
|
||||||
|
|
||||||
|
use D3\MailAuthenticationCheck\Mechanism\AbstractMechanism;
|
||||||
|
|
||||||
|
class ReportUriAggregateData extends AbstractMechanism
|
||||||
|
{
|
||||||
|
public function getList(): array
|
||||||
|
{
|
||||||
|
return array_map(
|
||||||
|
function($item) {
|
||||||
|
return preg_replace('@^mailto:@', '', $item);
|
||||||
|
},
|
||||||
|
explode(',', strtolower((string) $this))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
31
src/Mechanism/DMARC/ReportUriForensicData.php
Normal file
31
src/Mechanism/DMARC/ReportUriForensicData.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* https://www.d3data.de
|
||||||
|
*
|
||||||
|
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
||||||
|
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
|
||||||
|
* @link https://www.oxidmodule.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace D3\MailAuthenticationCheck\Mechanism\DMARC;
|
||||||
|
|
||||||
|
use D3\MailAuthenticationCheck\Mechanism\AbstractMechanism;
|
||||||
|
|
||||||
|
class ReportUriForensicData extends AbstractMechanism
|
||||||
|
{
|
||||||
|
public function getList(): array
|
||||||
|
{
|
||||||
|
return array_map(
|
||||||
|
function($item) {
|
||||||
|
return preg_replace('@^mailto:@', '', $item);
|
||||||
|
},
|
||||||
|
explode(',', strtolower((string) $this))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
23
src/Mechanism/DMARC/ReportingFormat.php
Normal file
23
src/Mechanism/DMARC/ReportingFormat.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* https://www.d3data.de
|
||||||
|
*
|
||||||
|
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
||||||
|
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
|
||||||
|
* @link https://www.oxidmodule.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace D3\MailAuthenticationCheck\Mechanism\DMARC;
|
||||||
|
|
||||||
|
use D3\MailAuthenticationCheck\Mechanism\AbstractMechanism;
|
||||||
|
|
||||||
|
class ReportingFormat extends AbstractMechanism
|
||||||
|
{
|
||||||
|
public const DEFAULT = 'afrf';
|
||||||
|
}
|
23
src/Mechanism/DMARC/ReportingInterval.php
Normal file
23
src/Mechanism/DMARC/ReportingInterval.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* https://www.d3data.de
|
||||||
|
*
|
||||||
|
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
||||||
|
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
|
||||||
|
* @link https://www.oxidmodule.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace D3\MailAuthenticationCheck\Mechanism\DMARC;
|
||||||
|
|
||||||
|
use D3\MailAuthenticationCheck\Mechanism\AbstractMechanism;
|
||||||
|
|
||||||
|
class ReportingInterval extends AbstractMechanism
|
||||||
|
{
|
||||||
|
public const DEFAULT = 86400;
|
||||||
|
}
|
33
src/Mechanism/DMARC/SpfAlignment.php
Normal file
33
src/Mechanism/DMARC/SpfAlignment.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* https://www.d3data.de
|
||||||
|
*
|
||||||
|
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
||||||
|
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
|
||||||
|
* @link https://www.oxidmodule.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace D3\MailAuthenticationCheck\Mechanism\DMARC;
|
||||||
|
|
||||||
|
use D3\MailAuthenticationCheck\Mechanism\AbstractMechanism;
|
||||||
|
|
||||||
|
class SpfAlignment extends AbstractMechanism
|
||||||
|
{
|
||||||
|
public const DEFAULT = 'r';
|
||||||
|
|
||||||
|
public function isStrict(): bool
|
||||||
|
{
|
||||||
|
return strtolower((string) $this) === 's';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isRelaxed(): bool
|
||||||
|
{
|
||||||
|
return !$this->isStrict();
|
||||||
|
}
|
||||||
|
}
|
35
src/Mechanism/DMARC/SubRejectPolicy.php
Normal file
35
src/Mechanism/DMARC/SubRejectPolicy.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* https://www.d3data.de
|
||||||
|
*
|
||||||
|
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
||||||
|
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
|
||||||
|
* @link https://www.oxidmodule.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace D3\MailAuthenticationCheck\Mechanism\DMARC;
|
||||||
|
|
||||||
|
use D3\MailAuthenticationCheck\Mechanism\AbstractMechanism;
|
||||||
|
use D3\MailAuthenticationCheck\Model\DMARCResult;
|
||||||
|
|
||||||
|
class SubRejectPolicy extends AbstractMechanism
|
||||||
|
{
|
||||||
|
public function getValue(): string
|
||||||
|
{
|
||||||
|
switch (strtolower((string) $this)) {
|
||||||
|
case 'quarantine':
|
||||||
|
return DMARCResult::REJECT_QUARANTINE;
|
||||||
|
case 'reject':
|
||||||
|
return DMARCResult::REJECT_REJECT;
|
||||||
|
case 'none':
|
||||||
|
default:
|
||||||
|
return DMARCResult::REJECT_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
156
src/Model/DMARCRecord.php
Normal file
156
src/Model/DMARCRecord.php
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* https://www.d3data.de
|
||||||
|
*
|
||||||
|
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
||||||
|
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
|
||||||
|
* @link https://www.oxidmodule.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace D3\MailAuthenticationCheck\Model;
|
||||||
|
|
||||||
|
use D3\MailAuthenticationCheck\Enum\DMARCMechanism as Mechanism;
|
||||||
|
use D3\MailAuthenticationCheck\Mechanism\AbstractMechanism;
|
||||||
|
use D3\MailAuthenticationCheck\Mechanism\DMARC\DkimAlignment;
|
||||||
|
use D3\MailAuthenticationCheck\Mechanism\DMARC\Percentage;
|
||||||
|
use D3\MailAuthenticationCheck\Mechanism\DMARC\ReportingFormat;
|
||||||
|
use D3\MailAuthenticationCheck\Mechanism\DMARC\ReportingInterval;
|
||||||
|
use D3\MailAuthenticationCheck\Mechanism\DMARC\SpfAlignment;
|
||||||
|
use D3\MailAuthenticationCheck\Mechanism\DMARC\ForensicReportOptions;
|
||||||
|
use D3\MailAuthenticationCheck\Mechanism\DMARC\RejectPolicy;
|
||||||
|
use D3\MailAuthenticationCheck\Mechanism\DMARC\ReportUriAggregateData;
|
||||||
|
use D3\MailAuthenticationCheck\Mechanism\DMARC\ReportUriForensicData;
|
||||||
|
use D3\MailAuthenticationCheck\Mechanism\DMARC\SubRejectPolicy;
|
||||||
|
use LogicException;
|
||||||
|
|
||||||
|
class DMARCRecord
|
||||||
|
{
|
||||||
|
private string $rawRecord;
|
||||||
|
|
||||||
|
public function __construct(string $rawRecord = '')
|
||||||
|
{
|
||||||
|
$this->rawRecord = $rawRecord;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRawRecord(): string
|
||||||
|
{
|
||||||
|
return $this->rawRecord;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return AbstractMechanism[]
|
||||||
|
*/
|
||||||
|
public function getTerms(): iterable
|
||||||
|
{
|
||||||
|
$terms = explode(' ', $this->rawRecord);
|
||||||
|
|
||||||
|
array_shift($terms); // Remove first part (v=DMARC1)
|
||||||
|
|
||||||
|
foreach ($terms as $term) {
|
||||||
|
if(empty($term)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
preg_match('`^(?<qualifier>(p|rua|ruf|sp|aspf|adkim|fo|ri|rf|pct))=(?<term>[^;]*);*$`U', $term, $matches);
|
||||||
|
|
||||||
|
switch(strtolower($matches['qualifier'])) {
|
||||||
|
case Mechanism::REJECT_POLICY:
|
||||||
|
yield new RejectPolicy($term, $matches['qualifier'], $matches['term']);
|
||||||
|
break;
|
||||||
|
case Mechanism::SUB_REJECT_POLICY:
|
||||||
|
yield new SubRejectPolicy($term, $matches['qualifier'], $matches['term']);
|
||||||
|
break;
|
||||||
|
case Mechanism::REPORT_URI_AGGREGATE:
|
||||||
|
yield new ReportUriAggregateData($term, $matches['qualifier'], $matches['term']);
|
||||||
|
break;
|
||||||
|
case Mechanism::REPORT_URI_FORENSIC:
|
||||||
|
yield new ReportUriForensicData($term, $matches['qualifier'], $matches['term']);
|
||||||
|
break;
|
||||||
|
case Mechanism::SPF_ALIGNMENT:
|
||||||
|
yield new SpfAlignment($term, $matches['qualifier'], $matches['term']);
|
||||||
|
break;
|
||||||
|
case Mechanism::DKIM_ALIGNMENT:
|
||||||
|
yield new DkimAlignment($term, $matches['qualifier'], $matches['term']);
|
||||||
|
break;
|
||||||
|
case Mechanism::REPORTING_INTERVAL:
|
||||||
|
yield new ReportingInterval($term, $matches['qualifier'], $matches['term']);
|
||||||
|
break;
|
||||||
|
case Mechanism::REPORTING_FORMAT:
|
||||||
|
yield new ReportingFormat($term, $matches['qualifier'], $matches['term']);
|
||||||
|
break;
|
||||||
|
case Mechanism::FORENSIC_REPORT_OPTIONS:
|
||||||
|
yield new ForensicReportOptions($term, $matches['qualifier'], $matches['term']);
|
||||||
|
break;
|
||||||
|
case Mechanism::PERCENTAGE:
|
||||||
|
yield new Percentage($term, $matches['qualifier'], $matches['term']);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new LogicException('Unknown mechanism '.$matches['qualifier']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isValid(): bool
|
||||||
|
{
|
||||||
|
return (bool) preg_match(
|
||||||
|
'/(^v=DMARC1;)?( +(p=(none|quarantine|reject);|rua=mailto:([^;]*);|ruf=mailto:([^;]*);|sp=(none|quarantine|reject);|aspf=s;|adkim=s;|fo=((?!:)(:?\w+)+);))/i',
|
||||||
|
$this->rawRecord
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return RejectPolicy
|
||||||
|
* @throws LogicException
|
||||||
|
*/
|
||||||
|
public function getRejectPolicy(): RejectPolicy
|
||||||
|
{
|
||||||
|
/** @var RejectPolicy $mechanism */
|
||||||
|
$mechanism = $this->getTerm(RejectPolicy::class);
|
||||||
|
return $mechanism;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ReportUriAggregateData
|
||||||
|
* @throws LogicException
|
||||||
|
*/
|
||||||
|
public function getReportUriAggregate(): ReportUriAggregateData
|
||||||
|
{
|
||||||
|
/** @var ReportUriAggregateData $mechanism */
|
||||||
|
$mechanism = $this->getTerm(ReportUriAggregateData::class);
|
||||||
|
return $mechanism;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ReportUriForensicData
|
||||||
|
* @throws LogicException
|
||||||
|
*/
|
||||||
|
public function getReportUriForensic(): ReportUriForensicData
|
||||||
|
{
|
||||||
|
/** @var ReportUriForensicData $mechanism */
|
||||||
|
$mechanism = $this->getTerm(ReportUriForensicData::class);
|
||||||
|
return $mechanism;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $className
|
||||||
|
*
|
||||||
|
* @return AbstractMechanism
|
||||||
|
* @throws LogicException
|
||||||
|
*/
|
||||||
|
public function getTerm(string $className): AbstractMechanism
|
||||||
|
{
|
||||||
|
foreach ($this->getTerms() as $term) {
|
||||||
|
if ( $term instanceof $className ) {
|
||||||
|
return $term;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new LogicException('undefined term '.$className);
|
||||||
|
}
|
||||||
|
}
|
67
src/Model/DMARCResult.php
Normal file
67
src/Model/DMARCResult.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* https://www.d3data.de
|
||||||
|
*
|
||||||
|
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
||||||
|
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
|
||||||
|
* @link https://www.oxidmodule.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace D3\MailAuthenticationCheck\Model;
|
||||||
|
|
||||||
|
use D3\MailAuthenticationCheck\Model\DMARCRecord as Record;
|
||||||
|
|
||||||
|
class DMARCResult
|
||||||
|
{
|
||||||
|
public const REJECT_NONE = 'none';
|
||||||
|
public const REJECT_QUARANTINE = 'quarantine';
|
||||||
|
public const REJECT_REJECT = 'reject';
|
||||||
|
|
||||||
|
public const NONE = 'None';
|
||||||
|
public const TEMPERROR = 'TempError';
|
||||||
|
public const PERMERROR = 'PermError';
|
||||||
|
|
||||||
|
private string $result;
|
||||||
|
|
||||||
|
protected Record $record;
|
||||||
|
|
||||||
|
public function hasResult(): bool
|
||||||
|
{
|
||||||
|
return isset($this->result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResult(): string
|
||||||
|
{
|
||||||
|
return $this->result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function setRecord(Record $record): self
|
||||||
|
{
|
||||||
|
$this->record = $record;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRecord(): Record
|
||||||
|
{
|
||||||
|
return $this->record ?? new Record('');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function setResult(string $result): self
|
||||||
|
{
|
||||||
|
$this->result = $result;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user