├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── config.dist.php ├── functions.php └── update.php /.gitignore: -------------------------------------------------------------------------------- 1 | config.php 2 | .php-cs-fixer.cache 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing guidelines 2 | Thank you for your interest in supporting this project. I highly appreciate that you consider contributing to it. 3 | 4 | ## Pull requests 5 | Unfortunately, I usually cannot find the time to review and merge Pull requests. For this reason, I have released these contributing guidelines, which basically state exactly this. I just want to be upfront about this, because I know that many contributors invest a lot of time into a pull request and that it can be frustrating, if it does not get merged. However, I will make sure to fix urgent issues and add new features that are wanted and needed every now and then. Feel free to fork this script and adjust it to your own needs 🙂 6 | 7 | ## Issues 8 | If you notice an issue, want to give feedback, or want to request a new feature, make sure to open an Issue here. I will regularly look at them and fix urgent ones as soon as possible. Other users of the script might also be able to help you. 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Lars-Sören Steck 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dynamic DNS client for netcup DNS API 2 | *This project is not affiliated with the company netcup GmbH. Although it is developed by an employee, it is not an official client by netcup GmbH and was developed in my free time.* 3 | *netcup is a registered trademark of netcup GmbH, Karlsruhe, Germany.* 4 | 5 | **A simple dynamic DNS client written in PHP for use with the netcup DNS API.** 6 | 7 | ## Requirements 8 | * Be a netcup customer: https://www.netcup.de – or for international customers: https://www.netcup.eu 9 | * You don't have to be a domain reseller to use the necessary functions for this client – every customer with a domain may use it. 10 | * netcup API key and API password, which can be created within your CCP at https://www.customercontrolpanel.de 11 | * PHP-CLI with CURL extension 12 | * A domain :wink: 13 | 14 | ## Features 15 | ### Implemented 16 | * All necessary API functions for DNS actions implemented (REST API) 17 | * Determines correct public IP address, uses fallback API for determining the IP address, in case main API does return invalid / no IP 18 | * Automatically retries API requests on errors 19 | * IPv4 and IPv6 Support (can be individually enabled / disabled) 20 | * Possible to manually provide IPv4 / IPv6 address to set as a CLI option 21 | * Update everything you want in one go: Every combination of domains, subdomains, domain root, and domain wildcard is possible 22 | * Creation of DNS record, if it doesn't already exist 23 | * If configured, lowers TTL to 300 seconds for the domain on each run, if necessary 24 | * Hiding output (quiet option) 25 | 26 | ### Missing 27 | * Caching the IP provided to netcup DNS, to avoid running into (currently extremely tolerant) rate limits in the DNS API 28 | * Probably a lot more :grin: – to be continued... 29 | 30 | ## Getting started 31 | ### Download 32 | Download the [latest version](https://github.com/stecklars/dynamic-dns-netcup-api/releases/latest) from the releases or clone the repository: 33 | 34 | `$ git clone https://github.com/stecklars/dynamic-dns-netcup-api.git` 35 | 36 | I'm always trying to keep the master branch stable. 37 | 38 | Then, allow `update.php` to be executed by your user: 39 | 40 | `chmod u+x update.php` 41 | 42 | ### Configuration 43 | Configuration is very simple: 44 | * Copy `config.dist.php` to `config.php` 45 | * `cp config.dist.php config.php` 46 | * Fill out `config.php` with the required values. The options are explained in there. 47 | 48 | ### How to use 49 | `./update.php` 50 | 51 | You should probably run this script every few minutes, so that your IP is updated as quickly as possible. Add it to your cronjobs and run it regularly, for example every five minutes. 52 | 53 | ### CLI options 54 | Just add these Options after the command like `./update.php --quiet` 55 | 56 | | short option | long option | function | 57 | | ------------ | ------------------ |----------------------------------------------------------:| 58 | | -q | --quiet | The script won't output notices, only errors and warnings | 59 | | -c | --config | Manually provide a path to the config file | 60 | | -4 | --ipv4 | Manually provide the IPv4 address to set | 61 | | -6 | --ipv6 | Manually provide the IPv6 address to set | 62 | | -h | --help | Outputs this help | 63 | | -v | --version | Outputs the current version of the script | 64 | 65 | If you have ideas on how to improve this script, please don't hesitate to create an issue. Thank you! 66 | -------------------------------------------------------------------------------- /config.dist.php: -------------------------------------------------------------------------------- 1 | 1, 84 | CURLOPT_USERAGENT => USERAGENT, 85 | CURLOPT_TIMEOUT => 30, 86 | CURLOPT_RETURNTRANSFER => 1, 87 | CURLOPT_FAILONERROR => 1, 88 | CURLOPT_HTTPHEADER => array('Content-Type: application/json'), 89 | CURLOPT_POSTFIELDS => $request, 90 | ); 91 | curl_setopt_array($ch, $curlOptions); 92 | return $ch; 93 | } 94 | 95 | // Create cURL handler for get requests (for getting the current public IP) 96 | function initializeCurlHandlerGetIP($url) 97 | { 98 | $ch = curl_init($url); 99 | $curlOptions = array( 100 | CURLOPT_USERAGENT => USERAGENT, 101 | CURLOPT_TIMEOUT => 30, 102 | CURLOPT_RETURNTRANSFER => 1, 103 | CURLOPT_FAILONERROR => 1 104 | ); 105 | curl_setopt_array($ch, $curlOptions); 106 | return $ch; 107 | } 108 | 109 | // Check if curl request was successful 110 | function wasCurlSuccessful($ch) 111 | { 112 | if (curl_errno($ch)) { 113 | return false; 114 | } 115 | return true; 116 | } 117 | 118 | // Retrys a curl request for the specified amount of retries after a failure 119 | function retryCurlRequest($ch, $tryCount, $tryLimit) 120 | { 121 | $accessed_url = curl_getinfo($ch)['url']; 122 | 123 | if (curl_errno($ch)) { 124 | $curl_errno = curl_errno($ch); 125 | $curl_error_msg = curl_error($ch); 126 | } 127 | 128 | if (curl_errno($ch)) { 129 | if ($tryCount === 1) { 130 | outputWarning("cURL Error while accessing $accessed_url: ($curl_errno) $curl_error_msg - Retrying in 30 seconds. (Try $tryCount / $tryLimit)"); 131 | } 132 | } else { 133 | outputWarning("API at $accessed_url returned invalid answer. Retrying in 30 seconds. (Try $tryCount / $tryLimit)"); 134 | } 135 | sleep(30); 136 | outputWarning("Retrying now."); 137 | $result = curl_exec($ch); 138 | if (curl_errno($ch)) { 139 | $curl_errno = curl_errno($ch); 140 | $curl_error_msg = curl_error($ch); 141 | $tryCount++; 142 | if (curl_errno($ch)) { 143 | outputWarning("cURL Error while accessing $accessed_url: ($curl_errno) $curl_error_msg - Retrying in 30 seconds. (Try $tryCount / $tryLimit)"); 144 | } 145 | return false; 146 | } else { 147 | unset($curl_errno); 148 | unset($curl_error_msg); 149 | return $result; 150 | } 151 | } 152 | 153 | // Sends $request to netcup Domain API and returns the result 154 | function sendRequest($request, $apiSessionRetry = false) 155 | { 156 | 157 | $ch = initializeCurlHandlerPostNetcupAPI($request); 158 | $result = curl_exec($ch); 159 | 160 | if (!wasCurlSuccessful($ch)) { 161 | $retryCount = 1; 162 | $retryLimit = 3; 163 | while (!$result && $retryCount < $retryLimit) { 164 | $result = retryCurlRequest($ch, $retryCount, $retryLimit); 165 | $retryCount++; 166 | } 167 | } 168 | 169 | if ($result === false) { 170 | outputStderr("Max retries reached ($retryCount / $retryLimit). Exiting due to cURL network error."); 171 | exit(1); 172 | } 173 | 174 | $result = json_decode($result, true); 175 | 176 | // Due to a bug in the netcup CCP DNS API, sometimes sessions expire too early (statuscode 4001, error message: "The session id is not in a valid format.") 177 | // We work around this bug by trying to login again once. 178 | // See Github issue #21. 179 | if ($result['statuscode'] === 4001 && $apiSessionRetry === false) { 180 | outputWarning("Received API error 4001: The session id is not in a valid format. Most likely the session expired. Logging in again and retrying once."); 181 | $newApisessionid = login(CUSTOMERNR, APIKEY, APIPASSWORD); 182 | 183 | global $apisessionid; 184 | $apisessionid = $newApisessionid; 185 | 186 | $request = json_decode($request, true); 187 | $request['param']['apisessionid'] = $newApisessionid; 188 | $request = json_encode($request); 189 | 190 | return sendRequest($request, true); 191 | } 192 | 193 | // If everything seems to be ok, proceed... 194 | curl_close($ch); 195 | unset($ch); 196 | 197 | return $result; 198 | } 199 | 200 | //Outputs $text to Stdout 201 | function outputStdout($message) 202 | { 203 | global $quiet; 204 | 205 | //If quiet option is set, don't output anything on stdout 206 | if ($quiet === true) { 207 | return; 208 | } 209 | 210 | $date = date("Y/m/d H:i:s O"); 211 | $output = sprintf("[%s][NOTICE] %s\n", $date, $message); 212 | echo $output; 213 | } 214 | 215 | //Outputs warning to stderr 216 | function outputWarning($message) 217 | { 218 | global $quiet; 219 | 220 | //If quiet option is set, don't output anything on stderr 221 | if ($quiet === true) { 222 | return; 223 | } 224 | 225 | $date = date("Y/m/d H:i:s O"); 226 | $output = sprintf("[%s][WARNING] %s\n", $date, $message); 227 | 228 | fwrite(STDERR, $output); 229 | } 230 | 231 | //Outputs error to Stderr 232 | function outputStderr($message) 233 | { 234 | $date = date("Y/m/d H:i:s O"); 235 | $output = sprintf("[%s][ERROR] %s\n", $date, $message); 236 | 237 | fwrite(STDERR, $output); 238 | } 239 | 240 | //Returns list of domains with their subdomains for which we are supposed to perform changes 241 | function getDomains() 242 | { 243 | if (! defined('DOMAINLIST')) { 244 | outputWarning("You are using an outdated configuration format (for configuring domain / host). This is deprecated and might become incompatible very soon. Please update to the new configuration format (using 'DOMAINLIST'). Please check the documentation in config.dist.php for more information."); 245 | if (! defined('DOMAIN')) { 246 | outputStderr("Your configuration file is incorrect. You did not configure any domains ('DOMAINLIST' or 'DOMAIN' option (deprecated) in the config). Please check the documentation in config.dist.php. Exiting."); 247 | exit(1); 248 | } 249 | if (! defined('HOST')) { 250 | outputStderr("Your configuration file is incorrect. You did not configure any hosts (subdomains; 'HOST' option in the config). Please check the documentation in config.dist.php. Exiting."); 251 | exit(1); 252 | } 253 | return array(DOMAIN => array(HOST)); 254 | } 255 | 256 | $domains = preg_replace('/\s+/', '', DOMAINLIST); 257 | 258 | $domainsExploded = explode(';', $domains); 259 | foreach ($domainsExploded as $element) { 260 | $arr = explode(':', $element); 261 | $domainlist[$arr[0]] = $arr[1]; 262 | } 263 | 264 | foreach ($domainlist as $domain => $subdomainlist) { 265 | $subdomainarray = explode(',', $subdomainlist); 266 | $result[$domain] = $subdomainarray; 267 | } 268 | 269 | return $result; 270 | } 271 | 272 | function isIPV4Valid($ipv4) 273 | { 274 | if (filter_var($ipv4, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { 275 | return true; 276 | } else { 277 | return false; 278 | } 279 | } 280 | 281 | function isIPV6Valid($ipv6) 282 | { 283 | if (filter_var($ipv6, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { 284 | return true; 285 | } else { 286 | return false; 287 | } 288 | } 289 | 290 | //Returns current public IPv4 address. 291 | function getCurrentPublicIPv4() 292 | { 293 | // If user provided an IPv4 address manually as a CLI option 294 | global $providedIPv4; 295 | if (isset($providedIPv4)) { 296 | outputStdout(sprintf('Using manually provided IPv4 address "%s"', $providedIPv4)); 297 | return $providedIPv4; 298 | } 299 | 300 | outputStdout('Getting IPv4 address from ' . IPV4_ADDRESS_URL . '.'); 301 | 302 | $url = IPV4_ADDRESS_URL; 303 | $ch = initializeCurlHandlerGetIP($url); 304 | $publicIP = trim(curl_exec($ch)); 305 | 306 | if (!wasCurlSuccessful($ch) || !isIPV4Valid($publicIP)) { 307 | $retryCount = 1; 308 | $retryLimit = 3; 309 | while ((!$publicIP || !isIPV4Valid($publicIP)) && $retryCount < $retryLimit) { 310 | $publicIP = trim(retryCurlRequest($ch, $retryCount, $retryLimit)); 311 | $retryCount++; 312 | } 313 | 314 | if (!isIPV4Valid($publicIP) || $publicIP === false) { 315 | outputWarning(IPV4_ADDRESS_URL . " didn't return a valid IPv4 address (Try $retryCount / $retryLimit). Trying fallback " . IPV4_ADDRESS_URL_FALLBACK); 316 | $url = IPV4_ADDRESS_URL_FALLBACK; 317 | $ch = initializeCurlHandlerGetIP($url); 318 | $publicIP = trim(curl_exec($ch)); 319 | if (!wasCurlSuccessful($ch) || !isIPV4Valid($publicIP)) { 320 | $retryCount = 1; 321 | $retryLimit = 3; 322 | while ((!$publicIP || !isIPV4Valid($publicIP)) && $retryCount < $retryLimit) { 323 | $publicIP = trim(retryCurlRequest($ch, $retryCount, $retryLimit)); 324 | $retryCount++; 325 | } 326 | if (!isIPV4Valid($publicIP) || $publicIP === false) { 327 | return false; 328 | } 329 | } 330 | } 331 | } 332 | curl_close($ch); 333 | unset($ch); 334 | return $publicIP; 335 | } 336 | 337 | //Returns current public IPv6 address 338 | function getCurrentPublicIPv6() 339 | { 340 | // If user provided an IPv6 address manually as a CLI option 341 | global $providedIPv6; 342 | if (isset($providedIPv6)) { 343 | outputStdout(sprintf('Using manually provided IPv6 address "%s"', $providedIPv6)); 344 | return $providedIPv6; 345 | } 346 | 347 | outputStdout('Getting IPv6 address from ' . IPV6_ADDRESS_URL . '.'); 348 | 349 | $url = IPV6_ADDRESS_URL; 350 | $ch = initializeCurlHandlerGetIP($url); 351 | $publicIP = trim(curl_exec($ch)); 352 | 353 | if (!wasCurlSuccessful($ch) || !isIPV6Valid($publicIP)) { 354 | $retryCount = 1; 355 | $retryLimit = 3; 356 | while ((!$publicIP || !isIPV6Valid($publicIP)) && $retryCount < $retryLimit) { 357 | $publicIP = trim(retryCurlRequest($ch, $retryCount, $retryLimit)); 358 | $retryCount++; 359 | } 360 | 361 | if (!isIPV6Valid($publicIP) || $publicIP === false) { 362 | outputWarning(IPV6_ADDRESS_URL . " didn't return a valid IPv6 address (Try $retryCount / $retryLimit). Trying fallback " . IPV6_ADDRESS_URL_FALLBACK); 363 | $url = IPV6_ADDRESS_URL_FALLBACK; 364 | $ch = initializeCurlHandlerGetIP($url); 365 | $publicIP = trim(curl_exec($ch)); 366 | if (!wasCurlSuccessful($ch) || !isIPV6Valid($publicIP)) { 367 | $retryCount = 1; 368 | $retryLimit = 3; 369 | while ((!$publicIP || !isIPV6Valid($publicIP)) && $retryCount < $retryLimit) { 370 | $publicIP = trim(retryCurlRequest($ch, $retryCount, $retryLimit)); 371 | $retryCount++; 372 | } 373 | if (!isIPV6Valid($publicIP) || $publicIP === false) { 374 | return false; 375 | } 376 | } 377 | } 378 | } 379 | curl_close($ch); 380 | unset($ch); 381 | return $publicIP; 382 | } 383 | 384 | //Login into netcup domain API and returns Apisessionid 385 | function login($customernr, $apikey, $apipassword) 386 | { 387 | outputStdout("Logging into netcup CCP DNS API."); 388 | 389 | $logindata = array( 390 | 'action' => 'login', 391 | 'param' => 392 | array( 393 | 'customernumber' => $customernr, 394 | 'apikey' => $apikey, 395 | 'apipassword' => $apipassword, 396 | ), 397 | ); 398 | 399 | $request = json_encode($logindata); 400 | 401 | $result = sendRequest($request); 402 | 403 | if ($result['status'] === SUCCESS) { 404 | return $result['responsedata']['apisessionid']; 405 | } 406 | 407 | // Error from API: "More than 180 requests per minute. Please wait and retry later. Please contact our customer service to find out if the limitation of requests can be increased." 408 | if ($result['statuscode'] === 4013) { 409 | $result['longmessage'] = $result['longmessage'] . ' [ADDITIONAL INFORMATION: This error from the netcup DNS API also often indicates that you have supplied wrong API credentials. Please check them in the config file.]'; 410 | } 411 | 412 | outputStderr(sprintf("Error while logging in: %s Exiting.", $result['longmessage'])); 413 | return false; 414 | } 415 | 416 | //Logout of netcup domain API, returns boolean 417 | function logout($customernr, $apikey, $apisessionid) 418 | { 419 | outputStdout("Logging out from netcup CCP DNS API."); 420 | $logoutdata = array( 421 | 'action' => 'logout', 422 | 'param' => 423 | array( 424 | 'customernumber' => $customernr, 425 | 'apikey' => $apikey, 426 | 'apisessionid' => $apisessionid, 427 | ), 428 | ); 429 | 430 | $request = json_encode($logoutdata); 431 | 432 | $result = sendRequest($request); 433 | 434 | if ($result['status'] === SUCCESS) { 435 | return true; 436 | } 437 | 438 | outputStderr(sprintf("Error while logging out: %s Exiting.", $result['longmessage'])); 439 | return false; 440 | } 441 | 442 | //Get info about dns zone from netcup domain API, returns result 443 | function infoDnsZone($domainname, $customernr, $apikey, $apisessionid) 444 | { 445 | outputStdout(sprintf('Getting Domain info for "%s".', $domainname)); 446 | 447 | $infoDnsZoneData = array( 448 | 'action' => 'infoDnsZone', 449 | 'param' => 450 | array( 451 | 'domainname' => $domainname, 452 | 'customernumber' => $customernr, 453 | 'apikey' => $apikey, 454 | 'apisessionid' => $apisessionid, 455 | ), 456 | ); 457 | 458 | $request = json_encode($infoDnsZoneData); 459 | 460 | $result = sendRequest($request); 461 | 462 | if ($result['status'] === SUCCESS) { 463 | return $result; 464 | } 465 | 466 | outputStderr(sprintf("Error while getting DNS Zone info: %s Exiting.", $result['longmessage'])); 467 | return false; 468 | } 469 | 470 | //Get info about dns records from netcup domain API, returns result 471 | function infoDnsRecords($domainname, $customernr, $apikey, $apisessionid) 472 | { 473 | outputStdout(sprintf('Getting DNS records data for "%s".', $domainname)); 474 | 475 | $infoDnsRecordsData = array( 476 | 'action' => 'infoDnsRecords', 477 | 'param' => 478 | array( 479 | 'domainname' => $domainname, 480 | 'customernumber' => $customernr, 481 | 'apikey' => $apikey, 482 | 'apisessionid' => $apisessionid, 483 | ), 484 | ); 485 | 486 | $request = json_encode($infoDnsRecordsData); 487 | 488 | $result = sendRequest($request); 489 | 490 | if ($result['status'] === SUCCESS) { 491 | return $result; 492 | } 493 | 494 | outputStderr(sprintf("Error while getting DNS Record info: %s Exiting.", $result['longmessage'])); 495 | return false; 496 | } 497 | 498 | //Updates DNS Zone using the netcup domain API and returns boolean 499 | function updateDnsZone($domainname, $customernr, $apikey, $apisessionid, $dnszone) 500 | { 501 | outputStdout(sprintf('Updating DNS zone for "%s".', $domainname)); 502 | 503 | $updateDnsZoneData = array( 504 | 'action' => 'updateDnsZone', 505 | 'param' => 506 | array( 507 | 'domainname' => $domainname, 508 | 'customernumber' => $customernr, 509 | 'apikey' => $apikey, 510 | 'apisessionid' => $apisessionid, 511 | 'dnszone' => $dnszone, 512 | ), 513 | ); 514 | 515 | $request = json_encode($updateDnsZoneData); 516 | 517 | $result = sendRequest($request); 518 | 519 | if ($result['status'] === SUCCESS) { 520 | return true; 521 | } 522 | 523 | outputStderr(sprintf("Error while updating DNS Zone: %s Exiting.", $result['longmessage'])); 524 | return false; 525 | } 526 | 527 | //Updates DNS records using the netcup domain API and returns boolean 528 | function updateDnsRecords($domainname, $customernr, $apikey, $apisessionid, $dnsrecords) 529 | { 530 | outputStdout(sprintf('Updating DNS records for "%s".', $domainname)); 531 | 532 | $updateDnsZoneData = array( 533 | 'action' => 'updateDnsRecords', 534 | 'param' => 535 | array( 536 | 'domainname' => $domainname, 537 | 'customernumber' => $customernr, 538 | 'apikey' => $apikey, 539 | 'apisessionid' => $apisessionid, 540 | 'dnsrecordset' => array( 541 | 'dnsrecords' => $dnsrecords, 542 | ), 543 | ), 544 | ); 545 | 546 | $request = json_encode($updateDnsZoneData); 547 | 548 | $result = sendRequest($request); 549 | 550 | if ($result['status'] === SUCCESS) { 551 | return true; 552 | } 553 | 554 | outputStderr(sprintf("Error while updating DNS Records: %s Exiting.", $result['longmessage'])); 555 | return false; 556 | } 557 | -------------------------------------------------------------------------------- /update.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | $subdomains) { 70 | outputStdout(sprintf('Beginning work on domain "%s"', $domain)); 71 | 72 | // Let's get infos about the DNS zone 73 | if ($infoDnsZone = infoDnsZone($domain, CUSTOMERNR, APIKEY, $apisessionid)) { 74 | outputStdout("Successfully received Domain info."); 75 | } else { 76 | exit(1); 77 | } 78 | //TTL Warning 79 | if (CHANGE_TTL !== true && $infoDnsZone['responsedata']['ttl'] > 300) { 80 | outputStdout("TTL is higher than 300 seconds - this is not optimal for dynamic DNS, since DNS updates will take a long time. Ideally, change TTL to lower value. You may set CHANGE_TTL to True in config.php, in which case TTL will be set to 300 seconds automatically."); 81 | } 82 | 83 | //If user wants it, then we lower TTL, in case it doesn't have correct value 84 | if (CHANGE_TTL === true && $infoDnsZone['responsedata']['ttl'] !== "300") { 85 | $infoDnsZone['responsedata']['ttl'] = 300; 86 | 87 | if (updateDnsZone($domain, CUSTOMERNR, APIKEY, $apisessionid, $infoDnsZone['responsedata'])) { 88 | outputStdout("Lowered TTL to 300 seconds successfully."); 89 | } else { 90 | outputStderr("Failed to set TTL... Continuing."); 91 | } 92 | } 93 | 94 | //Let's get the DNS record data. 95 | if ($infoDnsRecords = infoDnsRecords($domain, CUSTOMERNR, APIKEY, $apisessionid)) { 96 | outputStdout("Successfully received DNS record data."); 97 | } else { 98 | exit(1); 99 | } 100 | 101 | foreach ($subdomains as $subdomain) { 102 | outputStdout(sprintf('Updating DNS records for subdomain "%s" of domain "%s".', $subdomain, $domain)); 103 | 104 | if (USE_IPV4 === true) { 105 | //Find the host defined in config.php 106 | $foundHostsV4 = array(); 107 | 108 | foreach ($infoDnsRecords['responsedata']['dnsrecords'] as $record) { 109 | if ($record['hostname'] === $subdomain && $record['type'] === "A") { 110 | $foundHostsV4[] = array( 111 | 'id' => $record['id'], 112 | 'hostname' => $record['hostname'], 113 | 'type' => $record['type'], 114 | 'priority' => $record['priority'], 115 | 'destination' => $record['destination'], 116 | 'deleterecord' => $record['deleterecord'], 117 | 'state' => $record['state'], 118 | ); 119 | } 120 | } 121 | 122 | //If we can't find the host, create it. 123 | if (count($foundHostsV4) === 0) { 124 | outputStdout(sprintf("A record for host %s doesn't exist, creating necessary DNS record.", $subdomain)); 125 | $foundHostsV4[] = array( 126 | 'hostname' => $subdomain, 127 | 'type' => 'A', 128 | 'destination' => 'newly created Record', 129 | ); 130 | } 131 | 132 | //If the host with A record exists more than one time... 133 | if (count($foundHostsV4) > 1) { 134 | outputStderr(sprintf("Found multiple A records for the host %s – Please specify a host for which only a single A record exists in config.php. Exiting.", $subdomain)); 135 | exit(1); 136 | } 137 | 138 | $ipv4change = false; 139 | 140 | //Has the IP changed? 141 | foreach ($foundHostsV4 as $record) { 142 | if ($record['destination'] !== $publicIPv4) { 143 | //Yes, it has changed. 144 | $ipv4change = true; 145 | 146 | outputStdout(sprintf("IPv4 address has changed. Before: %s; Now: %s", $record['destination'], $publicIPv4)); 147 | } else { 148 | //No, it hasn't changed. 149 | outputStdout("IPv4 address hasn't changed. Current IPv4 address: ".$publicIPv4); 150 | } 151 | } 152 | 153 | //Yes, it has changed. 154 | if ($ipv4change === true) { 155 | $foundHostsV4[0]['destination'] = $publicIPv4; 156 | //Update the record 157 | if (updateDnsRecords($domain, CUSTOMERNR, APIKEY, $apisessionid, $foundHostsV4)) { 158 | outputStdout("IPv4 address updated successfully!"); 159 | } else { 160 | exit(1); 161 | } 162 | } 163 | } 164 | 165 | if (USE_IPV6 === true) { 166 | //Find the host defined in config.php 167 | $foundHostsV6 = array(); 168 | 169 | foreach ($infoDnsRecords['responsedata']['dnsrecords'] as $record) { 170 | if ($record['hostname'] === $subdomain && $record['type'] === "AAAA") { 171 | $foundHostsV6[] = array( 172 | 'id' => $record['id'], 173 | 'hostname' => $record['hostname'], 174 | 'type' => $record['type'], 175 | 'priority' => $record['priority'], 176 | 'destination' => $record['destination'], 177 | 'deleterecord' => $record['deleterecord'], 178 | 'state' => $record['state'], 179 | ); 180 | } 181 | } 182 | 183 | //If we can't find the host, create it. 184 | if (count($foundHostsV6) === 0) { 185 | outputStdout(sprintf("AAAA record for host %s doesn't exist, creating necessary DNS record.", $subdomain)); 186 | $foundHostsV6[] = array( 187 | 'hostname' => $subdomain, 188 | 'type' => 'AAAA', 189 | 'destination' => 'newly created Record', 190 | ); 191 | } 192 | 193 | //If the host with AAAA record exists more than one time... 194 | if (count($foundHostsV6) > 1) { 195 | outputStderr(sprintf("Found multiple AAAA records for the host %s – Please specify a host for which only a single AAAA record exists in config.php. Exiting.", $subdomain)); 196 | exit(1); 197 | } 198 | 199 | $ipv6change = false; 200 | 201 | //Has the IP changed? 202 | foreach ($foundHostsV6 as $record) { 203 | if ($record['destination'] !== $publicIPv6) { 204 | //Yes, it has changed. 205 | $ipv6change = true; 206 | outputStdout(sprintf("IPv6 address has changed. Before: %s; Now: %s", $record['destination'], $publicIPv6)); 207 | } else { 208 | //No, it hasn't changed. 209 | outputStdout("IPv6 address hasn't changed. Current IPv6 address: ".$publicIPv6); 210 | } 211 | } 212 | 213 | //Yes, it has changed. 214 | if ($ipv6change === true) { 215 | $foundHostsV6[0]['destination'] = $publicIPv6; 216 | //Update the record 217 | if (updateDnsRecords($domain, CUSTOMERNR, APIKEY, $apisessionid, $foundHostsV6)) { 218 | outputStdout("IPv6 address updated successfully!"); 219 | } else { 220 | exit(1); 221 | } 222 | } 223 | } 224 | } 225 | } 226 | 227 | //Logout 228 | if (logout(CUSTOMERNR, APIKEY, $apisessionid)) { 229 | outputStdout("Logged out successfully!"); 230 | } else { 231 | exit(1); 232 | } 233 | --------------------------------------------------------------------------------