Are there any self-hosted (Open Source) Dynamic DNS services, that support devices behind NAT?

Hi All,

Is anyone aware of any self-hosted (Open Source) Dynamic DNS services, that support devices behind NAT?

I’m aware that RouterOS supports DNS updates using the below, but you have to supply the IP.

/tool dns-update

I’m not sure how I can discover the external IP without fetching a file and saving it to disk. I will need to check the external IP every few seconds, so I don’t want to hammer the flash memory.

Thanks

/ip cloud public-address

Unfortunately the device has 3 WAN connections, so IP cloud won’t work for all of them.

Try dns exit. It sets what you post to it and doesn’t care from where…

I’m looking for self-hosted. A few scripts I’ve written in the past have hammered dynamic DNS services and they’ve either asked me to lower the requests, or asked for more money.

How often are you running these scripts?

Sent from my SCH-I545 using Tapatalk

Every 10 seconds or so. I need to be able to check the external IP without / tool fetch writing to disk.

As far as I can tell, the best way to do this will be to run a Dynamic DNS server I can update with / tool fetch, then resolve the address.

You could run your own BIND DNS server. ROS has a built-in tool for updating BIND DNS servers…http://wiki.mikrotik.com/wiki/Manual:Tools/Dynamic_DNS.

I’m planning on going down the BIND route, but the dns-update command needs to know the address, which I can’t discover without writing to a file, which I’m trying to avoid.

I don’t know what’s the scale of this or anything, but what about a simple proxy script hosted on any external web server? You could hammer it as much as you wanted and it would only update the address with dynamic DNS service if it actually changed.

This is essentially the plan, but it will need to integrate with a DNS server. I was hoping there was a semi made or partially made solution.

My idea was to keep using the same third-party dynamic DNS services as before. I’m assuming that the address actually changes much less often than every 10 seconds you were sending updates previously. So if proxy script only sent updates to third-party server when address changed, you could fit in normal use.
With updating being a simple http(s) request, you can do the basic thing with ten line php script (with not much error checking and such, of course).

I’d also be sending queries every 10 seconds. That’s the issue.

But you would be sending them to your server/script, not to the third-party update server. Unless of course the IP address actually changes every single time.

Sure, I’d only be updating the IP address everynow and then, but I’d also be querying it every 10 seconds too

First, querying does not matter that much, millions of clients do it all the time and I doubt that any dyndns service ever worried about that. They care about updates, because those are expensive.

And no, you wouldn’t have to. You would just send a request from your behind-NAT device as often as you need to http://yourserver.tld/update.php?user=user&pass=pass without any address. The script would take the address from where the request came from. Then it would compare it to locally stored one and only in case it’s different, it would send an update request to dyndns server. Not a single query needed.

DNS providers do get upset if you send to many queries.

https://help.dyn.com/standard-dns/i-received-a-notice-about-being-over-quota-for-queries/

Ok, forget about the first paragraph then. But as I wrote, you don’t need to send any queries.

What I had in mind was something like this:

<?php
  require_once('Net/DNS2.php');

function do_update($user, $ip, &$error) {
  if($user->type == 'server') {
    $u = new Net_DNS2_Updater($user->zone, array('nameservers' => array($user->server)));
    try {
      $u->deleteAny($user->host, 'A');
      $a = Net_DNS2_RR::fromString($user->host.' '.$user->ttl.' IN A '.$ip);
      $u->add($a);
      $u->signTSIG($user->key_name, $user->key_value);
      $u->update();
      return true;
    } catch(Net_DNS2_Exception $e) {
      $error = $e->getMessage();
      return false;
    }
  } elseif($user->type == 'url') {
    $url = $user->url.$ip;
    $ch = curl_init($url);
    curl_setopt_array($ch, array(
      CURLOPT_CONNECTTIMEOUT => 10,
      CURLOPT_TIMEOUT => 10,
      CURLOPT_HEADER => false,
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_FAILONERROR => true
    ));
    $data = curl_exec($ch);
    $err = curl_errno($ch);
    curl_close($ch);
    if($err != 0) {
      $error = 'curl error '.$err;
      return false;
    } else {
      //FIXME: check $data for some string?
      return true;
    }
  } else {
    $error = 'unknown type';
    return false;
  }
}

  if(!isset($_GET['user']) || !isset($_GET['pass'])) die('missing parameter(s)');
  $ip = $_SERVER['REMOTE_ADDR'];
  $data = json_decode(file_get_contents('data.json'));
  foreach($data->users as $user) {
    if($user->user == $_GET['user'] && $user->pass == $_GET['pass']) {
      if($user->ip != $ip) {
        if(do_update($user, $ip, $err)) {
          $user->ip = $ip;
          file_put_contents('data.json', json_encode($data));
          die('updated');
        } else {
          die('update error: '.$err);
        }
      }
      die('no update needed');
    }
  }
  die('login error');
?>

This is slightly extended version that supports both URL-based updates with externals services and updates on your own DNS server. Config file (data.json; used also to save current IP address) looks like this (it’s standard json):

{"users":[{
  "user":"user1",
  "pass":"pass",
  "ip":"",
  "type":"server",
  "zone":"example.net",
  "host":"dyndnshost.example.net",
  "server":"192.0.2.1",
  "ttl":60,
  "key_name":"tsigkey",
  "key_value":"supersecretkey"
},{
  "user":"user2",
  "pass":"pass",
  "ip":"",
  "type":"url",
  "url":"http:\/\/dyndns.example.com\/update?user=xxx&pass=xxx&ip="
}]}

All tested and working, but it’s just a quick test, no warranties of any kind. :slight_smile: