array ( 'friendlyName' => 'Username', 'validators' => array( 'required' => 'required', ) ), 'password' =>array ( 'friendlyName' => 'User Password', 'type'=> 'password', ), 'resellerAccount' =>array ( 'friendlyName' => 'Reseller Account', 'type'=> 'yesno', ), 'hash' =>array ( 'friendlyName' => 'Access Key', 'type'=> 'textarea', ), 'hostname' =>array ( 'friendlyName' => 'Hostname/IP', 'validators' => array( 'required' => 'required', ) ), 'ssl' =>array ( 'friendlyName' => 'Enable SSL', 'type'=> 'yesno', ), 'timeout' =>array ( 'friendlyName' => 'Timeout', 'help' => 'Set timeout in second for the API connection. If value is empty, the default value is 30', ), 'default_ip' =>array ( 'friendlyName' => 'Default IP', 'validators' => array( 'required' => 'required', 'pattern' => Patterns::IP4_OR_IP6, ) ), 'allow_create' =>array ( 'friendlyName' => 'In cPanel Zones', 'type' => 'yesno', 'help' => 'Allow to create zones when already in CPanel as account', ), 'template' =>array ( 'friendlyName' => 'Template', 'help' => 'Default zone template from your cPanel' ), ); public $availableTypes = array('A', 'AAAA','CAA', 'NS', 'MX', 'CNAME', 'DNAME', 'LOC', 'TXT', 'SRV', 'PTR', 'AFSDB', 'NAPTR', 'RP', 'SOA'); protected $uapi; public function testConnection() { try { $this->get('get_nameserver_config'); return true; } catch( exceptions\DNSSubmoduleException $e ) { $this->get('listzones', ['api.chunk.enable' => 1, 'api.chunk.size' => 1]); return true; } } public function getRecords($recordType = false) { $out = $this->get('dumpzone', array( 'domain' => $this->domain )); $return = array(); if(!isset($out->zone[0]->record)){ throw new exceptions\DNSSubmoduleException('Unable to fetch records from server', dns\SubmoduleExceptionCodes::COMMAND_ERROR); } foreach($out->zone[0]->record as $r) { $recordAdapter = '\MGModule\DNSManager2\mgLibs\custom\dns\submodules\cPanel\\'.$r->type.'Adapter'; if (class_exists($recordAdapter)) { $return[] = $recordAdapter::toDnsManagerRecord((array)$r); } elseif ( in_array((string)$r->type, $recordType !== false ? [strtoupper($recordType)] : $this->getAvailableRecordTypes()) ) { $return[] = dns\record\Record::tryToCreateFromArray((array)$r); } } return $return; } public function addRecord(dns\record\Record $record) { $adapterClassName = '\MGModule\DNSManager2\mgLibs\custom\dns\submodules\cPanel\\'.$record->type.'Adapter'; if(class_exists($adapterClassName)) { /** @var dns\record\Record $record */ $record = new $adapterClassName($record); } $input = $record->toMergedArray(false); $input['domain'] = $this->domain; $input['name'] = IdnaHelper::idnaEncode($record->nameToAbsolute($this->domain)); $this->logAction('addRecord', $input, $record); $this->get('addzonerecord', $input); return true; } public function editRecord(dns\record\Record $record) { $records = $this->getRecords(); if(empty($records)) return false; $lines = array(); foreach($records as $r) { $lines[$r['line']] = $r; } if(!isset($lines[$record['line']]) || !$record->isEqualTo($lines[$record['line']])) { $adapterClassName = '\MGModule\DNSManager2\mgLibs\custom\dns\submodules\cPanel\\'.$record->type.'Adapter'; if(class_exists($adapterClassName)) { /** @var dns\record\Record $record */ $record = new $adapterClassName($record); } $input = $record->toMergedArray(false); $input['domain'] = $this->domain; $input['name'] = IdnaHelper::idnaEncode($record->nameToAbsolute($this->domain)); $this->get('editzonerecord', $input); } } public function deleteRecord(dns\record\Record $record) { $this->get('removezonerecord', array( 'zone' => $this->domain, 'line' => $record['line'] )); } public function zoneExists() { try { $this->get('dumpzone', ['domain' => $this->domain]); } catch( exceptions\DNSSubmoduleException $e ) { if( $e->getCode() == dns\SubmoduleExceptionCodes::COMMAND_ERROR ) { return false; } throw $e; } return true; } public function activateZone() { if($this->ip != '') { if(!filter_var($this->ip, FILTER_VALIDATE_IP)) { throw new exceptions\DNSSubmoduleException('IP is not valid!', dns\SubmoduleExceptionCodes::INVALID_PARAMETERS); } } else { $this->ip = $this->config['default_ip']; } $params = array( 'domain' => $this->domain, 'ip' => $this->ip ); if($this->config['resellerAccount'] == "on"){ $params['trueowner'] = $this->config['username']; } if($this->config['template'] != "") $params['template'] = $this->config['template']; try { $this->get('adddns', $params); } catch (exceptions\DNSSubmoduleException $e) { if(preg_match('/is owned by another user/', $e->getMessage()) && $this->config['allow_create'] == 'on') { $res = $this->get('listaccts', array('want' => 'user', 'searchtype' => 'domain', 'search' => $this->domain, 'searchmethod' => 'exact')); $user = $res->acct[0]->user; $params['trueowner'] = $user; $this->get('adddns', $params); return ; } throw $e; } } public function terminateZone() { $this->get('killdns', array( 'domain' => $this->domain )); } public function getZones() { $out = array(); $res = $this->get('listzones', array()); foreach($res->zone as $zone) { $out[(string)$zone->domain] = ''; } //TODO: tu skonczyłem - dokończyc whm1 try { $res = $this->get('listaccts', array('want' => 'domain,ip')); foreach ($res->acct as $zone) { if(isset($out[(string)$zone->domain])) { $out[(string)$zone->domain] = (string)$zone->ip; } } } catch(exceptions\DNSSubmoduleException $exc) { } return $out; } private function get($function, $params=array()) { $params['api.version'] = 1; $url = ($this->config['ssl']? 'https://'.$this->config['hostname'].':2087' : 'http://'.$this->config['hostname'].':2086').'/json-api/'.$function; $ch = curl_init(); if(is_array($params)) { $url .= '?'; foreach($params as $key=>$value) { $value = urlencode($value); $url .= "{$key}={$value}&"; } } $url = trim($url, '&'); $this->log('REQUEST: ' . $url); $chOptions = array ( CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_TIMEOUT => is_numeric($this->config['timeout']) ? intval($this->config['timeout']) : 30 ); if($this->config['hash'] != '') { $header[0] = 'Authorization: WHM '.$this->config['username'].':'.str_replace(array("\r", "\n"),"", $this->config['hash']); $chOptions[CURLOPT_HTTPHEADER] = $header; } elseif($this->config['password'] != '') { $chOptions[CURLOPT_USERPWD] = $this->config['username'].':'.$this->config['password']; } else { throw new exceptions\DNSSubmoduleException('Password or Access key is required', dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM); } curl_setopt_array($ch, $chOptions); $out = curl_exec($ch); $this->log('RESPONSE: ' . $out); if (curl_errno($ch)) { throw new exceptions\DNSSubmoduleException("cURL Error: " . curl_errno($ch) . " - " . curl_error($ch), dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM); } $out_info = curl_getinfo($ch); if($out_info['http_code'] != 200) { if($out_info['http_code'] == 301 || $out_info['http_code'] == 302){ throw new exceptions\DNSSubmoduleException('Module require SSL', dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM); } } curl_close($ch); if(strpos($out, 'SSL encryption is required for access to this server') !== FALSE) { throw new exceptions\DNSSubmoduleException('SSL encryption is required for access to this server', dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM); } $a = json_decode($out); if($a===FALSE) { throw new exceptions\DNSSubmoduleException('Unable to parse response', dns\SubmoduleExceptionCodes::INVALID_RESPONSE); } if ($a->cpanelresult->error && $a->cpanelresult->data->result == 0) { throw new exceptions\DNSSubmoduleException($a->cpanelresult->error, dns\SubmoduleExceptionCodes::COMMAND_ERROR); } if(isset($a->status) && $a->status == 0){ throw new exceptions\DNSSubmoduleException($a->statusmsg?:'Unknown Error', dns\SubmoduleExceptionCodes::COMMAND_ERROR); } if(isset($a->data->result) && $a->data->result == 0) { throw new exceptions\DNSSubmoduleException($a->data->reason?:'Unknown Error', dns\SubmoduleExceptionCodes::COMMAND_ERROR); } if(!empty($a->error)) { throw new exceptions\DNSSubmoduleException($a->error, dns\SubmoduleExceptionCodes::COMMAND_ERROR); } if(isset($a->metadata->result) && $a->metadata->result == 0) { throw new exceptions\DNSSubmoduleException($a->metadata->reason, dns\SubmoduleExceptionCodes::COMMAND_ERROR); } // if(!isset($a->data)) { // throw new exceptions\DNSSubmoduleException("There is no data in output", dns\SubmoduleExceptionCodes::INVALID_RESPONSE); // } return isset($a->data)?$a->data:$a; } private function logAction($action, $request, $response) { $addonConfig = \MGModule\DNSManager2\addon::config(); logmodulecall( $addonConfig['name'], 'cPanel '.($action), array($request), array($response), null, array($this->config['hostname'], $this->config['hostname'], $this->config['hash']) ); } public function getSignKeys(){ $fetchDSRecords = $this->get('fetch_ds_records_for_domains', array('domain' => $this->domain)); $dnssec = new \MGModule\DNSManager2\mgLibs\custom\dns\dnssec\DnsSec(); foreach($fetchDSRecords->domains[0]->ds_records->keys as $dsKey => $dsRecord){ foreach($dsRecord->digests as $diggestKey => $diggestRecord){ $ds = new dns\record\type\DS(); $ds->setKeytag($dsRecord->key_tag); $ds->setAlgorithm($dsRecord->algo_num); $ds->setDigestType($diggestRecord->algo_num); $ds->setDigest($diggestRecord->digest); $dnssec->addDs($ds); } switch (strtolower($dsRecord->key_type)){ case 'csk': $dnskey = new dns\record\type\DNSKEY(); $dnskey->setFlags($dsRecord->flags); $dnskey->setProtocol(3); $dnskey->setAlgorithm($dsRecord->algo_num); $zoneKey = new dns\dnssec\CSK(); $zoneKey->setId($dsKey); $zoneKey->setBits($dsRecord->bits); $zoneKey->setDnsKey($dnskey); $dnssec->addKey($zoneKey); break; case 'ksk': $dnskey = new dns\record\type\DNSKEY(); $dnskey->setFlags($dsRecord->flags); $dnskey->setProtocol(3); $dnskey->setAlgorithm($dsRecord->algo_num); $ksk = new dns\dnssec\KSK(); $ksk->setId($dsKey); $ksk->setBits($dsRecord->bits); $ksk->setDnsKey($dnskey); $dnssec->addKey($ksk); break; case 'zsk': $dnskey = new dns\record\type\DNSKEY(); $dnskey->setFlags($dsRecord->flags); $dnskey->setProtocol(3); $dnskey->setAlgorithm($dsRecord->algo_num); $zsk = new dns\dnssec\ZSK(); $zsk->setId($dsKey); $zsk->setBits($dsRecord->bits); $zsk->setDnsKey($dnskey); $dnssec->addKey($zsk); break; } } return $dnssec; } public function sign(){ $this->get('enable_dnssec_for_domains', array('domain' => $this->domain)); } public function unsign(){ $this->get('disable_dnssec_for_domains', array('domain' => $this->domain)); } public function rectify(){ return true; } public function isSigned(){ $fetchDSRecords = $this->get('fetch_ds_records_for_domains', array('domain' => $this->domain)); $checkDsRecords = $fetchDSRecords->domains[0]->ds_records; if(empty(get_object_vars($checkDsRecords))) return false; return true; } /* No need to use it anymore. It is not deleted to have it in future implementations - just in case */ /* private function uAPI() { $res = $this->get('listaccts', array('want' => 'user', 'searchtype' => 'domain', 'search' => $this->domain, 'searchmethod' => 'exact')); $user = $res->acct[0]->user; if (!is_null($this->uapi)) { return $this->uapi; } $this->uapi = new cPanelUapi\Uapi(); $this->uapi->setLogin($this->config, $user) ->createSession(); return $this->uapi; } */ }