Script updates for DLink DDNS

This describes how to setup a Linux box to automatically update the DLink Dynamic DNS facility.

Background:
DLink offers a dynamic DNS service to it's customers.

If your router is directly connected to the Internet then the setup and configuration is very straight forward. Just use the product documentation to set it up.

If your router is not directly connected to the Internet then this may be of some help.

I have a DLink DSR-250 connecting to an upstream router. The upstream router NATs it's connection to the Internet. This results in the DSR-250 having a WAN IP like 192.168.0.2. Not much help if you're using the built-in DDNS facility.

To work around this, I wrote a cron script that retrieves the public IP in use and compares it to the last value retrieved. If they are different then an update is sent to the DDNS server.


#!/bin/bash
function updateDlinkDDNS() {
   typeset -i RTN_CODE=1
   readonly IPADDRESS="${1}"
   readonly TEMPLATE="UpdateDlinkDSR250IPAddress.template"
   readonly TEMPFILE="updateDlinkDDNS.$$"
   if [[ -f ${TEMPLATE} ]]; then
      sed -e "s/1.2.3.4/$1/" < ${TEMPLATE} >| ${TEMPFILE}
      cat ${TEMPFILE} | nc members.dyndns.org 80 >| response.$$
      if tail -1 response.$$ | grep -q good; then
         echo "$(date) Update from $(<myipaddress.existing) to $(<myipaddress.new)"
         RTN_CODE=0
      else
         cat response.$$ >&2
         echo "" >&2
      fi
      rm response.$$
      rm ${TEMPFILE}
   else
      echo "Missing template file" >&2
   fi
   return ${RTN_CODE}
}
 
cd /home/jardine/Projects/UpdateDlinkDSR250IPAddress
if wget bot.whatismyipaddress.com -qnv -O myipaddress.new >>/dev/null; then
   if [[ ! -f myipaddress.existing ]]; then
      touch myipaddress.existing
   fi
 
   # Check if current IP address is different than existing IP address
   if ! diff myipaddress.new myipaddress.existing >>/dev/null; then
      if updateDlinkDDNS "$(< myipaddress.new)"; then
         cp myipaddress.new myipaddress.existing
      fi
   else
      echo "$(date) No work. Previous IP $(<myipaddress.existing) is the same as current IP $(<myipaddress.new)"
   fi
else
   echo "$(date) Failed to get my own IP address. wget returned $?" >&2
fi >> UpdateDlinkDSR250IPAddress.stdout 2>> UpdateDlinkDSR250IPAddress.stderr

The real trick was to figure out what was being sent by the router. I used WireShark to capture an update from my router and saw this:

GET /nic/update?system=dyndns&hostname=yourhost.dlinkddns.com&myip=192.168.0.2&wildcard=OFF& HTTP/1.0
Host: members.dyndns.org
Authorization: Basic <Password Hash>
User-Agent: D-Link/DSR-250/1.09B32
Connection: close
Pragma: no-cache

I changed the IP address to "1.2.3.4" and used it as a template file. The cron script does a search/replace with public IP.

Hints on finding the router update within the WireShark capture:

  • Capturing the right data
    • Start the capture
    • Configure DLinkDDNS on the router & save
    • Stop the capture
  • Search for DNS Lookups
    • Display Filter: (udp.dstport == 53)
    • Find a lookup for "members.dyndns.org"
    • Note the IP address in the response
  • Search for packets to the noted IP address
    • Display Filter: (ip.addr == ?)
    • Right-Click the first packet and Follow TCP Stream