Hopefully you’ve reached this page through Google, otherwise it’s unlikely you’ll have reason to read the rest of this blog post.
I recently received a new TalkTalk Super Router v3, which is made by Huawei and has the model number HG635. I was interested in getting some kind of Dynamic DNS set up and was pleased to see that it had some settings under Internet->Internet Services.
The options available were DynDns.org, TZO and other. As DynDns had just gone down the paid router after many years of being free I was keen to see if I could do something myself with “Other”. From here were a collection of settings including Protocol set to GNUDip.http. After a while of Googling it looked possible to set up my own GNUDip running on Perl speaking to my own DNS server. Except I couldn’t be bother to install Perl and I no longer managed the DNS server but now used CloudFlare.
I already had some code through the CloudFlare API where I opened a webpage from my home wifi, it took the IP I was coming from and update a CloudFlare record, I just needed to get my router to do that bit for me.
So I read through the GNUDip spec, and worked out each stage and created a PHP script which would provide the seed information, verify a change request and then update CloudFlare for me.
The PHP code is below and can be used to not only update CloudFlare, but any DNS provider if they have an API you can call.
Step 1:
In CloudFlare create a new subdomain to be used for your DDNS record. You want to make sure that it’s Off Cloud.
Step 2:
Find your CloudFlare API key
Step 3:
Run this PHP code, it’s only needed once to find the record ID of the subdomain you are using:
<html>
<head>
<title>Zones</title>
<style>
table { font-family: sans-serif; }
tr:nth-child(odd) { background: #ddd; }
td, th { padding: 3px; }
tr.header { background: pink; }
</style>
</head>
<body>
<table>
<tr class="header">
<th>Record ID</th>
<th>Name</th>
<th>Content</th>
<th>Type</th>
<th>Cloudflare On</th>
</tr>
<?php
$file = file_get_contents(
'https://www.cloudflare.com/api_json.html' .
'?a=rec_load_all'.
'&tkn=cd9843940935390a03939e9302094b43529ab'.
'&email=[email protected]'.
'&z=artesea.co.uk'
);
$json = json_decode($file,1);
foreach($json['response']['recs']['objs'] as $host) {
?>
<tr>
<td><?=$host['rec_id']?></td>
<td><?=$host['name']?></td>
<td><?=$host['content']?></td>
<td><?=$host['type']?></td>
<td><?=($host['props']['cloud_on'])?'Yes':'No'?></td>
</tr>
<?php
}
?>
</table>
</body>
</html>
Step 4:
Now you’ve found the Record ID (in this case 138325147) you can create this PHP file. This is the one your router will speak to, so it needs to be on a webserver outside of your home network. I’ve also called the file cfddns.php
<?php
$timeout = 60; //seconds
$remote_ip = $_SERVER['REMOTE_ADDR'];
$key = 'JKLDlfmmkgkweglrkegj4:fjksd489ikjklJS|Ld'; #random gibberish, I just keyboard smash
$username = 'myddnsuser';
$password = 'reallysafepassword';
$cloudflare_api_key = 'cd9843940935390a03939e9302094b43529ab';
$cloudflare_email = '[email protected]';
$cloudflare_domain = 'artesea.co.uk';
$cloudflare_subdomain = 'ddns';
$cloudflare_record_id = '138325147';
// have we been passed data?
if($_GET['user']) {
$salt = $_GET['salt'];
$time = $_GET['time'];
$sign = $_GET['sign'];
$domn = $_GET['domn'];
$user = $_GET['user'];
$pass = $_GET['pass'];
$reqc = (int)$_GET['reqc'];
$addr = $_GET['addr']; //IP address
log_good("REMOTE IP: " . $remote_ip . " QUERY STRING: " . $_SERVER['QUERY_STRING']);
if($salt && $time && $sign && $domn && $user && $pass) {
//check and update DNS
# validate the signature
if(md5($salt.$time.$key) != $sign) {
log_fail("Invalid signature for " . $user);
}
# check salt timeout
if(time() > $time + $timeout) {
log_fail("Salt value to old for " . $user);
}
#confirm request code
if($reqc < 0 || $reqc > 2) {
log_fail("Invalid client request code for " . $user);
}
# only one user so check
if($user != $username) {
log_fail("Unknown user " . $user);
}
# only one domain so check
if($domn != $cloudflare_subdomain.'.'.$cloudflare_domain) {
log_fail("Unknown domain " . $domn);
}
if($pass != md5(md5($password).'.'.$salt)) {
log_fail("Invalid password for " . $user);
}
if($addr == '0.0.0.0' && $reqc = 0) {
$reqc = 1;
}
if($addr == '' && $reqc = 2) {
$addr = $remote_ip;
}
if($reqc == 1) {
// GO OFFLINE
// No idea what to do with this request so just return back success
html_output(array("retc" => 2), 'Successful offline');
log_good("SUCCESS: Offline request made");
}
else {
//DO STUFF WITH CLOUDFLARE
$file = file_get_contents(
'https://www.cloudflare.com/api_json.html' .
'?a=rec_edit'.
'&type=A'.
'&id='.$cloudflare_record_id.
'&name='.$cloudflare_subdomain.
'&content='.$remote_ip.
'&ttl=1'.
'&service_mode=0'.
'&tkn='.$cloudflare_api_key.
'&email='.$cloudflare_email.
'&z='.$cloudflare_domain
);
$meta['retc'] = 0;
if($reqc == 2) {
$meta['addr'] = $addr;
}
html_output($meta, 'Successful update');
log_good("SUCCESS: IP updated to " . $addr);
}
}
else {
log_fail("Missing information");
}
}
else {
$chars = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$len = strlen($chars) - 1;
$salt = '';
for($i=0; $i<10; $i++) {
$salt .= $chars[mt_rand(0, $len)];
}
$meta['time'] = time();
$meta['salt'] = $salt;
$meta['sign'] = md5($salt.$meta['time'].$key);
html_output($meta, 'Salt generated');
log_good("SALT SET: time=".$meta['time']." salt=".$meta['salt']." sign=".$meta['sign'] . " REMOTE IP: ".$remote_ip);
}
function html_output($meta, $body) {
header("Content-Type: text/html; charset=iso-8859-1");
$body .= "\n<pre>" . print_r($meta,1) . "</pre>";
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd\">
<html>
<head>
<title>GnuDIP Update Server</title>
<?php
foreach($meta as $name => $content) {
?>
<meta name="<?=$name?>" content="<?=$content?>">
<?php
}
?>
</head>
<body>
<h2>GnuDIP Update Server</h2>
<?=$body?>
</body>
</html>
<?php
}
function log_good($text) {
error_log(date("y/m/d H:i:s")." ".$text."\n", 3, "ddns.log");
}
function log_fail($text) {
error_log(date("y/m/d H:i:s")." ERROR: ".$text."\n", 3, "ddns.log");
html_output(array("retc" => 1), $text);
exit();
}
Step 5:
Get the router to speak to the script
Step 6:
Sit back, hopefully from now onwards your router will update the IP address every time it changes.
Perfect. Works a treat! Thanks for the detailed instructions. The only hitch I encountered was the quote marks not pasting correctly in to a php file. A quick find and replace sorted that out. Thanks again!
I tried using this with my 3 day old HG633, and turns out there’s a huge issue with the firmware where it truncates the last two digit of the time and sign fields. (Along with Port Forwarding not working at all)
You wouldn’t happen to have a copy of the firmware running on your router I could flash on to mine by chance? They’re running the same hardware so it should work.
Thanks in Advance
Michael my router details are
Product type: HG635
Hardware version: G.1.01
Software version: v1.06t (the latest acording to this page)
Looking at the logs I get the salt and time returned correctly (are you sure the script you are using is working) although over the last 10 months the IP address has never actually changed.
As for port forwarding that’s working fine too, although a bit of a learning curve to set up compared to other routers I’ve used.
@Ryan Cullen, the HG635 doesn’t have these issues, they are solely relevant to the HG633 even though hardware wise they are built on similar bases, and their web interfaces are identical along with feature list, the firmware on my router breaks both of what I mentioned earlier.
Did anyone ever get DDNS working with HG633 on Cloudflare?
Not sure if the truncated error still exists, I’ve tried going through the steps 3 times now but getting invalid signature error:-
16/08/08 17:12:36 SALT SET: time=1470690756 salt=nDZB90VfOD sign=a52e01cbbe7ee3a1857968a76a52821a REMOTE IP: 92.31.218.111
16/08/08 17:12:36 REMOTE IP: 92.31.218.111 QUERY STRING: salt=nDZB90VfO&time=147069075&sign=a52e01cbbe7ee3a1857968a76a52821&user=myddnsuser&domn=home.mydomain.com&pass=2f7ed79171c522400179c73951fb679c&reqc=0&addr=92.31.218.111
16/08/08 17:12:36 ERROR: Invalid signature for myddnsuser