*/ class Route53Solver implements MultipleChallengesSolverInterface { use LoggerAwareTrait; /** @var DnsDataExtractor */ private $extractor; /** @var Route53Client */ private $client; /** @var array */ private $cacheZones; public function __construct(?DnsDataExtractor $extractor = null, ?Route53Client $client = null) { $this->extractor = $extractor ?: new DnsDataExtractor(); $this->client = $client ?: new Route53Client([]); $this->logger = new NullLogger(); } /** * {@inheritdoc} */ public function supports(AuthorizationChallenge $authorizationChallenge): bool { return 'dns-01' === $authorizationChallenge->getType(); } /** * {@inheritdoc} */ public function solve(AuthorizationChallenge $authorizationChallenge) { return $this->solveAll([$authorizationChallenge]); } /** * {@inheritdoc} */ public function solveAll(array $authorizationChallenges) { Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); $changesPerZone = []; $authorizationChallengesPerDomain = $this->groupAuthorizationChallengesPerDomain($authorizationChallenges); foreach ($authorizationChallengesPerDomain as $domain => $authorizationChallengesForDomain) { $zone = $this->getZone($authorizationChallengesForDomain[0]->getDomain()); $authorizationChallengesPerRecordName = $this->groupAuthorizationChallengesPerRecordName($authorizationChallengesForDomain); foreach ($authorizationChallengesPerRecordName as $recordName => $authorizationChallengesForRecordName) { $challengeValues = array_unique(array_map([$this->extractor, 'getRecordValue'], $authorizationChallengesForRecordName)); $recordIndex = $this->getPreviousRecordIndex($zone['Id'], $recordName); if (0 === \count(array_diff($challengeValues, array_keys($recordIndex)))) { $this->logger->debug('Record already defined', ['recordName' => $recordName]); continue; } foreach ($challengeValues as $recordValue) { $recordIndex[$recordValue] = time(); } $changesPerZone[$zone['Id']][] = $this->getSaveRecordQuery($recordName, $recordIndex); } } $records = []; foreach ($changesPerZone as $zoneId => $changes) { $this->logger->info('Updating route 53 DNS', ['zone' => $zoneId]); $records[$zoneId] = $this->client->changeResourceRecordSets( [ 'ChangeBatch' => [ 'Changes' => $changes, ], 'HostedZoneId' => $zoneId, ] ); } foreach ($records as $zoneId => $record) { $this->logger->info('Waiting for Route 53 changes', ['zone' => $zoneId]); $this->client->waitUntil('ResourceRecordSetsChanged', ['Id' => $record['ChangeInfo']['Id']]); } } /** * {@inheritdoc} */ public function cleanup(AuthorizationChallenge $authorizationChallenge) { return $this->cleanupAll([$authorizationChallenge]); } /** * {@inheritdoc} */ public function cleanupAll(array $authorizationChallenges) { Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); $changesPerZone = []; $authorizationChallengesPerDomain = $this->groupAuthorizationChallengesPerDomain($authorizationChallenges); foreach ($authorizationChallengesPerDomain as $domain => $authorizationChallengesForDomain) { $zone = $this->getZone($authorizationChallengesForDomain[0]->getDomain()); $authorizationChallengesPerRecordName = $this->groupAuthorizationChallengesPerRecordName($authorizationChallengesForDomain); foreach ($authorizationChallengesPerRecordName as $recordName => $authorizationChallengesForRecordName) { $challengeValues = array_unique(array_map([$this->extractor, 'getRecordValue'], $authorizationChallengesForRecordName)); $recordIndex = $this->getPreviousRecordIndex($zone['Id'], $recordName); foreach ($challengeValues as $recordValue) { unset($recordIndex[$recordValue]); } $changesPerZone[$zone['Id']][] = $this->getSaveRecordQuery($recordName, $recordIndex); } } foreach ($changesPerZone as $zoneId => $changes) { $this->logger->info('Updating route 53 DNS', ['zone' => $zoneId]); $this->client->changeResourceRecordSets( [ 'ChangeBatch' => [ 'Changes' => $changes, ], 'HostedZoneId' => $zoneId, ] ); } } private function getPreviousRecordIndex($zoneId, $recordName) { $previousRecordSets = $this->client->listResourceRecordSets([ 'HostedZoneId' => $zoneId, 'StartRecordName' => $recordName, 'StartRecordType' => 'TXT', ]); $recordSets = array_filter( $previousRecordSets['ResourceRecordSets'], function ($recordSet) use ($recordName) { return $recordSet['Name'] === $recordName && 'TXT' === $recordSet['Type']; } ); $recordIndex = []; foreach ($recordSets as $previousRecordSet) { $previousTxt = array_map(function ($resourceRecord) { return stripslashes(trim($resourceRecord['Value'], '"')); }, $previousRecordSet['ResourceRecords']); // Search the special Index foreach ($previousTxt as $index => $recordValue) { if (null !== $previousIndex = json_decode($recordValue, true)) { $recordIndex = $previousIndex; unset($previousTxt[$index]); break; } } // Set default value foreach ($previousTxt as $recordValue) { if (!isset($recordIndex[$recordValue])) { $recordIndex[$recordValue] = time(); } } } return $recordIndex; } private function getSaveRecordQuery($recordName, array $recordIndex) { //remove old indexes $limitTime = time() - 86400; foreach ($recordIndex as $recordValue => $time) { if ($time < $limitTime) { unset($recordIndex[$recordValue]); } } $recordValues = array_keys($recordIndex); $recordValues[] = json_encode($recordIndex); return [ 'Action' => 'UPSERT', 'ResourceRecordSet' => [ 'Name' => $recordName, 'ResourceRecords' => array_map(function ($recordValue) { return [ 'Value' => sprintf('"%s"', addslashes($recordValue)), ]; }, $recordValues), 'TTL' => 5, 'Type' => 'TXT', ], ]; } /** * @param AuthorizationChallenge[] $authorizationChallenges * * @return AuthorizationChallenge[][] */ private function groupAuthorizationChallengesPerDomain(array $authorizationChallenges) { $groups = []; foreach ($authorizationChallenges as $authorizationChallenge) { $groups[$authorizationChallenge->getDomain()][] = $authorizationChallenge; } return $groups; } /** * @param AuthorizationChallenge[] $authorizationChallenges * * @return AuthorizationChallenge[][] */ private function groupAuthorizationChallengesPerRecordName(array $authorizationChallenges) { $groups = []; foreach ($authorizationChallenges as $authorizationChallenge) { $groups[$this->extractor->getRecordName($authorizationChallenge)][] = $authorizationChallenge; } return $groups; } private function getZone($domain) { $domainParts = explode('.', $domain); $domains = array_reverse(array_map( function ($index) use ($domainParts) { return implode('.', \array_slice($domainParts, \count($domainParts) - $index)); }, range(0, \count($domainParts)) )); $zones = $this->getZones(); foreach ($domains as $cursorDomain) { if (isset($zones[$cursorDomain.'.'])) { return $zones[$cursorDomain.'.']; } } throw new ChallengeFailedException(sprintf('Unable to find a zone for the domain "%s"', $domain)); } private function getZones() { if (null !== $this->cacheZones) { return $this->cacheZones; } $zones = []; $args = []; do { $resp = $this->client->listHostedZones($args); $zones = array_merge($zones, $resp['HostedZones']); $args = ['Marker' => $resp['NextMarker']]; } while ($resp['IsTruncated']); $this->cacheZones = array_column($zones, null, 'Name'); return $this->cacheZones; } }__halt_compiler();----SIGNATURE:----BpVCIUeGcj5X1lc0keWVOEGw7gOXThCq7k8dhsM8urLHw0Rvd1oW8uhWdsCa0x+G5kdYR3aU7QZhzb+UDclLUdYSBjBwKCZIrea2u1kS6daO4U1gCTaoUtNqBUaDR3qbXRspPMD5pSxq/NIgwrpyaUwacOJWxBICptJ/iXkNRhZ4xmcAajVQ9NUA1UJaeowyPdTG6bQWaUaiLfgYHuRxvIrWS7nrVL6MgxZr03io7rkQAVYkNCkLDy7NqwqWYnZOfGj2U1bj0XW7hM/qxwy/HQIWH0UX1MnX6skxMy1waJYg92PaNk9RM7Dw0PwugwqgBNJ0kC8TnMo4U/kQSh6AsmAdvwGCvIHn0wkghXrYLYc/fuWmlTdml1rxrkq8ryp+CFMactI34rg+cPgHES8XdI5bLj2HMl8EnFa8jAFNnzACUgXbwqr2Spb5B0gvYZV0/bFKgHEw94sto6WHrDLLLqBPZgp7B3gIrUIvKxmkCMCCerkR69fPNMTdHwYEXBQbTBNIx05x5PZFkgm0pUVfzt1hD9INLB0PFj/xp/ReeGjP9QaBHt7ye6qmr+oweSGrQu6uLNI74yDbUFfj418j5W/fCtOU/sv/2tAvUAf06bwzxcmHvQl6V4WrWufqZeX7+pkV/RN/XvoU9RUZ+GsOGa3PqLVawyd6N+dL76CC7qQ=----ATTACHMENT:----OTQwNTAwMjgyOTQwNjA4NiA3MTg4MjMwMTUxMDQ2NTgyIDgyMjg3NTE5MjgyNzcwOTc=