<?php /** * The main class for sending and parsing server requests to the * GoDaddy?® TotalDNS management system. Eventually this class * could split into multiple classes representing the various * components such as the Service, Account, Zone, and Record(s). */ class GoDaddyDNS{ /** * Class variables */ private $_config; private $_curlHandle; private $_lastResponse; /** * Initialize the configuration array with configuration defaults. */ public function __construct($config = array()) { // Apply default configuration settings $this->_config = array_merge(array( 'username' =>'', 'password' =>'', 'domain' =>'', 'cookie_file' => tempnam(sys_get_temp_dir(), 'Curl'), 'auto_remove_cookie_file' => true, 'auto_logout' => true, 'godaddy_dns_zonefile_url' => 'https://dns.godaddy.com/ZoneFile.aspx?zoneType=0&sa=&zone=', 'godaddy_dns_zonefile_ws_url' => 'https://dns.godaddy.com/ZoneFile_WS.asmx' ), $config); $this->_authenticate($this->_config["username"],$this->_config["password"],$this->_config["domain"]); } /** * Destroy the curl handle and unlink the cookies file. */ public function __destruct() { if ($this->_config['auto_logout']) { $this->logout(); } if ($this->_curlHandle) { curl_close($this->_curlHandle); } if ($this->_config['auto_remove_cookie_file'] && file_exists($this->_config['cookie_file'])) { unlink($this->_config['cookie_file']); } } /** * Login to the user's account, returning an error if the credentials are * invalid or the login fails. */ private function _authenticate($username, $password,$domain) { $this->_lastResponse = $this->_fetchURL($this->_config['godaddy_dns_zonefile_url'].$domain); if (!$this->isLoggedIn($username)) { // User is not already logged in, build and submit a login request $postUrl = curl_getinfo($this->_curlHandle, CURLINFO_EFFECTIVE_URL); $post = array( 'Login$userEntryPanel2$LoginImageButton.x' => 0, 'Login$userEntryPanel2$LoginImageButton.y' => 0, 'Login$userEntryPanel2$UsernameTextBox' => $username, 'Login$userEntryPanel2$PasswordTextBox' => $password, '__EVENTARGUMENT' => $this->_getField('__EVENTARGUMENT'), '__EVENTTARGET' => $this->_getField('__EVENTTARGET'), '__VIEWSTATE' => $this->_getField('__VIEWSTATE'), ); $this->_lastResponse = $this->_fetchURL($postUrl, $post); if (!$this->isLoggedIn($username, $this->_lastResponse)) { // Invalid username/password or unknown response received return false; } } return true; } /** * Check to see if the expected user is logged in. */ public function isLoggedIn($username) { if (preg_match('#Welcome: <span id="ctl00_lblUser" .*?\>(.*)</span>#', $this->_lastResponse, $match)) { if (strtolower($match[1]) == strtolower($username) || $match[2] == $username) { return true; } else { // An unexpected user was logged in $this->logout(); } } return false; } /** * Log the user out. */ public function logout() { if (preg_match('#<a [^>]+href="(.*?)"[^>]*>Log Out</a>#', $this->_lastResponse, $match)) { $this->_lastResponse = $this->_fetchURL($match[1]); if (preg_match('#<img src="([^"]+)" height="1" width="1" />#', $this->_lastResponse, $match)) { $this->_lastResponse = $this->_fetchURL($match[1]); return true; } } return false; } /** * Add new record */ public function AddRecord($host,$type = 'A',$pointsTo,$ttl=3600){ $domain=$this->_config["domain"]; $next_record_id=$this->_nextRecordIndex(); switch (strtoupper($type)) { case 'A': $post = array( 'sInput' => '<PARAMS> <PARAM name="host" value="'.$host.'" /> <PARAM name="pointsTo" value="'.$pointsTo.'" /> <PARAM name="lstIndex" value="'.$next_record_id.'" /> <PARAM name="ttl" value="'.$ttl.'" /> </PARAMS>', ); $calloutResponse = $this->_fetchURL($this->_config['godaddy_dns_zonefile_ws_url'] . '/AddNewARecord', http_build_query($post, '', '&')); if (strpos($calloutResponse, 'SUCCESS') === false) { return false; } // Commit the updates $post = array( 'sInput' => '<PARAMS> <PARAM name="domainName" value="' . $domain . '" /> <PARAM name="zoneType" value="0" /> <PARAM name="aRecEditCount" value="1" /> <PARAM name="aRecEdit0Index" value="'.$next_record_id.'" /> <PARAM name="aRecDeleteCount" value="0" /> <PARAM name="cnameRecEditCount" value="0" /> <PARAM name="cnameRecDeleteCount" value="0" /> <PARAM name="mxRecEditCount" value="0" /> <PARAM name="mxRecDeleteCount" value="0" /> <PARAM name="txtRecEditCount" value="0" /> <PARAM name="txtRecDeleteCount" value="0" /> <PARAM name="srvRecEditCount" value="0" /> <PARAM name="srvRecDeleteCount" value="0" /> <PARAM name="aaaaRecEditCount" value="0" /> <PARAM name="aaaaRecDeleteCount" value="0" /> <PARAM name="soaRecEditCount" value="0" /> <PARAM name="soaRecDeleteCount" value="0" /> <PARAM name="nsRecEditCount" value="0" /> <PARAM name="nsRecDeleteCount" value="0" /> </PARAMS>', ); $calloutResponse = $this->_fetchURL($this->_config['godaddy_dns_zonefile_ws_url'] . '/SaveRecords', http_build_query($post, '', '&')); if (strpos($calloutResponse, 'SUCCESS') === false) { return false; } return true; default: // Other record types are currently unsupported throw new Exception('Unknown record type encountered: ' . $type); } } /** * Delete record */ public function deleteRecord($record){ $host=$record["host"]; $domain=$this->_config["domain"]; switch (strtoupper($record["type"])) { case 'A': $post = array( 'sInput' => $record['index'].'|true', ); $calloutResponse = $this->_fetchURL($this->_config['godaddy_dns_zonefile_ws_url'] . '/FlagARecForDeletion', http_build_query($post, '', '&')); if (strpos($calloutResponse, 'SUCCESS') === false) { return false; } // Commit the updates $post = array( 'sInput' => '<PARAMS> <PARAM name="domainName" value="' . $domain . '" /> <PARAM name="zoneType" value="0" /> <PARAM name="aRecEditCount" value="0" /> <PARAM name="aRecDeleteCount" value="1" /> <PARAM name="aRecDelete0Index" value="' . $record['index'] . '" /> <PARAM name="cnameRecEditCount" value="0" /> <PARAM name="cnameRecDeleteCount" value="0" /> <PARAM name="mxRecEditCount" value="0" /> <PARAM name="mxRecDeleteCount" value="0" /> <PARAM name="txtRecEditCount" value="0" /> <PARAM name="txtRecDeleteCount" value="0" /> <PARAM name="srvRecEditCount" value="0" /> <PARAM name="srvRecDeleteCount" value="0" /> <PARAM name="aaaaRecEditCount" value="0" /> <PARAM name="aaaaRecDeleteCount" value="0" /> <PARAM name="soaRecEditCount" value="0" /> <PARAM name="soaRecDeleteCount" value="0" /> <PARAM name="nsRecEditCount" value="0" /> <PARAM name="nsRecDeleteCount" value="0" /> </PARAMS>', ); $calloutResponse = $this->_fetchURL($this->_config['godaddy_dns_zonefile_ws_url'] . '/SaveRecords', http_build_query($post, '', '&')); if (strpos($calloutResponse, 'SUCCESS') === false) { return false; } return true; case 'CNAME': case 'MX': case 'TXT': case 'SRV': case 'AAAA': case 'NS': default: // Other record types are currently unsupported throw new Exception('Unknown record type encountered: ' . $type); } } /** * Find and return the details about a host record, return false if nothing is found. * * Note: The only type of records currently supported are "A" records. */ public function findRecords($host,$type = 'A') { $domain=strtolower($this->_config["domain"]); $currentZone = $this->_getField('ctl00$cphMain$hdnCurrentZone'); if (strtolower($currentZone) != strtolower($domain)) { // Request zone details if not already loaded - // could keep a separate cache of each zone's records in the future $this->_lastResponse = $this->_fetchUrl($this->_config['godaddy_dns_zonefile_url'] . $domain); } $records=array(); $offset=0; while(preg_match("#Undo{$type}Edit\('tbl{$type}Records_([0-9]+)?', '({$host})', '([^']+)?', '([^']+)?', '([^']+)?', '([^']+)?', '([^']+)?'\);#is", $this->_lastResponse, $match,0,$offset)) { array_push($records,array_combine(array('match', 'index', 'host', 'data', 'ttl', 'host_td', 'points_to', 'rec_modified','type'), array_merge($match,array($type)))); $offset=strpos($this->_lastResponse,$match[0],$offset)+strlen($match[0]); } return $records; } private function _nextRecordIndex($type = 'A'){ return preg_match_all("#Undo{$type}Edit\('tbl{$type}Records_([0-9]+)?', '([^']+)', '([^']+)?', '([^']+)?', '([^']+)?', '([^']+)?', '([^']+)?'\);#is", $this->_lastResponse, $match,0,$offset); } /** * Connect to the remote server using CURL. */ private function _fetchURL($url, $post = null, $referer = '', $agent = 'Mozilla/5.0 (compatible; PHP; cURL)', $language = 'en', $timeout = 30) { // Initialize CURL if (!$this->_curlHandle) { if (!function_exists('curl_init')) { die('CURL is not loaded or compiled into this version of PHP.'); } if (!is_writable($this->_config['cookie_file'])) { die('Cookie jar file is not writable: ' . $this->_config['cookie_file']); } $this->_curlHandle = curl_init(); curl_setopt_array($this->_curlHandle, array( CURLOPT_CONNECTTIMEOUT => $timeout, CURLOPT_TIMEOUT => $timeout, CURLOPT_HEADER => false, CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_AUTOREFERER => true, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_COOKIEJAR => $this->_config['cookie_file'], CURLOPT_COOKIEFILE => $this->_config['cookie_file'], )); } // Set the options curl_setopt($this->_curlHandle, CURLOPT_URL, $url); curl_setopt($this->_curlHandle, CURLOPT_REFERER, $referer); curl_setopt($this->_curlHandle, CURLOPT_USERAGENT, $agent); $extraHeaders = array( 'Accept-Language: ' . $language, ); curl_setopt($this->_curlHandle, CURLOPT_HTTPHEADER, $extraHeaders); if ($post) { curl_setopt($this->_curlHandle, CURLOPT_POST, true); curl_setopt($this->_curlHandle, CURLOPT_POSTFIELDS, $post); } else { curl_setopt($this->_curlHandle, CURLOPT_HTTPGET, true); } // Execute the request, returning the results return curl_exec($this->_curlHandle); } /** * Parse and return a named field's value from the last response. */ private function _getField($name) { if (preg_match_all('#<input[^>]+>#is', $this->_lastResponse, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $fieldHtml = $match[0]; if ($this->_getFieldAttribute('name', $fieldHtml) == $name) { return $this->_getFieldAttribute('value', $fieldHtml); } } } return false; } /** * Get the attribute from a field's html. */ private function _getFieldAttribute($attribute, $fieldHtml) { if (preg_match('#' . $attribute . '=["\']([^"\']+)?["\']#is', $fieldHtml, $match)) { return $match[1]; } return false; } } ?>
Usage: Add record
Usage: Delete record
<? $dns = new GoDaddyDNS(array( "username"=>'username', "password"=>'password', 'domain' =>'domain.com' )); $dns->AddRecord("@","A","123.123.123.123",3600); ?>
Usage: Delete record
<?php $dns = new GoDaddyDNS(array( "username"=>'username', "password"=>'password', 'domain' =>'domain.com' )); $records = $dns->findRecords("@"); foreach ($records as $record){ if ($record["data"]=="123.123.123.123"){ $dns->deleteRecord($record); } } $dns->deleteRecord($record); ?>
This is very good man, but only works up to the first 50 domains. This is how many you get per page.
ReplyDelete$next_record_id always returns 50, and domains beyond 50 are not being added...
Marcin
first 50 SUBdomains I meant, sorry.
DeleteHere's my fix. Bear in mind deleting records, if you have more than 50, may show the same issue. This fixes adding domains:
ReplyDeleteprivate function _nextRecordIndex($type = 'A') {
preg_match("#;n{$type}RecordCount=(\d+);#is", $this->_lastResponse, $match);
return intval($match[1]);
// return preg_match_all("#Undo{$type}Edit\('tbl{$type}Records_([0-9]+)?', '([^']+)', '([^']+)?', '([^']+)?', '([^']+)?', '([^']+)?', '([^']+)?'\);#is", $this->_lastResponse, $match,0,$offset);
}
Very Good Class. But Authenstication method returns false. Although my username and password are correct (the same I use to login to godaddy.com).
ReplyDeleteIs there something changed? How can I solve this.