array( 'friendlyName' => 'Email', 'validators' => array( 'required' => 'required', ) ), 'key' => array( 'friendlyName' => 'API Key', 'type' => 'password' ), 'token' => [ 'friendlyName' => 'API Token', 'type' => 'password' ], 'proxied' =>array ( 'friendlyName' => 'Enable Proxy Option For Records', 'type'=> 'yesno', 'help' => 'For A, AAAA, CNAME records only', ), ); public $availableTypes = ['A', 'AAAA', 'CAA', 'CNAME', 'DS', 'MX', 'NS', 'SRV', 'TXT']; protected $zoneid; protected $zoneObj; public function testConnection() { $info = $this->get( 'user', array() ); return $info->result ? true : false; } public function zoneExists() { try { $zone = $this->getZone( $this->domain ); } catch (exceptions\DNSSubmoduleException $e) { if ( $e->getCode() == dns\SubmoduleExceptionCodes::COMMAND_ERROR ) { return false; } throw $e; } return ($zone) ? true : false; } public function getNameServers($index = false) { $zone = $this->getZone($this->domain); return $zone->name_servers; } public function getRecords( $recordType = false ) { $out = $this->getAllRecords(); $return = []; foreach( $out as $item ) { $item->name = rtrim($item->name,'.').'.'; $type = strtoupper((string)$item->type); if( in_array($type, $recordType !== false ? [strtoupper($recordType)] : $this->getAvailableRecordTypes(),true) ) { switch( $type ) { case 'MX': $record = dns\record\Record::tryToCreateFromArray((array)$item); $record->line = (string)$item->id; $record->rdata->preference = $item->priority; $record->rdata->exchange = $item->content; $return[] = $record; break; case 'SRV': $record = dns\record\Record::tryToCreateFromArray((array)$item); $record->line = (string)$item->id; $record->rdata->port = $item->data->port; $record->rdata->weight = $item->data->weight; $record->rdata->priority = $item->data->priority; $record->rdata->target = $item->data->target; $record->name = $item->data->service . '.' . $item->data->proto . '.' . $item->data->name; $record->absoluteName = false; $return[] = $record; break; case 'DS': $record = dns\record\Record::tryToCreateFromArray((array)$item); $record->line = (string)$item->id; $rdata = new dns\record\type\DS(); list($rdata->keytag, $rdata->algorithm, $rdata->digesttype, $rdata->digest) = explode("\t", $item->content); $record->rdata = $rdata; $return[] = $record; break; default: $record = dns\record\Record::tryToCreateFromArray((array)$item); $record->line = (string)$item->id; $record->rdata->fromString((string)$item->content); $return[] = $record; } } } return $return; } public function processRecodParams( $record ) { $params = $this->proccessMainRecordParams( $record ); $method = 'process' . $record->type . 'RecordParams'; $params['proxied'] = $this->isProxyEnabled(); if ( method_exists( $this, $method ) ) { $this->$method( $record, $params ); } return $params; } public function proccessMainRecordParams( $record ) { $record->recordName = $record->name; return array( 'zone' => $this->domain, 'name' => $record->nameToAbsolute( $this->domain, false ), 'ttl' => (int)$record->ttl, 'type' => $record->type, 'priority' => (int)$record->rdata->priority, 'content' => $record->rdata->toString(), ); } public function processDSRecordParams( $record, &$params ) { /** @var dns\record\type\DS $rdata */ $rdata = $record->rdata; unset($params['content']); $params['data'] = [ 'key_tag'=>(int)$rdata->keytag, 'algorithm'=>(int)$rdata->algorithm, 'digest_type'=>(int)$rdata->digesttype, 'digest'=>$rdata->digest ]; } public function processNSRecordParams( $record, &$params ) { $params['proxied'] = false; //cannot be proxied } public function processTXTRecordParams( $record, &$params ) { $params['proxied'] = false; //cannot be proxied $params['content'] = trim($params['content'], '"'); } public function processMXRecordParams( $record, &$params ) { $params['content'] = $record->rdata->exchange; $params['priority'] = (int)$record->rdata->preference; $params['proxied'] = false; //cannot be proxied } public function processSRVRecordParams( $record, &$params ) { $params['name'] = $record->recordName; $elemnts = $this->getSRVElements($record); $params['data'] = array( 'name' => $elemnts->name, // . $chunks[3], 'priority' => (int)$record->rdata->priority, 'weight' => (int)$record->rdata->weight, 'port' => (int)$record->rdata->port, 'target' => $record->rdata->target, 'service' => $elemnts->service, 'proto' => $elemnts->proto ); $params['proxied'] = false; //cannot be proxied } private function getSRVElements( $record ) { $chunks = explode( '.', $record->recordName ); $elemnts = new \stdClass(); $elemnts->service = $chunks[0]; $elemnts->proto = $chunks[1]; unset( $chunks['0'], $chunks[1] ); $elemnts->name = (empty( $chunks )) ? $this->domain : implode( '.', $chunks ); return $elemnts; } public function addRecord( dns\record\Record $record ) { $params = $this->processRecodParams( $record ); $this->get( $this->getZoneUrl( $this->getZoneID(), array('dns_records') ), $this->setPOSTparams( $params ), 'POST'); sleep( 10 ); } public function editRecord( dns\record\Record $record ) { $recordid = $record->line; $params = $this->processRecodParams( $record ); $this->get( $this->getZoneUrl( $this->getZoneID(), array('dns_records', $recordid) ), $this->setPOSTparams( $params ), 'PUT' ); sleep( 20 ); } public function deleteRecord( dns\record\Record $record ) { $this->get( $this->getZoneUrl( $this->getZoneID(), array('dns_records', $record->line) ), array(), 'DELETE' ); sleep( 10 ); } public function activateZone() { $this->get( 'zones', $this->setPOSTparams( array('name' => $this->domain, 'jump_start' => false) ), 'POST' ); //Wait 10 seconds for cloudflare sleep( 10 ); } public function terminateZone() { $this->get( $this->getZoneUrl( $this->getZoneID() ), array(), 'DELETE' ); } public function getZones() { $out = $this->getAllZones(); $return = array(); foreach ($out as $item) { $return[(string) $item->name] = (string) $item->name; } return $return; } public function getZone( $zone ) { try { if ( !empty( $this->zoneObj[$zone] ) ) { return $this->zoneObj[$zone]; } $out = $this->get( 'zones', $this->setGETParams( array('name' => $zone) ) ); $this->zoneObj[$zone] = $out->result[0]; } catch (Exception $ex) { } return $this->zoneObj[$zone]; } public function getZoneID() { if ( $this->zoneid ) { return $this->zoneid; } $this->zoneid = $this->getZone( $this->domain )->id; return $this->zoneid; } protected function setGETParams( $params ) { return array('GET' => $params); } protected function setPOSTparams( $params ) { return array('POST' => $params); } protected function getZoneUrl( $zoneid, $additional = array() ) { if ( !empty( $additional ) ) { $addurl = ''; foreach ($additional as $value) { $addurl .= '/' . $value; } } return 'zones/' . $zoneid . $addurl; } public function processURL( $url, $get = array() ) { if ( !empty( $get ) ) { $additional = '?' . http_build_query( $get ); } return $this->hostname . $url . $additional; } private function get( $function, $params = array(), $type = 'GET', $debug = false ) { $query = $this->processURL( $function, $params['GET'] ); $curl = curl_init(); curl_setopt( $curl, CURLOPT_URL, $query ); curl_setopt( $curl, CURLOPT_SSL_VERIFYPEER, 0 ); curl_setopt( $curl, CURLOPT_SSL_VERIFYHOST, 0 ); curl_setopt( $curl, CURLOPT_HEADER, 0 ); curl_setopt( $curl, CURLOPT_TIMEOUT, 999 ); curl_setopt( $curl, CURLOPT_RETURNTRANSFER, 1 ); if ( $type ) { curl_setopt( $curl, CURLOPT_CUSTOMREQUEST, $type ); } if( trim($this->config['token']) ) { $auth = [ 'Authorization: Bearer ' . $this->config['token'], 'Content-Type: application/json' ]; } else { $auth = [ 'X-Auth-Email: ' . $this->config['email'], 'X-Auth-Key: ' . $this->config['key'], 'Content-Type: application/json' ]; } curl_setopt( $curl, CURLOPT_HTTPHEADER, $auth ); $post = (empty( $params['POST'] )) ? array() : $params['POST']; curl_setopt( $curl, CURLOPT_POST, 1 ); curl_setopt( $curl, CURLOPT_POSTFIELDS, json_encode( $post ) ); $result = curl_exec( $curl ); if ( $debug ) { echo("
" . print_r( (__LINE__ . ': ' ), true ) . print_r( $query, true ) . "
"); echo("
" . print_r( (__LINE__ . ': ' ), true ) . print_r( $params, true ) . "
"); die( "
" . print_r( __FILE__, true ) . print_r( __LINE__, true ) . "
" . print_r( $result, true ) . "
" ); } $out = $this->checkForErrors($curl, $result); curl_close( $curl ); return $out; } private function checkForErrors( $curl, $result ) { if ( curl_errno( $curl ) ) { throw new exceptions\DNSSubmoduleException( "cURL Error: " . curl_errno( $curl ) . " - " . curl_error( $curl ), dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM ); } if ( !$result ) { throw new exceptions\DNSSubmoduleException( 'Unable to retrieve response', dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM ); } $obj = json_decode( $result ); if ( empty( $obj ) ) { throw new exceptions\DNSSubmoduleException( 'Unable to parse response', dns\SubmoduleExceptionCodes::INVALID_RESPONSE ); } if ( !$obj->success ) { //$error = (isset( $obj->errors ) && !empty( $obj->errors )) ? (is_array( $obj->errors )) ? $obj->errors[0]->message : $obj->errors->message : // $obj->message; if(!empty($obj->error)) { $error = $obj->error; } elseif(isset( $obj->errors ) && !empty( $obj->errors )) { if(is_array( $obj->errors )) { $error = $obj->errors[0]->message.(is_array($obj->errors[0]->error_chain) ? ': '.$obj->errors[0]->error_chain[0]->message : ''); } else { $error = $obj->errors->message; } } else { $obj->message; } throw new exceptions\DNSSubmoduleException( $error, dns\SubmoduleExceptionCodes::COMMAND_ERROR ); } return $obj; } private function isProxyEnabled() { return $this->config['proxied'] === 'on'; } /** * Get sign keys */ public function getSignKeys() { $dnssec = new dns\dnssec\DnsSec(); $zoneid = $this->getZoneID(); $result = json_decode($this->get('zones/' . $zoneid . '/dnssec')); foreach ( $result->result as $record ) { $ds = new dns\record\type\DS(); $ds->setKeytag($record->key_tag); $ds->setAlgorithm($record->algorithm); $ds->setDigestType($record->digest_type); $ds->setDigest($record->digest); $dnssec->addDs($ds); //CSK $dsSplitted = explode(' ', $record->ds); $dnskey = new dns\record\type\DNSKEY(); $dnskey->setFlags($record->flags); $dnskey->setProtocol($dsSplitted[2]); $dnskey->setAlgorithm($record->algorithm); $dnskey->setPublicKey($record->public_key); $csk = new dns\dnssec\CSK(); $csk->setId($dsSplitted[4]); // $csk->setBits(); $csk->setDnsKey($dnskey); $csk->setLifetime($dsSplitted[1]); $dnssec->addKey($csk); } return $dnssec; } /** * Sign DNS zone */ public function sign() { $this->changeStatus(true); } public function changeStatus( $status = true ) { $status = $status ? 'active' : 'disabled'; $zoneid = $this->getZoneID(); $params['POST'] = [ 'status' => $status ]; $this->get('zones/' . $zoneid . '/dnssec', $params, 'PATCH'); } /** * Unsign DNS zone */ public function unsign() { $this->changeStatus(false); } /** * Rectify DNS zone */ public function rectify() { // TODO: Implement rectify() method. } /** * */ public function isSigned() { try { $zoneid = $this->getZoneID(); $response = json_decode($this->get('zones/' . $zoneid . '/dnssec')); if ( isset($response->result) ) { $status = ($response->result->status == 'active' ? true : false); } else { throw new Exception('There was problem with getting response from cloudflare'); } return $status; } catch (\Exception $e) { return false; } } private function getAllZones() { $page = 1; $out = []; while( $zones = $this->get('zones', $this->setGETParams(['per_page' => 100, 'page' => $page]))->result ) { $out = array_merge($out, $zones); $page++; } return $out; } private function getAllRecords() { $page = 1; $out = []; while( $records = $this->get($this->getZoneUrl($this->getZoneID(), ['dns_records']), ['GET' => ['per_page' => 100, 'page' => $page]])->result ) { $out = array_merge($out, $records); $page++; } return $out; } }