Skip to content

Instantly share code, notes, and snippets.

@ConnerWill
Created April 15, 2025 19:24
Show Gist options
  • Save ConnerWill/334eaa9d2d99f2ffa394d8497825e2f6 to your computer and use it in GitHub Desktop.
Save ConnerWill/334eaa9d2d99f2ffa394d8497825e2f6 to your computer and use it in GitHub Desktop.
Script to update DDNS IP for DynuDNS domain
#!/usr/bin/env bash
#### OPTIONS
set -o errexit -o pipefail
#### DEFINITIONS
PROG=$(basename "${0}")
DDNS_USERNAME="${DDNS_USERNAME:-}"
DDNS_PASSWORD="${DDNS_PASSWORD:-}"
DDNS_HOSTNAME="${DDNS_HOSTNAME:-}"
DDNS_ALIAS="${DDNS_ALIAS:-}"
UPDATE_ALL="${DDNS_UPDATE_ALL:-false}"
IPV4="${DDNS_IPV4:-auto}"
IPV6="${DDNS_IPV6:-no}"
DRY_RUN=false
#### FUNCTIONS
## Function to throw an error
function throw_error() {
local error_input="${1}"
printf "[ERROR]: %s\n" "${error_input}" >&2
exit 1
}
function is_installed(){
local input="${1}"
if ! command -v "${input}" >/dev/null 2>&1; then
throw_error "Cannot find '${input}' in PATH. Please make sure '${input}' is installed and is in your PATH"
fi
}
function usage() {
cat << EOF
Usage: ${PROG} [options]
Options:
-u, --username <username> Dynu username (required if not updating single hostname)
-p, --password <password> Dynu password (required)
-h, --hostname <hostname> Hostname to update (required if not updating all domains)
-a, --alias <alias> Subdomain alias to update
-A, --all Update all domains in account (requires username)
-4, --ipv4 <ip|no|auto> IPv4 address, 'no' to skip, or 'auto' to auto-detect (default: auto)
-6, --ipv6 <ip|no|auto> IPv6 address, 'no' to skip, or 'auto' to auto-detect (default: auto)
-d, --dry-run Print the curl command without executing it
-?, --help Show this help message
Environment Variables:
DDNS_USERNAME Dynu username
DDNS_PASSWORD Dynu password
DDNS_HOSTNAME Hostname to update
DDNS_ALIAS Subdomain alias
DDNS_UPDATE_ALL Set to 'true' to update all domains (default: false)
DDNS_IPV4 IPv4 address, 'no', or 'auto' (default: auto)
DDNS_IPV6 IPv6 address, 'no', or 'auto' (default: auto)
EOF
exit 0
}
function parse_args() {
while [[ $# -gt 0 ]]; do
case "${1}" in
-u|--username)
DDNS_USERNAME="${2}"
shift 2
;;
-p|--password)
DDNS_PASSWORD="${2}"
shift 2
;;
-h|--hostname)
DDNS_HOSTNAME="${2}"
shift 2
;;
-a|--alias)
DDNS_ALIAS="${2}"
shift 2
;;
-A|--all)
UPDATE_ALL=true
shift
;;
-4|--ipv4)
IPV4="${2}"
shift 2
;;
-6|--ipv6)
IPV6="${2}"
shift 2
;;
-d|--dry-run)
DRY_RUN=true
shift
;;
-\?|--help)
usage
;;
*)
throw_error "Unknown option: ${1}"
;;
esac
done
}
function validate_args() {
if [[ -z "${DDNS_PASSWORD}" ]]; then
throw_error "Password is required (set DDNS_PASSWORD or use --password)"
fi
if [[ "${UPDATE_ALL}" == true && -z "${DDNS_USERNAME}" ]]; then
throw_error "Username is required when updating all domains (set DDNS_USERNAME or use --username)"
fi
if [[ "${UPDATE_ALL}" == false && -z "${DDNS_HOSTNAME}" ]]; then
throw_error "Hostname is required when not updating all domains (set DDNS_HOSTNAME or use --hostname)"
fi
}
function build_url() {
local base_url="https://api.dynu.com/nic/update"
local params=()
if [[ "${UPDATE_ALL}" == true ]]; then
params+=("username=${DDNS_USERNAME}")
fi
params+=("password=${DDNS_PASSWORD}")
if [[ -n "${DDNS_HOSTNAME}" ]]; then
params+=("hostname=${DDNS_HOSTNAME}")
fi
if [[ -n "${DDNS_ALIAS}" ]]; then
params+=("alias=${DDNS_ALIAS}")
fi
if [[ "${IPV4}" != "auto" ]]; then
params+=("myip=${IPV4}")
fi
if [[ "${IPV6}" != "auto" ]]; then
params+=("myipv6=${IPV6}")
fi
query_string=$(IFS='&'; echo "${params[*]}")
local query_string
echo "${base_url}?${query_string}"
}
#### MAIN
is_installed "curl"
parse_args "$@"
validate_args
url=$(build_url)
if [[ "${DRY_RUN}" == true ]]; then
echo "Dry run: curl -s \"${url}\""
exit 0
fi
response=$(curl -s "${url}")
if [[ "${response}" == "good"* || "${response}" == "nochg"* ]]; then
echo "DDNS update successful: ${response}"
else
throw_error "DDNS update failed: ${response}"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment