Skip to content

Instantly share code, notes, and snippets.

@marcelschmidtdev
Last active April 10, 2023 20:26
Show Gist options
  • Save marcelschmidtdev/f58f6d4874a6734763fab18d371b850a to your computer and use it in GitHub Desktop.
Save marcelschmidtdev/f58f6d4874a6734763fab18d371b850a to your computer and use it in GitHub Desktop.
Fritz!Box DynDNS update script for Cloudflare
<?php
// Fritz!Box update URL: https://example.com/upd.php?key=<pass>&zone=<domain>&ipv4=<ipaddr>&a=[records to update]
// Replace [records to update] in the url.
// If you want to update multiple records of your zone, just add them comma separated: '&a=sub,sub2,*,@'
// Optionally you can update AAAA records by appending '&ipv6=<ip6addr>&aaaa=[records to update]' to the url.
// If no AAAA record is defined but ipv6 is used, it will update both ip4 and ip6 defined for A records.
// $_SERVER['REMOTE_ADDR'] no longer works as FritzOS 7.50 started using IPv6
$key = $_GET['key'] ?? NULL;
$zone = $_GET['zone'] ?? NULL;
$a_hosts = isset($_GET['a']) ? explode(',', $_GET['a']) : array();
$ip_v4 = $_GET['ipv4'] ?? NULL;
$aaaa_hosts = isset($_GET['aaaa']) ? explode(',', $_GET['aaaa']) : array(); //optional
$ip_v6 = $_GET['ipv6'] ?? NULL; //optional
$api_base = 'https://api.cloudflare.com/client/v4/zones';
function printHelp()
{
$file_name = basename($_SERVER["SCRIPT_FILENAME"]);
$params = htmlspecialchars('?key=<pass>&zone=<domain>&ipv4=<ipaddr>&a=') . '<i>[records to update]</i>';
$optional = htmlspecialchars('&ipv6=<ip6addr>&aaaa=') . '<i>[records to update]</i>';
echo "Fritz!Box update URL: <b>https://{$_SERVER['HTTP_HOST']}/{$file_name}{$params}</b><br>";
echo 'Replace <b><i>[records to update]</i></b> in the url.<br>';
echo 'If you want to update multiple records of your zone, just add them comma separated: <i>\'&a=sub,sub2,*,@\'</i><br>';
echo "Optionally you can update AAAA records by appending <b>'{$optional}'</b> to the url.<br>";
echo 'If no AAAA record is defined but ipv6 is used, it will update both ip4 and ip6 defined for A records.';
};
function transformToRecordNames(&$hosts, $zone_name)
{
if ($hosts == NULL)
{
return;
}
foreach ($hosts as &$host)
{
$host = $host == '@' ? $zone_name : $host . '.' . $zone_name;
}
unset($host);
unset($hosts);
}
if(empty($_GET))
{
printHelp();
return http_response_code(200);
}
if ($key == NULL || $zone == NULL || empty($a_hosts) || $ip_v4 == NULL || (!empty($aaaa_hosts) && $ip_v6 == NULL))
{
printHelp();
return http_response_code(400);
}
transformToRecordNames($a_hosts, $zone);
transformToRecordNames($aaaa_hosts, $zone);
if ($aaaa_hosts == NULL && $ip_v6 != NULL)
{
$aaaa_hosts = $a_hosts;
}
// get zone identifier
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $api_base . '?name=' . $zone . '&status=active');
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, FALSE);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: ' . 'Bearer ' . $key, 'Content-Type: application/json'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$response = json_decode(curl_exec($ch));
curl_close($ch);
if($response -> success == FALSE)
{
foreach ($response -> errors as $error)
{
echo 'Error fetching zone identifier: ';
echo $error -> message;
echo '<br>';
error_log('Error fetching zone identifier: ' . $error -> message);
if(isset($error -> error_chain))
{
foreach ($error -> error_chain as $error_detail)
{
error_log($error_detail -> message);
echo $error_detail -> message;
echo '<br>';
}
}
}
return http_response_code(500);
}
if(empty($response -> result))
{
echo 'Error fetching zone identifier:<br>Check if you have access to the given zone.';
error_log('Error fetching zone identifier: Check if you have access to the given zone.');
return http_response_code(500);
}
$zone_id = $response -> result[0] -> id;
// get record identifier
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $api_base . '/' . $zone_id . '/dns_records?type=A,AAAA');
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, FALSE);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: ' . 'Bearer ' . $key, 'Content-Type: application/json'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$response = json_decode(curl_exec($ch));
curl_close($ch);
if($response -> success == FALSE)
{
foreach ($response -> errors as $value)
{
echo 'Error fetching record identifier:<br>';
echo $value -> message;
echo '<br>';
error_log('Error fetching record identifier: ' . $value -> message);
}
return http_response_code(500);
}
$records = array();
foreach ($response -> result as $value)
{
if(($value -> type == 'A' && in_array($value -> name, $a_hosts)) || ($value -> type == 'AAAA' && in_array($value -> name, $aaaa_hosts)))
{
array_push($records, $value);
}
}
if (count($records) != count($a_hosts) + count($aaaa_hosts))
{
echo 'Error updating records:<br>';
echo 'Check if the records you are trying to update already exist in the given zone.';
error_log('Error updating records: Check if the records you are trying to update already exist in the given zone.');
return http_response_code(500);
}
// update record(s)
$mh = curl_multi_init();
$ch_arr = array();
$results = array();
foreach ($records as $rec)
{
$record_name = $rec -> name;
$record_id = $rec -> id;
$record_type = $rec -> type;
$record_content = $record_type == 'A' ? $ip_v4 : $ip_v6;
$put_data = array('type' => $record_type, 'name' => $record_name, 'content' => $record_content, 'ttl' => 60, 'proxied' => FALSE );
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $api_base . '/' . $zone_id . '/dns_records/' . $record_id);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, FALSE);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: ' . 'Bearer ' . $key, 'Content-Type: application/json'));
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($put_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
array_push($ch_arr, $ch);
curl_multi_add_handle($mh, $ch);
}
$running = null;
do
{
curl_multi_exec($mh, $running);
}
while($running > 0);
foreach ($ch_arr as $ch)
{
array_push($results, json_decode(curl_multi_getcontent($ch)));
curl_multi_remove_handle($mh, $ch);
}
curl_multi_close($mh);
foreach ($results as $result)
{
if($result -> success == FALSE)
{
foreach ($result -> errors as $index => $value)
{
echo 'Error updating record:<br>';
echo $value -> message;
echo '<br>';
error_log('Error updating record: ' . $value -> message);
}
return http_response_code(500);
}
}
echo 'SUCCESS';
?>
@marcelschmidtdev
Copy link
Author

I'm having some problem: frtizbox reports "Errore Dynamic DNS: l'aggiornamento Dynamic DNS ha avuto esito positivo, ma alla fine si è verificato un errore nella risoluzione DNS."
Checking on Cloudflare Dash the IP adress has not been modified!
I'm trying to update a single subdomain!
Thanks so much

Are you using that exact URL scheme /upd.php?email=<username>&key=<pass>&hosts=<domain>?
Does your PHP server support curl and have you checked your server logs?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment