[Script] Namecheap Digitalocean Dynamic DNS Update Script

Namecheap Dynamic DNS Update Script
http://wiki.mikrotik.com/wiki/Dynamic_DNS_Update_Script_for_Namecheap

This is a ROS 7 script (IIRC, the fetch command is bit different in ROS 6) which checks if the IP for an interface has changed, updates namecheap.com’s dynamic DNS and also sends you an email about it. :astonished:

As it turns out, it’s also good for letting me know when electricity has been restored after a blackout or when the ISP comes back from an outage. :slight_smile:

ROS now has /ip cloud with Mikrotik’s own ddns. With this script and Namecheap, you can have a much cooler and shorter ddns domain. And also a backup in case /ip cloud is down.

My first script here so please let me know if things can be improved. I’m quite the newbie to Mikrotik. The script was originally taken from the Mikrotik wiki for another ddns provider and then modified for Namecheap.

Email tool needs to be setup beforehand. Just change the variables to yours. There’s no error checking if the ddns update fails or sending email encounters an error.

Schedule this script to run every few minutes as you please. For pppoe, you can also run it from ppp profile up and down script.


:local "NC_DDNS_SERVER" "dynamicdns.park-your-domain.com"
:local "NC_DDNS_HOSTNAMES_ARRAY" {"host1";"host2"}
:local "NC_DDNS_HOSTNAMES" [:tostr $"NC_DDNS_HOSTNAMES_ARRAY"]

:local "NC_DDNS_DOMAIN" "example.com"
:local "NC_TOKEN" "0123456789abcdef"
:local "TELEGRAM_SERVER" "api.telegram.org"
:local "TELEGRAM_KEY" "bot0123456789abcdef"
:local "TELEGRAM_CHAT_ID" "012345678"
:local "ISP_NAME" "My_ISP"
:local "WAN_INTERFACE" "pppoe-out1"
:local "LOG_FILE_PREFIX" "/disk1/logs/DDNS_NC."

:global "ddns_previous_ip"
:local "ddns_current_ip" [ /ip address get [/ip address find interface=$"WAN_INTERFACE" ] address ]
:local "current_date" [/system clock get date]
:local "current_time" [/system clock get time]
:local "system_name" [/system identity get name]
:local "system_uptime" [/system resource get uptime]
:local "system_free_memory" [/system resource get free-memory]
:local "system_cpu_load" [/system resource get cpu-load]
:local "system_version" ("ROS " . [/system/package/get [find name=routeros] version] )

# Strip the net mask off the IP address
:set "ddns_current_ip" [:pick $"ddns_current_ip" 0 [:find $"ddns_current_ip" "/"]]

:if ([ :typeof $"ddns_previous_ip" ] = nil ) do={ :global "ddns_previous_ip" "0" }
:if ([ :typeof $"ddns_current_ip" ] = nil ) do={
    :log info ("DDNS: No ip address present on $"WAN_INTERFACE" interface, please check.")
    } else={
        :if ($"ddns_current_ip" != $"ddns_previous_ip") do={
            :foreach hostname in=$"NC_DDNS_HOSTNAMES_ARRAY" do={
                :log info ("DDNS: Updating $hostname.$"NC_DDNS_DOMAIN" $"ddns_previous_ip" -> $"ddns_current_ip"")
                :local str "https://$"NC_DDNS_SERVER"/update?host=$"hostname"&domain=$"NC_DDNS_DOMAIN"&password=$"NC_TOKEN"&ip=$"ddns_current_ip""
                #:log info $str
                /tool fetch url=$str mode=https dst-path=($"LOG_FILE_PREFIX".$hostname)
            }
            #:log info $"NC_DDNS_HOSTNAMES"
            :log info "DDNS: Sending Email"
            /tool e-mail send to=myself@example.com subject="$"ISP_NAME" IP $"current_date" $"current_time" $"system_name"" body="$"system_name" $"current_date" $"current_time" \r$"system_version"\rNamecheap: $"NC_DDNS_HOSTNAMES" \rDomain: $"NC_DDNS_DOMAIN" \r$"ISP_NAME" IP: $"ddns_current_ip" \rPrevious IP: $"ddns_previous_ip" \rUptime: $"system_uptime" \rFree memory: $"system_free_memory" kb \rCPU Load: $"system_cpu_load" % "
            :log info "DDNS: Sending Telegram"
            :local str "https://$"TELEGRAM_SERVER"/$"TELEGRAM_KEY"/sendMessage?chat_id=$"TELEGRAM_CHAT_ID"&parse_mode=Markdown&text=$"ISP_NAME" $"system_name" $"system_version"%0ANamecheap: $"NC_DDNS_HOSTNAMES"%0ADomain: $"NC_DDNS_DOMAIN"%0A$"current_date" $"current_time"%0A$"ISP_NAME" IP: $"ddns_current_ip"%0APrevious IP: $"ddns_previous_ip"%0AUptime: $"system_uptime"%0AFree memory: $"system_free_memory" kb%0ACPU Load: $"system_cpu_load" %"
            #:log info $str
            /tool fetch url=$str mode=https keep-result=no   
            :global "ddns_previous_ip" $"ddns_current_ip"
        } else={
            :log info "DDNS: IP has not changed. DDNS will not be updated."
          }
      }

Below is a snippet of code for updating Digitalocean’s DNS using its API. You can use this instead of Namecheap in the script above or both simultaneously. Fingers crossed DO’s DNS recordids are persistent.

# Only recordid(s) in DO_DDNS_RECORDID_ARRAY are used in Digitalocean's DNS API.
:local "DO_DDNS_RECORDID_ARRAY" {"host1"=123456789;"host2"=987654321}
:local "DO_DDNS_DOMAIN" "example.com"
:local "DO_TTL" 30
:local "DO_TOKEN" "dop_v1_abcdefghijklmn1234567890opqrstuvwxyz"
:local "DO_HEADER" "Content-Type: application/json,Authorization: Bearer $"DO_TOKEN""
:local "DO_LOG_FILE_PREFIX" "/disk1/logs/DDNS_DO."

:local "do_data" "{\"ttl\":$"DO_TTL", \"data\":\"$"ddns_current_ip"\"}"

:foreach hostname,recordid in=$"DO_DDNS_RECORDID_ARRAY" do={
  :local "do_url" "https://api.digitalocean.com/v2/domains/$"DO_DDNS_DOMAIN"/records/$"recordid""
  :log info $"do_url"
  /tool fetch mode=https http-method=put http-header-field=$"DO_HEADER" http-data=$"do_data" url=$"do_url" dst-path=($"DO_LOG_FILE_PREFIX".$hostname)
}

Added to the Wiki:

http://wiki.mikrotik.com/wiki/Dynamic_DNS_Update_Script_for_Namecheap

I used the above as a starting point for my own namecheap dynamic DNS update script. I tied it into my DHCP client script for my WAN interface instead, so there is no need to run it every 5 minutes or anything like that. I also didn’t bother with the e-mail or keeping a pile of variables globally. Anyways, I thought I’d share it here since this is where I ended up when I wanted dynamic DNS with namecheap on my MikroTik router…
:if ($bound=1) do {

the name (within the domain) to update – must already exist!

:local host

the domain name

:local domain

key for namecheap updates

:local password

get wan IP (easy in the DHCP script)

:local wanip $“lease-address”

:log info “DHCP got WAN IP of $wanip”

:local url “https://dynamicdns.park-your-domain.com/update?host=$host&domain=$domain&password=$password&ip=$wanip

#:log info “URL args: $url”

this should to the deed! (note: no verification is done here, check namecheap yourself at first!)

/tool fetch url=$url mode=https keep-result=no

} else {
#:log info “DHCP was unbound”
}You’ll want to login to namecheap first, lookup your DNS key for updates, and make an initial entry in your DNS table (of type “A + Dynamic DNS Record”.)

The script doesn’t do much (any) error checking, but I figure if it works once, it’ll probably keep working. And I don’t do anything when the DHCP is unbound.. I don’t think that hurts much of anything, plus I don’t know that we can remove a DNS entry remotely on namecheap.

Hope this helps someone! Cheers!

So if you have more than one host name do you need to recreate this script for each sub domain host name? Or is there a way to enter all your sub domain host name in one script!

Did some simple modifications for myself.

# the name (within the domain) to update -- must already exist!
:local hosts [:toarray value="record1,record2,record3"];
# the domain name
:local domain <domain>
# key for namecheap updates
:local password <pass key>
# get wan IP
:local ddnsip [ /ip address get [/ip address find interface=<wan interface> ] address ]
:log info "WAN IP is $ddnsip"
# Strip the net mask off the IP address
:for i from=( [:len $ddnsip] - 1) to=0 do={
    :if ( [:pick $ddnsip $i] = "/") do={ 
        :set ddnsip [:pick $ddnsip 0 $i]
       } 
   }
:foreach host in $hosts do={
:local url "https://dynamicdns.park-your-domain.com/update?host=$host&domain=$domain&password=$password&ip=$ddnsip"
:log info "URL args: $url"
/tool fetch url=$url mode=https keep-result=no
}

Hi !

I have searched but did not find how to create the script and where ton install the script ? Sorry i’m new with Mikrotik.

So in your example is the domain cctv1.soonwai.com? I cannot get the script to update my subdomain

:global ddnshostname "cctv1"
:global ddnsdomain "soonwai.com"

I have been using it for last one year and it works so well. Recently, I have got ipv6 address from my isp. Is there a way to let the script support updating AAAA record ?

You can also create a CNAME record that resolves to your mikrotik ddns url. Saves you the cost of ddns hosting.

Hi,
I found that the original script does not work anymore and removed from wiki.
(I don’t know how long it is broken, because my ips are not changed.)

I did some debugging on command line, found that tool fetch with “address” will cause https problems.

Now namecheap only accept “url” mode.
/tool fetch url=$str mode=https keep-result=no

Additionally, original script export a lot of global variables, but only lastip is needed, others can be local.

Here’s the fixed script

:local ddnsserv "dynamicdns.park-your-domain.com"
:local ddnshostname "home"
:local ddnsdomain "yourdomain.com"
:local ddnspass "masked"
:local ddnsip [ /ip address get [/ip address find interface=pppoe-out1 ] address ]
:global ddnslastip

# Strip the net mask off the IP address
:for i from=( [:len $ddnsip] - 1) to=0 do={
    :if ( [:pick $ddnsip $i] = "/") do={ 
        :set ddnsip [:pick $ddnsip 0 $i]
       } 
   }

:if ([ :typeof $ddnslastip ] = nil ) do={ :global ddnslastip "0" }
:if ([ :typeof $ddnsip ] = nil ) do={
   :log info ("DDNS: No ip address present on pppoe interface.")
} else={
  :if ($ddnsip != $ddnslastip) do={
    :log info "DDNS: IP changed, previous IP=$ddnslastip, new IP=$ddnsip"
    :log info ("DDNS: Updating Namecheap Dynamic DNS")
    :local str "https://$ddnsserv/update?host=$ddnshostname&domain=$ddnsdomain&password=$ddnspass&ip=$ddnsip"
    /tool fetch url=$str mode=https keep-result=no
    :global ddnslastip $ddnsip
  } else={
    #:log info "DDNS: IP unchanged. No update required."
  }
}

Regards,
Alvin

Hi folks,
have been trying to use the latest version of this script, however, can’t seem to get it working… copy/pasted the script, adjusted with my domain/subdomain/pw/interface, but nada.
Can anyone post a recent script that works ?
Many thanks.

I can confirm that the latest script does work. Make sure that you adjust line number 5 where it sets the ddnsip so it checks the correct interface

L’ha già scritto che ha cambiato interfaccia. Lo script è pieno di errori, quindi a seconda della versione di RouterOS, che ovviamente nessuno scrive, hai risultati diversi.

I just wanted to chime in, that the script from 2017 when using DHCP-Client indeed works with the same logic.
I use it for my domain with minor cosmetic modifications:

:local host "*"
:local domain "my_domain"
:local password "my_api_key"

:if ($bound=1) do={
    :local ipaddress $"lease-address"
    :log info "IP lease received, updating DDNS"
    /tool fetch url="https://dynamicdns.park-your-domain.com/update?host=$host&domain=$domain&password=$password&ip=$ipaddress" mode=https keep-result=no
} else={
    :log info "No IP lease, nothing to be done with DDNS"
}
:log info "DHCP Client script ran successfully"

The last line is a sanity check, that the script really worked. Sadly the behaviour of scripts is to silently error out, and unless you do a log output at the very end, you can’t be sure that it really ran in full.

This worked great for me. I replaced a scheduled script with this. Thanks!

Er … , how do I add a script ?

[edit]
Found it - just not able to test it with a bonded interface strangely … more testing.
For those following along at home - you must go to the DHCP Client in webfig, then open the interface (mine is a bond1 since it’s a bonded interface), and select “Advanced”. WHere there is a scripting option.

[edit]
ah, well, first thing is that the “host” should be the subdomain you have registered as an A+ DDNS record. So, change that to a meaningful name.
Next up, strangely why I can’t trigger the script when there is a DHCP refresh, the logs don’t show it running, and Namecheap does not update the IP for the domain.
[edit]
Checking the logs, note that you need to scroll to the END of the logs, they’re not sorts in reverse chronological order as you might expect, I am seeing proper error messages like this:

Download from dynamicdns.park-your-domain.com FAILED: resolving error

Which I assume means that we’re in a bit of a race condition here, DNS has not been applied yet and we need it to be. Checking DNS resolution after the DHCP address is acquired does appear to be ok –

[@MikroTik] > put [:resolve dynamicdns.park-your-domain.com]
104.22.62.177

[edit]
Yep, that was exactly it. The DNS was not set by the time I was trying to use a DNS name. Once I added a short delay in the script then the DNS name was updated properly.

Here’s the updated script:

:local host "subdomain"
:local domain "domain.com"
:local password "my_api_key"

:if ($bound=1) do={
    :local ipaddress $"lease-address"
    :delay 2000ms;
    :log info "IP lease received, updating DDNS"
    /tool fetch url="https://dynamicdns.park-your-domain.com/update?host=$host&domain=$domain&password=$password&ip=$ipaddress" mode=https keep-result=no
} else={
    :log info "No IP lease, nothing to be done with DDNS"
}
:log info "DHCP Client script ran successfully"