[ 'friendlyName' => 'Access Key Id', 'validators' => [ 'required' => 'required' ] ], 'secretAccessKey' => [ 'friendlyName' => 'Secret Access Key', 'type' => 'password', 'validators' => [ 'required' => 'required' ] ], 'region' => [ 'friendlyName' => 'Region' ], 'delegation_set' => [ 'friendlyName' => 'Delegation Set Id', 'validators' => [ ] ], 'soa_edit' => [ 'friendlyName' => 'Allow To Edit SOA Records', 'type' => 'yesno' ], 'delete_aws_ns' => [ 'friendlyName' => 'Delete AWS NS Records After Zone Creation', 'type' => 'yesno' ], 'use_white_label_nameservers' => [ 'friendlyName' => 'Using White Label Nameservers', 'type' => 'yesno', 'help' => 'Check this box if you are using White Label Nameservers' ], ]; public $availableTypes = ['A', 'AAAA', 'ALIAS', 'CNAME', 'MX', 'NAPTR', 'NS', 'PTR', 'SOA', 'SPF', 'SRV', 'TXT']; /** @var AWSRoute53API */ private $connection; public function testConnection() { if(!extension_loaded('SimpleXML')) { throw new DNSSubmoduleException('This server requires SimpleXML PHP extension', dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM); } $this->loadConnectionInstance(); /** @var AWSRoute53ResponseInterface $zones */ $zones = $this->connection->testConnection(); if($zones->getResponseType() === 'error') { throw new DNSSubmoduleException($zones->getResponseMessage(), dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM); } return true; } public function getNameServers( $index = false ) { if($this->config['use_white_label_nameservers'] === 'on') { return (array)parent::getNameServers(); } $this->loadConnectionInstance(); /** @var AWSRoute53ResponseInterface $zones */ $zones = $this->connection->listZonesByName($this->domain); if( $zones->getResponseType() === 'error' ) { throw new DNSSubmoduleException($zones->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR); } $zone = AwsRouteHelpers\AWSRoute53ResponseParseHelper::findZoneOnZoneList($zones->getParsedResponseBody(), $this->domain); if( !$zone ) { throw new DNSSubmoduleException('Zone name is not valid!', dns\SubmoduleExceptionCodes::COMMAND_ERROR); } $hostedZone = json_decode(json_encode($this->connection->getHostedZoneById($zone)->getParsedResponseBody()), true); return (array)$hostedZone['DelegationSet']['NameServers']['NameServer']; } public function getRecords($recordType = false) { $this->loadConnectionInstance(); /** @var AWSRoute53ResponseInterface $zones */ $zones = $this->connection->listZonesByName($this->domain); if($zones->getResponseType() === 'error') { throw new DNSSubmoduleException($zones->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR); } $zone = AwsRouteHelpers\AWSRoute53ResponseParseHelper::findZoneOnZoneList($zones->getParsedResponseBody(), $this->domain); if(!$zone) { throw new DNSSubmoduleException('Zone name is not valid!', dns\SubmoduleExceptionCodes::COMMAND_ERROR); } /** @var AWSRoute53ResponseInterface $records */ $records = $this->connection->listRecords($zone); if($records->getResponseType() === 'error') { throw new DNSSubmoduleException($records->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR); } $availableTypes = $recordType ? [$recordType] : $this->availableTypes; return AwsRouteHelpers\AWSRoute53ResponseParseHelper::prepareRecordList($records->getParsedResponseBody(), $availableTypes, $this->config['soa_edit'] === 'on'); } public function addRecord(dns\record\Record $record) { $this->loadConnectionInstance(); /** @var AWSRoute53ResponseInterface $zones */ $zones = $this->connection->listZonesByName($this->domain); if($zones->getResponseType() === 'error') { throw new DNSSubmoduleException($zones->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR); } $zone = AwsRouteHelpers\AWSRoute53ResponseParseHelper::findZoneOnZoneList($zones->getParsedResponseBody(), $this->domain); if(!$zone) { throw new DNSSubmoduleException('Zone name is not valid!', dns\SubmoduleExceptionCodes::COMMAND_ERROR); } $aliasType = $this->gatAliasTypeIfRequired($record); if(!$aliasType) { $rSet = $this->matchSetForRecord($record); } if(isset($rSet) && $rSet) { AwsRouteHelpers\AWSRoute53ResponseParseHelper::mergeHostsRecordsForRdata($rSet, $record, $rSet->type); return $this->editRecord($rSet); } $record->name = $record->nameToAbsolute($this->domain); /** @var AWSRoute53ResponseInterface $aRecord */ $aRecord = $this->connection->createRecord($zone, $record, $aliasType); if($aRecord->getResponseType() === 'error') { throw new DNSSubmoduleException($aRecord->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR); } return true; } public function editRecord(dns\record\Record $record) { $this->loadConnectionInstance(); /** @var AWSRoute53ResponseInterface $zones */ $zones = $this->connection->listZonesByName($this->domain); if($zones->getResponseType() === 'error') { throw new DNSSubmoduleException($zones->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR); } $zone = AwsRouteHelpers\AWSRoute53ResponseParseHelper::findZoneOnZoneList($zones->getParsedResponseBody(), $this->domain); if(!$zone) { throw new DNSSubmoduleException('Zone name is not valid!', dns\SubmoduleExceptionCodes::COMMAND_ERROR); } $aliasType = $this->gatAliasTypeIfRequired($record); /** @var AWSRoute53ResponseInterface $aRecord */ $aRecord = $this->connection->updateRecord($zone, $record, $aliasType); if($aRecord->getResponseType() === 'error') { throw new DNSSubmoduleException($aRecord->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR); } return true; } public function deleteRecord(dns\record\Record $record) { $this->loadConnectionInstance(); /** @var AWSRoute53ResponseInterface $zones */ $zones = $this->connection->listZonesByName($this->domain); if($zones->getResponseType() === 'error') { throw new DNSSubmoduleException($zones->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR); } $zone = AwsRouteHelpers\AWSRoute53ResponseParseHelper::findZoneOnZoneList($zones->getParsedResponseBody(), $this->domain); if(!$zone) { throw new DNSSubmoduleException('Zone name is not valid!', dns\SubmoduleExceptionCodes::COMMAND_ERROR); } $aliasType = $this->gatAliasTypeIfRequired($record); if(!$aliasType) { $rSet = $this->matchSetForRecord($record); } if(isset($rSet) && $rSet) { return $this->editRecord($rSet); } /** @var AWSRoute53ResponseInterface $aRecord */ $aRecord = $this->connection->deleteRecord($zone, $record, $aliasType); if($aRecord->getResponseType() === 'error') { throw new DNSSubmoduleException($aRecord->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR); } return true; } public function zoneExists() { $this->loadConnectionInstance(); /** @var AWSRoute53ResponseInterface $zones */ $zones = $this->connection->listZonesByName($this->domain); if($zones->getResponseType() === 'error') { throw new DNSSubmoduleException($zones->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR); } $zone = AwsRouteHelpers\AWSRoute53ResponseParseHelper::findZoneOnZoneList($zones->getParsedResponseBody(), $this->domain); return $zone ? true : false; } public function activateZone() { $this->loadConnectionInstance(); $this->log('ACTIVATE ZONE'); if($this->zoneExists()) { throw new DNSSubmoduleException('Domain name already exists!', dns\SubmoduleExceptionCodes::COMMAND_ERROR); } if(empty($this->domain)) { throw new DNSSubmoduleException('Domain name is not valid!', dns\SubmoduleExceptionCodes::INVALID_PARAMETERS); } /** @var AWSRoute53ResponseInterface $zone */ $zone = $this->connection->createZone($this->domain, $this->config['delegation_set']); if($zone->getResponseType() === 'error') { throw new DNSSubmoduleException($zone->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR); } return true; } public function terminateZone() { $records = $this->getRecords(); foreach($records as $record) { try { $this->deleteRecord($record); } catch(DNSSubmoduleException $exc) { } } /** @var AWSRoute53ResponseInterface $zones */ $zones = $this->connection->listZonesByName($this->domain); if($zones->getResponseType() === 'error') { throw new DNSSubmoduleException($zones->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR); } $zonesId = AwsRouteHelpers\AWSRoute53ResponseParseHelper::findZoneOnZoneList($zones->getParsedResponseBody(), $this->domain); if($zonesId === false) { throw new DNSSubmoduleException('Zone name is not valid!', dns\SubmoduleExceptionCodes::COMMAND_ERROR); } /** @var AWSRoute53ResponseInterface $deleted */ $deleted = $this->connection->deleteHostedZone($zonesId); if($deleted->getResponseType() === 'error') { throw new DNSSubmoduleException($deleted->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR); } return true; } public function getZones() { $this->loadConnectionInstance(); $zones = $this->connection->listZones(); /** @var AWSRoute53ResponseInterface $zones */ if($zones->getResponseType() === 'error') { throw new DNSSubmoduleException($zones->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR); } return AwsRouteHelpers\AWSRoute53ResponseParseHelper::zoneListXmlToArray($zones->getParsedResponseBody()); } private function loadConnectionInstance() { if(!$this->connection) { $responseHandler = new AwsRouteHelpers\AWSRoute53Response(); $requestHandler = new AwsRouteHelpers\AWSRoute53Request( $responseHandler, $this->config['accessKeyId'], $this->config['secretAccessKey'], $this->config['region'] ); $apiHandler = new AwsRouteHelpers\AWSRoute53API($requestHandler); $this->connection = $apiHandler; } } public function updateRDNS($ip, $ttl = false, $value = false) { $revDnsZoneName = dns\utils\ReverseDNSHelper::reverseZoneName($ip); $zoneId = $this->getRevDNSZoneID($revDnsZoneName); if(!$zoneId) { $zoneId = $this->createRevDnsZone($revDnsZoneName); } $revRecord = dns\utils\ReverseDNSHelper::createPTRRecord($ip, $ttl, $value); $revRecord->name .= '.'.$revDnsZoneName; /** @var AWSRoute53ResponseInterface $aRecord */ $aRecord = $this->connection->updateRecord($zoneId, $revRecord); if($aRecord->getResponseType() === 'error') { throw new DNSSubmoduleException($aRecord->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR); } return true; } public function removeRDNS($ip) { $revDnsZoneName = dns\utils\ReverseDNSHelper::reverseZoneName($ip); $zoneId = $this->getRevDNSZoneID($revDnsZoneName); if(!$zoneId) { return true; } /** @var AWSRoute53ResponseInterface $records */ $records = $this->connection->listRecords($zoneId); if($records->getResponseType() === 'error') { throw new DNSSubmoduleException($records->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR); } $recordPrev = dns\utils\ReverseDNSHelper::reverseRecordName($ip); $recordName = $recordPrev.'.'.$revDnsZoneName; $recordToRemove = AwsRouteHelpers\AWSRoute53ResponseParseHelper::findPtrRecordByName($records->getParsedResponseBody(), $recordName); if(!$recordToRemove) { return true; } /** @var AWSRoute53ResponseInterface $aRecord */ $aRecord = $this->connection->deleteRecord($zoneId, $recordToRemove); if($aRecord->getResponseType() === 'error') { throw new DNSSubmoduleException($aRecord->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR); } return true; } public function getRDNSRecord($ip) { $revDnsZoneName = dns\utils\ReverseDNSHelper::reverseZoneName($ip); $zoneId = $this->getRevDNSZoneID($revDnsZoneName); if(!$zoneId) { return []; } /** @var AWSRoute53ResponseInterface $records */ $records = $this->connection->listRecords($zoneId); if($records->getResponseType() === 'error') { throw new DNSSubmoduleException($records->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR); } return AwsRouteHelpers\AWSRoute53ResponseParseHelper::prepareRecordList($records->getParsedResponseBody(), $this->availableTypes, $this->config['soa_edit'] === 'on'); } private function getRevDNSZoneID($zoneName) { $this->loadConnectionInstance(); /** @var AWSRoute53ResponseInterface $zones */ $zones = $this->connection->listZonesByName($zoneName); if($zones->getResponseType() === 'error') { throw new DNSSubmoduleException($zones->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR); } $zone = AwsRouteHelpers\AWSRoute53ResponseParseHelper::findZoneOnZoneList($zones->getParsedResponseBody(), $zoneName); return $zone ? : false; } private function createRevDnsZone($revDnsZoneName) { /** @var AWSRoute53ResponseInterface $zone */ $zone = $this->connection->createZone($revDnsZoneName); if($zone->getResponseType() === 'error') { throw new DNSSubmoduleException($zone->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR); } return AwsRouteHelpers\AWSRoute53ResponseParseHelper::getZoneIdFromCreateConfirmation($zone->getParsedResponseBody()); } private function gatAliasTypeIfRequired($record) { if($record->type !== 'ALIAS') { return false; } $dNamePos = stripos($record->name, trim($this->domain, '.')); if(!$dNamePos && $dNamePos !== 0) { $record->name .= '.'.$this->domain; } $deafultType = 'A'; $recordsList = $this->getRecords(); foreach($recordsList as $rec) { $trimedName = trim($rec->name, '.'); if(($rec->name === $record->rdata->target || $trimedName === $record->rdata->target ) && $rec->type === 'AAAA') { return 'AAAA'; } } return $deafultType; } public function convertInputFormData(&$input) { AwsRouteHelpers\AWSRoute53ResponseParseHelper::convertInputFormData($input); } /** * @param dns\record\Record $record * @return bool|dns\record\Record * @throws DNSSubmoduleException */ public function matchSetForRecord( dns\record\Record $record) { $cRecord = clone $record; $allowedTypes = ['MX', 'A', 'AAAA', 'NS']; if(!in_array($cRecord->type, $allowedTypes, true) ) { return false; } $basicRdata = $cRecord->rdata->toString(); $cRecord->createRDATAObject($cRecord->type); $cRecord->rdata = null; $recordList = $this->getRecords(); foreach($recordList as $rec) { if($cRecord->type === $rec->type && trim($cRecord->name, '.') === trim($rec->name, '.')) { if($rec->rdata->toString() === $basicRdata) { continue; } AwsRouteHelpers\AWSRoute53ResponseParseHelper::mergeHostsRecordsForRdata($cRecord, $rec, $cRecord->type); } } return $cRecord->rdata !== null ? $cRecord : false; } public function removeDefaultServerRecords($defaultModuleRecords) { if($this->config['delete_aws_ns'] !== 'on') { return false; } $cRecords = $this->getRecords(); foreach($cRecords as $cRecord) { if($cRecord->type !== 'NS') { continue; } $found = false; foreach($defaultModuleRecords as $dRecord) { if($this->areRecordsEqual($cRecord, $dRecord)) { $found = true; break; } } if($found === false) { $this->deleteRecord($cRecord); } } return true; } private function areRecordsEqual( dns\record\Record $rec1, dns\record\Record $rec2) { $recAname = rtrim($rec1->name,'.'); $recBname = rtrim($rec2->name,'.'); return $recAname === $recBname && $rec1->type === $rec2->type && $rec1->ttl === $rec2->ttl && $rec1->rdata->toString() === $rec2->rdata->toString(); } }