AWSRoute53.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
  1. <?php
  2. namespace MGModule\DNSManager2\mgLibs\custom\dns\submodules;
  3. use MGModule\DNSManager2\mgLibs\custom\dns;
  4. use MGModule\DNSManager2\mgLibs\custom\dns\exceptions\DNSSubmoduleException;
  5. use MGModule\DNSManager2\mgLibs\custom\dns\interfaces;
  6. use MGModule\DNSManager2\mgLibs\custom\dns\submodules\AWSRoute53 as AwsRouteHelpers;
  7. use MGModule\DNSManager2\mgLibs\custom\dns\submodules\AWSRoute53\AWSRoute53API;
  8. use MGModule\DNSManager2\mgLibs\custom\dns\submodules\AWSRoute53\AWSRoute53ResponseInterface;
  9. use MGModule\DNSManager2\mgLibs\custom\dns\submodules\AWSRoute53\AWSRoute53ResponseParseHelper;
  10. use MGModule\DNSManager2\mgLibs\custom\helpers\IdnaHelper;
  11. class AWSRoute53 extends dns\SubmoduleAbstract implements
  12. interfaces\SubmoduleRDNSInterface,
  13. interfaces\SubmoduleTTLInterface,
  14. interfaces\SubmoduleImportInterface,
  15. interfaces\SubmoduleRemoveDefaultRecords,
  16. interfaces\SubmoduleCustomParseEditZoneInput
  17. {
  18. public $configFields = [
  19. 'accessKeyId' => [
  20. 'friendlyName' => 'Access Key Id',
  21. 'validators' => [
  22. 'required' => 'required'
  23. ]
  24. ],
  25. 'secretAccessKey' => [
  26. 'friendlyName' => 'Secret Access Key',
  27. 'type' => 'password',
  28. 'validators' => [
  29. 'required' => 'required'
  30. ]
  31. ],
  32. 'region' => [
  33. 'friendlyName' => 'Region'
  34. ],
  35. 'delegation_set' => [
  36. 'friendlyName' => 'Delegation Set Id',
  37. 'validators' => [
  38. ]
  39. ],
  40. 'soa_edit' => [
  41. 'friendlyName' => 'Allow To Edit SOA Records',
  42. 'type' => 'yesno'
  43. ],
  44. 'delete_aws_ns' => [
  45. 'friendlyName' => 'Delete AWS NS Records After Zone Creation',
  46. 'type' => 'yesno'
  47. ],
  48. 'use_white_label_nameservers' => [
  49. 'friendlyName' => 'Using White Label Nameservers',
  50. 'type' => 'yesno',
  51. 'help' => 'Check this box if you are using White Label Nameservers'
  52. ],
  53. ];
  54. public $availableTypes = ['A', 'AAAA', 'ALIAS', 'CNAME', 'MX', 'NAPTR', 'NS', 'PTR', 'SOA', 'SPF', 'SRV', 'TXT'];
  55. /** @var AWSRoute53API */
  56. private $connection;
  57. public function testConnection()
  58. {
  59. if(!extension_loaded('SimpleXML'))
  60. {
  61. throw new DNSSubmoduleException('This server requires SimpleXML PHP extension', dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM);
  62. }
  63. $this->loadConnectionInstance();
  64. /** @var AWSRoute53ResponseInterface $zones */
  65. $zones = $this->connection->testConnection();
  66. if($zones->getResponseType() === 'error')
  67. {
  68. throw new DNSSubmoduleException($zones->getResponseMessage(), dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM);
  69. }
  70. return true;
  71. }
  72. public function getNameServers( $index = false )
  73. {
  74. if($this->config['use_white_label_nameservers'] === 'on')
  75. {
  76. return (array)parent::getNameServers();
  77. }
  78. $this->loadConnectionInstance();
  79. /** @var AWSRoute53ResponseInterface $zones */
  80. $zones = $this->connection->listZonesByName($this->domain);
  81. if( $zones->getResponseType() === 'error' )
  82. {
  83. throw new DNSSubmoduleException($zones->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR);
  84. }
  85. $zone = AwsRouteHelpers\AWSRoute53ResponseParseHelper::findZoneOnZoneList($zones->getParsedResponseBody(), $this->domain);
  86. if( !$zone )
  87. {
  88. throw new DNSSubmoduleException('Zone name is not valid!', dns\SubmoduleExceptionCodes::COMMAND_ERROR);
  89. }
  90. $hostedZone = json_decode(json_encode($this->connection->getHostedZoneById($zone)->getParsedResponseBody()), true);
  91. return (array)$hostedZone['DelegationSet']['NameServers']['NameServer'];
  92. }
  93. public function getRecords($recordType = false)
  94. {
  95. $this->loadConnectionInstance();
  96. $zone = $this->getZone();
  97. /** @var AWSRoute53ResponseInterface $records */
  98. $records = $this->connection->listRecords($zone);
  99. if($records->getResponseType() === 'error')
  100. {
  101. throw new DNSSubmoduleException($records->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR);
  102. }
  103. $availableTypes = $recordType ? [$recordType] : $this->availableTypes;
  104. $records = AwsRouteHelpers\AWSRoute53ResponseParseHelper::prepareRecordList($records->getParsedResponseBody(), $availableTypes, $this->config['soa_edit'] === 'on');
  105. return array_map(function( dns\record\Record $record ) {
  106. return $record;
  107. }, $records);
  108. }
  109. public function addRecord(dns\record\Record $record)
  110. {
  111. $this->loadConnectionInstance();
  112. $zone = $this->getZone();
  113. $rrSets = $this->buildRRSets($this->getRecords());
  114. //synchronization purposes - modifying record names to match AWS standards - shouldn't change workflow of Route53 module
  115. $record->name = $this->prepareNameForSynchro($record->name, $this->domain);
  116. if($record->type == 'TXT')
  117. {
  118. $this->explodeTxtRdata($record);
  119. }
  120. if( ($index = $this->findMatchingRecordSet($rrSets, $record)) !== false )
  121. {
  122. /** @var dns\record\RRSet $rrSet */
  123. $rrSet = $rrSets[$index];
  124. $rrSet->setTtl($record->ttl);
  125. $rrSet->pushRecord($record);
  126. }
  127. else
  128. {
  129. $rrSet = new dns\record\RRSet($record->name, $record->type, $record->ttl, [$record]);
  130. }
  131. $response = $this->connection->updateRRSet($rrSet, $zone,$this->gatAliasTypeIfRequired($record));
  132. if( $response->getResponseType() === 'error' )
  133. {
  134. throw new DNSSubmoduleException($response->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR);
  135. }
  136. $this->resetConnection();
  137. return true;
  138. }
  139. public function editRecord(dns\record\Record $record)
  140. {
  141. $this->loadConnectionInstance();
  142. $record->name = IdnaHelper::idnaEncode($record->name);
  143. $zone = $this->getZone();
  144. $rrSets = $this->buildRRSets($this->getRecords());
  145. //Record RRSet based on line property
  146. if( ($recordToDeleteIndex = $this->findRecordInRecordSets($rrSets, $record)) !== false )
  147. {
  148. //RRSet Changed
  149. if( $rrSets[$recordToDeleteIndex]->getName() !== $record->name )
  150. {
  151. //RRSet exist so we just push
  152. if( ($recordToAddIntex = $this->findMatchingRecordSet($rrSets, $record)) !== false )
  153. {
  154. $rrsetToAddRecord = $rrSets[$recordToAddIntex];
  155. $rrsetToAddRecord->pushRecord($record);
  156. $rrsetToAddRecord->setTtl($record->ttl);
  157. }
  158. else
  159. {
  160. $rrsetToAddRecord = new dns\record\RRSet($record->name, $record->type, $record->ttl, [$record]);
  161. }
  162. $response = $this->connection->updateRRSet($rrsetToAddRecord,$zone,$this->gatAliasTypeIfRequired($record));
  163. if($response->getResponseType() === 'error')
  164. {
  165. throw new DNSSubmoduleException($response->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR);
  166. }
  167. if( $rrSets[$recordToDeleteIndex]->countRecords() <= 1 )
  168. {
  169. $response = $this->connection->removeRRSet($rrSets[$recordToDeleteIndex], $zone,$this->gatAliasTypeIfRequired($record));
  170. }
  171. else
  172. {
  173. $rrSets[$recordToDeleteIndex]->removeRecord($record);
  174. $response = $this->connection->updateRRSet($rrSets[$recordToDeleteIndex], $zone,$this->gatAliasTypeIfRequired($record));
  175. }
  176. }
  177. else
  178. {
  179. //RRSet Not Changed
  180. $rrSets[$recordToDeleteIndex]->replaceRecord($record);
  181. $rrSets[$recordToDeleteIndex]->setTtl($record->ttl);
  182. $response = $this->connection->updateRRSet($rrSets[$recordToDeleteIndex], $zone,$this->gatAliasTypeIfRequired($record));
  183. }
  184. if($response->getResponseType() === 'error')
  185. {
  186. throw new DNSSubmoduleException($response->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR);
  187. }
  188. }
  189. else
  190. {
  191. throw new DNSSubmoduleException('Record Not Found on server', dns\SubmoduleExceptionCodes::INVALID_PARAMETERS);
  192. }
  193. $this->resetConnection();
  194. return true;
  195. }
  196. public function deleteRecord(dns\record\Record $record)
  197. {
  198. $record->nameToAbsolute($this->domain);
  199. $record->name = IdnaHelper::idnaEncode($record->name);
  200. $this->loadConnectionInstance();
  201. if(strpos($record->name, $this->domain) === false) {
  202. $record->name .= '.' . $this->domain;
  203. }
  204. $zone = $this->getZone();
  205. $rrSets = $this->buildRRSets($this->getRecords());
  206. if( ($index = $this->findMatchingRecordSet($rrSets, $record)) !== false )
  207. {
  208. if( $rrSets[$index]->countRecords() <= 1 )
  209. {
  210. $response = $this->connection->removeRRSet($rrSets[$index],$zone,$this->gatAliasTypeIfRequired($record));
  211. }
  212. else
  213. {
  214. $rrSets[$index]->removeRecord($record);
  215. $response =$this->connection->updateRRSet($rrSets[$index],$zone,$this->gatAliasTypeIfRequired($record));
  216. }
  217. if($response->getResponseType() === 'error')
  218. {
  219. throw new DNSSubmoduleException($response->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR);
  220. }
  221. }
  222. $this->resetConnection();
  223. return true;
  224. }
  225. public function zoneExists()
  226. {
  227. $this->loadConnectionInstance();
  228. /** @var AWSRoute53ResponseInterface $zones */
  229. $zones = $this->connection->listZonesByName($this->domain);
  230. if($zones->getResponseType() === 'error')
  231. {
  232. throw new DNSSubmoduleException($zones->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR);
  233. }
  234. $zone = AwsRouteHelpers\AWSRoute53ResponseParseHelper::findZoneOnZoneList($zones->getParsedResponseBody(), $this->domain);
  235. return $zone ? true : false;
  236. }
  237. public function activateZone()
  238. {
  239. $this->loadConnectionInstance();
  240. $this->log('ACTIVATE ZONE');
  241. if($this->zoneExists())
  242. {
  243. throw new DNSSubmoduleException('Domain name already exists!', dns\SubmoduleExceptionCodes::COMMAND_ERROR);
  244. }
  245. if(empty($this->domain))
  246. {
  247. throw new DNSSubmoduleException('Domain name is not valid!', dns\SubmoduleExceptionCodes::INVALID_PARAMETERS);
  248. }
  249. /** @var AWSRoute53ResponseInterface $zone */
  250. $zone = $this->connection->createZone($this->domain, $this->config['delegation_set']);
  251. if($zone->getResponseType() === 'error')
  252. {
  253. throw new DNSSubmoduleException($zone->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR);
  254. }
  255. $this->resetConnection();
  256. return true;
  257. }
  258. public function terminateZone()
  259. {
  260. $records = $this->getRecords();
  261. foreach($records as $record)
  262. {
  263. try
  264. {
  265. $this->deleteRecord($record);
  266. }
  267. catch(DNSSubmoduleException $exc)
  268. {
  269. }
  270. }
  271. $this->loadConnectionInstance();
  272. /** @var AWSRoute53ResponseInterface $zones */
  273. $zones = $this->connection->listZonesByName($this->domain);
  274. if($zones->getResponseType() === 'error')
  275. {
  276. throw new DNSSubmoduleException($zones->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR);
  277. }
  278. $zonesId = AwsRouteHelpers\AWSRoute53ResponseParseHelper::findZoneOnZoneList($zones->getParsedResponseBody(), $this->domain);
  279. if($zonesId === false)
  280. {
  281. throw new DNSSubmoduleException('Zone name is not valid!', dns\SubmoduleExceptionCodes::COMMAND_ERROR);
  282. }
  283. /** @var AWSRoute53ResponseInterface $deleted */
  284. $deleted = $this->connection->deleteHostedZone($zonesId);
  285. if($deleted->getResponseType() === 'error')
  286. {
  287. throw new DNSSubmoduleException($deleted->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR);
  288. }
  289. $this->resetConnection();
  290. return true;
  291. }
  292. public function getZones()
  293. {
  294. $zones = [];
  295. $client = \Aws\Route53\Route53Client::factory([
  296. 'credentials' => [
  297. 'key' => $this->config['accessKeyId'],
  298. 'secret' => $this->config['secretAccessKey'],
  299. ],
  300. 'version' => '2013-04-01',
  301. 'region' => $this->config['region']
  302. ]);
  303. do
  304. {
  305. $params = ['MaxItems' => 100];
  306. if( isset($result) )
  307. {
  308. $params['Marker'] = $result->get('NextMarker');
  309. }
  310. $result = $client->listHostedZones($params);
  311. $zones[] = array_column($result->get('HostedZones'), 'Name');
  312. } while( $result->hasKey('NextMarker') );
  313. $zones = array_merge(...$zones);
  314. return array_combine($zones, array_fill(0,count($zones),''));
  315. }
  316. private function loadConnectionInstance()
  317. {
  318. if(!$this->connection)
  319. {
  320. $responseHandler = new AwsRouteHelpers\AWSRoute53Response();
  321. $requestHandler = new AwsRouteHelpers\AWSRoute53Request(
  322. $responseHandler,
  323. $this->config['accessKeyId'],
  324. $this->config['secretAccessKey'],
  325. $this->config['region']
  326. );
  327. $apiHandler = new AwsRouteHelpers\AWSRoute53API($requestHandler);
  328. $this->connection = $apiHandler;
  329. }
  330. }
  331. public function updateRDNS($ip, $ttl = false, $value = false)
  332. {
  333. $revDnsZoneName = dns\utils\ReverseDNSHelper::reverseZoneName($ip);
  334. $zoneId = $this->getRevDNSZoneID($revDnsZoneName);
  335. if(!$zoneId)
  336. {
  337. $zoneId = $this->createRevDnsZone($revDnsZoneName);
  338. }
  339. $revRecord = dns\utils\ReverseDNSHelper::createPTRRecord($ip, $ttl, $value);
  340. $revRecord->name .= '.'.$revDnsZoneName;
  341. /** @var AWSRoute53ResponseInterface $aRecord */
  342. $aRecord = $this->connection->updateRecord($zoneId, $revRecord);
  343. if($aRecord->getResponseType() === 'error')
  344. {
  345. throw new DNSSubmoduleException($aRecord->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR);
  346. }
  347. $this->resetConnection();
  348. return true;
  349. }
  350. public function removeRDNS($ip)
  351. {
  352. $revDnsZoneName = dns\utils\ReverseDNSHelper::reverseZoneName($ip);
  353. $zoneId = $this->getRevDNSZoneID($revDnsZoneName);
  354. if(!$zoneId)
  355. {
  356. return true;
  357. }
  358. /** @var AWSRoute53ResponseInterface $records */
  359. $records = $this->connection->listRecords($zoneId);
  360. if($records->getResponseType() === 'error')
  361. {
  362. throw new DNSSubmoduleException($records->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR);
  363. }
  364. $recordPrev = dns\utils\ReverseDNSHelper::reverseRecordName($ip);
  365. $recordName = $recordPrev.'.'.$revDnsZoneName;
  366. $recordToRemove = AwsRouteHelpers\AWSRoute53ResponseParseHelper::findPtrRecordByName($records->getParsedResponseBody(), $recordName);
  367. if(!$recordToRemove)
  368. {
  369. return true;
  370. }
  371. /** @var AWSRoute53ResponseInterface $aRecord */
  372. $aRecord = $this->connection->deleteRecord($zoneId, $recordToRemove);
  373. if($aRecord->getResponseType() === 'error')
  374. {
  375. throw new DNSSubmoduleException($aRecord->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR);
  376. }
  377. $this->resetConnection();
  378. return true;
  379. }
  380. public function getRDNSRecord($ip)
  381. {
  382. $revDnsZoneName = dns\utils\ReverseDNSHelper::reverseZoneName($ip);
  383. $zoneId = $this->getRevDNSZoneID($revDnsZoneName);
  384. if(!$zoneId)
  385. {
  386. return [];
  387. }
  388. /** @var AWSRoute53ResponseInterface $records */
  389. $records = $this->connection->listRecords($zoneId);
  390. if($records->getResponseType() === 'error')
  391. {
  392. throw new DNSSubmoduleException($records->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR);
  393. }
  394. $this->resetConnection();
  395. return AwsRouteHelpers\AWSRoute53ResponseParseHelper::prepareRecordList($records->getParsedResponseBody(), $this->availableTypes, $this->config['soa_edit'] === 'on');
  396. }
  397. private function getRevDNSZoneID($zoneName)
  398. {
  399. $this->loadConnectionInstance();
  400. /** @var AWSRoute53ResponseInterface $zones */
  401. $zones = $this->connection->listZonesByName($zoneName);
  402. if($zones->getResponseType() === 'error')
  403. {
  404. throw new DNSSubmoduleException($zones->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR);
  405. }
  406. $zone = AwsRouteHelpers\AWSRoute53ResponseParseHelper::findZoneOnZoneList($zones->getParsedResponseBody(), $zoneName);
  407. return $zone ? : false;
  408. }
  409. private function createRevDnsZone($revDnsZoneName)
  410. {
  411. /** @var AWSRoute53ResponseInterface $zone */
  412. $zone = $this->connection->createZone($revDnsZoneName);
  413. if($zone->getResponseType() === 'error')
  414. {
  415. throw new DNSSubmoduleException($zone->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR);
  416. }
  417. return AwsRouteHelpers\AWSRoute53ResponseParseHelper::getZoneIdFromCreateConfirmation($zone->getParsedResponseBody());
  418. }
  419. private function gatAliasTypeIfRequired($record)
  420. {
  421. if($record->type !== 'ALIAS')
  422. {
  423. return false;
  424. }
  425. $dNamePos = stripos($record->name, trim($this->domain, '.'));
  426. if(!$dNamePos && $dNamePos !== 0)
  427. {
  428. $record->name .= '.'.$this->domain;
  429. }
  430. $deafultType = 'A';
  431. $recordsList = $this->getRecords();
  432. foreach($recordsList as $rec)
  433. {
  434. $trimedName = trim($rec->name, '.');
  435. if(($rec->name === $record->rdata->target || $trimedName === $record->rdata->target ) && $rec->type === 'AAAA')
  436. {
  437. return 'AAAA';
  438. }
  439. }
  440. return $deafultType;
  441. }
  442. public function convertInputFormData(&$input)
  443. {
  444. AwsRouteHelpers\AWSRoute53ResponseParseHelper::convertInputFormData($input);
  445. }
  446. /**
  447. * @param dns\record\Record $record
  448. * @return bool|dns\record\Record
  449. * @throws DNSSubmoduleException
  450. */
  451. public function matchSetForRecord( dns\record\Record $record)
  452. {
  453. $cRecord = clone $record;
  454. $allowedTypes = ['MX', 'A', 'AAAA', 'NS'];
  455. if(!in_array($cRecord->type, $allowedTypes, true) )
  456. {
  457. return false;
  458. }
  459. $basicRdata = $cRecord->rdata->toString();
  460. $cRecord->createRDATAObject($cRecord->type);
  461. $cRecord->rdata = null;
  462. $recordList = $this->getRecords();
  463. foreach($recordList as $rec)
  464. {
  465. if($cRecord->type === $rec->type && trim($cRecord->name, '.') === trim($rec->name, '.'))
  466. {
  467. if($rec->rdata->toString() === $basicRdata)
  468. {
  469. continue;
  470. }
  471. AwsRouteHelpers\AWSRoute53ResponseParseHelper::mergeHostsRecordsForRdata($cRecord, $rec, $cRecord->type);
  472. }
  473. }
  474. return $cRecord->rdata !== null ? $cRecord : false;
  475. }
  476. public function removeDefaultServerRecords($defaultModuleRecords)
  477. {
  478. if($this->config['delete_aws_ns'] !== 'on')
  479. {
  480. return false;
  481. }
  482. $cRecords = $this->getRecords();
  483. foreach($cRecords as $cRecord)
  484. {
  485. if($cRecord->type !== 'NS')
  486. {
  487. continue;
  488. }
  489. $found = false;
  490. foreach($defaultModuleRecords as $dRecord)
  491. {
  492. if($this->areRecordsEqual($cRecord, $dRecord))
  493. {
  494. $found = true;
  495. break;
  496. }
  497. }
  498. if($found === false)
  499. {
  500. $this->deleteRecord($cRecord);
  501. }
  502. }
  503. return true;
  504. }
  505. private function areRecordsEqual( dns\record\Record $rec1, dns\record\Record $rec2)
  506. {
  507. $recAname = rtrim($rec1->name,'.');
  508. $recBname = rtrim($rec2->name,'.');
  509. return $recAname === $recBname &&
  510. $rec1->type === $rec2->type &&
  511. $rec1->ttl === $rec2->ttl &&
  512. $rec1->rdata->toString() === $rec2->rdata->toString();
  513. }
  514. private function getZone()
  515. {
  516. /** @var AWSRoute53ResponseInterface $zones */
  517. $zones = $this->connection->listZonesByName($this->domain);
  518. if( $zones->getResponseType() === 'error' )
  519. {
  520. throw new DNSSubmoduleException($zones->getResponseMessage(), dns\SubmoduleExceptionCodes::COMMAND_ERROR);
  521. }
  522. $zone = AwsRouteHelpers\AWSRoute53ResponseParseHelper::findZoneOnZoneList($zones->getParsedResponseBody(), $this->domain);
  523. if( !$zone )
  524. {
  525. throw new DNSSubmoduleException('Zone name is not valid!', dns\SubmoduleExceptionCodes::COMMAND_ERROR);
  526. }
  527. return $zone;
  528. }
  529. public function buildRRSets( array $records ): array
  530. {
  531. $rrsets = [];
  532. /** @var dns\record\Record $record */
  533. foreach($records as $record)
  534. {
  535. if( ($index = $this->findMatchingRecordSet($rrsets, $record)) !== false )
  536. {
  537. $rrsets[$index]->pushRecord($record);
  538. }
  539. else
  540. {
  541. $rrsets[] = new dns\record\RRSet($record->name, $record->type, $record->ttl, [$record]);
  542. }
  543. }
  544. return $rrsets;
  545. }
  546. public function findRecordInRecordSets($rrsets,dns\record\Record $record)
  547. {
  548. /** @var dns\record\RRSet $rrset */
  549. foreach($rrsets as $index => $rrset)
  550. {
  551. if($rrset->recordExists($record))
  552. {
  553. return $index;
  554. }
  555. }
  556. return false;
  557. }
  558. private function findMatchingRecordSet( array $rrsets, dns\record\Record $record )
  559. {
  560. /** @var dns\record\RRSet $rrset */
  561. foreach($rrsets as $index => $rrset)
  562. {
  563. if($rrset->isRecordMatchingRRset($record))
  564. {
  565. return $index;
  566. }
  567. }
  568. return false;
  569. }
  570. private function resetConnection()
  571. {
  572. $this->connection = null;
  573. }
  574. private function explodeTxtRdata(&$record)
  575. {
  576. //AWS doesn't alllow TXT rdata record to have more than 255 chars length - need to split it to a few strings
  577. $rdataSplitted = str_split(trim($record->rdata->txtdata, '"'), 255);
  578. $record->rdata->txtdata = '';
  579. foreach ($rdataSplitted as $rdataPart) {
  580. $record->rdata->txtdata .= '"' . $rdataPart . '"';
  581. }
  582. }
  583. function prepareNameForSynchro($recordName, $domain)
  584. {
  585. $startsWithDot = substr($recordName, 0, 1) == '.';
  586. $endsWithDot = substr($recordName, -1) == '.';
  587. if ($startsWithDot)
  588. {
  589. $recordName = substr($recordName, 1);
  590. }
  591. if (!$endsWithDot)
  592. {
  593. $recordName = $recordName . (empty($recordName) ? '' : '.') . $domain . '.';
  594. }
  595. return IdnaHelper::idnaEncode($recordName);
  596. }
  597. }