Skip to content

Instantly share code, notes, and snippets.

@janeczku
Forked from briped/gratisdns.sh
Last active September 24, 2023 20:17
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save janeczku/8424508 to your computer and use it in GitHub Desktop.
Save janeczku/8424508 to your computer and use it in GitHub Desktop.
A custom module to use DDNS provider "DNSexit.com" with Synology's DDNS update service
#!/bin/sh
# dnsexit.sh - A DnsExit.com DDNS custom module for Synology DSM
#
# Author:
# Brian Schmidt Pedersen (http://blog.briped.net)
#
# Version:
# 1.3.5
# Description:
# This is a custom DDNS module, for use with my Synology DiskStation. It
# allows me to use the DSM interface for DnsExit.com DDNS service.
#
# You can either add the provider manually or by using the "--install"
# feature.
#
# Using the "--install" feature
# Execute the script with the "--install" option:
# > dnsexit.sh --install
# If the script isn't executable, you can prepend sh:
# > sh dnsexit.sh --install
#
# This will copy (and overwrite if already exists) the dnsexit.sh file
# '/sbin/dnsexit.sh' and chmod +x it (make it eXecutable).
#
# If no other arguments are added to the "--install" option, a provider
# with the name "DnsExit.com" will be added. In order to use more hosts
# you can add a custom provider name after the "--install" option:
# > dnsexit.sh --install example.org
# or
# > dnsexit.sh --install My Custom Provider
# This adds a provider with the name "example.org", which will use the
# DnsExit.com DDNS module.
#
# You don't have to worry about spaces. The script reads everything after
# "--install" as one string. You might have to beware of using certain
# characters that might act as an escape character (backslash, ticks,
# backticks, single- and double quotes spring to mind).
#
# Manual installation
# Copy the 'dnsexit.sh' file to the '/sbin' folder:
# > cp /volume1/homes/USERNAME/dnsexit.sh /sbin/dnsexit.sh
# The above copy command assumes you have enabled "User home service" on
# your Synology, and also assumes that the home shares are located on
# your first volume "volume1".
#
# Then make the file eXecutable:
# > chmod +x /sbin/dnsexit.sh
#
# Then edit the "/etc.defaults/ddnsd_provider.conf" file to add DDNS
# providers. I only know of 'vi' editor on the Synolgoy devices, which
# can be a bit annoying to navigate, if you don't know how.
# Open "/etc.defaults/ddnsd_provider.conf" in vi:
# > vi /etc.defaults/ddnsd_provider.conf
# When opened, hold the DOWN ARROW until you reach the end of the config
# file, then hit (once only) the O key (the letter,not the number). This
# opens edit mode, below the current line. Then simply type (or paste,
# but remember to not paste the # comments) the following:
# [DnsExit.com]
# modulepath=/sbin/dnsexit.sh
# queryurl=https://ssl.gratisdns.dk/ddns.phtml?u=__USERNAME__&p=__PASSWORD__&d=__DOMAIN__&h=__HOSTNAME__&i=__MYIP__
#
# You can add multiple sections by adding the above, and changing the
# text between the square brackets (i.e. [DnsExit.com]).
#
# If you have a DS with two interfaces (like me), and the DDNS/ezCloud
# interface doesn't show the IP for the right interface, then you can
# make it, by setting the gateway manually in Network settings, to be the
# gateway of the interface which you want DDNS to use. Just bear in mind
# that this also affects which interface all the other services on the DS
# uses.
# Changelist:
# 1.3.5
# * Added a simple check, to ensure that DDNS wouldn't update if IP was 0.0.0.0
# (yes, I've had that happen to me)
# 1.3.4
# * Changed from cut to awk, to find the __DOMAIN__ from __HOSTNAME__, this
# is to ensure that the last two delimeters are always found (i.e.
# 'example.org' found from 'example.org', 'sub.example.org' and
# 'sub.sub.example.org' etc.)
# 1.3.3
# * Minor fixes and modifications to the "--install" feature.
# 1.3.2
# + Modified the "--install" feature to add a custom provider/domain.
# See the description for more details.
# 1.3.1
# * Fixed the "--install" feature.
# 1.3.0
# + Added a simple "--install" argument which simply appends the GratisDNS
# provider to the /etc.defaults/ddns_provider.conf file, if not already
# added.
# 1.2.7
# * __DOMAIN__ would be incorrect, if using 2nd. or 3rd. (or higher)
# level subdomains, since it was just the 2nd. and 3rd. delimiter
# that was returned. Changed to 2nd. delimiter and the rest.
# Thanks to Mathias Neerup for bringing my attention to this.
# 1.2.6
# * Just minor changes to the code, so Notepad++ could code-fold :)
# 1.2.5
# * Changed update_ca_bundle() a bit, since the age checking didn't
# seem to work before.
# 1.2.4
# * Fixed script to return "nochg" when IP isn't changed from IPcache
# 1.2.3
# * Fixed so the IP cache is also updated, even when DDNS provider
# responds with 'nochg', since obivously then the cached IP were
# wrong.
# 1.2.2
# * Fixed some return code checking.
# 1.2.1
# + Added more debug logging.
# 1.2.0
# + Moved the entire update ddns routine into its own function.
# + Moved the CA bundle checking and updating into its own function.
# 1.1.0
# + Changed the cURL update query to use CA bundle, for better
# security.
# 1.0.0
# + Release.
# Todo:
#
###############################################################################
{ # Read the supplied arguments into variables used in this script.
__USERNAME__="$(echo ${@} | cut -d' ' -f1)"
__PASSWORD__="$(echo ${@} | cut -d' ' -f2)"
__HOSTNAME__="$(echo ${@} | cut -d' ' -f3)"
# __DOMAIN__="$(echo ${__HOSTNAME__} | awk -F. '{print $(NF-1) "." $NF}')"
__MYIP__="$(echo ${@} | cut -d' ' -f4)"
# Alternate methods for getting the IP:
#__INTERFACE_IP__="$(ifconfig ${__INTERFACE__} | sed '/inet\ /!d;s/.*r://g;s/\ .*//g')"
#__MYIP__=$(curl --silent --interface ${__INTERFACE__} "http://automation.whatismyip.com/n09230945.asp")
# Where to store the IP used for the last DNS update:
__IP_CACHE__="/tmp/dnsexit_ddnsd_ip.txt"
# Where to store the logfile:
__LOGFILE__="/var/log/dnsexit_ddnsd.log"
# Where to store the CA bundle:
#__CA_BUNDLE__="/tmp/cacert.pem"
# The ddns_provider.conf path and filename:
__CONFIGFILE__="/etc.defaults/ddns_provider.conf"
# The scriptfile (this file) path and filename (used for the --install parameter):
__SCRIPTFILE__="/sbin/dnsexit.sh"
}
if [ "$(echo ${@} | cut -d' ' -f1)" == "--install" ]; then
cp -f ${0} ${__SCRIPTFILE__}
chmod +x ${__SCRIPTFILE__}
if [ "$(echo ${@} | cut -d' ' -f2-)" == "--install" ]; then
__MYDOMAIN__="DnsExit.com"
else
__MYDOMAIN__="$(echo ${@} | cut -d' ' -f2-)"
fi
if [ ! $(grep "${__MYDOMAIN__}" ${__CONFIGFILE__}) ]; then
# Add the custom domain to the ddns_provider.conf file.
echo "[${__MYDOMAIN__}]" >> ${__CONFIGFILE__}
echo " modulepath=/sbin/dnsexit.sh" >> ${__CONFIGFILE__}
echo " queryurl=http://update.dnsexit.com/RemoteUpdate.sv?login=__USERNAME__&password=__PASSWORD__&host=__HOSTNAME__&myip=__MYIP__" >> ${__CONFIGFILE__}
echo "Added '${__MYDOMAIN__}' to the DDNS Provider list."
fi
echo "Done!"
exit
fi
log() {
# Severity Levels:
# 0 Emergency System is unusable.
# A "panic" condition usually affecting multiple apps/servers/sites.
# At this level it would usually notify all tech staff on call.
# 1 Alert Action must be taken immediately.
# Should be corrected immediately, therefore notify staff who can fix
# the problem.An example would be the loss of a backup ISP connection
# 2 Critical Critical conditions.
# Should be corrected immediately, but indicates failure in a primary
# system, an example is a loss of primary ISP connection.
# 3 Error Error conditions.
# Non-urgent failures, these should be relayed to developers or
# admins; each item must be resolved within a given time.
# 4 Warning Warning conditions.
# Warning messages, not an error, but indication that an error will
# occur if action is not taken, e.g. file system 85% full - each item
# must be resolved within a given time.
# 5 Notice Normal but significant condition.
# Events that are unusual but not error conditions - might be
# summarized in an email to developers or admins to spot potential
# problems - no immediate action required.
# 6 Informational Informational messages.
# Normal operational messages - may be harvested for reporting,
# measuring throughput, etc - no action required.
# 7 Debug Debug-level messages.
# Info useful to developers for debugging the application, not useful
# during operations.
__LOGTIME__=$(date +"%b %e %T")
if [ "${#}" -lt 1 ]; then
false
else
__LOGMSG__="${1}"
fi
if [ "${#}" -lt 2 ]; then
__LOGPRIO__=7
else
__LOGPRIO__=${2}
fi
logger -p ${__LOGPRIO__} -t "$(basename ${0})" "${__LOGMSG__}"
echo "${__LOGTIME__} $(basename ${0}) (${__LOGPRIO__}): ${__LOGMSG__}" >> ${__LOGFILE__}
}
update_ddns() {
if [ -z "${1}" ]; then
log "update_ddns(): Missing Argument. URL string required." 3
false
else
__URL__="${1}"
fi
# Update DNS record:
if [ -f "${__CA_BUNDLE__}" ]; then
log "update_ddns(): Updating using --cacert \"${__CA_BUNDLE__}\"." 7
__RESPONSE__=$(curl --silent --cacert "${__CA_BUNDLE__}" "${__URL__}")
else
log "update_ddns(): Updating using --insecure." 7
__RESPONSE__=$(curl --silent --insecure "${__URL__}")
fi
if [ "${?}" -ne "0" ]; then
log "update_ddns(): cURL returned error code ${?} while trying to update DDNS." 3
false
fi
log "update_ddns(): __RESPONSE__=${__RESPONSE__}" 7
# Output:
# When you write your own module, you can use the following words to tell user what happen by print it.
# You can use your own message, but there is no multiple-language support.
#
# good - Update successfully.
# nochg - Update successfully but the IP address have not changed.
# nohost - The hostname specified does not exist in this user account.
# abuse - The hostname specified is blocked for update abuse.
# notfqdn - The hostname specified is not a fully-qualified domain name.
# badauth - Authenticate failed.
# 911 - There is a problem or scheduled maintenance on provider side
# badagent - The user agent sent bad request(like HTTP method/parameters is not permitted)
# badresolv - Failed to connect to because failed to resolve provider address.
# badconn - Failed to connect to provider because connection timeout.
case ${__RESPONSE__} in
*0=OK*)
echo ${__MYIP__} > ${__IP_CACHE__}
__STATUS__='good'
true
;;
*1=IP*)
echo ${__MYIP__} > ${__IP_CACHE__}
__STATUS__='nochg'
true
;;
'Forkerte værdier, opdatering kan ikke laves.<br><br>A record findes ikke.'|'Domæne kan IKKE administreres af bruger')
__STATUS__='nohost'
false
;;
*4=IP*)
__STATUS__='abuse'
false
;;
'Bruger login: 1Fejl i kodeord, prøv igen. Husk serveren ser forskel på STORE Og små BOGstAvER.'|'Bruger login: Bruger eksistere ikke, husk serveren ser forskel på STORE Og smÅ BOGstAvER.')
__STATUS__='badauth'
false
;;
'Bruger login: MD5 invalid')
# The error from the provider doesn't really match up with the
# status I forward to DDNSd. But it seems that a malformed URL,
# such as if the URL isn't enclosed in quotes or contains spaces
# etc. will give this error.
__STATUS__='badagent'
false
;;
*)
__STATUS__="${__RESPONSE__}"
false
;;
esac
log "update_ddns(): DDNSd Status: ${__STATUS__}" 6
}
{ # Get the last known DDNS IP
if [ ! -f ${__IP_CACHE__} ]; then
# If the file wasn't found, create it to avoid errors.
echo "127.0.0.1" > ${__IP_CACHE__}
fi
__OLDIP__=$(cat ${__IP_CACHE__})
}
if [ "${__MYIP__}" == "0.0.0.0" ] || [ "${__MYIP__}" == "0.0.0.0" ]; then
log "IP was '0.0.0.0', which is wrong. Not updating." 5
elif [ "${__OLDIP__}" == "${__MYIP__}" ]; then
log "IP not changed. ${__MYIP__}. Not updating." 6
__STATUS__="nochg"
else
__URL__="http://update.dnsexit.com/RemoteUpdate.sv?login=${__USERNAME__}&password=${__PASSWORD__}&host=${__HOSTNAME__}&myip=${__MYIP__}"
# Set the response and status variables, so they're available globally. That way I can read them outside the update_ddns() function.
__RESPONSE__="No response returned from DDNS provider."
__STATUS__="No status returned from update_ddns()."
log "IP changed. ${__OLDIP__} > ${__MYIP__}. Attempting to update DNS." 6
update_ddns "${__URL__}"
if [ "${?}" -ne "0" ]; then
log "update_dns(): ${__RESPONSE__}" 3
else
log "update_dns(): ${__RESPONSE__}" 6
fi
fi
printf "%s" "${__STATUS__}"
@julienbourdeau
Copy link

Is this working with the very last version of DSM 5.0 as of today ?
Thanks for sharing this script

@Fmstrat
Copy link

Fmstrat commented Oct 18, 2016

Password in URL over HTTP? I know this code is old, but anyone using it might want to make that HTTPS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment