dynDNS Update Script

I have been trouble shooting a problem with a group of routers that use the script. When the script is run, it will log that no update is needed even though I can see that the resolved IP of the *dyndns.org domain is not the actual IP of the device.

I can manually force the update and it works just fine. Which means there is something going on with the if statement that decides if the update happens.

After looking at the script I have noticed that the script never checks to see if the update command successfully completes, but the script still changes the value of the previous IP variable. Meaning, the variable the script uses to compare the current IP with is getting updated even if the actual dyndns update isn’t happening.

So if the below line of code ever does not get processed by the dyndns.org servers successfully, the scripts won’t know this, but wills till proceed to alter the previous IP variable. So the next time the script is run, if will see the newly updated previous IP variable and compare it with the contents of the current IP variable. But the IP address in the dyndns entry will still be the previous IP before the last time the script ran.

/tool fetch user=$username password=$password mode=http address="members.dyndns.org" \
      src-path="nic/update?system=dyndns&hostname=$hostname&myip=$currentIP&wildcard=no" \
      dst-path="/dyndns.txt"

I believe parsing the html page that is returned after executing this and testing to see if the word “good” appears and then containing the update of the previous IP variable in an if statement that checks if the result contains “good” should resolve this issue.

I have added this below the line above to help prevent this from happening.

    # parse the results of update
    :local resultLen [:len $result]
    :local startLoc [:find $result "Go" -1]
    :local endLoc [:find $result "od" -1]
    :local resultParse [:pick $result $startLoc $endLoc]
    :log info ("Update was successful: ".$resultParse)
    :if (($resultParse = "Good") do ={
	     :log info ("Update was successful: ".$resultParse)
        :set previousIP $currentIP
    } else={
        :log info ("UpdateDynDNS: Update failed!")
    }

This block should catch to see if Good appears in the returned text, and if it does it will allow the previousIP variable to be updated. If it does not appear, it will not happen, but the next time the script is called it should trigger the update again.

it will be external tool, not inside the RouterOS.

Offer my version of the dynDNS Update script:
Script check the current IP address in DNS resolve for hostname, and if needed it change in dynDNS profile

# Set needed variables
:local username "YourUserName"
:local password "YourPassword"
:local hostname "YourHostName"

:global dyndnsForce

# print some debug info
#:log info ("UpdateDynDNS: username = $username")
#:log info ("UpdateDynDNS: password = $password")
#:log info ("UpdateDynDNS: hostname = $hostname")

# get the current IP address from the internet (in case of double-nat)
/tool fetch mode=http address="checkip.dyndns.org" src-path="/" dst-path="/dyndns.checkip.html"
:local result [/file get dyndns.checkip.html contents]

# parse the current IP result
:local resultLen [:len $result]
:local startLoc [:find $result ": " -1]
:set startLoc ($startLoc + 2)
:local endLoc [:find $result "</body>" -1]
:local currentIP [:pick $result $startLoc $endLoc]
:log info "UpdateDynDNS: currentIP = $currentIP"

#get IP from DynDNS for our hostname
:local resolvedIP [:resolve $hostname]
:log info ("UpdateDynDNS: resolved IP =$resolvedIP")

# Remove the # on next line to force an update every single time - useful for debugging, but you could end up getting blacklisted by DynDNS!
#:set dyndnsForce true

# Determine if dyndns update is needed
# more dyndns updater request details available at http://www.dyndns.com/developers/specs/syntax.html
:if (($currentIP != $resolvedIP) || ($dyndnsForce = true)) do={
    :set dyndnsForce false
    /tool fetch user=$username password=$password mode=http address="members.dyndns.org" src-path="/nic/update?hostname=$hostname&myip=$currentIP" dst-path="/dyndns.txt"
    :local result [/file get dyndns.txt contents]
    :log info ("UpdateDynDNS: Dyndns update needed")
    :log info ("UpdateDynDNS: Dyndns Update Result: ".$result)
    :put ("Dyndns Update Result: ".$result)
} else={
    :log info ("UpdateDynDNS: No dyndns update needed")
}

Schedule it every 1 minute. It works for me perfectly on MikroTik RouterOS v5.xx :wink:

dimdjd: so every minute you have data written on your flash. Bad. Better scripts exist for the same purpose.

elgo: fair comment!
So, here is my version without the continual disk operations

# Set needed variables
:local username "YourUserName"
:local password "YourPassword"
:local hostname "YourHostName"
:global ddnsinterface "YourInternetInterfaceWithDynamicIP"
:global ddnsip ""

:global dyndnsForce
# Remove the # on next line to force an update every single time - useful for debugging, but you could end up getting blacklisted by DynDNS!
#:set dyndnsForce true

# Grab the current IP address on that interface.
:global ddnsip2 [/ip address get [/ip address find interface=$ddnsinterface ] address];
:set ddnsip [:pick $ddnsip2 0 [:find $ddnsip2 "/"]];
:log info ("UpdateDynDNS: currentIP = $ddnsip")

#get IP from DynDNS for my hostname
:local resolvedIP [:resolve $hostname]
:log info ("UpdateDynDNS: resolved IP =$resolvedIP")

# Determine if dyndns update is needed
# more dyndns updater request details available at http://www.dyndns.com/developers/specs/syntax.html
:if (($ddnsip != $resolvedIP) || ($dyndnsForce = true)) do={
    :set dyndnsForce false
    /tool fetch user=$username password=$password mode=http address="members.dyndns.org" src-path="/nic/update?hostname=$hostname&myip=$ddnsip" dst-path="/dyndns.txt"
    :local result [/file get dyndns.txt contents]
    :log info ("UpdateDynDNS: Dyndns update needed")
    :log info ("UpdateDynDNS: Dyndns Update Result: ".$result)
    :put ("Dyndns Update Result: ".$result)
} else={
    :log info ("UpdateDynDNS: No dyndns update needed")
}

Thanks, it really will save lives of MLC Flash chips, used in routerboard™ devices! :slight_smile:

where will it run?.. on dedicated server, not on the router itself?..

better to use one of the dyndns nameservers to resolve the name
avoiding DNS cache

like this

:local resolvedIP [:resolve domain-name=$hostname server=204.13.248.75]

I have a 450G version 5.12. Tried all scripts and none is working! Please advise.
Nevermind, everything’s peaches :slight_smile:

Note that performing a DNS query to see if the hostname needs updated is in breach of Dyn’s policies, as is using checkip.dyndns.org every minute:

Unacceptable Client Behavior

  • Send requests to or access anything other than /nic/update at the host members.dyndns.org.
  • Reverse engineer web requests to our website to create or delete hostnames.
  • Hardcode the IP address of any of the Dyn servers.
  • Attempt to update after receiving the notfqdn, abuse, nohost, badagent, badauth, badsys return codes or repeated nochg return codes without user intervention.
    *** Perform DNS updates to determine whether the client IP needs to be updated.**
    *** Access our web-based IP detection script (> http://checkip.dyndns.com/> ) more than once every 10 minutes**

Emphasis mine. Doing that may result in all RouterOS devices being blocked from submitting updates (Dyn do that for badly behaved user agents) and possibly even they’ll stop reporting a valid value at checkip.dyndns.org.

anyway what other ddns server can you use since dyndns is not free anymore are there scripts made for other servers

changeip.com - we are the only ones that have ssl updates in routeros, and actually support and use routeros. everyone else will just ban you for too many updates ; )

This is indeed the problem with that script.
max connect limit reached. You have to get rid of checking checkip.dyndns.org every minute or you will keep having problems.

http://www.hark.com/clips/fjppvkfnzr-i-been-saying-it
I think it shares my thoughts on this matter.
Just a single daemon, compiled from a ready-made source and integrated in ROs would have saved us from this thread.

I’ve rewritten [well, actually mostly written from scratch] a dyndns script.

The post is here: http://forum.mikrotik.com/t/dyndns-update-script/54850/1

I have a few changes I’m working in still, but this should work just fine.
It does NOT do double-NAT, but it does stay within the TOS at DynDNS.
[i.e. It DOES keep IP state between reboots, thus preventing a “nochg” update for the first run after a reboot.]
You can run it as often as you like, even once a second.
It only writes to flash when an IP change occurs. The writes are also very small, so flash write exhaustion should be a very minimal issue, if one at all.

The code is extensively commented, so you should very easily find your way around.

I’m planning on adding it to the Wiki, but this is where things are for now.

Oh, final things:
It’s written and tested on 5.12 only. I’d assume it should work on other releases, but I’m not sure which.
It was also written and tested on a RB-450G - again it should work on most anything, but I’ve spent no time validating on any other RB’s.

Finally, if you use it and do so successfully, could you please contribute back by letting me know:

  1. That it worked successfully.
  2. What ROS hardware.
  3. What ROS software version.
  4. If you make changes, could you let me know what for, and if possible submit the code back.

Thanks,
Greg

Hi,

When I change the firewall filter to drop all input on my gateway interface, I am not able to run this command successfully
/tool fetch mode=http address=“checkip.dyndns.org” src-path=“/” dst-path=“/dyndns.checkip.html”

I got the error “while resolving ip: could not get answer from dns server”

I have tried to accept TCP and UDP port 53 but still not working. And what really puzzle me is I am actually at the internal LAN behind the firewall, why this still happen ? Mikrotic really gave a lot of surprises to me :confused: In the past, I can just configure what I want with simple few clicks, but now, to do a simple thing like this will take me hours if not days. :frowning:

Any advise which port I should open ? Or what did I do wrong ?

if you drop everything on input, router cannot receive anything from the internet. first, accept established and related conections - that will allow all router-initiated sessions

nothing changed from the previous century

Set needed variables

:local username "YourUserName"
:local password "YourPassword"
:local hostname "YourHostName"
:global ddnsinterface "YourInternetInterfaceWithDynamicIP"
:global ddnsip ""

:global dyndnsForce

Remove the # on next line to force an update every single time - useful for debugging, but you could end up getting blacklisted by DynDNS!

#:set dyndnsForce true

Grab the current IP address on that interface.

:global ddnsip2 [/ip address get [/ip address find interface=$ddnsinterface ] address];
:set ddnsip [:pick $ddnsip2 0 [:find $ddnsip2 "/"]];
:log info ("UpdateDynDNS: currentIP = $ddnsip")

#get IP from DynDNS for my hostname
:local resolvedIP [:resolve $hostname]
:log info ("UpdateDynDNS: resolved IP =$resolvedIP")

Determine if dyndns update is needed

more dyndns updater request details available at http://www.dyndns.com/developers/specs/syntax.html

:if (($ddnsip != $resolvedIP) || ($dyndnsForce = true)) do={
:set dyndnsForce false
/tool fetch user=$username password=$password mode=http address="members.dyndns.org" src-path="/nic/update?hostname=$hostname&myip=$ddnsip" dst-path="/dyndns.txt"
:local result [/file get dyndns.txt contents]
:log info ("UpdateDynDNS: Dyndns update needed")
:log info ("UpdateDynDNS: Dyndns Update Result: ".$result)
:put ("Dyndns Update Result: ".$result)
} else={
:log info ("UpdateDynDNS: No dyndns update needed")
}

i use this script in my router 751... i want use external ip this script check my local ip how i fix??

Works on ROS v5.20

#To RUN script USE ----->>   /system script run dynDns
#
:local ddnsuser "username"
#
# CHANGE PASSWORD, hostname, username and interface to match yours!
#
:local ddnspass "pass"
:local theinterface "eth1"  
:local ddnshost "yourhost"
:local ipddns [:resolve $ddnshost];
:local ipfresh [ /ip address get [/ip address find interface=$theinterface ] address ]
:if ([ :typeof $ipfresh ] = nil ) do={
   :log info ("DynDNS: No ip address on $theinterface .")
} else={
   :for i from=( [:len $ipfresh] - 1) to=0 do={ 
      :if ( [:pick $ipfresh $i] = "/") do={ 
    :set ipfresh [:pick $ipfresh 0 $i];
      } 
}
 
:if ($ipddns != $ipfresh) do={
    :log info ("DynDNS: IP-DynDNS = $ipddns")
    :log info ("DynDNS: IP-Fresh = $ipfresh")
   :log info "DynDNS: Update IP needed, Sending UPDATE...!"
   :local str "/nic/update?hostname=$ddnshost&myip=$ipfresh&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG"
   /tool fetch address=members.dyndns.org src-path=$str mode=http user=$ddnsuser \
         password=$ddnspass dst-path=("/DynDNS.".$ddnshost)
    :delay 1
    :local str [/file find name="DynDNS.$ddnshost"];
    /file remove $str
    :global ipddns $ipfresh
  :log info "DynDNS: IP updated to $ipfresh!"
    } else={
     :log info "DynDNS: dont need changes";
    }
}
    }
} need changes";
    }
}members.dyndns.org src-path=$str mode=http user=$ddnsuser \
        password=$ddnspass dst-path=("/DynDNS.".$ddnshost)
    :delay 1
    :local str [/file find name="DynDNS.$ddnshost"];
    /file remove $str
    :global ddnslastip $ddnsip
  }
}

Hi,

I am using Greg Sowell’s DYNDns scripts for the vpn tunnels I am constructing.

http://gregsowell.com/?p=1523

and

http://wiki.mikrotik.com/wiki/Dynamic_DNS_Update_Script_for_DNSoMatic.com_behind_NAT

the “behind nat” script works for me on 5.14 , but it has caveats:

uses DNSOMATIC :frowning:



I would like to use a straight to DYNDNS script to :

A : update the Tik’s wan IP to automatic on change of IP or a manually via run or a removal of if then else set of commands…a force update , if you will .



B: straight to Dyndns script for the resolving of local and remote host ips for policy and peer in vpn tunnels , with the router associated dst and src path updated, just the Sowell script minus the DNSOMATIC stuff.

The Sowell method works, but I pay for DYNDNS , and really want to take out the middle man in this setup.

Good script!
But I had a problem. Once "dyndns.org" refused to update. So I turned on the test result updates. The changes are "# UP!!!".
I am a novice.

\

Set needed variables

:local username "YOURUSER"
:local password "YOURPASWORD"
:local hostname "YOURHOSTNAME.dyndns.org"

:global dyndnsForce
:global previousIP

print some debug info

:log info ("UpdateDynDNS: username = $username")
:log info ("UpdateDynDNS: password = $password")
:log info ("UpdateDynDNS: hostname = $hostname")
:log info ("UpdateDynDNS: previousIP = $previousIP")

get the current IP address from the internet (in case of double-nat)

/tool fetch mode=http address="checkip.dyndns.org" src-path="/" dst-path="/dyndns.checkip.html"
:delay 1
:local result [/file get dyndns.checkip.html contents]

parse the current IP result

:local resultLen [:len $result]
:local startLoc [:find $result ": " -1]
:set startLoc ($startLoc + 2)
:local endLoc [:find $result "" -1]
:local currentIP [:pick $result $startLoc $endLoc]
:log info "UpdateDynDNS: currentIP = $currentIP"

Remove the # on next line to force an update every single time - useful for debugging,

but you could end up getting blacklisted by DynDNS!

#:set dyndnsForce true

Determine if dyndns update is needed

more dyndns updater request details http://www.dyndns.com/developers/specs/syntax.html

:if (($currentIP != $previousIP) || ($dyndnsForce = true)) do={
:set dyndnsForce false
#:set previousIP $currentIP
:log info "$currentIP or $previousIP"

/file remove [find name="dyndns.txt"]

/tool fetch user=$username password=$password mode=http address="members.dyndns.org"
src-path="nic/update?system=dyndns&hostname=$hostname&myip=$currentIP&wildcard=no"
dst-path="/dyndns.txt"
:delay 1
:local result [/file get dyndns.txt contents]
:log info ("UpdateDynDNS: Dyndns update needed")
:log info ("UpdateDynDNS: Dyndns Update Result: ".$result)

:local StatusUp [:pick $result 0 4]
:if (($StatusUp = "good") || ($StatusUp="noch")) do={
:set previousIP $currentIP
} else={
}

:put ("Dyndns Update Result: ".$result)
} else={
:log info ("UpdateDynDNS: No dyndns update needed")
}