ddns-scripts: bump version

This commit is contained in:
coolsnowwolf 2024-10-28 23:17:20 +08:00
parent b99b66aea3
commit 058f961c23
119 changed files with 3675 additions and 912 deletions

1096
net/ddns-scripts/Makefile Executable file → Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +0,0 @@
#
# Please read http://wiki.openwrt.org/doc/uci/ddns
#
config ddns "global"
option ddns_dateformat "%F %R"
# option ddns_rundir "/var/run/ddns"
# option ddns_logdir "/var/log/ddns"
option ddns_loglines "250"
option upd_privateip "0"

View File

@ -1,201 +0,0 @@
#!/bin/sh
g_pslfile=/usr/share/public_suffix_list.dat.gz
[ -f "$g_pslfile" ] || g_pslfile="$(dirname $0)/public_suffix_list.dat.gz"
g_pslerr=0
g_cfgfile="ddns"
# modify timer settings from interval and unit to dhms format
timer2dhms() {
# $1 Number and
# $2 Unit of time interval
local t=0
case $2 in
days) t=$(( $1 * 86400 ));;
hours) t=$(( $1 * 3600 ));;
minutes) t=$(( $1 * 60 ));;
*) t=$1;;
esac
local d=$(( $t / 86400 ))
local h=$(( $t % 86400 / 3600 ))
local m=$(( $t % 3600 / 60 ))
local s=$(( $t % 60 ))
if [ $d -gt 0 ]; then printf "%dd %02dh %02dm %02ds" "$d" "$h" "$m" "$s"
elif [ $h -gt 0 ]; then printf "%dh %02dm %02ds" "$h" "$m" "$s"
elif [ $m -gt 0 ]; then printf "%dm %02ds" "$m" "$s"
else printf "%ds" "$s"; fi
unset d h m s t
return 0
}
# using function to not confuse function calls with existing ones inside /lib/functions.sh
update_config() {
uc_uci="$(which uci) -q" # ignore errors
uc_cfg=""
uc_name=""
uc_var=""
uc_val=""
package() { return 0; }
config () {
uc_cfg="$1"
uc_name="$2"
# Type = ddns Name = global
if [ "$uc_cfg" = "$g_cfgfile" -a "$uc_name" = "global" ]; then
option() {
uc_var="$1"; shift
uc_val="$*"
case "$uc_var" in
allow_local_ip) $uc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_privateip";;
date_format) $uc_uci rename $g_cfgfile.$uc_name.$uc_var="ddns_dateformat";;
log_lines) $uc_uci rename $g_cfgfile.$uc_name.$uc_var="ddns_loglines";;
log_dir) $uc_uci rename $g_cfgfile.$uc_name.$uc_var="ddns_logdir";;
run_dir) $uc_uci rename $g_cfgfile.$uc_name.$uc_var="ddns_rundir";;
# leave all other options currently unchanged
*) ;;
esac
}
# Type = service Name = ???
elif [ "$uc_cfg" = "service" ]; then
option() {
uc_var="$1"; shift
uc_val="$*"
case "$uc_var" in
# fix some option service_name values
# and some settings for specific providers
service_name|upd_provider)
case "$uc_val" in
freedns\.afraid\.org|afraid\.org)
$uc_uci set $g_cfgfile.$uc_name.$uc_var="afraid.org-keyauth";;
Bind-nsupdate)
$uc_uci set $g_cfgfile.$uc_name.$uc_var="bind-nsupdate";;
dyndns\.org|dyndns\.com)
$uc_uci set $g_cfgfile.$uc_name.$uc_var="dyn.com";;
free\.editdns\.net)
$uc_uci set $g_cfgfile.$uc_name.$uc_var="editdns.net";;
FreeDNS\.42\.pl)
$uc_uci set $g_cfgfile.$uc_name.$uc_var="freedns.42.pl";;
domains\.google\.com)
$uc_uci set $g_cfgfile.$uc_name.$uc_var="google.com";;
loopia\.com)
$uc_uci set $g_cfgfile.$uc_name.$uc_var="loopia.se";;
NoIP\.com|No-IP\.com)
$uc_uci set $g_cfgfile.$uc_name.$uc_var="no-ip.com";;
spdns\.de)
$uc_uci set $g_cfgfile.$uc_name.$uc_var="spdyn.de";;
strato\.de)
$uc_uci set $g_cfgfile.$uc_name.$uc_var="strato.com";;
*)
# all others leave unchanged
;;
esac
# rename option service_name to option upd_provider
# $uc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_provider"
;;
domain|upd_object)
# verify if lookup_host is set
$uc_uci get $g_cfgfile.$uc_name.lookup_host >/dev/null 2>&1 || \
$uc_uci set $g_cfgfile.$uc_name.lookup_host="$uc_val"
if [ -f "$g_pslfile" ]; then
# if service_name/upd_provider cloudflare_v1 then change domain/upd_object to new syntax
# there is no sort order inside uci data so we need multiple checks
uco_provider=$($uc_uci get $g_cfgfile.$uc_name.upd_provider 2>/dev/null) || \
uco_provider=$($uc_uci get $g_cfgfile.$uc_name.service_name 2>/dev/null)
unset uco_provider
fi
# rename option domain to option upd_object
# $uc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_object"
;;
# dns_server)
# # if bind-nsupdate takeover old "dns_server" value as new "upd_nsupd_server" value
# uco_provider=$($uc_uci get $g_cfgfile.$uc_name.upd_provider 2>/dev/null) || \
# uco_provider=$($uc_uci get $g_cfgfile.$uc_name.service_name 2>/dev/null)
# [ "$uco_provider" = "Bind-nsupdate" -o \
# "$uco_provider" = "bind-nsupdate" ] && \
# $uc_uci set $g_cfgfile.$uc_name.upd_nsupd_server="$uc_val"
# # rename option dns_server to new option global_dnssvr
# $udc_uci rename $g_cfgfile.$uc_name.$uc_var="global_dnssvr"
# ;;
# bind_network)
# $udc_uci set $g_cfgfile.$uc_name.upd_url_bindnet="$uc_val"
# $udc_uci rename $g_cfgfile.$uc_name.$uc_var="lip_url_bindnet"
# ;;
# proxy)
# # proxy value must include protocoll
# $udc_uci set $g_cfgfile.$uc_name.$uc_var="http://$uc_val"
# $udc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_url_proxy"
# ;;
# use_ipv6)
# $udc_uci set $g_cfgfile.$uc_name.$uc_var="$(( 4 + ( 2 * $uc_val ) ))"
# $udc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_ipversion"
# TODO update_url)
# TODO update_script)
# other renames
# TODO lookup_host) -> rip_host
# enabled) $udc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_enabled";;
# force_dnstcp) $udc_uci rename $g_cfgfile.$uc_name.$uc_var="rip_host_dnstcp";;
# is_glue) $udc_uci rename $g_cfgfile.$uc_name.$uc_var="rip_host_isglue";;
# ip_interface) $udc_uci rename $g_cfgfile.$uc_name.$uc_var="lip_iface";;
# ip_network) $udc_uci rename $g_cfgfile.$uc_name.$uc_var="lip_net";;
# use_https) $udc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_url_secure";;
# cacert) $udc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_url_cacert";;
# username) $udc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_username";;
# password) $udc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_password";;
# param_opt) $udc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_paramopt";;
# param_enc) $udc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_paramenc";;
# leave all other options currently unchanged
*) ;;
esac
return 0
}
return 0
# ignore unknown
else
return 0
fi
}
# read config file
uc_data=$($uc_uci -S -n export "$g_cfgfile")
uc_ret="$?"
# Error then create config file
[ $uc_ret -ne 0 ] && {
touch /etc/config/$uc_cfgfile
chmod 644 /etc/config/$uc_cfgfile
}
# No error and uc_data then execute (eval)
# this will call functions defined above
[ $uc_ret -eq 0 -a -n "$uc_data" ] && eval "$uc_data"
# add config ddns "global" (ignore error if exists)
$uc_uci set ddns.global="$g_cfgfile"
# write changes to config file
$uc_uci commit "$g_cfgfile"
unset uc_uci uc_cfg uc_name uc_var uc_val uc_ret uc_data
return 0
}
# clear LuCI indexcache
rm -f /tmp/luci-indexcache >/dev/null 2>&1
# do config update
update_config
#cleanup
[ $g_pslerr -ne 0 ] && {
unset g_pslfile g_pslerr g_cfgfile
return 1
}
[ -f "$g_pslfile" ] && rm -f "$g_pslfile"
unset g_pslfile g_pslerr g_cfgfile
return 0

View File

@ -0,0 +1,32 @@
#
# Please read https://openwrt.org/docs/guide-user/base-system/ddns
#
config ddns "global"
option ddns_dateformat "%F %R"
# option ddns_rundir "/var/run/ddns"
# option ddns_logdir "/var/log/ddns"
option ddns_loglines "250"
option upd_privateip "0"
config service "myddns_ipv4"
option service_name "dyndns.org"
option lookup_host "yourhost.example.com"
option domain "yourhost.example.com"
option username "your_username"
option password "your_password"
option interface "wan"
option ip_source "network"
option ip_network "wan"
config service "myddns_ipv6"
option update_url "http://[USERNAME]:[PASSWORD]@your.provider.net/nic/update?hostname=[DOMAIN]&myip=[IP]"
option lookup_host "yourhost.example.com"
option domain "yourhost.example.com"
option username "your_username"
option password "your_password"
option use_ipv6 "1"
option interface "wan6"
option ip_source "network"
option ip_network "wan6"

View File

@ -2,10 +2,6 @@
START=95
STOP=10
boot() {
start "$@"
}
reload() {
/usr/lib/ddns/dynamic_dns_updater.sh -- reload
return 0

View File

@ -1,97 +0,0 @@
#!/bin/sh
#.Distributed under the terms of the GNU General Public License (GPL) version 2.0
#.based on Yuval Adam's route53.sh found at https://github.com/yuvadm/route53-ddns/blob/master/route53.sh
#.2017 Max Berger <max at berger dot name>
[ -z "$CURL_SSL" ] && write_log 14 "Amazon AWS Route53 communication require cURL with SSL support. Please install"
[ -z "$username" ] && write_log 14 "Service section not configured correctly! Missing key as 'username'"
[ -z "$password" ] && write_log 14 "Service section not configured correctly! Missing secret as 'password'"
[ -z "$domain" ] && write_log 14 "Service section not configured correctly! Missing zone id as 'domain'"
set -euo pipefail
IFS=$'\n\t'
ENDPOINT="route53.amazonaws.com"
RECORD_TTL=300
RECORD_NAME="$lookup_host".
[ $use_ipv6 -eq 0 ] && RECORD_TYPE="A"
[ $use_ipv6 -eq 1 ] && RECORD_TYPE="AAAA"
RECORD_VALUE="$LOCAL_IP"
HOSTED_ZONE_ID="$domain"
API_PATH="/2013-04-01/hostedzone/${HOSTED_ZONE_ID}/rrset/"
AWS_ACCESS_KEY_ID="$username"
AWS_SECRET_ACCESS_KEY="$password"
AWS_REGION='us-east-1'
AWS_SERVICE='route53'
hash() {
msg=$1
echo -en "$msg" | openssl dgst -sha256 | sed 's/^.* //'
}
sign_plain() {
# Sign message using a plaintext key
key=$1
msg=$2
echo -en "$msg" | openssl dgst -hex -sha256 -hmac "$key" | sed 's/^.* //'
}
sign() {
# Sign message using a hex formatted key
key=$1
msg=$2
echo -en "$msg" | openssl dgst -hex -sha256 -mac HMAC -macopt "hexkey:${key}" | sed 's/^.* //'
}
request_body="<?xml version=\"1.0\" encoding=\"UTF-8\"?> \
<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\"> \
<ChangeBatch> \
<Changes> \
<Change> \
<Action>UPSERT</Action> \
<ResourceRecordSet> \
<Name>${RECORD_NAME}</Name> \
<Type>${RECORD_TYPE}</Type> \
<TTL>${RECORD_TTL}</TTL> \
<ResourceRecords> \
<ResourceRecord> \
<Value>${RECORD_VALUE}</Value> \
</ResourceRecord> \
</ResourceRecords> \
</ResourceRecordSet> \
</Change> \
</Changes> \
</ChangeBatch> \
</ChangeResourceRecordSetsRequest>"
fulldate=$(date --utc +%Y%m%dT%H%M%SZ)
shortdate=$(date --utc +%Y%m%d)
signed_headers="host;x-amz-date"
request_hash=$(hash "$request_body")
canonical_request="POST\n${API_PATH}\n\nhost:route53.amazonaws.com\nx-amz-date:${fulldate}\n\n${signed_headers}\n${request_hash}"
date_key=$(sign_plain "AWS4${AWS_SECRET_ACCESS_KEY}" "${shortdate}")
region_key=$(sign "$date_key" $AWS_REGION)
service_key=$(sign "$region_key" $AWS_SERVICE)
signing_key=$(sign "$service_key" aws4_request)
credential="${shortdate}/${AWS_REGION}/${AWS_SERVICE}/aws4_request"
sigmsg="AWS4-HMAC-SHA256\n${fulldate}\n${credential}\n$(hash "$canonical_request")"
signature=$(sign "$signing_key" "$sigmsg")
authorization="AWS4-HMAC-SHA256 Credential=${AWS_ACCESS_KEY_ID}/${credential}, SignedHeaders=${signed_headers}, Signature=${signature}"
ANSWER=$(curl \
-X "POST" \
-H "Host: route53.amazonaws.com" \
-H "X-Amz-Date: ${fulldate}" \
-H "Authorization: ${authorization}" \
-H "Content-Type: text/xml" \
-d "$request_body" \
"https://${ENDPOINT}${API_PATH}")
write_log 7 "${ANSWER}"
echo ${ANSWER} | grep Error >/dev/null && return 1
echo ${ANSWER} | grep ChangeInfo >/dev/null && return 0
return 2

View File

@ -0,0 +1,172 @@
#!/bin/sh
#
# Copyright (C) 2020 TDT AG <development@tdt.de>
#
# This is free software, licensed under the GNU General Public License v2.
# See https://www.gnu.org/licenses/gpl-2.0.txt for more information.
#
. /lib/functions.sh
DDNS_PACKAGE_DIR="/usr/share/ddns"
URL="https://raw.githubusercontent.com/openwrt/packages/master/net/ddns-scripts/files"
usage() {
local code="$1"
local msg="$2"
echo "$msg"
echo ""
echo "Usage: $(basename "$0") <command> <action> <service>"
echo ""
echo "Supported ddns <command>:"
echo " service: Command for custom ddns service providers"
echo ""
echo "Supported ddns 'service' command <action>:"
echo " update: Update local custom ddns service list"
echo " list-available: List all available custom service providers"
echo " list-installed: List all installed custom service providers"
echo " install <service>: Install custom service provider"
echo " remove <service>: Remove custom service provider"
echo " purge: Remove local custom ddns services"
exit "$code"
}
action_update() {
local cacert
config_load ddns
config_get url global 'url' "${URL}${DDNS_PACKAGE_DIR}"
config_get cacert global 'cacert' "IGNORE"
url="${url}/list"
mkdir -p "${DDNS_PACKAGE_DIR}"
if [ "$cacert" = "IGNORE" ]; then
uclient-fetch \
--no-check-certificate \
"$url" \
-O "${DDNS_PACKAGE_DIR}/list"
elif [ -f "$cacert" ]; then
uclient-fetch \
--ca-certificate="${cacert}" \
"$url" \
-O "${DDNS_PACKAGE_DIR}/list"
elif [ -n "$cacert" ]; then
echo "Certification file not found ($cacert)"
exit 5
fi
}
action_list_available() {
if [ -f "${DDNS_PACKAGE_DIR}/list" ]; then
cat "${DDNS_PACKAGE_DIR}/list"
else
echo "No custom service list file found. Please download first"
exit 3
fi
}
action_list_installed() {
if [ -d "${DDNS_PACKAGE_DIR}/custom" ]; then
ls "${DDNS_PACKAGE_DIR}/custom"
else
echo "No custom services installed"
exit 4
fi
}
action_install() {
local service="$1"
local url cacert
config_load ddns
config_get url global 'url' "${URL}${DDNS_PACKAGE_DIR}/default"
config_get cacert global 'cacert' "IGNORE"
url="${url}/${service}.json"
if [ -z "$service" ]; then
usage "4" "No custom service specified"
fi
mkdir -p "${DDNS_PACKAGE_DIR}/custom"
if [ "$cacert" = "IGNORE" ]; then
uclient-fetch \
--no-check-certificate \
"${url}" \
-O "${DDNS_PACKAGE_DIR}/custom/${service}.json"
elif [ -f "$cacert" ]; then
uclient-fetch \
--ca-certifcate="${cacert}" \
"${url}" \
-O "${DDNS_PACKAGE_DIR}/custom/${service}.json"
elif [ -n "$cacert" ]; then
echo "Certification file not found ($cacert)"
exit 5
fi
}
action_remove() {
local service="$1"
if [ -z "$service" ]; then
usage "4" "No custom service specified"
fi
rm "${DDNS_PACKAGE_DIR}/custom/${service}.json"
}
action_purge() {
rm -rf "${DDNS_PACKAGE_DIR}/custom"
rm -rf "${DDNS_PACKAGE_DIR}/list"
}
sub_service() {
local action="$1"
local service="$2"
case "$action" in
update)
action_update
;;
list-available)
action_list_available
;;
list-installed)
action_list_installed
;;
purge)
action_purge
;;
install)
action_install "$service"
;;
remove)
action_remove "$service"
;;
*)
usage "2" "Action not supported"
;;
esac
}
main() {
local cmd="$1"
local action="$2"
local service="$3"
[ "$#" -eq 0 ] && usage "1"
case "${cmd}" in
service)
sub_service "${action}" "${service}"
;;
*)
usage "1" "Command not supported"
;;
esac
}
main "$@"

View File

@ -21,9 +21,14 @@
. /lib/functions/network.sh
# GLOBAL VARIABLES #
VERSION="2.7.8-1"
if [ -f "/usr/share/ddns/version" ]; then
VERSION="$(cat "/usr/share/ddns/version")"
else
VERSION="unknown"
fi
SECTION_ID="" # hold config's section name
VERBOSE=0 # default mode is log to console, but easily changed with parameter
DRY_RUN=0 # run without actually doing (sending) any changes
MYPROG=$(basename $0) # my program call name
LOGFILE="" # logfile - all files are set in dynamic_dns_updater.sh
@ -43,8 +48,8 @@ CURR_TIME=0 # holds the current uptime
NEXT_TIME=0 # calculated time for next FORCED update
EPOCH_TIME=0 # seconds since 1.1.1970 00:00:00
CURRENT_IP="" # holds the current IP read from the box
REGISTERED_IP="" # holds the IP read from DNS
LOCAL_IP="" # holds the local IP read from the box
URL_USER="" # url encoded $username from config file
URL_PASS="" # url encoded $password from config file
@ -53,7 +58,7 @@ URL_PENC="" # url encoded $param_enc from config file
UPD_ANSWER="" # Answer given by service on success
ERR_LAST=0 # used to save $? return code of program and function calls
ERR_UPDATE=0 # error counter on different local and registered ip
RETRY_COUNT=0 # error counter on different current and registered IPs
PID_SLEEP=0 # ProcessID of current background "sleep"
@ -63,31 +68,36 @@ IPV4_REGEX="[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}"
# IPv6 ( ( 0-9a-f 1-4char ":") min 1x) ( ( 0-9a-f 1-4char )optional) ( (":" 0-9a-f 1-4char ) min 1x)
IPV6_REGEX="\(\([0-9A-Fa-f]\{1,4\}:\)\{1,\}\)\(\([0-9A-Fa-f]\{1,4\}\)\{0,1\}\)\(\(:[0-9A-Fa-f]\{1,4\}\)\{1,\}\)"
# characters that are dangerous to pass to a shell command line
SHELL_ESCAPE="[\"\'\`\$\!();><{}?|\[\]\*\\\\]"
# dns character set. "-" must be the last character
DNS_CHARSET="[@a-zA-Z0-9._-]"
# domains can have * for wildcard. "-" must be the last character
DNS_CHARSET_DOMAIN="[@a-zA-Z0-9._*-]"
# detect if called by ddns-lucihelper.sh script, disable retrys (empty variable == false)
LUCI_HELPER=$(printf %s "$MYPROG" | grep -i "luci")
# Name Server Lookup Programs
BIND_HOST=$(which host)
KNOT_HOST=$(which khost)
DRILL=$(which drill)
HOSTIP=$(which hostip)
NSLOOKUP=$(which nslookup)
BIND_HOST=$(command -v host)
KNOT_HOST=$(command -v khost)
DRILL=$(command -v drill)
HOSTIP=$(command -v hostip)
NSLOOKUP=$(command -v nslookup)
# Transfer Programs
WGET=$(which wget)
WGET_SSL=$(which wget-ssl)
CURL=$(which curl)
WGET=$(command -v wget)
$WGET -V 2>/dev/null | grep -F -q +https && WGET_SSL=$WGET
CURL=$(command -v curl)
# CURL_SSL not empty then SSL support available
CURL_SSL=$($CURL -V 2>/dev/null | grep -F "https")
# CURL_PROXY not empty then Proxy support available
if [ -f /tmp/vCURL_PROXY ]; then
CURL_PROXY=$(cat /tmp/vCURL_PROXY);
else
CURL_PROXY=$(find /lib /usr/lib -name libcurl.so* -exec strings {} 2>/dev/null \; | grep -im1 "all_proxy")
echo -n $CURL_PROXY >/tmp/vCURL_PROXY
fi
CURL_PROXY=$(find /lib /usr/lib -name libcurl.so* -exec strings {} 2>/dev/null \; | grep -im1 "all_proxy")
UCLIENT_FETCH=$(which uclient-fetch)
UCLIENT_FETCH=$(command -v uclient-fetch)
# Global configuration settings
# allow NON-public IP's
@ -280,11 +290,11 @@ write_log() {
[ $__LEVEL -eq 7 ] && return # no syslog for debug messages
__CMD=$(echo -e "$__CMD" | tr -d '\n' | tr '\t' ' ') # remove \n \t chars
[ $__EXIT -eq 1 ] && {
$__CMD # force syslog before exit
eval '$__CMD' # force syslog before exit
exit 1
}
[ $use_syslog -eq 0 ] && return
[ $((use_syslog + __LEVEL)) -le 7 ] && $__CMD
[ $((use_syslog + __LEVEL)) -le 7 ] && eval '$__CMD'
return
}
@ -299,74 +309,61 @@ write_log() {
urlencode() {
# $1 Name of Variable to store encoded string to
# $2 string to encode
local __STR __LEN __CHAR __OUT
local __ENC=""
local __POS=1
local __ENC
[ $# -ne 2 ] && write_log 12 "Error calling 'urlencode()' - wrong number of parameters"
__STR="$2" # read string to encode
__LEN=${#__STR} # get string length
while [ $__POS -le $__LEN ]; do
# read one chat of the string
__CHAR=$(expr substr "$__STR" $__POS 1)
case "$__CHAR" in
[-_.~a-zA-Z0-9] )
# standard char
__OUT="${__CHAR}"
;;
* )
# special char get %hex code
__OUT=$(printf '%%%02x' "'$__CHAR" )
;;
esac
__ENC="${__ENC}${__OUT}" # append to encoded string
__POS=$(( $__POS + 1 )) # increment position
done
__ENC="$(awk -v str="$2" 'BEGIN{ORS="";for(i=32;i<=127;i++)lookup[sprintf("%c",i)]=i
for(k=1;k<=length(str);++k){enc=substr(str,k,1);if(enc!~"[-_.~a-zA-Z0-9]")enc=sprintf("%%%02x", lookup[enc]);print enc}}')"
eval "$1=\"$__ENC\"" # transfer back to variable
return 0
}
# extract url or script for given DDNS Provider from
# file /etc/ddns/services for IPv4 or from
# file /etc/ddns/services_ipv6 for IPv6
# $1 Name of Variable to store url to
# $2 Name of Variable to store script to
# $3 Name of Variable to store service answer to
# $1 Name of the provider
# $2 Provider directory
# $3 Name of Variable to store url to
# $4 Name of Variable to store script to
# $5 Name of Variable to store service answer to
get_service_data() {
local provider="$1"
shift
local dir="$1"
shift
. /usr/share/libubox/jshn.sh
local name data url answer script
[ $# -ne 3 ] && write_log 12 "Error calling 'get_service_data()' - wrong number of parameters"
__FILE="/etc/ddns/services" # IPv4
[ $use_ipv6 -ne 0 ] && __FILE="/etc/ddns/services_ipv6" # IPv6
[ -f "${dir}/${provider}.json" ] || {
eval "$1=\"\""
eval "$2=\"\""
eval "$3=\"\""
return 1
}
# workaround with variables; pipe create subshell with no give back of variable content
mkfifo pipe_$$
# only grep without # or whitespace at linestart | remove "
# grep -v -E "(^#|^[[:space:]]*$)" $__FILE | sed -e s/\"//g > pipe_$$ &
sed '/^#/d; /^[ \t]*$/d; s/\"//g' $__FILE > pipe_$$ &
json_load_file "${dir}/${provider}.json"
json_get_var name "name"
if [ "$use_ipv6" -eq "1" ]; then
json_select "ipv6"
else
json_select "ipv4"
fi
json_get_var data "url"
json_get_var answer "answer"
json_select ".."
json_cleanup
while read __SERVICE __DATA __ANSWER; do
if [ "$__SERVICE" = "$service_name" ]; then
# check if URL or SCRIPT is given
__URL=$(echo "$__DATA" | grep "^http")
[ -z "$__URL" ] && __SCRIPT="/usr/lib/ddns/$__DATA"
# check if URL or SCRIPT is given
url=$(echo "$data" | grep "^http")
[ -z "$url" ] && script="/usr/lib/ddns/${data}"
eval "$1=\"$__URL\""
eval "$2=\"$__SCRIPT\""
eval "$3=\"$__ANSWER\""
rm pipe_$$
return 0
fi
done < pipe_$$
rm pipe_$$
eval "$1=\"\"" # no service match clear variables
eval "$2=\"\""
eval "$3=\"\""
return 1
eval "$1=\"$url\""
eval "$2=\"$script\""
eval "$3=\"$answer\""
return 0
}
# Calculate seconds from interval and unit
@ -479,14 +476,35 @@ timeout() {
return $status
}
# sanitize a variable
# $1 variable name
# $2 allowed shell pattern
# $3 disallowed shell pattern
sanitize_variable() {
local __VAR=$1
eval __VALUE=\$$__VAR
local __ALLOWED=$2
local __REJECT=$3
# removing all allowed should give empty string
if [ -n "$__ALLOWED" ]; then
[ -z "${__VALUE//$__ALLOWED}" ] || write_log 12 "sanitize on $__VAR found characters outside allowed subset"
fi
# removing rejected pattern should give the same string as the input
if [ -n "$__REJECT" ]; then
[ "$__VALUE" = "${__VALUE//$__REJECT}" ] || write_log 12 "sanitize on $__VAR found rejected characters"
fi
}
# verify given host and port is connectable
# $1 Host/IP to verify
# $2 Port to verify
verify_host_port() {
local __HOST=$1
local __PORT=$2
local __NC=$(which nc)
local __NCEXT=$($(which nc) --help 2>&1 | grep "\-w" 2>/dev/null) # busybox nc compiled with extensions
local __NC=$(command -v nc)
local __NCEXT=$($(command -v nc) --help 2>&1 | grep "\-w" 2>/dev/null) # busybox nc compiled with extensions
local __IP __IPV4 __IPV6 __RUNPROG __PROG __ERR
# return codes
# 1 system specific error
@ -529,18 +547,21 @@ verify_host_port() {
return 2
}
# extract IP address
if [ -n "$BIND_HOST" -o -n "$KNOT_HOST" ]; then # use BIND host or Knot host if installed
__IPV4=$(cat $DATFILE | awk -F "address " '/has address/ {print $2; exit}' )
__IPV6=$(cat $DATFILE | awk -F "address " '/has IPv6/ {print $2; exit}' )
if [ -n "$BIND_HOST" ]; then # use BIND host if installed
__IPV4="$(awk -F "address " '/has address/ {print $2; exit}' "$DATFILE")"
__IPV6="$(awk -F "address " '/has IPv6/ {print $2; exit}' "$DATFILE")"
elif [ -n "$KNOT_HOST" ]; then # use Knot host if installed
__IPV4="$(awk -F "address " '/has IPv4/ {print $2; exit}' "$DATFILE")"
__IPV6="$(awk -F "address " '/has IPv6/ {print $2; exit}' "$DATFILE")"
elif [ -n "$DRILL" ]; then # use drill if installed
__IPV4=$(cat $DATFILE | awk '/^'"$lookup_host"'/ {print $5}' | grep -m 1 -o "$IPV4_REGEX")
__IPV6=$(cat $DATFILE | awk '/^'"$lookup_host"'/ {print $5}' | grep -m 1 -o "$IPV6_REGEX")
__IPV4="$(awk '/^'"$__HOST"'/ {print $5}' "$DATFILE" | grep -m 1 -o "$IPV4_REGEX")"
__IPV6="$(awk '/^'"$__HOST"'/ {print $5}' "$DATFILE" | grep -m 1 -o "$IPV6_REGEX")"
elif [ -n "$HOSTIP" ]; then # use hostip if installed
__IPV4=$(cat $DATFILE | grep -m 1 -o "$IPV4_REGEX")
__IPV6=$(cat $DATFILE | grep -m 1 -o "$IPV6_REGEX")
__IPV4="$(grep -m 1 -o "$IPV4_REGEX" "$DATFILE")"
__IPV6="$(grep -m 1 -o "$IPV6_REGEX" "$DATFILE")"
else # use BusyBox nslookup
__IPV4=$(cat $DATFILE | sed -ne "/^Name:/,\$ { s/^Address[0-9 ]\{0,\}: \($IPV4_REGEX\).*$/\\1/p }")
__IPV6=$(cat $DATFILE | sed -ne "/^Name:/,\$ { s/^Address[0-9 ]\{0,\}: \($IPV6_REGEX\).*$/\\1/p }")
__IPV4="$(sed -ne "/^Name:/,\$ { s/^Address[0-9 ]\{0,\}: \($IPV4_REGEX\).*$/\\1/p }" "$DATFILE")"
__IPV6="$(sed -ne "/^Name:/,\$ { s/^Address[0-9 ]\{0,\}: \($IPV6_REGEX\).*$/\\1/p }" "$DATFILE")"
fi
}
@ -610,11 +631,11 @@ verify_dns() {
return $__ERR
elif [ $__ERR -ne 0 ]; then
__CNT=$(( $__CNT + 1 )) # increment error counter
# if error count > retry_count leave here
[ $retry_count -gt 0 -a $__CNT -gt $retry_count ] && \
write_log 14 "Verify DNS server '$1' failed after $retry_count retries"
# if error count > retry_max_count leave here
[ $retry_max_count -gt 0 -a $__CNT -gt $retry_max_count ] && \
write_log 14 "Verify DNS server '$1' failed after $retry_max_count retries"
write_log 4 "Verify DNS server '$1' failed - retry $__CNT/$retry_count in $RETRY_SECONDS seconds"
write_log 4 "Verify DNS server '$1' failed - retry $__CNT/$retry_max_count in $RETRY_SECONDS seconds"
sleep $RETRY_SECONDS &
PID_SLEEP=$!
wait $PID_SLEEP # enable trap-handler
@ -670,11 +691,11 @@ verify_proxy() {
return $__ERR
elif [ $__ERR -gt 0 ]; then
__CNT=$(( $__CNT + 1 )) # increment error counter
# if error count > retry_count leave here
[ $retry_count -gt 0 -a $__CNT -gt $retry_count ] && \
write_log 14 "Verify Proxy server '$1' failed after $retry_count retries"
# if error count > retry_max_count leave here
[ $retry_max_count -gt 0 -a $__CNT -gt $retry_max_count ] && \
write_log 14 "Verify Proxy server '$1' failed after $retry_max_count retries"
write_log 4 "Verify Proxy server '$1' failed - retry $__CNT/$retry_count in $RETRY_SECONDS seconds"
write_log 4 "Verify Proxy server '$1' failed - retry $__CNT/$retry_max_count in $RETRY_SECONDS seconds"
sleep $RETRY_SECONDS &
PID_SLEEP=$!
wait $PID_SLEEP # enable trap-handler
@ -693,16 +714,19 @@ do_transfer() {
[ $# -ne 1 ] && write_log 12 "Error in 'do_transfer()' - wrong number of parameters"
# Use ip_network as default for bind_network if not separately specified
[ -z "$bind_network" ] && [ "$ip_source" = "network" ] && [ "$ip_network" ] && bind_network="$ip_network"
# lets prefer GNU Wget because it does all for us - IPv4/IPv6/HTTPS/PROXY/force IP version
if [ -n "$WGET_SSL" -a $USE_CURL -eq 0 ]; then # except global option use_curl is set to "1"
__PROG="$WGET_SSL -nv -t 1 -O $DATFILE -o $ERRFILE" # non_verbose no_retry outfile errfile
if [ -n "$WGET_SSL" ] && [ $USE_CURL -eq 0 ]; then # except global option use_curl is set to "1"
__PROG="$WGET --hsts-file=/tmp/.wget-hsts -nv -t 1 -O $DATFILE -o $ERRFILE" # non_verbose no_retry outfile errfile
# force network/ip to use for communication
if [ -n "$bind_network" ]; then
local __BINDIP
# set correct program to detect IP
[ $use_ipv6 -eq 0 ] && __RUNPROG="network_get_ipaddr" || __RUNPROG="network_get_ipaddr6"
eval "$__RUNPROG __BINDIP $bind_network" || \
write_log 13 "Can not detect local IP using '$__RUNPROG $bind_network' - Error: '$?'"
write_log 13 "Can not detect current IP using '$__RUNPROG $bind_network' - Error: '$?'"
write_log 7 "Force communication via IP '$__BINDIP'"
__PROG="$__PROG --bind-address=$__BINDIP"
fi
@ -725,14 +749,19 @@ do_transfer() {
# disable proxy if no set (there might be .wgetrc or .curlrc or wrong environment set)
[ -z "$proxy" ] && __PROG="$__PROG --no-proxy"
# user agent string if provided
if [ -n "$user_agent" ]; then
# replace single and double quotes
user_agent=$(echo $user_agent | sed "s/'/ /g" | sed 's/"/ /g')
__PROG="$__PROG --user-agent='$user_agent'"
fi
__RUNPROG="$__PROG '$__URL'" # build final command
__PROG="GNU Wget" # reuse for error logging
# 2nd choice is cURL IPv4/IPv6/HTTPS
# libcurl might be compiled without Proxy or HTTPS Support
elif [ -n "$CURL" ]; then
# CURL_SSL not empty then SSL support available
CURL_SSL=$($(which curl) -V 2>/dev/null | grep "Protocols:" | grep -F "https")
__PROG="$CURL -RsS -o $DATFILE --stderr $ERRFILE"
# check HTTPS support
[ -z "$CURL_SSL" -a $use_https -eq 1 ] && \
@ -740,8 +769,8 @@ do_transfer() {
# force network/interface-device to use for communication
if [ -n "$bind_network" ]; then
local __DEVICE
network_get_physdev __DEVICE $bind_network || \
write_log 13 "Can not detect local device using 'network_get_physdev $bind_network' - Error: '$?'"
network_get_device __DEVICE $bind_network || \
write_log 13 "Can not detect local device using 'network_get_device $bind_network' - Error: '$?'"
write_log 7 "Force communication via device '$__DEVICE'"
__PROG="$__PROG --interface $__DEVICE"
fi
@ -776,13 +805,7 @@ do_transfer() {
# uclient-fetch possibly with ssl support if /lib/libustream-ssl.so installed
elif [ -n "$UCLIENT_FETCH" ]; then
# UCLIENT_FETCH_SSL not empty then SSL support available
if [ -f /tmp/vUCLIENT_FETCH_SSL ]; then
UCLIENT_FETCH_SSL=$(cat /tmp/vCURL_PROXY);
else
UCLIENT_FETCH_SSL=$(find /lib /usr/lib -name libustream-ssl.so* 2>/dev/null)
echo -n $UCLIENT_FETCH_SSL >/tmp/vUCLIENT_FETCH_SSL
fi
UCLIENT_FETCH_SSL=$(find /lib /usr/lib -name libustream-ssl.so* 2>/dev/null)
__PROG="$UCLIENT_FETCH -q -O $DATFILE"
# force network/ip not supported
[ -n "$__BINDIP" ] && \
@ -848,11 +871,11 @@ do_transfer() {
}
__CNT=$(( $__CNT + 1 )) # increment error counter
# if error count > retry_count leave here
[ $retry_count -gt 0 -a $__CNT -gt $retry_count ] && \
write_log 14 "Transfer failed after $retry_count retries"
# if error count > retry_max_count leave here
[ $retry_max_count -gt 0 -a $__CNT -gt $retry_max_count ] && \
write_log 14 "Transfer failed after $retry_max_count retries"
write_log 4 "Transfer failed - retry $__CNT/$retry_count in $RETRY_SECONDS seconds"
write_log 4 "Transfer failed - retry $__CNT/$retry_max_count in $RETRY_SECONDS seconds"
sleep $RETRY_SECONDS &
PID_SLEEP=$!
wait $PID_SLEEP # enable trap-handler
@ -904,26 +927,26 @@ send_update() {
fi
}
get_local_ip () {
# $1 Name of Variable to store local IP (LOCAL_IP)
get_current_ip () {
# $1 Name of Variable to store current IP
local __CNT=0 # error counter
local __RUNPROG __DATA __URL __ERR
[ $# -ne 1 ] && write_log 12 "Error calling 'get_local_ip()' - wrong number of parameters"
write_log 7 "Detect local IP on '$ip_source'"
[ $# -ne 1 ] && write_log 12 "Error calling 'get_current_ip()' - wrong number of parameters"
write_log 7 "Detect current IP on '$ip_source'"
while : ; do
if [ -n "$ip_network" ]; then
if [ -n "$ip_network" -a "$ip_source" = "network" ]; then
# set correct program
network_flush_cache # force re-read data from ubus
[ $use_ipv6 -eq 0 ] && __RUNPROG="network_get_ipaddr" \
|| __RUNPROG="network_get_ipaddr6"
eval "$__RUNPROG __DATA $ip_network" || \
write_log 13 "Can not detect local IP using $__RUNPROG '$ip_network' - Error: '$?'"
[ -n "$__DATA" ] && write_log 7 "Local IP '$__DATA' detected on network '$ip_network'"
elif [ -n "$ip_interface" ]; then
write_log 13 "Can not detect current IP using $__RUNPROG '$ip_network' - Error: '$?'"
[ -n "$__DATA" ] && write_log 7 "Current IP '$__DATA' detected on network '$ip_network'"
elif [ -n "$ip_interface" -a "$ip_source" = "interface" ]; then
local __DATA4=""; local __DATA6=""
if [ -n "$(which ip)" ]; then # ip program installed
if [ -n "$(command -v ip)" ]; then # ip program installed
write_log 7 "#> ip -o addr show dev $ip_interface scope global >$DATFILE 2>$ERRFILE"
ip -o addr show dev $ip_interface scope global >$DATFILE 2>$ERRFILE
__ERR=$?
@ -939,10 +962,17 @@ get_local_ip () {
# 5: eth1 inet6 fd43:5368:6f6d:6500:a00:27ff:fed0:1032/64 scope global dynamic \ valid_lft 14352sec preferred_lft 14352sec
# 5: eth1 inet6 2002:b0c7:f326::a00:27ff:fed0:1032/64 scope global dynamic \ valid_lft 14352sec preferred_lft 14352sec
# remove remove remove replace replace
# link inet6 fxxx sec forever=>-1 / => ' ' to separate subnet from ip
sed "/link/d; /inet6 f/d; s/sec//g; s/forever/-1/g; s/\// /g" $DATFILE | \
awk '{ print $3" "$4" "$NF }' > $ERRFILE # temp reuse ERRFILE
if [ $upd_privateip -eq 0 ]; then
# remove remove remove replace replace
# link inet6 fxxx sec forever=>-1 / => ' ' to separate subnet from ip
sed "/link/d; /inet6 f/d; s/sec//g; s/forever/-1/g; s/\// /g" $DATFILE | \
awk '{ print $3" "$4" "$NF }' > $ERRFILE # temp reuse ERRFILE
else
# remove remove replace replace
# link sec forever=>-1 / => ' ' to separate subnet from ip
sed "/link/d; s/sec//g; s/forever/-1/g; s/\// /g" $DATFILE | \
awk '{ print $3" "$4" "$NF }' > $ERRFILE # temp reuse ERRFILE
fi
# we only need inet? IP prefered time
local __TIME4=0; local __TIME6=0
@ -999,27 +1029,27 @@ get_local_ip () {
fi
fi
[ $use_ipv6 -eq 0 ] && __DATA="$__DATA4" || __DATA="$__DATA6"
[ -n "$__DATA" ] && write_log 7 "Local IP '$__DATA' detected on interface '$ip_interface'"
elif [ -n "$ip_script" ]; then
[ -n "$__DATA" ] && write_log 7 "Current IP '$__DATA' detected on interface '$ip_interface'"
elif [ -n "$ip_script" -a "$ip_source" = "script" ]; then
write_log 7 "#> $ip_script >$DATFILE 2>$ERRFILE"
eval $ip_script >$DATFILE 2>$ERRFILE
__ERR=$?
if [ $__ERR -eq 0 ]; then
__DATA=$(cat $DATFILE)
[ -n "$__DATA" ] && write_log 7 "Local IP '$__DATA' detected via script '$ip_script'"
[ -n "$__DATA" ] && write_log 7 "Current IP '$__DATA' detected via script '$ip_script'"
else
write_log 3 "$ip_script Error: '$__ERR'"
write_log 7 "$(cat $ERRFILE)" # report error
fi
elif [ -n "$ip_url" ]; then
elif [ -n "$ip_url" -a "$ip_source" = "web" ]; then
do_transfer "$ip_url"
# use correct regular expression
[ $use_ipv6 -eq 0 ] \
&& __DATA=$(grep -m 1 -o "$IPV4_REGEX" $DATFILE) \
|| __DATA=$(grep -m 1 -o "$IPV6_REGEX" $DATFILE)
[ -n "$__DATA" ] && write_log 7 "Local IP '$__DATA' detected on web at '$ip_url'"
[ -n "$__DATA" ] && write_log 7 "Current IP '$__DATA' detected on web at '$ip_url'"
else
write_log 12 "Error in 'get_local_ip()' - unhandled ip_source '$ip_source'"
write_log 12 "Error in 'get_current_ip()' - unhandled ip_source '$ip_source'"
fi
# valid data found return here
[ -n "$__DATA" ] && {
@ -1034,22 +1064,22 @@ get_local_ip () {
[ $VERBOSE -gt 1 ] && {
# VERBOSE > 1 then NO retry
write_log 4 "Get local IP via '$ip_source' failed - Verbose Mode: $VERBOSE - NO retry on error"
write_log 4 "Get current IP via '$ip_source' failed - Verbose Mode: $VERBOSE - NO retry on error"
return 1
}
__CNT=$(( $__CNT + 1 )) # increment error counter
# if error count > retry_count leave here
[ $retry_count -gt 0 -a $__CNT -gt $retry_count ] && \
write_log 14 "Get local IP via '$ip_source' failed after $retry_count retries"
write_log 4 "Get local IP via '$ip_source' failed - retry $__CNT/$retry_count in $RETRY_SECONDS seconds"
# if error count > retry_max_count leave here
[ $retry_max_count -gt 0 -a $__CNT -gt $retry_max_count ] && \
write_log 14 "Get current IP via '$ip_source' failed after $retry_max_count retries"
write_log 4 "Get current IP via '$ip_source' failed - retry $__CNT/$retry_max_count in $RETRY_SECONDS seconds"
sleep $RETRY_SECONDS &
PID_SLEEP=$!
wait $PID_SLEEP # enable trap-handler
PID_SLEEP=0
done
# we should never come here there must be a programming error
write_log 12 "Error in 'get_local_ip()' - program coding error"
write_log 12 "Error in 'get_current_ip()' - program coding error"
}
get_registered_ip() {
@ -1126,7 +1156,7 @@ get_registered_ip() {
__RUNPROG="$__PROG $lookup_host >$DATFILE 2>$ERRFILE"
__PROG="hostip"
elif [ -n "$NSLOOKUP" ]; then # last use BusyBox nslookup
NSLOOKUP_MUSL=$($(which nslookup) localhost 2>&1 | grep -F "(null)") # not empty busybox compiled with musl
NSLOOKUP_MUSL=$($(command -v nslookup) localhost 2>&1 | grep -F "(null)") # not empty busybox compiled with musl
[ $force_dnstcp -ne 0 ] && \
write_log 14 "Busybox nslookup - no support for 'DNS over TCP'"
[ -n "$NSLOOKUP_MUSL" -a -n "$dns_server" ] && \
@ -1181,11 +1211,11 @@ get_registered_ip() {
}
__CNT=$(( $__CNT + 1 )) # increment error counter
# if error count > retry_count leave here
[ $retry_count -gt 0 -a $__CNT -gt $retry_count ] && \
write_log 14 "Get registered/public IP for '$lookup_host' failed after $retry_count retries"
# if error count > retry_max_count leave here
[ $retry_max_count -gt 0 -a $__CNT -gt $retry_max_count ] && \
write_log 14 "Get registered/public IP for '$lookup_host' failed after $retry_max_count retries"
write_log 4 "Get registered/public IP for '$lookup_host' failed - retry $__CNT/$retry_count in $RETRY_SECONDS seconds"
write_log 4 "Get registered/public IP for '$lookup_host' failed - retry $__CNT/$retry_max_count in $RETRY_SECONDS seconds"
sleep $RETRY_SECONDS &
PID_SLEEP=$!
wait $PID_SLEEP # enable trap-handler

View File

@ -137,11 +137,11 @@ case "$1" in
if [ "$ip_source" = "web" -o "$ip_source" = "script" ]; then
# we wait only 3 seconds for an
# answer from "web" or "script"
write_log 7 "-----> timeout 3 -- get_local_ip IP"
timeout 3 -- get_local_ip IP
write_log 7 "-----> timeout 3 -- get_current_ip IP"
timeout 3 -- get_current_ip IP
else
write_log 7 "-----> get_local_ip IP"
get_local_ip IP
write_log 7 "-----> get_current_ip IP"
get_current_ip IP
fi
__RET=$?
;;

View File

@ -37,9 +37,7 @@ Parameters:
'1' output to console
'2' output to console AND logfile
+ run once WITHOUT retry on error
'3' output to console AND logfile
+ run once WITHOUT retry on error
+ NOT sending update to DDNS service
-d dry run (don't send any changes)
EOF
}
@ -50,10 +48,11 @@ usage_err() {
exit 1
}
while getopts ":hv:n:S:V" OPT; do
while getopts ":hv:dn:S:V" OPT; do
case "$OPT" in
h) usage; exit 0;;
v) VERBOSE=$OPTARG;;
d) DRY_RUN=1;;
n) NETWORK=$OPTARG;;
S) SECTION_ID=$OPTARG;;
V) printf %s\\n "ddns-scripts $VERSION"; exit 0;;
@ -90,7 +89,7 @@ case "$1" in
exit 1
;;
reload)
killall -1 dynamic_dns_updater.sh 2>/dev/null
killall dynamic_dns_updater.sh 2>/dev/null
exit $?
;;
*) usage_err "unknown command - $1";;
@ -108,6 +107,8 @@ LOGFILE="$ddns_logdir/$SECTION_ID.log" # log file
# only with this data of this run for easier diagnostic
# new one created by write_log function
[ $VERBOSE -gt 1 -a -f $LOGFILE ] && rm -f $LOGFILE
# Previously -v 3 could we used for dry run
[ $VERBOSE -ge 3 ] && DRY_RUN=1
# TRAP handler
trap "trap_handler 0 \$?" 0 # handle script exit with exit status
@ -145,10 +146,10 @@ trap "trap_handler 15" 15 # SIGTERM Termination
#
# use_syslog log activity to syslog
#
# ip_source source to detect current local IP ('network' or 'web' or 'script' or 'interface')
# ip_source source to detect current IP ('network' or 'web' or 'script' or 'interface')
# ip_network local defined network to read IP from i.e. 'wan' or 'wan6'
# ip_url URL to read local address from i.e. http://checkip.dyndns.com/ or http://checkipv6.dyndns.com/
# ip_script full path and name of your script to detect local IP
# ip_url URL to read current IP from i.e. http://checkip.dyndns.com/ or http://checkipv6.dyndns.com/
# ip_script full path and name of your script to detect current IP
# ip_interface physical interface to use for detecting
#
# check_interval check for changes every !!! checks below 10 minutes make no sense because the Internet
@ -159,13 +160,13 @@ trap "trap_handler 15" 15 # SIGTERM Termination
#
# retry_interval if error was detected retry in
# retry_unit 'days' 'hours' 'minutes' 'seconds'
# retry_count number of retries before scripts stops
# retry_max_count number of retries before scripts stops
#
# use_ipv6 detecting/sending IPv6 address
# force_ipversion force usage of IPv4 or IPv6 for the whole detection and update communication
# dns_server using a non default dns server to get Registered IP from Internet
# force_dnstcp force communication with DNS server via TCP instead of default UDP
# proxy using a proxy for communication !!! ALSO used to detect local IP via web => return proxy's IP !!!
# proxy using a proxy for communication !!! ALSO used to detect current IP via web => return proxy's IP !!!
# use_logfile self-explanatory "/var/log/ddns/$SECTION_ID.log"
# is_glue the record that should be updated is a glue record
#
@ -180,7 +181,7 @@ ERR_LAST=$? # save return code - equal 0 if SECTION_ID found
# set defaults if not defined
[ -z "$enabled" ] && enabled=0
[ -z "$retry_count" ] && retry_count=0 # endless retry
[ -z "$retry_max_count" ] && retry_max_count=0 # endless retry
[ -z "$use_syslog" ] && use_syslog=2 # syslog "Notice"
[ -z "$use_https" ] && use_https=0 # not use https
[ -z "$use_logfile" ] && use_logfile=1 # use logfile by default
@ -222,9 +223,9 @@ case $VERBOSE in
0) write_log 7 "verbose mode : 0 - run normal, NO console output";;
1) write_log 7 "verbose mode : 1 - run normal, console mode";;
2) write_log 7 "verbose mode : 2 - run once, NO retry on error";;
3) write_log 7 "verbose mode : 3 - run once, NO retry on error, NOT sending update";;
*) write_log 14 "error detecting VERBOSE '$VERBOSE'";;
esac
[ $DRY_RUN -ge 1 ] && write_log 7 "Dry Run: NOT sending update"
# check enabled state otherwise we don't need to continue
[ $enabled -eq 0 ] && write_log 14 "Service section disabled!"
@ -232,7 +233,14 @@ esac
# determine what update url we're using if a service_name is supplied
# otherwise update_url is set inside configuration (custom update url)
# or update_script is set inside configuration (custom update script)
[ -n "$service_name" ] && get_service_data update_url update_script UPD_ANSWER
[ -n "$service_name" ] && {
# Check first if we have a custom service provider with this name
get_service_data "$service_name" "/usr/share/ddns/custom" update_url update_script UPD_ANSWER
if [ "$?" != "0" ]; then
get_service_data "$service_name" "/usr/share/ddns/default" update_url update_script UPD_ANSWER
fi
}
[ -z "$update_url" -a -z "$update_script" ] && write_log 14 "No update_url found/defined or no update_script found/defined!"
[ -n "$update_script" -a ! -f "$update_script" ] && write_log 14 "Custom update_script not found!"
@ -247,6 +255,15 @@ esac
# without lookup host and possibly other required options we can do nothing for you
[ -z "$lookup_host" ] && write_log 14 "Service section not configured correctly! Missing 'lookup_host'"
# verify validity of variables
[ -n "$lookup_host" ] && sanitize_variable lookup_host "$DNS_CHARSET" ""
[ -n "$dns_server" ] && sanitize_variable dns_server "$DNS_CHARSET" ""
[ -n "$domain" ] && sanitize_variable domain "$DNS_CHARSET_DOMAIN" ""
# Filter shell escape characters, if these are required in the URL, they
# can still be passed url encoded
[ -n "$param_opt" ] && sanitize_variable param_opt "" "$SHELL_ESCAPE"
[ -n "$update_url" ] && {
# only check if update_url is given, update_scripts have to check themselves
[ -z "$domain" ] && $(echo "$update_url" | grep "\[DOMAIN\]" >/dev/null 2>&1) && \
@ -264,8 +281,8 @@ esac
# verify ip_source 'script' if script is configured and executable
if [ "$ip_source" = "script" ]; then
set -- $ip_script #handling script with parameters, we need a trick
[ -z "$1" ] && write_log 14 "No script defined to detect local IP!"
[ -x "$1" ] || write_log 14 "Script to detect local IP not executable!"
[ -z "$1" ] && write_log 14 "No script defined to detect current IP!"
[ -x "$1" ] || write_log 14 "Script to detect current IP not executable!"
fi
# compute update interval in seconds
@ -277,7 +294,7 @@ get_seconds RETRY_SECONDS ${retry_interval:-60} ${retry_unit:-"seconds"} # defau
write_log 7 "check interval: $CHECK_SECONDS seconds"
write_log 7 "force interval: $FORCE_SECONDS seconds"
write_log 7 "retry interval: $RETRY_SECONDS seconds"
write_log 7 "retry counter : $retry_count times"
write_log 7 "retry max count : $retry_max_count times"
# kill old process if it exists & set new pid file
stop_section_processes "$SECTION_ID"
@ -305,9 +322,6 @@ else
write_log 7 "last update: $(eval $EPOCH_TIME)"
fi
# verify DNS server
[ -n "$dns_server" ] && verify_dns "$dns_server"
# verify Proxy server and set environment
[ -n "$proxy" ] && {
verify_proxy "$proxy" && {
@ -331,8 +345,8 @@ ERR_LAST=$?
write_log 6 "Starting main loop at $(eval $DATE_PROG)"
while : ; do
get_local_ip LOCAL_IP # read local IP
[ $use_ipv6 -eq 1 ] && expand_ipv6 "$LOCAL_IP" LOCAL_IP # on IPv6 we use expanded version
get_current_ip CURRENT_IP # read current IP
[ $use_ipv6 -eq 1 ] && expand_ipv6 "$CURRENT_IP" CURRENT_IP # on IPv6 we use expanded version
# prepare update
# never updated or forced immediate then NEXT_TIME = 0
@ -342,24 +356,23 @@ while : ; do
get_uptime CURR_TIME # get current uptime
# send update when current time > next time or local ip different from registered ip
if [ $CURR_TIME -ge $NEXT_TIME -o "$LOCAL_IP" != "$REGISTERED_IP" ]; then
if [ $VERBOSE -gt 2 ]; then
write_log 7 "Verbose Mode: $VERBOSE - NO UPDATE send"
elif [ "$LOCAL_IP" != "$REGISTERED_IP" ]; then
write_log 7 "Update needed - L: '$LOCAL_IP' <> R: '$REGISTERED_IP'"
# send update when current time > next time or current ip different from registered ip
if [ $CURR_TIME -ge $NEXT_TIME -o "$CURRENT_IP" != "$REGISTERED_IP" ]; then
if [ $DRY_RUN -ge 1 ]; then
write_log 7 "Dry Run: NO UPDATE send"
elif [ "$CURRENT_IP" != "$REGISTERED_IP" ]; then
write_log 7 "Update needed - L: '$CURRENT_IP' <> R: '$REGISTERED_IP'"
else
write_log 7 "Forced Update - L: '$LOCAL_IP' == R: '$REGISTERED_IP'"
write_log 7 "Forced Update - L: '$CURRENT_IP' == R: '$REGISTERED_IP'"
fi
ERR_LAST=0
[ $VERBOSE -lt 3 ] && {
# only send if VERBOSE < 3
send_update "$LOCAL_IP"
[ $DRY_RUN -eq 0 ] && {
send_update "$CURRENT_IP"
ERR_LAST=$? # save return value
}
# error sending local IP to provider
# error sending current IP to provider
# we have no communication error (handled inside send_update/do_transfer)
# but update was not recognized
# do NOT retry after RETRY_SECONDS, do retry after CHECK_SECONDS
@ -368,9 +381,9 @@ while : ; do
if [ $ERR_LAST -eq 0 ]; then
get_uptime LAST_TIME # we send update, so
echo $LAST_TIME > $UPDFILE # save LASTTIME to file
[ "$LOCAL_IP" != "$REGISTERED_IP" ] \
&& write_log 6 "Update successful - IP '$LOCAL_IP' send" \
|| write_log 6 "Forced update successful - IP: '$LOCAL_IP' send"
[ "$CURRENT_IP" != "$REGISTERED_IP" ] \
&& write_log 6 "Update successful - IP '$CURRENT_IP' send" \
|| write_log 6 "Forced update successful - IP: '$CURRENT_IP' send"
elif [ $ERR_LAST -eq 127 ]; then
write_log 3 "No update send to DDNS Provider"
else
@ -379,26 +392,25 @@ while : ; do
fi
# now we wait for check interval before testing if update was recognized
# only sleep if VERBOSE <= 2 because otherwise nothing was send
[ $VERBOSE -le 2 ] && {
[ $DRY_RUN -eq 0 ] && {
write_log 7 "Waiting $CHECK_SECONDS seconds (Check Interval)"
sleep $CHECK_SECONDS &
PID_SLEEP=$!
wait $PID_SLEEP # enable trap-handler
PID_SLEEP=0
} || write_log 7 "Verbose Mode: $VERBOSE - NO Check Interval waiting"
} || write_log 7 "Dry Run: NO Check Interval waiting"
REGISTERED_IP="" # clear variable
get_registered_ip REGISTERED_IP # get registered/public IP
[ $use_ipv6 -eq 1 ] && expand_ipv6 "$REGISTERED_IP" REGISTERED_IP # on IPv6 we use expanded version
# IP's are still different
if [ "$LOCAL_IP" != "$REGISTERED_IP" ]; then
if [ "$CURRENT_IP" != "$REGISTERED_IP" ]; then
if [ $VERBOSE -le 1 ]; then # VERBOSE <=1 then retry
ERR_UPDATE=$(( $ERR_UPDATE + 1 ))
[ $retry_count -gt 0 -a $ERR_UPDATE -gt $retry_count ] && \
write_log 14 "Updating IP at DDNS provider failed after $retry_count retries"
write_log 4 "Updating IP at DDNS provider failed - starting retry $ERR_UPDATE/$retry_count"
RETRY_COUNT=$(( $RETRY_COUNT + 1 ))
[ $retry_max_count -gt 0 -a $RETRY_COUNT -gt $retry_max_count ] && \
write_log 14 "Updating IP at DDNS provider failed after $retry_max_count retries"
write_log 4 "Updating IP at DDNS provider failed - starting retry $RETRY_COUNT/$retry_max_count"
continue # loop to beginning
else
write_log 4 "Updating IP at DDNS provider failed"
@ -406,7 +418,7 @@ while : ; do
fi
else
# we checked successful the last update
ERR_UPDATE=0 # reset error counter
RETRY_COUNT=0 # reset error counter
fi
# force_update=0 or VERBOSE > 1 - leave here

View File

@ -15,7 +15,7 @@
# option password - cloudflare api key, you can get it from cloudflare.com/my-account/
# option domain - "hostname@yourdomain.TLD" # syntax changed to remove split_FQDN() function and tld_names.dat.gz
#
# The proxy status would not be changed by this script. Please change it in Cloudflare dashboard manually.
# The proxy status would not be changed by this script. Please change it in Cloudflare dashboard manually.
#
# variable __IP already defined with the ip-address to use for update
#
@ -27,21 +27,26 @@
[ $use_https -eq 0 ] && use_https=1 # force HTTPS
# used variables
local __HOST __DOMAIN __FULLDOMAIN __TYPE __URLBASE __PRGBASE __RUNPROG __DATA __IPV6 __ZONEID __RECID __PROXIED
local __HOST __DOMAIN __TYPE __URLBASE __PRGBASE __RUNPROG __DATA __IPV6 __ZONEID __RECID __PROXIED
local __URLBASE="https://api.cloudflare.com/client/v4"
local __TTL=120
# split __HOST __DOMAIN from $domain
[ "${domain:0:2}" == "@." ] && domain="${domain/./}"
[ "$domain" == "${domain/@/}" ] && domain="${domain/./@}"
__HOST="${domain%%@*}"
__DOMAIN="${domain#*@}"
[ -z "$__HOST" -o "$__HOST" == "$__DOMAIN" ] && __HOST="@"
if [ "$__HOST" = "@" ]; then
__FULLDOMAIN="${__DOMAIN}"
else
__FULLDOMAIN="${__HOST}.${__DOMAIN}"
fi
# given data:
# @example.com for "domain record"
# host.sub@example.com for a "host record"
__HOST=$(printf %s "$domain" | cut -d@ -f1)
__DOMAIN=$(printf %s "$domain" | cut -d@ -f2)
# Cloudflare v4 needs:
# __DOMAIN = the base domain i.e. example.com
# __HOST = the FQDN of record to modify
# i.e. example.com for the "domain record" or host.sub.example.com for "host record"
# handling domain record then set __HOST = __DOMAIN
[ -z "$__HOST" ] && __HOST=$__DOMAIN
# handling host record then rebuild fqdn host@domain.tld => host.domain.tld
[ "$__HOST" != "$__DOMAIN" ] && __HOST="${__HOST}.${__DOMAIN}"
# set record type
[ $use_ipv6 -eq 0 ] && __TYPE="A" || __TYPE="AAAA"
@ -129,23 +134,27 @@ else
fi
__PRGBASE="$__PRGBASE --header 'Content-Type: application/json' "
# read zone id for registered domain.TLD
__RUNPROG="$__PRGBASE --request GET '$__URLBASE/zones?name=$__DOMAIN'"
cloudflare_transfer || return 1
# extract zone id
__ZONEID=$(grep -o '"id":\s*"[^"]*' $DATFILE | grep -o '[^"]*$' | head -1)
[ -z "$__ZONEID" ] && {
write_log 4 "Could not detect 'zone id' for domain.tld: '$__DOMAIN'"
return 127
}
if [ -n "$zone_id" ]; then
__ZONEID="$zone_id"
else
# read zone id for registered domain.TLD
__RUNPROG="$__PRGBASE --request GET '$__URLBASE/zones?name=$__DOMAIN'"
cloudflare_transfer || return 1
# extract zone id
__ZONEID=$(grep -o '"id":\s*"[^"]*' $DATFILE | grep -o '[^"]*$' | head -1)
[ -z "$__ZONEID" ] && {
write_log 4 "Could not detect 'zone id' for domain.tld: '$__DOMAIN'"
return 127
}
fi
# read record id for A or AAAA record of host.domain.TLD
__RUNPROG="$__PRGBASE --request GET '$__URLBASE/zones/$__ZONEID/dns_records?name=${__FULLDOMAIN}&type=$__TYPE'"
__RUNPROG="$__PRGBASE --request GET '$__URLBASE/zones/$__ZONEID/dns_records?name=$__HOST&type=$__TYPE'"
cloudflare_transfer || return 1
# extract record id
__RECID=$(grep -o '"id":\s*"[^"]*' $DATFILE | grep -o '[^"]*$' | head -1)
[ -z "$__RECID" ] && {
write_log 4 "Could not detect 'record id' for host.domain.tld: '${__FULLDOMAIN}'"
write_log 4 "Could not detect 'record id' for host.domain.tld: '$__HOST'"
return 127
}
@ -182,7 +191,7 @@ __PROXIED=$(grep -o '"proxied":\s*[^",]*' $DATFILE | grep -o '[^:]*$')
# use file to work around " needed for json
cat > $DATFILE << EOF
{"id":"$__ZONEID","type":"$__TYPE","name":"${__FULLDOMAIN}","content":"$__IP","ttl":$__TTL,"proxied":$__PROXIED}
{"id":"$__ZONEID","type":"$__TYPE","name":"$__HOST","content":"$__IP","ttl":$__TTL,"proxied":$__PROXIED}
EOF
# let's complete transfer command
@ -190,3 +199,4 @@ __RUNPROG="$__PRGBASE --request PUT --data @$DATFILE '$__URLBASE/zones/$__ZONEID
cloudflare_transfer || return 1
return 0

View File

@ -0,0 +1,86 @@
# inside url we need domain, username and password
[ -z "$domain" ] && write_log 14 "Service section not configured correctly! Missing 'domain'"
[ -z "$username" ] && write_log 14 "Service section not configured correctly! Missing 'username'"
[ -z "$password" ] && write_log 14 "Service section not configured correctly! Missing 'password'"
local urlCp='http://cp.cnkuai.cn/'
local urlLogin='http://cp.cnkuai.cn/userlogin.asp'
local urlCaptcha='http://cp.cnkuai.cn/inc/image.asp'
local urlDnsA='http://cp.cnkuai.cn/dns_a.asp'
local urlDnsAAAA='http://cp.cnkuai.cn/dns_ipv6.asp'
local urlDnsSave='http://cp.cnkuai.cn/dns_save.asp'
getPixel(){
local filename=$1
local x=$(($2*3))
local y=$(($3*3))
local width=48
hexdump -s "$((x+width*y))" -n 3 -e '3/1 "%02X"' "$filename"
}
captchaChar(){
local filename=$1
local xoffset=$2
if [ "$(getPixel "$filename" $((xoffset+2)) 5)" = '000000' ]; then
echo '1'
elif [ "$(getPixel "$filename" $((xoffset+5)) 7)" = '000000' ]; then
echo '2'
elif [ "$(getPixel "$filename" $((xoffset+4)) 3)" = '000000' ]; then
echo '4'
elif [ "$(getPixel "$filename" $((xoffset+6)) 4)" = '000000' ]; then
echo '7'
elif [ "$(getPixel "$filename" $((xoffset+5)) 8)" = '000000' ]; then
echo '8'
elif [ "$(getPixel "$filename" $((xoffset+6)) 8)" = '000000' ]; then
echo '9'
elif [ "$(getPixel "$filename" $((xoffset+5)) 6)" = '000000' ]; then
echo '3'
elif [ "$(getPixel "$filename" $((xoffset+0)) 4)" = '000000' ]; then
echo '5'
elif [ "$(getPixel "$filename" $((xoffset+1)) 5)" = '000000' ]; then
echo '6'
else
echo '0'
fi
}
captcha(){
local str
str=$(captchaChar "$1" 9)
str=$str$(captchaChar "$1" 18)
str=$str$(captchaChar "$1" 26)
str=$str$(captchaChar "$1" 35)
echo "$str"
}
#clean
rm /tmp/cnkuai.*
#login to cnkuai dns cp
curl -c '/tmp/cnkuai.cookiejar' "$urlCaptcha" | gif2rgb > /tmp/cnkuai.rgb || return 1
yzm=$(captcha "/tmp/cnkuai.rgb")
curl -b '/tmp/cnkuai.cookiejar' -c '/tmp/cnkuai.cookiejar' -H "Content-Type: application/x-www-form-urlencoded" -H "Referer: $urlCp" -d "userid=$URL_USER&password=$URL_PASS&yzm=$yzm&B1=%C8%B7%C8%CF%B5%C7%C2%BD&lx=0&userlx=3" -X POST "$urlLogin" > /dev/null || return 1
if [ "$use_ipv6" -eq 0 ]; then
curl -b '/tmp/cnkuai.cookiejar' -c '/tmp/cnkuai.cookiejar' "$urlDnsA" > /tmp/cnkuai.html || return 1
else
curl -b '/tmp/cnkuai.cookiejar' -c '/tmp/cnkuai.cookiejar' "$urlDnsAAAA" > /tmp/cnkuai.html || return 1
fi
local domainline
domainline=$(awk "/<td>$domain<\/td>/{ print NR; exit }" /tmp/cnkuai.html)
local domainid
domainid=$(awk "NR==$((domainline+3))" /tmp/cnkuai.html | sed 's/^.*name=\x27domainid\x27 value="//g' | sed 's/".*$//g')
local dnslistid
dnslistid=$(awk "NR==$((domainline+3))" /tmp/cnkuai.html | sed 's/^.*name=\x27dnslistid\x27 value="//g' | sed 's/".*$//g')
local data
if [ "$use_ipv6" -eq 0 ]; then
data="T2=$__IP&T3=120&act=dns_a_edit&domainid=$domainid&dnslistid=$dnslistid&B1=%D0%DE%B8%C4"
else
data="T2=$__IP&T3=120&act=dns_ipv6_edit&domainid=$domainid&dnslistid=$dnslistid&B1=%D0%DE%B8%C4"
fi
curl -b '/tmp/cnkuai.cookiejar' -c '/tmp/cnkuai.cookiejar' -H "Content-Type: application/x-www-form-urlencoded" -H "Referer: $urlDnsA" -d "$data" -X POST "$urlDnsSave" > /dev/null || return 1
return 0

View File

@ -0,0 +1,40 @@
# Script for sending user defined updates using the DigitalOcean API
# 2015 Artem Yakimenko <code at temik dot me>
# 2021 George Giannou <giannoug at gmail dot com>
# Options passed from /etc/config/ddns:
# Domain - the domain name managed by DigitalOcean (e.g. example.com)
# Username - the hostname of the domain (e.g. myrouter)
# Password - DigitalOcean personal access token (API key)
# Optional Parameter - the API domain record ID of the hostname (e.g. 21694203)
# Use the following command to find your API domain record ID (replace TOKEN and DOMAIN with your own):
# curl -X GET -H 'Content-Type: application/json' \
# -H "Authorization: Bearer TOKEN" \
# "https://api.digitalocean.com/v2/domains/DOMAIN/records"
. /usr/share/libubox/jshn.sh
[ -z "$domain" ] && write_log 14 "Service section not configured correctly! Missing domain name as 'Domain'"
[ -z "$username" ] && write_log 14 "Service section not configured correctly! Missing hostname as 'Username'"
[ -z "$password" ] && write_log 14 "Service section not configured correctly! Missing personal access token as 'Password'"
[ -z "$param_opt" ] && write_log 14 "Service section not configured correctly! Missing API domain record ID as 'Optional Parameter'"
# Construct JSON payload
json_init
json_add_string name "$username"
json_add_string data "$__IP"
__STATUS=$(curl -Ss -X PUT "https://api.digitalocean.com/v2/domains/${domain}/records/${param_opt}" \
-H "Authorization: Bearer ${password}" \
-H "Content-Type: application/json" \
-d "$(json_dump)" \
-w "%{http_code}\n" -o $DATFILE 2>$ERRFILE)
if [ $? -ne 0 ]; then
write_log 14 "Curl failed: $(cat $ERRFILE)"
return 1
elif [ -z $__STATUS ] || [ $__STATUS != 200 ]; then
write_log 14 "Curl failed: $__STATUS \ndigitalocean.com answered: $(cat $DATFILE)"
return 1
fi

View File

@ -0,0 +1,137 @@
#!/bin/sh
# Check inputs
[ -z "$username" ] && write_log 14 "Configuration error! [User name] cannot be empty"
[ -z "$password" ] && write_log 14 "Configuration error! [Password] cannot be empty"
# Check external tools
[ -n "$CURL_SSL" ] || write_log 13 "Dnspod communication require cURL with SSL support. Please install"
[ -n "$CURL_PROXY" ] || write_log 13 "cURL: libcurl compiled without Proxy support"
# Declare variables
#local __URLBASE __HOST __DOMAIN __TYPE __CMDBASE __POST __POST1 __RECIP __RECID __TTL
__URLBASE="https://dnsapi.cn"
# Get host and domain from $domain
[ "${domain:0:2}" = "@." ] && domain="${domain/./}" # host
[ "$domain" = "${domain/@/}" ] && domain="${domain/./@}" # host with no sperator
__HOST="${domain%%@*}"
__DOMAIN="${domain#*@}"
[ -z "$__HOST" -o "$__HOST" = "$__DOMAIN" ] && __HOST=@
# Set record type
[ $use_ipv6 = 0 ] && __TYPE=A || __TYPE=AAAA
# Build base command
build_command() {
__CMDBASE="$CURL -Ss"
# bind host/IP
if [ -n "$bind_network" ]; then
local __DEVICE
network_get_physdev __DEVICE $bind_network || write_log 13 "Can not detect local device using 'network_get_physdev $bind_network' - Error: '$?'"
write_log 7 "Force communication via device '$__DEVICE'"
__CMDBASE="$__CMDBASE --interface $__DEVICE"
fi
# Force IP version
if [ $force_ipversion = 1 ]; then
[ $use_ipv6 = 0 ] && __CMDBASE="$__CMDBASE -4" || __CMDBASE="$__CMDBASE -6"
fi
# Set CA
if [ $use_https = 1 ]; then
if [ "$cacert" = IGNORE ]; then
__CMDBASE="$__CMDBASE --insecure"
elif [ -f "$cacert" ]; then
__CMDBASE="$__CMDBASE --cacert $cacert"
elif [ -d "$cacert" ]; then
__CMDBASE="$__CMDBASE --capath $cacert"
elif [ -n "$cacert" ]; then
write_log 14 "No valid certificate(s) found at '$cacert' for HTTPS communication"
fi
fi
# Set if no proxy (might be an error with .wgetrc or env)
[ -z "$proxy" ] && __CMDBASE="$__CMDBASE --noproxy '*'"
__CMDBASE="$__CMDBASE -d"
}
# Dnspod API
dnspod_transfer() {
__CNT=0
case "$1" in
0) __A="$__CMDBASE '$__POST' $__URLBASE/Record.List" ;;
1) __A="$__CMDBASE '$__POST1' $__URLBASE/Record.Create" ;;
2) __A="$__CMDBASE '$__POST1&record_id=$__RECID&ttl=$__TTL' $__URLBASE/Record.Modify" ;;
esac
write_log 7 "#> $__A"
while ! __TMP=$(eval $__A 2>&1); do
write_log 3 "[$__TMP]"
if [ $VERBOSE -gt 1 ]; then
write_log 4 "Transfer failed - detailed mode: $VERBOSE - Do not try again after an error"
return 1
fi
__CNT=$(($__CNT + 1))
[ $retry_count -gt 0 -a $__CNT -gt $retry_count ] && write_log 14 "Transfer failed after $retry_count retries"
write_log 4 "Transfer failed - $__CNT Try again in $RETRY_SECONDS seconds"
sleep $RETRY_SECONDS &
PID_SLEEP=$!
wait $PID_SLEEP
PID_SLEEP=0
done
__ERR=$(jsonfilter -s "$__TMP" -e "@.status.code")
[ $__ERR = 1 ] && return 0
[ $__ERR = 10 ] && [ $1 = 0 ] && return 0
__TMP=$(jsonfilter -s "$__TMP" -e "@.status.message")
local A="$(date +%H%M%S) ERROR : [$__TMP] - Terminate process"
logger -p user.err -t ddns-scripts[$$] $SECTION_ID: ${A:15}
printf "%s\n" " $A" >>$LOGFILE
exit 1
}
# Add record
add_domain() {
dnspod_transfer 1
printf "%s\n" " $(date +%H%M%S) : Record add successfully: [$([ "$__HOST" = @ ] || echo $__HOST.)$__DOMAIN],[IP:$__IP]" >>$LOGFILE
return 0
}
# Modify record
update_domain() {
dnspod_transfer 2
printf "%s\n" " $(date +%H%M%S) : Record modified successfully: [$([ "$__HOST" = @ ] || echo $__HOST.)$__DOMAIN],[IP:$__IP],[TTL:$__TTL]" >>$LOGFILE
return 0
}
# Get DNS record
describe_domain() {
ret=0
__POST="login_token=$username,$password&format=json&domain=$__DOMAIN&sub_domain=$__HOST"
__POST1="$__POST&value=$__IP&record_type=$__TYPE&record_line_id=0"
dnspod_transfer 0
__TMP=$(jsonfilter -s "$__TMP" -e "@.records[@.type='$__TYPE' && @.line_id='0']")
if [ -z "$__TMP" ]; then
printf "%s\n" " $(date +%H%M%S) : Record not exist: [$([ "$__HOST" = @ ] || echo $__HOST.)$__DOMAIN]" >>$LOGFILE
ret=1
else
__RECIP=$(jsonfilter -s "$__TMP" -e "@.value")
if [ "$__RECIP" != "$__IP" ]; then
__RECID=$(jsonfilter -s "$__TMP" -e "@.id")
__TTL=$(jsonfilter -s "$__TMP" -e "@.ttl")
printf "%s\n" " $(date +%H%M%S) : Record needs to be updated: [Record IP:$__RECIP] [Local IP:$__IP]" >>$LOGFILE
ret=2
fi
fi
}
build_command
describe_domain
if [ $ret = 1 ]; then
sleep 3
add_domain
elif [ $ret = 2 ]; then
sleep 3
update_domain
else
printf "%s\n" " $(date +%H%M%S) : Record needs not update: [Record IP:$__RECIP] [Local IP:$__IP]" >>$LOGFILE
fi
return 0

View File

@ -5,7 +5,7 @@ local __URL="https://freedns.42.pl/xmlrpc.php"
[ -z "$username" ] && write_log 14 "Service section not configured correctly! Missing 'username'"
[ -z "$password" ] && write_log 14 "Service section not configured correctly! Missing 'password'"
[ $ip_dynamic -eq 1 ] && __IP='\&lt;dynamic\&gt;'
PROG="$(which curl) -sk"
PROG="$(command -v curl) -sk"
write_log 7 "sending update to freedns.42.pl with ip $__IP"
XMLDATA="<?xml version='1.0'?><methodCall><methodName>xname.updateArecord</methodName><params><param><value><struct><member><name>name</name><value><string>[RECORDNAME]</string></value></member><member><name>zone</name><value><string>[ZONENAME]</string></value></member><member><name>oldaddress</name><value><string>*</string></value></member><member><name>updatereverse</name><value><string>0</string></value></member><member><name>user</name><value><string>[USERNAME]</string></value></member><member><name>ttl</name><value><string>600</string></value></member><member><name>newaddress</name><value><string>[IP]</string></value></member><member><name>password</name><value><string>[PASSWORD]</string></value></member></struct></value></param></params></methodCall>"
XMLDATA=$(echo $XMLDATA | sed -e "s#\[USERNAME\]#$URL_USER#g" -e "s#\[PASSWORD\]#$URL_PASS#g" \

View File

@ -0,0 +1,48 @@
#!/bin/sh
# Thanks goes to Alex Griffin who provided this script.
. /usr/share/libubox/jshn.sh
local __TTL=600
local __RRTYPE
local __ENDPOINT="https://api.gandi.net/v5/livedns"
local __STATUS
[ -z "$username" ] && write_log 14 "Service section not configured correctly! Missing subdomain as 'username'"
[ -z "$password" ] && write_log 14 "Service section not configured correctly! Missing API Key as 'password'"
[ $use_ipv6 -ne 0 ] && __RRTYPE="AAAA" || __RRTYPE="A"
# Construct JSON payload
json_init
json_add_int rrset_ttl "$__TTL"
json_add_array rrset_values
json_add_string "" "$__IP"
json_close_array
# Log the curl command
write_log 7 "curl -s -X PUT \"$__ENDPOINT/domains/$domain/records/$username/$__RRTYPE\" \
-H \"Authorization: Apikey $password\" \
-H \"Content-Type: application/json\" \
-d \"$(json_dump)\" \
--connect-timeout 30"
__STATUS=$(curl -s -X PUT "$__ENDPOINT/domains/$domain/records/$username/$__RRTYPE" \
-H "Authorization: Apikey $password" \
-H "Content-Type: application/json" \
-d "$(json_dump)" \
--connect-timeout 30 \
-w "%{http_code}\n" -o $DATFILE 2>$ERRFILE)
local __ERRNO=$?
if [ $__ERRNO -ne 0 ]; then
write_log 14 "Curl failed with $__ERRNO: $(cat $ERRFILE)"
return 1
elif [ -z $__STATUS ] || [ $__STATUS != 201 ]; then
write_log 14 "LiveDNS failed: $__STATUS \ngandi.net answered: $(cat $DATFILE)"
return 1
fi
write_log 7 "gandi.net answered: $(cat $DATFILE)"
return 0

View File

@ -0,0 +1,272 @@
#!/bin/sh
#
#.Distributed under the terms of the GNU General Public License (GPL) version 2.0
#.2022 Chris Barrick <chrisbarrick@google.com>
#
# This script sends DDNS updates using the Google Cloud DNS REST API.
# See: https://cloud.google.com/dns/docs/reference/v1
#
# This script uses a GCP service account. The user is responsible for creating
# the service account, ensuring it has permission to update DNS records, and
# for generating a service account key to be used by this script. The records
# to be updated must already exist.
#
# Arguments:
#
# - $username: The service account name.
# Example: ddns-service-account@my-dns-project.iam.gserviceaccount.com
#
# - $password: The service account key. You can paste the key directly into the
# "password" field or upload the key file to the router and set the field
# equal to the file path. This script supports JSON keys or the raw private
# key as a PEM file. P12 keys are not supported. File names must end with
# `*.json` or `*.pem`.
#
# - $domain: The domain to update.
#
# - $param_enc: The additional required arguments, as form-urlencoded data,
# i.e. `key1=value1&key2=value2&...`. The required arguments are:
# - project: The name of the GCP project that owns the DNS records.
# - zone: The DNS zone in the GCP API.
# - Example: `project=my-dns-project&zone=my-dns-zone`
#
# - $param_opt: Optional TTL for the records, in seconds. Defaults to 3600 (1h).
#
# Dependencies:
# - ddns-scripts (for the base functionality)
# - openssl-util (for the authentication flow)
# - curl (for the GCP REST API)
. /usr/share/libubox/jshn.sh
# Authentication
# ---------------------------------------------------------------------------
# The authentication flow works like this:
#
# 1. Construct a JWT claim for access to the DNS readwrite scope.
# 2. Sign the JWT with the service accout key, proving we have access.
# 3. Exchange the JWT for an access token, valid for 5m.
# 4. Use the access token for API calls.
#
# See https://developers.google.com/identity/protocols/oauth2/service-account
# A URL-safe variant of base64 encoding, used by JWTs.
base64_urlencode() {
openssl base64 | tr '/+' '_-' | tr -d '=\n'
}
# Prints the service account private key in PEM format.
get_service_account_key() {
# The "password" field provides us with the service account key.
# We allow the user to provide it to us in a few different formats.
#
# 1. If $password is a string ending in `*.json`, it is a file path,
# pointing to a JSON service account key as downloaded from GCP.
#
# 2. If $password is a string ending with `*.pem`, it is a PEM private
# key, extracted from the JSON service account key.
#
# 3. If $password starts with `{`, then the JSON service account key
# was pasted directly into the password field.
#
# 4. If $password starts with `---`, then the PEM private key was pasted
# directly into the password field.
#
# We do not support P12 service account keys.
case "${password}" in
(*".json")
jsonfilter -i "${password}" -e @.private_key
;;
(*".pem")
cat "${password}"
;;
("{"*)
jsonfilter -s "${password}" -e @.private_key
;;
("---"*)
printf "%s" "${password}"
;;
(*)
write_log 14 "Could not parse the service account key."
;;
esac
}
# Sign stdin using the service account key. Prints the signature.
# The input is the JWT header-payload. Used to construct a signed JWT.
sign() {
# Dump the private key to a tmp file so openssl can get to it.
local tmp_keyfile="$(mktemp -t gcp_dns_sak.pem.XXXXXX)"
chmod 600 ${tmp_keyfile}
get_service_account_key > ${tmp_keyfile}
openssl dgst -binary -sha256 -sign ${tmp_keyfile}
rm ${tmp_keyfile}
}
# Print the JWT header in JSON format.
# Currently, Google only supports RS256.
jwt_header() {
json_init
json_add_string "alg" "RS256"
json_add_string "typ" "JWT"
json_dump
}
# Prints the JWT claim-set in JSON format.
# The claim is for 5m of readwrite access to the Cloud DNS API.
jwt_claim_set() {
local iat=$(date -u +%s) # Current UNIX time, UTC.
local exp=$(( iat + 300 )) # Expiration is 5m in the future.
json_init
json_add_string "iss" "${username}"
json_add_string "scope" "https://www.googleapis.com/auth/ndev.clouddns.readwrite"
json_add_string "aud" "https://oauth2.googleapis.com/token"
json_add_string "iat" "${iat}"
json_add_string "exp" "${exp}"
json_dump
}
# Generate a JWT signed by the service account key, which can be exchanged for
# a Google Cloud access token, authorized for Cloud DNS.
get_jwt() {
local header=$(jwt_header | base64_urlencode)
local payload=$(jwt_claim_set | base64_urlencode)
local header_payload="${header}.${payload}"
local signature=$(printf "%s" ${header_payload} | sign | base64_urlencode)
echo "${header_payload}.${signature}"
}
# Request an access token for the Google Cloud service account.
get_access_token_raw() {
local grant_type="urn:ietf:params:oauth:grant-type:jwt-bearer"
local assertion=$(get_jwt)
${CURL} -v https://oauth2.googleapis.com/token \
--data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer' \
--data-urlencode "assertion=${assertion}" \
| jsonfilter -e @.access_token
}
# Get the access token, stripping the trailing dots.
get_access_token() {
# Since tokens may contain internal dots, we only trim the suffix if it
# starts with at least 8 dots. (The access token has *many* trailing dots.)
local access_token="$(get_access_token_raw)"
echo "${access_token%%........*}"
}
# Google Cloud DNS API
# ---------------------------------------------------------------------------
# Cloud DNS offers a straight forward RESTful API.
#
# - The main class is a ResourceRecordSet. It's a collection of DNS records
# that share the same domain, type, TTL, etc. Within a record set, the only
# difference between the records are their values.
#
# - The record sets live under a ManagedZone, which in turn lives under a
# Project. All we need to know about these are their names.
#
# - This implementation only makes PATCH requests to update existing record
# sets. The user must have already created at least one A or AAAA record for
# the domain they are updating. It's fine to start with a dummy, like 0.0.0.0.
#
# - The API requires SSL, and this implementation uses curl.
# Prints a ResourceRecordSet in JSON format.
format_record_set() {
local domain="$1"
local record_type="$2"
local ttl="$3"
shift 3 # The remaining arguments are the IP addresses for this record set.
json_init
json_add_string "kind" "dns#resourceRecordSet"
json_add_string "name" "${domain}." # trailing dot on the domain
json_add_string "type" "${record_type}"
json_add_string "ttl" "${ttl}"
json_add_array "rrdatas"
for value in $@; do
json_add_string "" "${value}"
done
json_close_array
json_dump
}
# Makes an HTTP PATCH request to the Cloud DNS API.
patch_record_set() {
local access_token="$1"
local project="$2"
local zone="$3"
local domain="$4"
local record_type="$5"
local ttl="$6"
shift 6 # The remaining arguments are the IP addresses for this record set.
# Note the trailing dot after the domain name. It's fully qualified.
local url="https://dns.googleapis.com/dns/v1/projects/${project}/managedZones/${zone}/rrsets/${domain}./${record_type}"
local record_set=$(format_record_set ${domain} ${record_type} ${ttl} $@)
${CURL} -v ${url} \
-X PATCH \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${access_token}" \
-d "${record_set}"
}
# Main entrypoint
# ---------------------------------------------------------------------------
# Parse the $param_enc into project and zone variables.
# The arguments are the names for those variables.
parse_project_zone() {
local project_var=$1
local zone_var=$2
IFS='&'
for entry in $param_enc
do
case "${entry}" in
('project='*)
local project_val=$(echo "${entry}" | cut -d'=' -f2)
eval "${project_var}=${project_val}"
;;
('zone='*)
local zone_val=$(echo "${entry}" | cut -d'=' -f2)
eval "${zone_var}=${zone_val}"
;;
esac
done
unset IFS
}
main() {
local access_token project zone ttl record_type
# Dependency checking
[ -z "${CURL_SSL}" ] && write_log 14 "Google Cloud DNS requires cURL with SSL support"
[ -z "$(openssl version)" ] && write_log 14 "Google Cloud DNS update requires openssl-utils"
# Argument parsing
[ -z ${param_opt} ] && ttl=3600 || ttl="${param_opt}"
[ $use_ipv6 -ne 0 ] && record_type="AAAA" || record_type="A"
parse_project_zone project zone
# Sanity checks
[ -z "${username}" ] && write_log 14 "Config is missing 'username' (service account name)"
[ -z "${password}" ] && write_log 14 "Config is missing 'password' (service account key)"
[ -z "${domain}" ] && write_log 14 "Config is missing 'domain'"
[ -z "${project}" ] && write_log 14 "Could not parse project name from 'param_enc'"
[ -z "${zone}" ] && write_log 14 "Could not parse zone name from 'param_enc'"
[ -z "${ttl}" ] && write_log 14 "Could not parse TTL from 'param_opt'"
[ -z "${record_type}" ] && write_log 14 "Could not determine the record type"
# Push the record!
access_token="$(get_access_token)"
patch_record_set "${access_token}" "${project}" "${zone}" "${domain}" "${record_type}" "${ttl}" "${__IP}"
}
main $@

View File

@ -0,0 +1,152 @@
#!/bin/sh
#
# script for sending updates to huaweicloud.com
# 2023-2024 sxlehua <sxlehua at qq dot com>
# API documentation at https://support.huaweicloud.com/api-dns/dns_api_62003.html
# API signature documentation at https://support.huaweicloud.com/api-dns/dns_api_30003.html
#
# This script is parsed by dynamic_dns_functions.sh inside send_update() function
#
# useage:
# using following options from /etc/config/ddns
# option username - huaweicloud Access Key Id
# option password - huaweicloud Secret Access KeyAK、SK documentation from https://support.huaweicloud.com/devg-apisign/api-sign-provide-aksk.html
# option domain - "hostname@yourdomain.TLD" # syntax changed to remove split_FQDN() function and tld_names.dat.gz
#
# Check inputs
[ -z "$username" ] && write_log 14 "Configuration error! [username] cannot be empty"
[ -z "$password" ] && write_log 14 "Configuration error! [password] cannot be empty"
[ -z "$CURL" ] && [ -z "$CURL_SSL" ] && write_log 14 "huaweicloud API require cURL with SSL support. Please install"
command -v openssl >/dev/null 2>&1 || write_log 14 "huaweicloud API require openssl-util support. Please install"
# public variable
local __HOST __DOMAIN __TYPE __ZONE_ID __RECORD_ID
local __ENDPOINT="dns.cn-north-1.myhuaweicloud.com"
local __TTL=120
[ $use_ipv6 -eq 0 ] && __TYPE="A" || __TYPE="AAAA"
# Get host and domain from $domain
[ "${domain:0:2}" == "@." ] && domain="${domain/./}" # host
[ "$domain" == "${domain/@/}" ] && domain="${domain/./@}" # host with no sperator
__HOST="${domain%%@*}"
__DOMAIN="${domain#*@}"
[ -z "$__HOST" -o "$__HOST" == "$__DOMAIN" ] && __HOST="@"
hcloud_transfer() {
local method=$1
local path=$2
local query=$3
local body=$4
local timestamp=$(date -u +'%Y%m%dT%H%M%SZ')
local contentType=""
if [ ! "$method" = "GET" ]; then
contentType="application/json"
fi
local _H_Content_Type=""
local canonicalUri="${path}"
# add / if need
echo $canonicalUri | grep -qE "/$" || canonicalUri="$canonicalUri/"
local canonicalQuery="$query" # for extend
local canonicalHeaders="host:$__ENDPOINT\nx-sdk-date:$timestamp\n"
local signedHeaders="host;x-sdk-date"
if [ ! "$contentType" = "" ]; then
canonicalHeaders="content-type:$contentType\n${canonicalHeaders}"
signedHeaders="content-type;$signedHeaders"
_H_Content_Type="Content-Type: ${contentType}"
fi
local hexencode=$(printf "%s" "$body" | openssl dgst -sha256 -hex 2>/dev/null | sed 's/^.* //')
local canonicalRequest="$method\n$canonicalUri\n$canonicalQuery\n$canonicalHeaders\n$signedHeaders\n$hexencode"
canonicalRequest="$(printf "$canonicalRequest%s")"
local stringToSign="SDK-HMAC-SHA256\n$timestamp\n$(printf "%s" "$canonicalRequest" | openssl dgst -sha256 -hex 2>/dev/null | sed 's/^.* //')"
stringToSign="$(printf "$stringToSign%s")"
local signature=$(printf "%s" "$stringToSign" | openssl dgst -sha256 -hmac "$password" 2>/dev/null | sed 's/^.* //')
authorization="SDK-HMAC-SHA256 Access=$username, SignedHeaders=$signedHeaders, Signature=$signature"
reqUrl="$__ENDPOINT$path"
if [ ! -z "$query" ]; then
reqUrl="$reqUrl""?$query"
fi
curl -s -X "${method}" \
-H "Host: $__ENDPOINT" \
-H "$_H_Content_Type" \
-H "Authorization: $authorization" \
-H "X-Sdk-Date: $timestamp" \
-d "${body}" \
"https://$reqUrl"
if [ $? -ne 0 ]; then
write_log 14 "rest api error"
fi
}
get_zone() {
local resp=`hcloud_transfer GET /v2/zones "name=$__DOMAIN.&search_mode=equal" ""`
__ZONE_ID=`printf "%s" $resp | grep -Eo '"id":"[a-z0-9]+"' | cut -d':' -f2 | tr -d '"'`
if [ "$__ZONE_ID" = "" ]; then
write_log 14 "error, no zone"
fi
}
upd_record() {
local body="{\"name\":\"$__HOST.$__DOMAIN.\",\"type\":\"$__TYPE\",\"records\":[\"$__IP\"],\"ttl\":$__TTL}"
local resp=`hcloud_transfer PUT /v2/zones/"$__ZONE_ID"/recordsets/$__RECORD_ID "" "$body"`
local recordId=`printf "%s" $resp | grep -Eo '"id":"[a-z0-9]+"' | cut -d':' -f2 | tr -d '"'`
if [ ! "$recordId" = "" ]; then
write_log 7 "upd [$recordId] success [$__TYPE] [$__IP]"
else
write_log 14 "upd ecord error [$resp]"
fi
}
add_record() {
local body="{\"name\":\"$__HOST.$__DOMAIN.\",\"type\":\"$__TYPE\",\"records\":[\"$__IP\"],\"ttl\":$__TTL}"
local resp=`hcloud_transfer POST /v2/zones/"$__ZONE_ID"/recordsets "" "$body"`
local recordId=`printf "%s" $resp | grep -Eo '"id":"[a-z0-9]+"' | cut -d':' -f2 | tr -d '"'`
if [ ! "$recordId" = "" ]; then
write_log 7 "add [$recordId] success [$__TYPE] [$__IP]"
else
write_log 14 "add record error [$resp]"
fi
}
# Get DNS record
get_record() {
local ret=0
local resp=`hcloud_transfer GET /v2/zones/$__ZONE_ID/recordsets "name=$__HOST.$__DOMAIN.&search_mode=equal" ""`
__RECORD_ID=`printf "%s" $resp | grep -Eo '"id":"[a-z0-9]+"' | cut -d':' -f2 | tr -d '"' | head -1`
if [ "$__RECORD_ID" = "" ]; then
# Record needs to be add
ret=1
else
local remoteIp=`printf "%s" $resp | grep -Eo '"records":\[[^]]+]' | cut -d ':' -f 2-10 | tr -d '[' | tr -d ']' | tr -d '"' | head -1`
if [ ! "$remoteIp" = "$__IP" ]; then
# Record needs to be updated
ret=2
fi
fi
return $ret
}
get_zone
get_record
ret=$?
if [ $ret -eq 0 ]; then
write_log 7 "nochg [$__IP]"
fi
if [ $ret -eq 1 ]; then
add_record
fi
if [ $ret -eq 2 ]; then
upd_record
fi

View File

@ -0,0 +1,191 @@
#!/bin/sh
#
#.Distributed under the terms of the GNU General Public License (GPL) version 2.0
#.2023 Jihoon Han <rapid_renard@renard.ga>
#
#.based on Christian Schoenebeck's update_cloudflare_com_v4.sh
#.and on Neilpang's acme.sh found at https://github.com/acmesh-official/acme.sh
#
# Script for sending DDNS updates using the LuaDNS API
# See: https://luadns.com/api
#
# using following options from /etc/config/ddns
# option username - "Emaii" as registered on LuaDNS
# option password - "API Key" as generated at https://api.luadns.com/api_keys
# option domain - The domain to update (e.g. my.example.com)
#
# check parameters
[ -z "$CURL" ] && [ -z "$CURL_SSL" ] && write_log 14 "LuaDNS API require cURL with SSL support. Please install"
[ -z "$username" ] && write_log 14 "Service section not configured correctly! Missing e-mail as 'Username'"
[ -z "$password" ] && write_log 14 "Service section not configured correctly! Missing personal API key as 'Password'"
[ $use_https -eq 0 ] && use_https=1 # force HTTPS
# used variables
local __HOST __DOMAIN __TYPE __URLBASE __PRGBASE __RUNPROG __DATA __IPV6 __ZONEID __RECID
local __URLBASE="https://api.luadns.com/v1"
local __TTL=300
# set record type
[ $use_ipv6 -eq 0 ] && __TYPE="A" || __TYPE="AAAA"
# transfer function to use for LuaDNS
# all needed variables are set global here
# so we can use them directly
luadns_transfer() {
local __CNT=0
local __STATUS __ERR
while : ; do
write_log 7 "#> $__RUNPROG"
__STATUS=$(eval "$__RUNPROG")
__ERR=$? # save communication error
[ $__ERR -eq 0 ] && break # no error break while
write_log 3 "cURL Error: '$__ERR'"
write_log 7 "$(cat $ERRFILE)" # report error
[ $VERBOSE_MODE -gt 1 ] && {
# VERBOSE_MODE > 1 then NO retry
write_log 4 "Transfer failed - Verbose Mode: $VERBOSE_MODE - NO retry on error"
break
}
__CNT=$(( $__CNT + 1 )) # increment error counter
# if error count > retry_count leave here
[ $retry_count -gt 0 -a $__CNT -gt $retry_count ] && \
write_log 14 "Transfer failed after $retry_count retries"
write_log 4 "Transfer failed - retry $__CNT/$retry_count in $RETRY_SECONDS seconds"
sleep $RETRY_SECONDS &
PID_SLEEP=$!
wait $PID_SLEEP # enable trap-handler
PID_SLEEP=0
done
# handle HTTP error
[ $__STATUS -ne 200 ] && {
write_log 4 "LuaDNS reported an error:"
write_log 7 "$(cat $DATFILE)"
return 1
}
return 0
}
# Build base command to use
__PRGBASE="$CURL -RsS -w '%{http_code}' -o $DATFILE --stderr $ERRFILE"
# force network/interface-device to use for communication
if [ -n "$bind_network" ]; then
local __DEVICE
network_get_physdev __DEVICE $bind_network || \
write_log 13 "Can not detect local device using 'network_get_physdev $bind_network' - Error: '$?'"
write_log 7 "Force communication via device '$__DEVICE'"
__PRGBASE="$__PRGBASE --interface $__DEVICE"
fi
# force ip version to use
if [ $force_ipversion -eq 1 ]; then
[ $use_ipv6 -eq 0 ] && __PRGBASE="$__PRGBASE -4" || __PRGBASE="$__PRGBASE -6" # force IPv4/IPv6
fi
# set certificate parameters
if [ "$cacert" = "IGNORE" ]; then # idea from Ticket #15327 to ignore server cert
__PRGBASE="$__PRGBASE --insecure" # but not empty better to use "IGNORE"
elif [ -f "$cacert" ]; then
__PRGBASE="$__PRGBASE --cacert $cacert"
elif [ -d "$cacert" ]; then
__PRGBASE="$__PRGBASE --capath $cacert"
elif [ -n "$cacert" ]; then # it's not a file and not a directory but given
write_log 14 "No valid certificate(s) found at '$cacert' for HTTPS communication"
fi
# disable proxy if not set (there might be .wgetrc or .curlrc or wrong environment set)
# or check if libcurl compiled with proxy support
if [ -z "$proxy" ]; then
__PRGBASE="$__PRGBASE --noproxy '*'"
elif [ -z "$CURL_PROXY" ]; then
# if libcurl has no proxy support and proxy should be used then force ERROR
write_log 13 "cURL: libcurl compiled without Proxy support"
fi
# set headers
__PRGBASE="$__PRGBASE --user '$username:$password' "
__PRGBASE="$__PRGBASE --header 'Accept: application/json' "
if [ -n "$zone_id" ]; then
__ZONEID="$zone_id"
else
# read zone id for registered domain.TLD
__RUNPROG="$__PRGBASE --request GET '$__URLBASE/zones'"
luadns_transfer || return 1
# extract zone id
i=1
while : ; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100 -s)
[ -z "$h" ] && {
write_log 4 "Could not detect 'Zone ID' for the domain provided: '$domain'"
return 127
}
__ZONEID=$(grep -o -e "\"id\":[^,]*,\"name\":\"$h\"" $DATFILE | cut -d : -f 2 | cut -d , -f 1)
[ -n "$__ZONEID" ] && {
# LuaDNS API needs:
# __DOMAIN = the base domain i.e. example.com
# __HOST = the FQDN of record to modify
# i.e. example.com for the "domain record" or host.sub.example.com for "host record"
__HOST="$domain"
__DOMAIN="$h"
write_log 7 "Domain : '$__DOMAIN'"
write_log 7 "Zone ID : '$__ZONEID'"
write_log 7 "Host : '$__HOST'"
break
}
i=$(expr "$i" + 1)
done
fi
# read record id for A or AAAA record of host.domain.TLD
__RUNPROG="$__PRGBASE --request GET '$__URLBASE/zones/$__ZONEID/records'"
luadns_transfer || return 1
# extract record id
__RECID=$(grep -o -e "\"id\":[^,]*,\"name\":\"$__HOST.\",\"type\":\"$__TYPE\"" $DATFILE | head -n 1 | cut -d : -f 2 | cut -d , -f 1)
[ -z "$__RECID" ] && {
write_log 4 "Could not detect 'Record ID' for the domain provided: '$__HOST'"
return 127
}
write_log 7 "Record ID : '$__RECID'"
# extract current stored IP
__DATA=$(grep -o -e "\"id\":$__RECID,\"name\":\"$__HOST.\",\"type\":\"$__TYPE\",\"content\":[^,]*" $DATFILE | grep -o '[^"]*' | tail -n 1)
# check data
[ $use_ipv6 -eq 0 ] \
&& __DATA=$(printf "%s" "$__DATA" | grep -m 1 -o "$IPV4_REGEX") \
|| __DATA=$(printf "%s" "$__DATA" | grep -m 1 -o "$IPV6_REGEX")
# we got data so verify
[ -n "$__DATA" ] && {
# expand IPv6 for compare
if [ $use_ipv6 -eq 1 ]; then
expand_ipv6 $__IP __IPV6
expand_ipv6 $__DATA __DATA
[ "$__DATA" = "$__IPV6" ] && { # IPv6 no update needed
write_log 7 "IPv6 at LuaDNS already up to date"
return 0
}
else
[ "$__DATA" = "$__IP" ] && { # IPv4 no update needed
write_log 7 "IPv4 at LuaDNS already up to date"
return 0
}
fi
}
# update is needed
# let's build data to send
# use file to work around " needed for json
cat > $DATFILE << EOF
{"name":"$__HOST.","type":"$__TYPE","content":"$__IP","ttl":$__TTL}
EOF
# let's complete transfer command
__RUNPROG="$__PRGBASE --request PUT --data @$DATFILE '$__URLBASE/zones/$__ZONEID/records/$__RECID'"
luadns_transfer || return 1
return 0

View File

@ -10,13 +10,15 @@
# so we send a dummy (localhost) and a seconds later we send the correct IP addr
#
local __DUMMY
local __UPDURL="http://[USERNAME]:[PASSWORD]@dynupdate.no-ip.com/nic/update?hostname=[DOMAIN]&myip=[IP]"
local __UPDURL6="http://[USERNAME]:[PASSWORD]@dynupdate6.noip.com/nic/update?hostname=[DOMAIN]&myip=[IP]"
local __UPDURL="http://[USERNAME]:[PASSWORD]@dynupdate.noip.com/nic/update?hostname=[DOMAIN]&myip=[IP]"
# inside url we need username and password
[ -z "$username" ] && write_log 14 "Service section not configured correctly! Missing 'username'"
[ -z "$password" ] && write_log 14 "Service section not configured correctly! Missing 'password'"
# set IP version dependend dummy (localhost)
[ $use_ipv6 -eq 0 ] && __DUMMY="127.0.0.1" || __DUMMY="::1"
[ $use_ipv6 -eq 0 ] && __UPDURL=$__UPDURL || __UPDURL=$__UPDURL6
# lets do DUMMY transfer
write_log 7 "sending dummy IP to 'no-ip.com'"

View File

@ -0,0 +1,77 @@
#!/bin/sh
# Derived from update_gandi_net.sh
. /usr/share/libubox/jshn.sh
local __ENDPOINT="https://api.nsone.net/v1"
local __TTL=600
local __RRTYPE
local __URL
local __STATUS
[ -z "$username" ] && write_log 14 "Service section not configured correctly! Missing zone as 'username'"
[ -z "$password" ] && write_log 14 "Service section not configured correctly! Missing API Key as 'password'"
[ $use_ipv6 -ne 0 ] && __RRTYPE="AAAA" || __RRTYPE="A"
# Construct JSON payload
json_init
# {"answers":[{"answer":["1.1.1.1"]}]}
json_add_array answers
json_add_object
json_add_array answer
json_add_string "" "$__IP"
json_close_array
json_close_object
json_close_array
__URL="$__ENDPOINT/zones/$username/$domain/$__RRTYPE"
__STATUS=$(curl -s -X POST "$__URL" \
-H "X-NSONE-Key: $password" \
-H "Content-Type: application/json" \
-d "$(json_dump)" \
-w "%{http_code}\n" -o $DATFILE 2>$ERRFILE)
if [ $? -ne 0 ]; then
write_log 14 "Curl failed: $(cat $ERRFILE)"
return 1
elif [ -z $__STATUS ] || [ $__STATUS != 200 ]; then
write_log 4 "Request failed: $__STATUS, NS1 answered: $(cat $DATFILE)"
if [ $__STATUS = 404 ]; then
write_log 4 "Status is 404, trying to create a DNS record"
json_init
json_add_string "zone" "$username"
json_add_string "domain" "$domain"
json_add_string "type" "$__RRTYPE"
json_add_string "ttl" "$__TTL"
json_add_array answers
json_add_object
json_add_array answer
json_add_string "" "$__IP"
json_close_array
json_close_object
json_close_array
__STATUS=$(curl -s -X PUT "$__URL" \
-H "X-NSONE-Key: $password" \
-H "Content-Type: application/json" \
-d "$(json_dump)" \
-w "%{http_code}\n" -o $DATFILE 2>$ERRFILE)
if [ $? -ne 0 ]; then
write_log 14 "Curl failed: $(cat $ERRFILE)"
return 1
elif [ -z $__STATUS ] || [ $__STATUS != 200 ]; then
write_log 14 "Request failed: $__STATUS, NS1 answered: $(cat $DATFILE)"
return 1
fi
else
return 1
fi
fi
write_log 7 "NS1 answered: $(cat $DATFILE)"
return 0

View File

@ -17,8 +17,8 @@
#
local __TTL=600 #.preset DNS TTL (in seconds)
local __RRTYPE __PW __TCP
local __PROG=$(which nsupdate) # BIND nsupdate ?
[ -z "$__PROG" ] && __PROG=$(which knsupdate) # Knot nsupdate ?
local __PROG=$(command -v nsupdate) # BIND nsupdate ?
[ -z "$__PROG" ] && __PROG=$(command -v knsupdate) # Knot nsupdate ?
[ -z "$__PROG" ] && write_log 14 "'nsupdate' or 'knsupdate' not installed !"
[ -z "$username" ] && write_log 14 "Service section not configured correctly! Missing 'username'"

View File

@ -0,0 +1,142 @@
#!/bin/sh
# ONE.COM DDNS SCRIPT
# REQUIRES CURL
# $ opkg install curl
# SCRIPT BY LUGICO
# CONTACT: main@lugico.de
[ -z "$CURL" ] && [ -z "$CURL_SSL" ] && write_log 14 "one.com communication require cURL with SSL support. Please install"
[ -z "$domain" ] && write_log 14 "Service section not configured correctly! Missing 'domain'"
[ -z "$username" ] && write_log 14 "Service section not configured correctly! Missing 'username'"
[ -z "$password" ] && write_log 14 "Service section not configured correctly! Missing 'password'"
. /usr/share/libubox/jshn.sh
write_log 0 "one.com ddns script started"
local __SUBDOMAIN __MAINDOMAIN __LOGINURL __RECORDID
local __TTL=3600
COOKIEJAR=$(mktemp /tmp/one_com_cookiejar.XXXXXX) || exit 1
__SUBDOMAIN=$(echo $domain | sed -e 's/[^\.]*\.[^\.]*$//' -e 's/\.$//' )
__MAINDOMAIN=$(echo $domain | sed -e "s/${__SUBDOMAIN}\.//" )
# LOGGING IN
# GET LOGIN POST URL FROM FORM
__LOGINURL=$( $CURL \
-RsSL \
--stderr $ERRFILE \
-c $COOKIEJAR \
"https://www.one.com/admin/" \
| grep 'Login-form login autofill' \
| sed -e 's/.*action="//' -e 's/".*//' -e 's/\&amp;/\&/g' \
)
# POST LOGIN DATA
$CURL \
-RsSL \
--stderr $ERRFILE \
-c $COOKIEJAR \
-b $COOKIEJAR \
"${__LOGINURL}" \
-H "Content-Type: application/x-www-form-urlencoded" \
-X POST \
-d "username=${username}&password=${password}&credentialId=" \
| grep "Invalid username or password." > $DATFILE
if [ "$?" == "0" ] ; then
write_log 14 "Invalid credentials"
return 1
fi
# SETTING DOMAIN
$CURL -RsSL \
--stderr $ERRFILE \
-c $COOKIEJAR \
-b $COOKIEJAR \
"https://www.one.com/admin/select-admin-domain.do?domain=${__MAINDOMAIN}" \
| grep "<meta name=\"one.com:active-domain\" content=\"${__MAINDOMAIN}\"/>" > $DATFILE
if [ "$?" != "0" ] ; then
write_log 14 "Failed to select domain '${__MAINDOMAIN}'"
return 1
fi
# GETTING RECORD ID
records=$( $CURL \
-RsSL \
--stderr $ERRFILE \
-c $COOKIEJAR \
-b $COOKIEJAR \
"https://www.one.com/admin/api/domains/${__MAINDOMAIN}/dns/custom_records"
)
json_load "$records"
if json_is_a "result" "object" && \
json_select "result" && \
json_is_a "data" "array"
then
json_select "data"
i=1
while json_is_a ${i} "object" ; do
json_select "${i}"
json_select "attributes"
json_get_var "prefix" "prefix"
json_close_object
if [ "$prefix" == "$__SUBDOMAIN" ] ; then
json_get_var "__RECORDID" "id"
write_log 0 "Found record id : ${__RECORDID}"
break
fi
json_close_object
i=$(($i + 1))
done
fi
if [ "${__RECORDID}" == "" ] ; then
write_log 14 "domain record not found"
return 1
fi
# CREATING PATCH DATA
json_init
json_add_string "type" "dns_service_records"
json_add_string "id" "${__RECORDID}"
json_add_object "attributes"
json_add_string "type" "A"
json_add_string "prefix" "${__SUBDOMAIN}"
json_add_string "content" "${__IP}"
json_add_int "ttl" ${__TTL}
patchdata=$(json_dump)
# SENDING PATCH
$CURL \
-RsSL \
--stderr $ERRFILE \
-c $COOKIEJAR \
-b $COOKIEJAR \
-X PATCH \
-d "$patchdata" \
-H "Content-Type: application/json" \
"https://www.one.com/admin/api/domains/${__MAINDOMAIN}/dns/custom_records/${__RECORDID}" \
| grep "priority" > $DATFILE
if [ "$?" != "0" ] ; then
write_log 14 "one.com gave an unexpected response"
return 1
fi
rm $COOKIEJAR
write_log 0 "one.com ddns script finished without errors"
return 0

View File

@ -0,0 +1,63 @@
#!/bin/sh
# Derived from update_gandi_net.sh
. /usr/share/libubox/jshn.sh
local __TTL=600
local __RRTYPE
local __STATUS
local __RNAME
[ -z "$username" ] && write_log 14 "Service section not configured correctly! Missing subdomain as 'username'"
[ -z "$password" ] && write_log 14 "Service section not configured correctly! Missing API Key as 'password'"
[ -z "$param_opt" ] && write_log 14 "Service section not configured correctly! Missing PowerDNS URL as 'Optional Parameter'(param_opt)"
# Create endpoint from $param_opt
# e.g. param_opt=http://127.0.0.1:8081
local __ENDPOINT="$param_opt/api/v1/servers/localhost/zones"
[ $use_ipv6 -ne 0 ] && __RRTYPE="AAAA" || __RRTYPE="A"
# Make sure domain is period terminated
if [ ${domain: -1} != '.' ]; then
domain="${domain}."
fi
if [ $username == '@' ]; then
__RNAME="$domain"
else
__RNAME="$username.$domain"
fi
# Build JSON payload
json_init
json_add_array rrsets
json_add_object
json_add_string name "$__RNAME"
json_add_string type "$__RRTYPE"
json_add_int ttl $__TTL
json_add_string changetype "REPLACE"
json_add_array records
json_add_object
json_add_string content "$__IP"
json_add_boolean disabled 0
json_close_object
json_close_array
json_close_object
json_close_array
__STATUS=$(curl -Ss -X PATCH "$__ENDPOINT/$domain" \
-H "X-Api-Key: $password" \
-H "Content-Type: application/json" \
-d "$(json_dump)" \
-w "%{http_code}\n" \
-o $DATFILE 2>$ERRFILE)
if [ $? -ne 0 ]; then
write_log 14 "Curl failed: $(cat $ERRFILE)"
return 1
elif [ -z $__STATUS ] || [ $__STATUS != 204 ]; then
write_log 14 "PowerDNS request failed: $__STATUS \n$(cat $DATFILE)"
return 1
fi
return 0

View File

@ -0,0 +1,162 @@
#
# Distributed under the terms of the GNU General Public License (GPL) version 2.0
# 2024 Ansel Horn <dev@cahorn.net>
#
# Script for DDNS support via Porkbun's v3 API for the OpenWRT ddns-scripts package.
#
# Will attempt to create a new or edit an existing A or AAAA record for the
# given domain and subdomain. Existing CNAME and ALIAS records WILL NOT BE
# EDITED OR DELETED! "username" and "password" configurations should be set to
# Porkbun API key and secret key, respectively.
#
# Porkbun API documentation:
# https://porkbun.com/api/json/v3/documentation#DNS%20Create%20Record
#
# Source JSON parser
. /usr/share/libubox/jshn.sh
# Set API base URL
# Porkbun has warned it may change API hostname in the future:
# https://porkbun.com/api/json/v3/documentation#apiHost
__API="https://api.porkbun.com/api/json/v3"
# Check availability of cURL with SSL
[ -z "$CURL" ] && [ -z "$CURL_SSL" ] && write_log 14 "cURL with SSL support required! Please install"
# Validate configuration
[ -z "$domain" ] && write_log 14 "Service section not configured correctly! Missing 'domain'"
[ -z "$username" ] && write_log 14 "Service section not configured correctly! Missing 'username'"
[ -z "$password" ] && write_log 14 "Service section not configured correctly! Missing 'password'"
# Split FQDN into domain and subdomain(s)
__DOMAIN_REGEX='^\(\(.*\)\.\)\?\([^.]\+\.[^.]\+\)$'
echo $domain | grep "$__DOMAIN_REGEX" > /dev/null || write_log 14 "Invalid domain! Check 'domain' config"
__DOMAIN=$(echo $domain | sed -e "s/$__DOMAIN_REGEX/\3/")
__SUBDOMAIN=$(echo $domain | sed -e "s/$__DOMAIN_REGEX/\2/")
# Determine IPv4 or IPv6 address and record type
if [ "$use_ipv6" -eq 1 ]; then
expand_ipv6 "$__IP" __ADDR
__TYPE="AAAA"
else
__ADDR="$__IP"
__TYPE="A"
fi
# Inject authentication into API request JSON payload
function json_authenticate() {
json_add_string "apikey" "$username"
json_add_string "secretapikey" "$password"
}
# Make Porkbun API call
# $1 - Porkbun API endpoint
# $2 - request JSON payload
function api_call() {
local response url
url="$__API/$1"
write_log 7 "API endpoint URL: $url"
write_log 7 "API request JSON payload: $2"
response=$($CURL --data "$2" "$url")
write_log 7 "API response JSON payload: $response"
echo "$response"
}
# Check Porkbun API response status
function json_check_status() {
local status
json_get_var status "status"
[ "$status" == "SUCCESS" ] || write_log 14 "API request failed!"
}
# Review DNS record and, if it is the record we're looking for, get its id
function callback_review_record() {
local id name type
json_select "$2"
json_get_var id "id"
json_get_var name "name"
json_get_var type "type"
[ "$name" == "$domain" -a "$type" == "$__TYPE" ] && echo "$id"
json_select ..
}
# Retrieve all DNS records, find the first appropriate A/AAAA record, and get its id
function find_existing_record_id() {
local request response
json_init
json_authenticate
request=$(json_dump)
response=$(api_call "/dns/retrieve/$__DOMAIN" "$request")
json_load "$response"
json_check_status
json_for_each_item callback_review_record "records"
}
# Create a new A/AAAA record
function create_record() {
local request response
json_init
json_authenticate
json_add_string "name" "$__SUBDOMAIN"
json_add_string "type" "$__TYPE"
json_add_string "content" "$__ADDR"
request=$(json_dump)
response=$(api_call "/dns/create/$__DOMAIN" "$request")
json_load "$response"
json_check_status
}
# Retrieve an existing record and get its content
# $1 - record id to retrieve
function retrieve_record_content() {
local content request response
json_init
json_authenticate
request=$(json_dump)
response=$(api_call "/dns/retrieve/$__DOMAIN/$1" "$request")
json_load "$response"
json_check_status
json_select "records"
json_select 1
json_get_var content "content"
echo "$content"
}
# Edit an existing A/AAAA record
# $1 - record id to edit
function edit_record() {
local request response
json_init
json_authenticate
json_add_string "type" "$__TYPE"
json_add_string "content" "$__ADDR"
request=$(json_dump)
response=$(api_call "/dns/edit/$__DOMAIN/$1" "$request")
json_load "$response"
json_check_status
}
# Try to identify an appropriate existing DNS record to update
if [ -z $rec_id]; then
write_log 7 "Retrieving DNS $__TYPE record"
__ID=$(find_existing_record_id)
else
write_log 7 "Using user-supplied DNS record id: $rec_id"
__ID=$rec_id
fi
# Create or update DNS record with current IP address
if [ -z "$__ID" ]; then
write_log 7 "Creating new DNS $__TYPE record"
create_record
else
write_log 7 "Updating existing DNS $__TYPE record"
if [ "$(retrieve_record_content $__ID)" == "$__ADDR" ]; then
write_log 7 "Skipping Porkbun-unsupported forced noop update"
else
edit_record "$__ID"
fi
fi

View File

@ -0,0 +1,96 @@
#!/bin/sh
# Distributed under the terms of the GNU General Public License (GPL) version 2.0
# based on Yuval Adam's route53.sh found at https://github.com/yuvadm/route53-ddns/blob/master/route53.sh
# 2017 Max Berger <max at berger dot name>
[ -z "${CURL_SSL}" ] && write_log 14 "Amazon AWS Route53 communication require cURL with SSL support. Please install"
[ -z "${username}" ] && write_log 14 "Service section not configured correctly! Missing key as 'username'"
[ -z "${password}" ] && write_log 14 "Service section not configured correctly! Missing secret as 'password'"
[ -z "${domain}" ] && write_log 14 "Service section not configured correctly! Missing zone id as 'domain'"
ENDPOINT="route53.amazonaws.com"
RECORD_TTL=300
RECORD_NAME="${lookup_host}."
RECORD_VALUE="${__IP}"
[ ${use_ipv6} -eq 0 ] && RECORD_TYPE="A"
[ ${use_ipv6} -eq 1 ] && RECORD_TYPE="AAAA"
HOSTED_ZONE_ID="${domain}"
API_PATH="/2013-04-01/hostedzone/${HOSTED_ZONE_ID}/rrset/"
AWS_ACCESS_KEY_ID="${username}"
AWS_SECRET_ACCESS_KEY="${password}"
AWS_REGION='us-east-1'
AWS_SERVICE='route53'
hash() {
msg="$1"
echo -en "${msg}" | openssl dgst -sha256 | sed 's/^.* //'
}
sign_plain() {
# Sign message using a plaintext key
key="$1"
msg="$2"
echo -en "${msg}" | openssl dgst -hex -sha256 -hmac "${key}" | sed 's/^.* //'
}
sign() {
# Sign message using a hex formatted key
key="$1"
msg="$2"
echo -en "${msg}" | openssl dgst -hex -sha256 -mac HMAC -macopt "hexkey:${key}" | sed 's/^.* //'
}
request_body="<?xml version=\"1.0\" encoding=\"UTF-8\"?> \
<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\"> \
<ChangeBatch> \
<Changes> \
<Change> \
<Action>UPSERT</Action> \
<ResourceRecordSet> \
<Name>${RECORD_NAME}</Name> \
<Type>${RECORD_TYPE}</Type> \
<TTL>${RECORD_TTL}</TTL> \
<ResourceRecords> \
<ResourceRecord> \
<Value>${RECORD_VALUE}</Value> \
</ResourceRecord> \
</ResourceRecords> \
</ResourceRecordSet> \
</Change> \
</Changes> \
</ChangeBatch> \
</ChangeResourceRecordSetsRequest>"
fulldate="$(date --utc +%Y%m%dT%H%M%SZ)"
shortdate="$(date --utc +%Y%m%d)"
signed_headers="host;x-amz-date"
request_hash="$(hash "${request_body}")"
canonical_request="POST\n${API_PATH}\n\nhost:route53.amazonaws.com\nx-amz-date:${fulldate}\n\n${signed_headers}\n${request_hash}"
date_key="$(sign_plain "AWS4${AWS_SECRET_ACCESS_KEY}" "${shortdate}")"
region_key="$(sign "${date_key}" ${AWS_REGION})"
service_key="$(sign "${region_key}" ${AWS_SERVICE})"
signing_key="$(sign "${service_key}" aws4_request)"
credential="${shortdate}/${AWS_REGION}/${AWS_SERVICE}/aws4_request"
sigmsg="AWS4-HMAC-SHA256\n${fulldate}\n${credential}\n$(hash "${canonical_request}")"
signature="$(sign "${signing_key}" "${sigmsg}")"
authorization="AWS4-HMAC-SHA256 Credential=${AWS_ACCESS_KEY_ID}/${credential}, SignedHeaders=${signed_headers}, Signature=${signature}"
ANSWER="$(flock /tmp/$(basename -s .sh "$0").lock curl \
-X "POST" \
-H "Host: route53.amazonaws.com" \
-H "X-Amz-Date: ${fulldate}" \
-H "Authorization: ${authorization}" \
-H "Content-Type: text/xml" \
-d "$request_body" \
"https://${ENDPOINT}${API_PATH}")"
write_log 7 "${ANSWER}"
echo "${ANSWER}" | grep -F "Error" >/dev/null && return 1
echo "${ANSWER}" | grep -F "ChangeInfo" >/dev/null && return 0
return 2

View File

@ -0,0 +1,134 @@
#!/bin/sh
#
# 2021 Martijn Atema <martijn@atema.one>
#
# This script sends ddns updates using the TransIP API (see https://api.transip.nl/)
# and is parsed by dynamic_dns_functions.sh inside send_update().
#
# The following options provided by ddns are used:
# username - Username of account used for logging in to TransIP
# password - Private key generated at https://www.transip.nl/cp/account/api/
# (make sure to accept non-whitelisted IP addresses)
# domain - Base domain name registered at TransIP
# ('domain.tld' when updating 'hostname.domain.tld')
# param_enc - Name of DNS record to update
# ('hostname' when updating 'hostname.domain.tld')
# param_opt - TTL of the DNS record to update (in seconds)
#
# Note: Make sure that there is exactly one record of type A (for IPv4) or
# AAAA (for IPv6) with the specified name and TTL. That record will be
# updated by this script.
#
# The script requires cURL with SSL and the openssl binary
[ -z "${username}" ] && write_log 14 "Service config is missing 'username'"
[ -z "${password}" ] && write_log 14 "Service config is missing 'password' (private key)"
[ -z "${domain}" ] && write_log 14 "Service config is missing 'domain' (base domain name)"
[ -z "${param_enc}" ] && write_log 14 "Service config is missing 'param_enc' (DNS record name)"
[ -z "${param_opt}" ] && write_log 14 "Service config is missing 'param_opt' (DNS record TTL)"
[ -z "${CURL_SSL}" ] && write_log 14 "TransIP update requires cURL with SSL"
[ -z "$(openssl version)" ] && write_log 14 "TransIP update requires openssl binary"
. /usr/share/libubox/jshn.sh
# Re-format the private key and write to a temporary file
__tmp_keyfile="$(mktemp -t ddns-transip.XXXXXX)"
echo "${password}" | \
sed -e "s/-----BEGIN PRIVATE KEY-----\s*/&\n/" \
-e "s/-----END PRIVATE KEY-----/\n&/" \
-e "s/\S\{64\}\s*/&\n/g" \
> "${__tmp_keyfile}"
# Create authentication request
json_init
json_add_string "login" "${username}"
json_add_string "label" "DDNS-script ($(openssl rand -hex 4))"
json_add_string "nonce" $(openssl rand -hex 16)
json_add_boolean "read_only" 0
json_add_boolean "global_key" 1
__auth_body="$(json_dump)"
# Sign body using the private key and encode with base64
__auth_signature=$(echo -n "${__auth_body}" | \
openssl dgst -sha512 -sign "${__tmp_keyfile}" | \
openssl base64 | \
tr -d " \t\n\r")
rm "${__tmp_keyfile}"
# Send and parse request for a temporary authentication token
__auth_status=$(curl -s -X POST "https://api.transip.nl/v6/auth" \
-H "Content-Type: application/json" \
-H "Signature: ${__auth_signature}" \
-d "${__auth_body}" \
-w "%{http_code}\n" \
-o "${DATFILE}" 2>"${ERRFILE}")
# Logging for error and debug
if [ $? -ne 0 ]; then
write_log 14 "Curl failed: $(cat "${ERRFILE}")"
return 1
fi
if [ -z ${__auth_status} ] || [ ${__auth_status} -ne 201 ]; then
write_log 14 "TransIP authentication (status ${__auth_status}) failed: $(cat ${DATFILE})"
return 1
fi
write_log 7 "TransIP authentication successful"
## Extract token from the response
__auth_token=$(cat ${DATFILE} | sed 's/^.*"token" *: *"\([^"]*\)".*$/\1/')
# Create request body for update
json_init
json_add_object "dnsEntry"
json_add_string "name" "${param_enc}"
json_add_string "type" "$([ $use_ipv6 -ne 0 ] && echo -n AAAA || echo -n A)"
json_add_int "expire" "${param_opt}"
json_add_string "content" "${__IP}"
json_close_object
__update_body="$(json_dump)"
# Send update request
__update_status=$(curl -s -X PATCH "https://api.transip.nl/v6/domains/${domain}/dns" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${__auth_token}" \
-d "${__update_body}" \
-w "%{http_code}\n" \
-o "${DATFILE}" 2>"${ERRFILE}")
# Logging for error and debug
if [ $? -ne 0 ]; then
write_log 14 "Curl failed: $(cat "${ERRFILE}")"
return 1
fi
if [ -z ${__update_status} ] || [ ${__update_status} -ne 204 ]; then
write_log 14 "TransIP DNS update (status ${__update_status}) failed: $(cat ${DATFILE})"
return 1
fi
write_log 7 "TransIP DNS update successful"
return 0

View File

@ -0,0 +1,6 @@
{
"name": "3322.org",
"ipv4": {
"url": "http://[USERNAME]:[PASSWORD]@members.3322.org/dyndns/update?system=dyndns&hostname=[DOMAIN]&myip=[IP]"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "afraid.org-basicauth",
"ipv4": {
"url": "https://[USERNAME]:[PASSWORD]@freedns.afraid.org/nic/update?hostname=[DOMAIN]&myip=[IP]"
},
"ipv6": {
"url": "https://[USERNAME]:[PASSWORD]@freedns.afraid.org/nic/update?hostname=[DOMAIN]&myip=[IP]"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "afraid.org-keyauth",
"ipv4": {
"url": "https://freedns.afraid.org/dynamic/update.php?[PASSWORD]&address=[IP]"
},
"ipv6": {
"url": "https://freedns.afraid.org/dynamic/update.php?[PASSWORD]&address=[IP]"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "afraid.org-v2-basic",
"ipv4": {
"url": "https://[USERNAME]:[PASSWORD]@sync.afraid.org/u/?h=[DOMAIN]&ip=[IP]"
},
"ipv6": {
"url": "https://[USERNAME]:[PASSWORD]@v6.sync.afraid.org/u/?h=[DOMAIN]&ip=[IP]"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "afraid.org-v2-token",
"ipv4": {
"url": "https://sync.afraid.org/u/[PASSWORD]/?address=[IP]"
},
"ipv6": {
"url": "https://v6.sync.afraid.org/u/[PASSWORD]/?address=[IP]"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "all-inkl.com",
"ipv4": {
"url": "http://[USERNAME]:[PASSWORD]@dyndns.kasserver.com/?myip=[IP]"
},
"ipv6": {
"url": "http://[USERNAME]:[PASSWORD]@dyndns.kasserver.com/?myip=[IP]"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "bind-nsupdate",
"ipv4": {
"url": "update_nsupdate.sh"
},
"ipv6": {
"url": "update_nsupdate.sh"
}
}

View File

@ -0,0 +1,7 @@
{
"name": "changeip.com",
"ipv4": {
"url": "http://[USERNAME]:[PASSWORD]@nic.changeip.com/nic/update?u=[USERNAME]&p=[PASSWORD]&cmd=update&hostname=[DOMAIN]&ip=[IP]",
"answer": "Successful"
}
}

View File

@ -0,0 +1,10 @@
{
"name": "cloud.google.com-v1",
"ipv4": {
"url": "update_gcp_v1.sh"
},
"ipv6": {
"url": "update_gcp_v1.sh"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "cloudflare.com-v4",
"ipv4": {
"url": "update_cloudflare_com_v4.sh"
},
"ipv6": {
"url": "update_cloudflare_com_v4.sh"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "cnkuai.cn",
"ipv4": {
"url": "update_cnkuai_cn.sh"
},
"ipv6": {
"url": "update_cnkuai_cn.sh"
}
}

View File

@ -0,0 +1,11 @@
{
"name": "core-networks.de",
"ipv4": {
"url": "http://[USERNAME]:[PASSWORD]@dyndns.core-networks.de/?hostname=[DOMAIN]&myip=[IP]&keepipv6=1",
"answer": "good"
},
"ipv6": {
"url": "http://[USERNAME]:[PASSWORD]@dyndns.core-networks.de/?hostname=[DOMAIN]&myip=[IP]&keepipv4=1",
"answer": "good"
}
}

View File

@ -0,0 +1,7 @@
{
"name": "ddnss.de",
"ipv4": {
"url": "http://ip4.ddnss.de/upd.php?user=[USERNAME]&pwd=[PASSWORD]&host=[DOMAIN]&ip=[IP]",
"answer": "Updated|No change"
}
}

View File

@ -0,0 +1,6 @@
{
"name": "ddo.jp",
"ipv4": {
"url": "http://free.ddo.jp/dnsupdate.php?dn=[DOMAIN]&pw=[PASSWORD]&ip=[IP]"
}
}

View File

@ -0,0 +1,11 @@
{
"name": "desec.io",
"ipv4": {
"url": "https://update.dedyn.io/update?username=[USERNAME]&password=[PASSWORD]&hostname=[DOMAIN]&myipv4=[IP]&myipv6=preserve",
"answer": "good|nochg"
},
"ipv6": {
"url": "https://update.dedyn.io/update?username=[USERNAME]&password=[PASSWORD]&hostname=[DOMAIN]&myipv6=[IP]&myipv4=preserve",
"answer": "good|nochg"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "dhis.org",
"ipv4": {
"url": "http://[USERNAME]:[PASSWORD]@is.dhis.org/"
},
"ipv6": {
"url": "http://[USERNAME]:[PASSWORD]@is.dhis.org/"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "digitalocean.com-v2",
"ipv4": {
"url": "update_digitalocean_com_v2.sh"
},
"ipv6": {
"url": "update_digitalocean_com_v2.sh"
}
}

View File

@ -0,0 +1,7 @@
{
"name": "dnsdynamic.org",
"ipv4": {
"url": "http://[USERNAME]:[PASSWORD]@www.dnsdynamic.org/api/?hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
}
}

View File

@ -0,0 +1,6 @@
{
"name": "dnsever.com",
"ipv4": {
"url": "http://[USERNAME]:[PASSWORD]@dyna.dnsever.com/update.php?host[[DOMAIN]]"
}
}

View File

@ -0,0 +1,6 @@
{
"name": "dnsexit.com",
"ipv4": {
"url": "http://update.dnsexit.com/RemoteUpdate.sv?login=[USERNAME]&password=[PASSWORD]&host=[DOMAIN]&myip=[IP]"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "dnshome.de",
"ipv4": {
"url": "http://[USERNAME]:[PASSWORD]@www.dnshome.de/dyndns.php?hostname=[DOMAIN]&ip=[IP]"
},
"ipv6": {
"url": "http://[USERNAME]:[PASSWORD]@www.dnshome.de/dyndns.php?hostname=[DOMAIN]&ip6=[IP]"
}
}

View File

@ -0,0 +1,7 @@
{
"name": "dnsmadeeasy.com",
"ipv4": {
"url": "http://cp.dnsmadeeasy.com/servlet/updateip?username=[USERNAME]&password=[PASSWORD]&id=[DOMAIN]&ip=[IP]",
"answer": "success|ip-same"
}
}

View File

@ -0,0 +1,6 @@
{
"name": "dnsmax.com",
"ipv4": {
"url": "http://update.dnsmax.com/update/?username=[USERNAME]&password=[PASSWORD]&resellerid=1&clientname=openwrt&clientversion=8.09&protocolversion=2.0&updatehostname=[DOMAIN]&ip=[IP]"
}
}

View File

@ -0,0 +1,7 @@
{
"name": "dnsomatic.com",
"ipv4": {
"url": "http://[USERNAME]:[PASSWORD]@updates.dnsomatic.com/nic/update?hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
}
}

View File

@ -0,0 +1,7 @@
{
"name": "dnspark.com",
"ipv4": {
"url": "http://[USERNAME]:[PASSWORD]@control.dnspark.com/api/dynamic/update.php?hostname=[DOMAIN]&ip=[IP]",
"answer": "ok|nochange"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "dnspod.cn",
"ipv4": {
"url": "update_dnspod_cn.sh"
},
"ipv6": {
"url": "update_dnspod_cn.sh"
}
}

View File

@ -0,0 +1,11 @@
{
"name": "do.de",
"ipv4": {
"url": "http://ddns.do.de/?myip=[IP]&hostname=[DOMAIN]&username=[USERNAME]&password=[PASSWORD]",
"answer": "good|nochg"
},
"ipv6": {
"url": "http://ddns.do.de/?myip=[IP]&hostname=[DOMAIN]&username=[USERNAME]&password=[PASSWORD]",
"answer": "good|nochg"
}
}

View File

@ -0,0 +1,7 @@
{
"name": "domopoli.de",
"ipv4": {
"url": "http://[USERNAME]:[PASSWORD]@http://dyndns.domopoli.de/nic/update?hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
}
}

View File

@ -0,0 +1,11 @@
{
"name": "duckdns.org",
"ipv4": {
"url": "http://www.duckdns.org/update?domains=[DOMAIN]&token=[PASSWORD]&ip=[IP]",
"answer": "OK"
},
"ipv6": {
"url": "http://www.duckdns.org/update?domains=[DOMAIN]&token=[PASSWORD]&ipv6=[IP]",
"answer": "OK"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "duiadns.net",
"ipv4": {
"url": "http://ip.duiadns.net/dynamic.duia?host=[DOMAIN]&password=[PASSWORD]&ip4=[IP]"
},
"ipv6": {
"url": "http://ip.duiadns.net/dynamic.duia?host=[DOMAIN]&password=[PASSWORD]&ip6=[IP]"
}
}

View File

@ -0,0 +1,7 @@
{
"name": "dy.fi",
"ipv4": {
"url": "http://[USERNAME]:[PASSWORD]@www.dy.fi/nic/update?hostname=[DOMAIN]",
"answer": "good|nochg"
}
}

View File

@ -0,0 +1,11 @@
{
"name": "dyn.com",
"ipv4": {
"url": "http://[USERNAME]:[PASSWORD]@members.dyndns.org/v3/update?hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
},
"ipv6": {
"url": "http://[USERNAME]:[PASSWORD]@members.dyndns.org/v3/update?hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
}
}

View File

@ -0,0 +1,7 @@
{
"name": "dyndns.it",
"ipv4": {
"url": "http://[USERNAME]:[PASSWORD]@update.dyndns.it/nic/update?system=dyndns&hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
}
}

View File

@ -0,0 +1,11 @@
{
"name": "dyndns.org",
"ipv4": {
"url": "http://[USERNAME]:[PASSWORD]@members.dyndns.org/v3/update?hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
},
"ipv6": {
"url": "http://[USERNAME]:[PASSWORD]@members.dyndns.org/v3/update?hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "dynu.com",
"ipv4": {
"url": "http://api.dynu.com/nic/update?hostname=[DOMAIN]&myip=[IP]&username=[USERNAME]&password=[PASSWORD]"
},
"ipv6": {
"url": "http://api.dynu.com/nic/update?hostname=[DOMAIN]&myipv6=[IP]&username=[USERNAME]&password=[PASSWORD]"
}
}

View File

@ -0,0 +1,11 @@
{
"name": "dynv6.com",
"ipv4": {
"url": "http://dynv6.com/api/update?hostname=[DOMAIN]&token=[PASSWORD]&ipv4=[IP]",
"answer": "updated|unchanged"
},
"ipv6": {
"url": "http://dynv6.com/api/update?hostname=[DOMAIN]&token=[PASSWORD]&ipv6=[IP]",
"answer": "updated|unchanged"
}
}

View File

@ -0,0 +1,11 @@
{
"name": "easydns.com",
"ipv4": {
"url": "http://[USERNAME]:[PASSWORD]@api.cp.easydns.com/dyn/generic.php?hostname=[DOMAIN]&myip=[IP]",
"answer": "OK|NOERROR"
},
"ipv6": {
"url": "http://[USERNAME]:[PASSWORD]@api.cp.easydns.com/dyn/generic.php?hostname=[DOMAIN]&myip=[IP]",
"answer": "OK|NOERROR"
}
}

View File

@ -0,0 +1,6 @@
{
"name": "freedns.42.pl",
"ipv4": {
"url": "update_freedns_42_pl.sh"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "gandi.net",
"ipv4": {
"url": "update_gandi_net.sh"
},
"ipv6": {
"url": "update_gandi_net.sh"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "godaddy.com-v1",
"ipv4": {
"url": "update_godaddy_com_v1.sh"
},
"ipv6": {
"url": "update_godaddy_com_v1.sh"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "goip.de",
"ipv4": {
"url": "http://www.goip.de/setip?username=[USERNAME]&password=[PASSWORD]&subdomain=[DOMAIN]&ip=[IP]"
},
"ipv6": {
"url": "http://www.goip.de/setip?username=[USERNAME]&password=[PASSWORD]&subdomain=[DOMAIN]&ip6=[IP]"
}
}

View File

@ -0,0 +1,11 @@
{
"name": "google.com",
"ipv4": {
"url": "https://[USERNAME]:[PASSWORD]@domains.google.com/nic/update?hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
},
"ipv6": {
"url": "https://[USERNAME]:[PASSWORD]@domains.google.com/nic/update?hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
}
}

View File

@ -0,0 +1,11 @@
{
"name": "he.net",
"ipv4": {
"url": "http://[DOMAIN]:[PASSWORD]@dyn.dns.he.net/nic/update?hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
},
"ipv6": {
"url": "http://[DOMAIN]:[PASSWORD]@dyn.dns.he.net/nic/update?hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
}
}

View File

@ -0,0 +1,11 @@
{
"name": "hosting.de",
"ipv4": {
"url": "https://[USERNAME]:[PASSWORD]@ddns.hosting.de/nic/update?hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
},
"ipv6": {
"url": "https://[USERNAME]:[PASSWORD]@ddns.hosting.de/nic/update?hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "huaweicloud.com",
"ipv4": {
"url": "update_huaweicloud_com.sh"
},
"ipv6": {
"url": "update_huaweicloud_com.sh"
}
}

View File

@ -0,0 +1,11 @@
{
"name": "infomaniak.com",
"ipv4": {
"url": "https://[USERNAME]:[PASSWORD]@infomaniak.com/nic/update?hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
},
"ipv6": {
"url": "https://[USERNAME]:[PASSWORD]@infomaniak.com/nic/update?hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
}
}

View File

@ -0,0 +1,11 @@
{
"name": "inwx.de",
"ipv4": {
"url": "http://[USERNAME]:[PASSWORD]@dyndns.inwx.com/nic/update?myip=[IP]",
"answer": "good|nochg"
},
"ipv6": {
"url": "http://[USERNAME]:[PASSWORD]@dyndns.inwx.com/nic/update?myipv6=[IP]",
"answer": "good|nochg"
}
}

View File

@ -0,0 +1,7 @@
{
"name": "ipnodns.ru",
"ipv4": {
"url": "https://ipnodns.ru/cgi-bin/dyndns.cgi?login=[USERNAME]&secret=[PASSWORD]",
"answer": "ok"
}
}

View File

@ -0,0 +1,7 @@
{
"name": "joker.com",
"ipv4": {
"url": "http://svc.joker.com/nic/update?username=[USERNAME]&password=[PASSWORD]&myip=[IP]&hostname=[DOMAIN]",
"answer": "good|nochg"
}
}

View File

@ -0,0 +1,11 @@
{
"name": "loopia.se",
"ipv4": {
"url": "http://[USERNAME]:[PASSWORD]@dns.loopia.se/XDynDNSServer/XDynDNS.php?system=custom&hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
},
"ipv6": {
"url": "http://[USERNAME]:[PASSWORD]@dns.loopia.se/XDynDNSServer/XDynDNS.php?system=custom&hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "luadns.com-v1",
"ipv4": {
"url": "update_luadns_v1.sh"
},
"ipv6": {
"url": "update_luadns_v1.sh"
}
}

View File

@ -0,0 +1,7 @@
{
"name": "moniker.com",
"ipv4": {
"url": "https://dynamicdns.key-systems.net/update.php?hostname=[DOMAIN]&password=[PASSWORD]&ip=[IP]",
"answer": "success"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "mydns.jp",
"ipv4": {
"url": "http://www.mydns.jp/directip.html?MID=[USERNAME]&PWD=[PASSWORD]&IPV4ADDR=[IP]"
},
"ipv6": {
"url": "http://www.mydns.jp/directip.html?MID=[USERNAME]&PWD=[PASSWORD]&IPV6ADDR=[IP]"
}
}

View File

@ -0,0 +1,11 @@
{
"name": "myonlineportal.net",
"ipv4": {
"url": "http://myonlineportal.net/updateddns?hostname=[DOMAIN]&ip=[IP]&username=[USERNAME]&password=[PASSWORD]",
"answer": "good|nochg"
},
"ipv6": {
"url": "http://myonlineportal.net/updateddns?hostname=[DOMAIN]&ip6=[IP]&username=[USERNAME]&password=[PASSWORD]",
"answer": "good|nochg"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "mythic-beasts.com (API v2)",
"ipv4": {
"url": "https://[USERNAME]:[PASSWORD]@ipv4.api.mythic-beasts.com/dns/v2/dynamic/[DOMAIN]"
},
"ipv6": {
"url": "https://[USERNAME]:[PASSWORD]@ipv6.api.mythic-beasts.com/dns/v2/dynamic/[DOMAIN]"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "mythic-beasts.com",
"ipv4": {
"url": "http://dnsapi4.mythic-beasts.com/?domain=[USERNAME]&password=[PASSWORD]&command=REPLACE%20[DOMAIN]%2060%20A%20DYNAMIC_IP&origin=."
},
"ipv6": {
"url": "http://dnsapi6.mythic-beasts.com/?domain=[USERNAME]&password=[PASSWORD]&command=REPLACE%20[DOMAIN]%2060%20AAAA%20DYNAMIC_IP&origin=."
}
}

View File

@ -0,0 +1,6 @@
{
"name": "namecheap.com",
"ipv4": {
"url": "http://dynamicdns.park-your-domain.com/update?host=[USERNAME]&domain=[DOMAIN]&password=[PASSWORD]&ip=[IP]"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "njal.la",
"ipv4": {
"url": "https://njal.la/update/?h=[DOMAIN]&k=[PASSWORD]&a=[IP]"
},
"ipv6": {
"url": "https://njal.la/update/?h=[DOMAIN]&k=[PASSWORD]&aaaa=[IP]"
}
}

View File

@ -0,0 +1,10 @@
{
"name": "no-ip.com",
"ipv4": {
"url": "update_no-ip_com.sh"
},
"ipv6": {
"url": "update_no-ip_com.sh"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "no-ip.pl",
"ipv4": {
"url": "http://[USERNAME]:[PASSWORD]@update.no-ip.pl/?hostname=[DOMAIN]"
},
"ipv6": {
"url": "http://[USERNAME]:[PASSWORD]@update.no-ip.pl/?hostname=[DOMAIN]"
}
}

View File

@ -0,0 +1,11 @@
{
"name": "now-dns.com",
"ipv4": {
"url": "https://[USERNAME]:[PASSWORD]@now-dns.com/update?hostname=[DOMAIN]",
"answer": "good|nochg"
},
"ipv6": {
"url": "https://[USERNAME]:[PASSWORD]@now-dns.com/update?hostname=[DOMAIN]",
"answer": "good|nochg"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "ns1.com",
"ipv4": {
"url": "update_ns1_com.sh"
},
"ipv6": {
"url": "update_ns1_com.sh"
}
}

View File

@ -0,0 +1,11 @@
{
"name": "nsupdate.info",
"ipv4": {
"url": "http://[USERNAME]:[PASSWORD]@ipv4.nsupdate.info/nic/update?hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
},
"ipv6": {
"url": "http://[USERNAME]:[PASSWORD]@ipv6.nsupdate.info/nic/update?hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
}
}

View File

@ -0,0 +1,6 @@
{
"name": "one.com",
"ipv4": {
"url": "update_one_com.sh"
}
}

View File

@ -0,0 +1,7 @@
{
"name":"opendns.com",
"ipv4": {
"url": "http://[USERNAME]:[PASSWORD]@updates.opendns.com/nic/update?hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
}
}

View File

@ -0,0 +1,6 @@
{
"name": "oray.com",
"ipv4": {
"url": "http://[USERNAME]:[PASSWORD]@ddns.oray.com/ph/update?hostname=[DOMAIN]&myip=[IP]"
}
}

View File

@ -0,0 +1,11 @@
{
"name": "ovh.com",
"ipv4": {
"url": "https://[USERNAME]:[PASSWORD]@dns.eu.ovhapis.com/nic/update?system=dyndns&hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
},
"ipv6": {
"url": "https://[USERNAME]:[PASSWORD]@dns.eu.ovhapis.com/nic/update?system=dyndns&hostname=[DOMAIN]&myip=[IP]",
"answer": "good|nochg"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "PowerDNS",
"ipv4": {
"url": "update_pdns.sh"
},
"ipv6": {
"url": "update_pdns.sh"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "porkbun.com-v3",
"ipv4": {
"url": "update_porkbun_v3.sh"
},
"ipv6": {
"url": "update_porkbun_v3.sh"
}
}

View File

@ -0,0 +1,11 @@
{
"name": "regfish.de",
"ipv4": {
"url": "http://dyndns.regfish.de/?fqdn=[DOMAIN]&forcehost=1&authtype=secure&token=[PASSWORD]&ipv4=[IP]",
"answer": "success|100|101"
},
"ipv6": {
"url": "http://dyndns.regfish.de/?fqdn=[DOMAIN]&forcehost=1&authtype=secure&token=[PASSWORD]&ipv6=[IP]",
"answer": "success|100|101"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "route53-v1",
"ipv4": {
"url": "update_route53_v1.sh"
},
"ipv6": {
"url": "update_route53_v1.sh"
}
}

Some files were not shown because too many files have changed in this diff Show More