1
0
mirror of https://github.com/EV21/dynb.git synced 2025-12-26 16:39:32 +01:00

19 Commits
0.3.5 ... main

Author SHA1 Message Date
EV21
a98f973d4d hotfix: changed DomRobot API behaviour 2025-11-26 22:00:30 +01:00
9efc3df858 minor: 📝 update CHANGELOG 2023-02-26 18:55:04 +01:00
715e548dfb minor: increase version 2023-02-26 18:51:53 +01:00
1624691612 fix: set constant alpine version as latest couses issues 2023-02-26 18:51:03 +01:00
7d66155cc1 minor: 📝 update CHANGELOG 2023-02-26 18:00:39 +01:00
fdd4d03e74 fix: incorrect dns ip handling via domrobot 2023-02-26 17:57:42 +01:00
e743532664 minor: 📝 update README 2022-11-24 22:15:47 +01:00
3d76946db5 minor: 📝 update CHANGELOG 2022-11-21 16:15:10 +01:00
0448d698ae minor: increase version 2022-11-21 16:12:08 +01:00
2ac50d326f fix: handle some curl exit codes 2022-11-21 16:09:12 +01:00
327e896754 feat: use provider checkip API 2022-11-21 10:45:16 +01:00
d44f76e1ef minor: 📝 update CHANGELOG 2022-11-10 18:03:11 +01:00
6d16b0807e doc: 📝 update default values in table 2022-11-10 18:01:19 +01:00
05a58af921 feat: use provider DNS servers 2022-11-10 16:19:51 +01:00
0eec80f604 feat: temporarily disable an ip version if the connectivity/routing does not work 2022-11-10 12:01:36 +01:00
15ea0345bd minor: 📝 update CHANGELOG 2022-11-07 22:14:38 +01:00
c1869343f6 doc: 📝 add dynu.com as DynDNS2 provider 2022-11-07 22:12:52 +01:00
b683937e59 feat: add support for dynu.com as DynDNS2 provider 2022-11-07 22:12:35 +01:00
3eb90f61f7 refactor: multiple exractions 2022-11-07 16:21:41 +01:00
4 changed files with 472 additions and 248 deletions

View File

@@ -1,6 +1,55 @@
# Changelog # Changelog
## 0.5.3 (2023-02-26)
### Fix
* Set constant alpine version as latest couses issues. [Eduard Veit]
## 0.5.2 (2023-02-26)
### Fix
* Incorrect dns ip handling via domrobot. [Eduard Veit]
## 0.5.1 (2022-11-21)
### Features
* Use provider checkip API. [Eduard Veit]
### Fix
* Handle some curl exit codes. [Eduard Veit]
## 0.5.0 (2022-11-10)
### Features
* Use provider DNS servers. [Eduard Veit]
* Temporarily disable an ip version if the connectivity/routing does not work. [Eduard Veit]
### Documentation
* 📝 update default values in table. [Eduard Veit]
## 0.4.0 (2022-11-07)
### Features
* ✨ add support for dynu.com as DynDNS2 provider. [Eduard Veit]
### Documentation
* 📝 add dynu.com as DynDNS2 provider. [Eduard Veit]
## 0.3.5 (2022-11-03) ## 0.3.5 (2022-11-03)
### Features ### Features

View File

@@ -1,4 +1,4 @@
FROM alpine:latest FROM alpine:3.16.4
RUN \ RUN \
apk update \ apk update \

View File

@@ -23,15 +23,16 @@ IPv4 (A) and IPv6 (AAAA) record updates are supported.
The following update methods are currently implemented: The following update methods are currently implemented:
| Provider | API | TTL in seconds | Credentials | own domain via NS record | free (sub-) domain | | Provider | API | TTL in seconds | Credentials | own domain via NS record | free (sub-) domain |
|---------------------|-----------------------|----------------|----------------------------------------------------------------------------------------------|--------------------------|------------------------------------------------| |---------------------|-----------------------|----------------|----------------------------------------------------------------------------------------------|------------------------------------------|--------------------------------------|
| INWX.com | Domrobot JSON-RPC-API | 300 | customer login `username` & `password`. Mobile TAN (OTP) is currently not supported by DynB. | ✔️ | ⛔ choose one of your owned domains | | INWX.com | Domrobot JSON-RPC-API | 300 | customer login `username` & `password`. Mobile TAN (OTP) is currently not supported by DynB. | ✔️ 🆓 for your registered domains @ inwx | ⛔ for your registered domains @ inwx |
| INWX.com | DynDNS2 | 60 | specific dyndns `username` & `password` | ✔️ | ⛔ choose one of your owned domains per account | | INWX.com | DynDNS2 | 60 | specific dyndns `username` & `password` | ✔️ 🆓 for your registered domains @ inwx | ⛔ for your registered domains @ inwx |
| deSEC.io (dedyn.io) | DynDNS2 | 60 | `token` | ✔️ | ✔️ | | deSEC.io (dedyn.io) | DynDNS2 | 60 | `token` | ✔️ 🆓 | ✔️ |
| DuckDNS.org | DynDNS2 | 60 | `token` | ⛔ | ✔️ | | DuckDNS.org | DynDNS2 | 60 | `token` | ⛔ | ✔️ |
| dynv6.com | DynDNS2 | 60 | `token` | ✔️ | ✔️ | | dynv6.com | DynDNS2 | 60 | `token` | ✔️ 🆓 | ✔️ |
| ddnss.de | DynDNS2 | 10 | update key as `token` | ⛔ | ✔️ | | ddnss.de | DynDNS2 | 10 | `update key` as `token` | ⛔ | ✔️ |
| IPv64.net | DynDNS2 | 60 | `DynDNS Updatehash` as `token` | | ✔️ | | IPv64.net | DynDNS2 | 60 | `DynDNS Updatehash` as `token` | ✔️ 💲 | ✔️ |
| dynu.com | DynDNS2 | 30 | `username` & `password` | ✔️ 🆓 | ✔️ |
## 📦 Requirements ## 📦 Requirements
@@ -173,17 +174,17 @@ services:
## environment variables ## environment variables
| variable | default value | description | | variable | default value | description |
|-----------------------|-----------------|----------------------------------------------------------------------------------------------------------------| |-----------------------|--------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|
| DYNB_DYN_DOMAIN | undefined | required; `dyndns.example.com` | | DYNB_DYN_DOMAIN | undefined | required; `dyndns.example.com` |
| DYNB_SERVICE_PROVIDER | undefined | required; `deSEC`, `duckdns`, `dynv6`, `inwx`, `ddnss`, `ipv64` | | DYNB_SERVICE_PROVIDER | undefined | required; `deSEC`, `duckdns`, `dynv6`, `inwx`, `ddnss`, `ipv64` |
| DYNB_UPDATE_METHOD | undefined | required; `dyndns` or `domrobot` (with inwx) | | DYNB_UPDATE_METHOD | undefined | required; `dyndns` or `domrobot` (with inwx) |
| DYNB_IP_MODE | undefined | required; `4`, `6` or `64` for both | | DYNB_IP_MODE | undefined | required; `4`, `6` or `64` for both |
| DYNB_USERNAME | undefined | the requirement depends on your provider and the update method | | DYNB_USERNAME | undefined | the requirement depends on your provider and the update method |
| DYNB_PASSWORD | undefined | the requirement depends on your provider and the update method | | DYNB_PASSWORD | undefined | the requirement depends on your provider and the update method |
| DYNB_TOKEN | undefined | the requirement depends on your provider and the update method | | DYNB_TOKEN | undefined | the requirement depends on your provider and the update method |
| DYNB_INTERVAL | undefined | without this setting the script/docker container will run one time and exits | | DYNB_INTERVAL | undefined | without this setting the script/docker container will run one time and exits |
| DYNB_DEBUG | undefined | more console outputs | | DYNB_DEBUG | undefined | more console outputs |
| DYNB_IPv4_CHECK_SITE | api64.ipify.org | You need a website or Web-API that outputs your remote IP | | DYNB_IPv4_CHECK_SITE | tries in this order `ip64.ev21.de` (anonymous logging for 7 days), `api64.ipify.org`, `api.my-ip.io/ip`, `ip.anysrc.net/plain` | You need a website or Web-API that outputs your remote IP |
| DYNB_IPv6_CHECK_SITE | api64.ipify.org | You need a website or Web-API that outputs your remote IP | | DYNB_IPv6_CHECK_SITE | tries in this order `ip64.ev21.de` (anonymous logging for 7 days), `api64.ipify.org`, `api.my-ip.io/ip`, `ip.anysrc.net/plain` | You need a website or Web-API that outputs your remote IP |
| DYNB_DNS_CHECK_SERVER | undefined | If you are using a local DNS Resolver/Server make sure it answers with the public answer or set another server | | DYNB_DNS_CHECK_SERVER | using the DynDNS2 providers DNS servers | If you are using a local DNS Resolver/Server make sure it answers with the public answer or set another server |

622
dynb.sh
View File

@@ -12,67 +12,76 @@
#DYNB_DYN_DOMAIN= #DYNB_DYN_DOMAIN=
## service provider could be deSEC, duckdns, dynv6, inwx # service provider could be deSEC, duckdns, dynv6, inwx
#DYNB_SERVICE_PROVIDER= #DYNB_SERVICE_PROVIDER=
## update method options: domrobot, dyndns ## update method options: domrobot, dyndns
#DYNB_UPDATE_METHOD= #DYNB_UPDATE_METHOD=
## ip mode could be either: 4, 6 or dual for dualstack # ip mode could be either: 4, 6 or dual for dualstack
#DYNB_IP_MODE= #DYNB_IP_MODE=
## If you are using the DomRobot RPC-API enter your credentials for the web interface login here # If you are using the DomRobot RPC-API enter your credentials for the web interface login here
## If you are using the DynDNS2 protocol enter your credentials here # If you are using the DynDNS2 protocol enter your credentials here
#DYNB_USERNAME= #DYNB_USERNAME=
#DYNB_PASSWORD= #DYNB_PASSWORD=
## or use a token # or use a token
#DYNB_TOKEN= #DYNB_TOKEN=
## TTL (time to live) for the DNS record # TTL (time to live) for the DNS record
## This setting is only relevant for API based record updates (not DnyDNS2!) # This setting is only relevant for API based record updates (not DnyDNS2!)
## minimum allowed TTL value by inwx is 300 (5 minutes) # minimum allowed TTL value by inwx is 300 (5 minutes)
TTL=300 TTL=300
## The IP-Check sites (some sites have different urls for v4 and v6) # The IP-Check sites (some sites have different urls for v4 and v6)
## Pro tip: use your own ip check server for privacy # Pro tip: use your own ip check server for privacy
## it could be as simple as that... # it could be as simple as that...
## create an index.php with <?php echo $_SERVER'REMOTE_ADDR'; ?> # create an index.php with <?php echo $_SERVER'REMOTE_ADDR'; ?>
_ipv4_checker=api64.ipify.org #DYNB_IPv4_CHECK_SITE=
_ipv6_checker=api64.ipify.org #DYNB_IPv6_CHECK_SITE=
## An exernal DNS check server prevents wrong info from local DNS servers/resolvers # An exernal DNS check server prevents wrong info from local DNS servers/resolvers
_DNS_checkServer= #DYNB_DNS_CHECK_SERVER=9.9.9.9
## if you are actively using multiple network interfaces you might want to specify this # if you are actively using multiple network interfaces you might want to specify this
## normally the default value is okay # normally the default value is okay
#_network_interface=eth0 #_network_interface=eth0
_network_interface= _network_interface=
###################################################### ######################################################
## You don't need to change the following variables ## ## You don't need to change the following variables ##
_INWX_JSON_API_URL=https://api.domrobot.com/jsonrpc/ _INWX_JSON_API_URL=https://api.domrobot.com/jsonrpc/
_internet_connectivity_test_server=https://www.google.de _internet_connectivity_test_server=https://www.google.de
_new_IPv4= _default_check_ip_servers=("ip64.ev21.de" "api64.ipify.org" "api.my-ip.io/ip" "ip.anysrc.net/plain")
_new_IPv6= _ipv4_checker=
_ipv6_checker=
_DNS_checkServer=
_remote_IPv4=
_remote_IPv6=
_dns_records= _dns_records=
_main_domain= _main_domain=
_is_IPv4_enabled=false _is_IPv4_enabled=false
_is_IPv6_enabled=false _is_IPv6_enabled=false
_interface_str= _interface_str=
_status=
_eventTime=0 _eventTime=0
_errorCounter=0 _errorCounter=0
_response= _response=
_statusHostname= _statusHostname=
_statusUsername= _statusUsername=
_statusPassword= _statusPassword=
_version=0.3.5 _version=0.5.3
_userAgent="DynB/$_version github.com/EV21/dynb" _userAgent="DynB/$_version github.com/EV21/dynb"
_configFile=$HOME/.local/share/dynb/.env _configFile=$HOME/.local/share/dynb/.env
_statusFile=/tmp/dynb.status _statusFile=/tmp/dynb.status
_debug=0 _debug=false
_minimum_looptime=60 _minimum_looptime=60
_loopMode=0 _loopMode=false
_remote_ip=
_dns_ip=
_has_remote_ip_error=
_has_remote_ip4=false
_has_remote_ip6=false
# Ansi color code variables # Ansi color code variables
yellow_color="\e[0;33m" yellow_color="\e[0;33m"
@@ -103,9 +112,30 @@ function is_IPv6_address
fi fi
} }
## is_ip_address A B
# parameters required
# 1. param: ip_version
# 2. param: ip_address
function is_ip_address
{
local ip_version=$1
local ip_address=$2
case $ip_version in
4)
is_IPv4_address "$ip_address"
result=$?
;;
6)
is_IPv6_address "$ip_address"
result=$?
;;
esac
return "$result"
}
function loopMode function loopMode
{ {
if [[ $_loopMode -eq 1 ]] if [[ $_loopMode == "true" ]]
then return 0 then return 0
else return 1 else return 1
fi fi
@@ -113,7 +143,7 @@ function loopMode
function debugMode function debugMode
{ {
if [[ $_debug -eq 1 ]] if [[ $_debug == "true" ]]
then return 0 then return 0
else return 1 else return 1
fi fi
@@ -186,37 +216,145 @@ function fetchDNSRecords
_dns_records=$(echo "$_response" | jq '.resData.record[]') _dns_records=$(echo "$_response" | jq '.resData.record[]')
} }
## getRecordID
# requires parameter A or AAAA # requires parameter A or AAAA
# result to stdout # result to stdout
function getRecordID function getRecordID
{ {
echo "$_dns_records" | echo "$_dns_records" |
jq "select(.type == \"${1}\") | .id" jq --raw-output "select(.type == \"${1}\") | .id"
} }
# requires parameter A or AAAA ## do_dig_request A B
# result to stdout # sets variable: _dns_ip
#
# requires parameter
# 1. param: dns server address
# 2. param: A or AAAA
function do_dig_request
{
local dns_server=$1
local record_type=$2
dig_response=$(dig @"$dns_server" in "$record_type" +short "$DYNB_DYN_DOMAIN" 2>&1)
dig_exitcode=$?
if [[ $dig_exitcode -gt 0 ]]
then
errorMessage "DNS request for $record_type @ $dns_server failed with exit code: $dig_exitcode $dig_response"
unset _dns_ip
return 1
else
case $record_type in
A) is_ip_address 4 "$dig_response"
;;
AAAA) is_ip_address 6 "$dig_response"
;;
esac
if test $? -gt 0
then
test -n "$dig_response" || debugMessage "dig response: $dig_response"
unset _dns_ip
return 1
fi
fi
# If the dns resolver lists multiple records in the answer section we filter the first line
# using short option "-n" and not "--lines" because of alpines limited BusyBox head command
_dns_ip=$(echo "$dig_response" | head -n 1)
return 0
}
## getDNSIP A
# sets variable: _dns_ip
#
# requires parameter
# 1. param: A or AAAA
function getDNSIP() { function getDNSIP() {
echo "$_dns_records" | local record_type=$1
jq --raw-output "select(.type == \"${1}\") | .content" if [[ $DYNB_UPDATE_METHOD == domrobot ]]
then
_dns_ip=$(echo "$_dns_records" |
jq --raw-output "select(.type == \"${record_type}\") | .content")
else
for current_dns_server in "${provider_dns_servers[@]}"
do
debugMessage "try dig DNS request with record type $record_type @$current_dns_server"
if do_dig_request "$current_dns_server" "$record_type"
then break
fi
done
fi
} }
## getRemoteIP A
# sets variable: _remote_ip,
#
# requires parameter # requires parameter
# 1. param: 4 or 6 for ip version # 1. param: 4 or 6 for ip version
# 2. param: IP check server address #
# result to stdout # result to stdout
function getRemoteIP function getRemoteIP
{ {
local ip_version=$1 local ip_version=$1
local ip_check_server=$2
curl --silent "$_interface_str" --user-agent "$_userAgent" \ if test -n "$_provider_check_ip"
--ipv"${ip_version}" --location "${ip_check_server}" then ip_check_servers=("$_provider_check_ip" "${_default_check_ip_servers[@]}")
local curls_status_code=$?
# shellcheck disable=2181
if [[ $curls_status_code -gt 0 ]]; then
errorMessage "IPCheck (getRemoteIP $ip_version) request failed"
exit 1
fi fi
case $ip_version in
4)
if test -n "$_ipv4_checker"
then ip_check_servers=("$_ipv4_checker" "${_default_check_ip_servers[@]}")
else ip_check_servers=("${_default_check_ip_servers[@]}")
fi
;;
6)
if test -n "$_ipv6_checker"
then ip_check_servers=("$_ipv6_checker" "${_default_check_ip_servers[@]}")
else ip_check_servers=("${_default_check_ip_servers[@]}")
fi
;;
esac
for current_check_server in "${ip_check_servers[@]}"
do
debugMessage "try getting remote IPv$ip_version via $current_check_server"
response=$(curl --silent "$_interface_str" --user-agent "$_userAgent" \
--ipv"${ip_version}" --location "${current_check_server}")
curls_status_code=$?
# shellcheck disable=2181
if [[ $curls_status_code -gt 0 ]]
then
errorMessage "Remote IPv$ip_version request @ ${current_check_server} failed with curl status code: $curls_status_code"
_has_remote_ip_error=true
return_value=1
else
if is_ip_address "$ip_version" "$response"
then
_has_remote_ip_error=false
_remote_ip="$response"
return_value=0
break
else
errorMessage "The response from the IP check server $current_check_server is not an IPv$ip_version address: $response"
_has_remote_ip_error=true
return_value=1
fi
fi
done
case $ip_version in
4)
if [[ $_has_remote_ip_error == true ]]
then _has_remote_ip4=false
else _has_remote_ip4=true
fi
;;
6)
if [[ $_has_remote_ip_error == true ]]
then export _has_remote_ip6=false
else export _has_remote_ip6=true
fi
;;
esac
return "$return_value"
} }
# requires parameter # requires parameter
@@ -227,12 +365,12 @@ function updateRecord
if [[ ${ip_version} == 4 ]] if [[ ${ip_version} == 4 ]]
then then
ID=$(getRecordID A) ID=$(getRecordID A)
IP=$_new_IPv4 IP=$_remote_IPv4
fi fi
if [[ ${ip_version} == 6 ]] if [[ ${ip_version} == 6 ]]
then then
ID=$(getRecordID AAAA) ID=$(getRecordID AAAA)
IP=$_new_IPv6 IP=$_remote_IPv6
fi fi
if [[ $IP != "" ]] if [[ $IP != "" ]]
then then
@@ -258,93 +396,116 @@ function updateRecord
fi fi
} }
function select_update_base_url function prepare_request_parameters
{ {
# default parameter values # default parameter values
myip_str=myip ipv4_parameter_name=myip
myipv6_str=myipv6 ipv6_parameter_name=myipv6
curl_parameters=("--user-agent" "$_userAgent")
INWX_DYNDNS_UPDATE_URL="https://dyndns.inwx.com/nic/update?"
DESEC_DYNDNS_UPDATE_URL="https://update.dedyn.io/?"
DUCKDNS_DYNDNS_UPDATE_URL="https://www.duckdns.org/update?domains=$DYNB_DYN_DOMAIN&token=$DYNB_TOKEN&"
DYNV6_DYNDNS_UPDATE_URL="https://dynv6.com/api/update?zone=$DYNB_DYN_DOMAIN&token=$DYNB_TOKEN&"
DDNSS_DYNDNS_UPDATE_URL="https://ddnss.de/upd.php?key=$DYNB_TOKEN&host=$DYNB_DYN_DOMAIN&"
IPV64NET_DYNDNS_UPDATE_URL="https://ipv64.net/nic/update?"
case $DYNB_SERVICE_PROVIDER in case $DYNB_SERVICE_PROVIDER in
inwx* | INWX*) [Ii][Nn][Ww][Xx]*)
dyndns_update_url=$INWX_DYNDNS_UPDATE_URL # inwx.de
;; # in case of dualstack use you need to request both parameters with the same request
deSEC* | desec* | dedyn*) # otherwise inwx will delete the not requested record type
dyndns_update_url="${DESEC_DYNDNS_UPDATE_URL}" curl_parameters+=("--user" "$DYNB_USERNAME:$DYNB_PASSWORD")
;; curl_parameters+=("--get") # inwx will ignore the ipv6 parameter if you don't put it into the url
dynv6*) dyndns_update_url="https://dyndns.inwx.com/nic/update"
dyndns_update_url="${DYNV6_DYNDNS_UPDATE_URL}" provider_dns_servers=("ns.inwx.de" "ns2.inwx.de" "ns3.inwx.eu")
myip_str=ipv4 ;;
myipv6_str=ipv6 [Dd][Yy][Nn][Uu]*)
;; curl_parameters+=("--user" "$DYNB_USERNAME:$DYNB_PASSWORD")
DuckDNS* | duckdns*) curl_parameters+=("--get")
dyndns_update_url="${DUCKDNS_DYNDNS_UPDATE_URL}" dyndns_update_url="https://api.dynu.com/nic/update"
myip_str=ip provider_dns_servers=("NS11.dynu.com" "NS10.dynu.com" "NS12.dynu.com")
myipv6_str=ipv6 ;;
;; [Dd][Ee][Ss][Ee][Cc]* | [Dd][Ee][Dd][Yy][Nn]* )
ddnss*) # deSEC.de / dedyn.io
dyndns_update_url="${DDNSS_DYNDNS_UPDATE_URL}" curl_parameters+=("--header" "Authorization: Token $DYNB_TOKEN")
## we are currently not using the syntax with ip auto detection curl_parameters+=("--get")
myip_str=ip curl_parameters+=("--data-urlencode" "hostname=$DYNB_DYN_DOMAIN")
myipv6_str=ip6 dyndns_update_url="https://update.dedyn.io"
;; provider_dns_servers=("ns1.desec.io" "ns2.desec.org")
_provider_check_ip="https://checkip.dedyn.io" # checkipv4 and checkipv6 is also available
;;
[Dd][Yy][Nn][Vv]6*)
# dynv6.com
ipv4_parameter_name=ipv4
ipv6_parameter_name=ipv6
curl_parameters+=("--get")
curl_parameters+=("--data-urlencode" "zone=$DYNB_DYN_DOMAIN")
curl_parameters+=("--data-urlencode" "token=$DYNB_TOKEN")
dyndns_update_url="https://dynv6.com/api/update"
provider_dns_servers=("ns1.dynv6.com" "ns2.dynv6.com" "ns3.dynv6.com")
;;
[Dd][Uu][Cc][Kk][Dd][Nn][Ss]*)
# DuckDNS.org
ipv4_parameter_name=ip
ipv6_parameter_name=ipv6
curl_parameters+=("--get")
curl_parameters+=("--data-urlencode" "domains=$DYNB_DYN_DOMAIN")
curl_parameters+=("--data-urlencode" "token=$DYNB_TOKEN")
dyndns_update_url="https://www.duckdns.org/update"
provider_dns_servers=("ns1.duckdns.org" "ns2.duckdns.org")
;;
[Dd][Dd][Nn][Ss][Ss]*)
# ddnss.de
ipv4_parameter_name=ip
ipv6_parameter_name=ip6
curl_parameters+=("--get")
curl_parameters+=("--data-urlencode" "host=$DYNB_DYN_DOMAIN")
curl_parameters+=("--data-urlencode" "key=$DYNB_TOKEN")
dyndns_update_url="https://ddnss.de/upd.php"
provider_dns_servers=("ns1.ddnss.de" "ns2.ddnss.de" "ns3.ddnss.de")
;;
[Ii][Pp][Vv]64*) [Ii][Pp][Vv]64*)
dyndns_update_url="${IPV64NET_DYNDNS_UPDATE_URL}" # IPv64.net
myip_str=ip ipv4_parameter_name=ip
myipv6_str=ip6 ipv6_parameter_name=ip6
;; curl_parameters+=("--request" "POST")
curl_parameters+=("--header" "Authorization: Bearer $DYNB_TOKEN")
curl_parameters+=("--data-urlencode" "domain=$DYNB_DYN_DOMAIN")
dyndns_update_url="https://ipv64.net/nic/update"
provider_dns_servers=("ns1.IPv64.net" "ns2.IPv64.net")
;;
*) *)
errorMessage "$DYNB_SERVICE_PROVIDER is not supported" errorMessage "$DYNB_SERVICE_PROVIDER is not supported"
exit 1 exit 1
;; ;;
esac esac
if test -n "$_DNS_checkServer"
then provider_dns_servers=("$_DNS_checkServer" "${provider_dns_servers[@]}")
fi
}
function prepare_ip_flag_parameters
{
debugMessage "IPv4 enabled: $_is_IPv4_enabled; IPv6 enabled: $_is_IPv6_enabled; has remote IPv4: $_has_remote_ip4; has remote IPv6: $_has_remote_ip6"
if [[ $_is_IPv4_enabled == true ]] && [[ $_is_IPv6_enabled == true ]] && [[ $_has_remote_ip4 == true ]] && [[ $_has_remote_ip6 == true ]]
then
ip_flag_parameters=("--data-urlencode" "${ipv4_parameter_name}=${_remote_IPv4}" "--data-urlencode" "${ipv6_parameter_name}=${_remote_IPv6}")
fi
if [[ $_is_IPv4_enabled == true ]] && [[ $_is_IPv6_enabled == false ]] || [[ $_is_IPv4_enabled == true ]] && [[ $_is_IPv6_enabled == true ]] && [[ $_has_remote_ip4 == true ]] && [[ $_has_remote_ip6 == false ]]
then
ip_flag_parameters=("--data-urlencode" "${ipv4_parameter_name}=${_remote_IPv4}")
fi
if [[ $_is_IPv4_enabled == false ]] && [[ $_is_IPv6_enabled == true ]] || [[ $_is_IPv4_enabled == true ]] && [[ $_is_IPv6_enabled == true ]] && [[ $_has_remote_ip4 == false ]] && [[ $_has_remote_ip6 == true ]]
then
ip_flag_parameters=("--data-urlencode" "${ipv6_parameter_name}=${_remote_IPv6}")
fi
} }
function send_request function send_request
{ {
case $DYNB_SERVICE_PROVIDER in local _response
inwx* | INWX*) debugMessage "curl parameters: ${curl_parameters[*]} ${dyndns_update_url}"
_response=$(curl --silent "$_interface_str" \ _response=$(
--user-agent "$_userAgent" \ curl --silent "$_interface_str" \
--user "$DYNB_USERNAME":"$DYNB_PASSWORD" \ "${curl_parameters[@]}" \
"${dyndns_update_url}") "${dyndns_update_url}")
analyse_response _curl_exit_code=$?
status_code=$? analyse_response
;; status_code=$?
deSEC* | desec* | dedyn*)
_response=$(curl --silent "$_interface_str" \
--user-agent "$_userAgent" \
--header "Authorization: Token $DYNB_TOKEN" \
--get --data-urlencode "hostname=$DYNB_DYN_DOMAIN" \
"${dyndns_update_url}")
analyse_response
status_code=$?
;;
[Ii][Pp][Vv]64* )
_response=$(curl --silent "$_interface_str" \
--user-agent "$_userAgent" \
--header "Authorization: Bearer $DYNB_TOKEN" \
--request POST \
--form "domain=$DYNB_DYN_DOMAIN" \
"${dyndns_update_url}")
analyse_response
status_code=$?
;;
dynv6* | duckDNS* | duckdns* | ddnss*)
_response=$(
curl --silent "$_interface_str" \
--user-agent "$_userAgent" \
"${dyndns_update_url}")
analyse_response
status_code=$?
;;
esac
return $status_code return $status_code
} }
@@ -366,7 +527,7 @@ function analyse_response
debugMessage "Response: $_response" debugMessage "Response: $_response"
return 1 return 1
;; ;;
*'Bad Request'*) 400* | *'Bad Request'*)
errorMessage "Bad Request." errorMessage "Bad Request."
debugMessage "Response: $_response" debugMessage "Response: $_response"
return 1 return 1
@@ -416,12 +577,16 @@ function analyse_response
errorMessage "$_response: There is an internal error in the dyndns update system. Retry update no sooner than 30 minutes." errorMessage "$_response: There is an internal error in the dyndns update system. Retry update no sooner than 30 minutes."
return 1 return 1
;; ;;
911 | 5*) servererror | 911 | 5*)
errorMessage "$_response: A fatal error on provider side such as a database outage. Retry update no sooner than 30 minutes." errorMessage "$_response: A fatal error on provider side such as a database outage. Retry update no sooner than 30 minutes."
return 1 return 1
;; ;;
*) *)
if [[ "$_response" == "$_status" ]]; then if test $_curl_exit_code -gt 0
then
analyse_curl_exit_code $_curl_exit_code
return $?
elif test -n "$_response" && [[ "$_response" == "$_previous_response_was" ]]; then
errorMessage "An unknown response code has been received: $_response" errorMessage "An unknown response code has been received: $_response"
return 1 return 1
else else
@@ -432,45 +597,57 @@ function analyse_response
esac esac
} }
function analyse_curl_exit_code
{
local current_curl_exit_code=$1
case $current_curl_exit_code in
6)
errorMessage "curl exit code: $_curl_exit_code; Couldn't resolve host: $dyndns_update_url"
;;
7)
errorMessage "curl exit code: $_curl_exit_code; Failed to connect to host: $dyndns_update_url"
;;
22)
errorMessage "curl exit code: $_curl_exit_code HTTP error code being 400 or above"
;;
*)
errorMessage "curl exit code: $_curl_exit_code"
;;
esac
return "$current_curl_exit_code"
}
# using DynDNS2 protocol # using DynDNS2 protocol
function dynupdate function dynupdate
{ {
select_update_base_url prepare_ip_flag_parameters
curl_parameters+=("${ip_flag_parameters[@]}")
# pre encode ip parameters send_request
if [[ $_is_IPv4_enabled == true ]] && [[ $_is_IPv6_enabled == true ]] request_status=$?
then
dyndns_update_url="${dyndns_update_url}${myip_str}=${_new_IPv4}&${myipv6_str}=${_new_IPv6}"
send_request
request_status=$?
fi
if [[ $_is_IPv4_enabled == true ]] && [[ $_is_IPv6_enabled == false ]]
then
dyndns_update_url="${dyndns_update_url}${myip_str}=${_new_IPv4}"
send_request
request_status=$?
fi
if [[ $_is_IPv4_enabled == false ]] && [[ $_is_IPv6_enabled == true ]]
then
dyndns_update_url="${dyndns_update_url}${myipv6_str}=${_new_IPv6}"
send_request
request_status=$?
fi
debugMessage "Update URL was: $dyndns_update_url"
return $request_status return $request_status
} }
function setStatus function setStatus
{ {
echo "_status=$1; _eventTime=$2; _errorCounter=$3; _statusHostname=$4; _statusUsername=$5; _statusPassword=$6" >/tmp/dynb.status echo "_curl_exit_code_was=$1; _previous_response_was=$2 _eventTime=$3; _errorCounter=$4; _statusHostname=$5; _statusUsername=$6; _statusPassword=$7" >/tmp/dynb.status
} }
declare _previous_response_was
declare _curl_exit_code_was
# handle errors from past update requests # handle errors from past update requests
function checkStatus function checkStatus
{ {
case $_status in local check_string
if [[ -z "${_previous_response_was}" ]]
then check_string=$_curl_exit_code_was
else check_string=$_previous_response_was
fi
case $check_string in
*nochg*) *nochg*)
if [[ _errorCounter -gt 1 ]]; then if [[ _errorCounter -gt 1 ]]
then
errorMessage "The update client was spamming unnecessary update requests, something might be wrong with your IP-Check site." errorMessage "The update client was spamming unnecessary update requests, something might be wrong with your IP-Check site."
errorMessage "Fix your config and then delete $_statusFile or restart your docker container" errorMessage "Fix your config and then delete $_statusFile or restart your docker container"
return 1 return 1
@@ -480,7 +657,7 @@ function checkStatus
if [[ "$_statusHostname" == "$DYNB_DYN_DOMAIN" && ("$_statusUsername" == "$DYNB_USERNAME" || $_statusUsername == "$DYNB_TOKEN") ]]; then if [[ "$_statusHostname" == "$DYNB_DYN_DOMAIN" && ("$_statusUsername" == "$DYNB_USERNAME" || $_statusUsername == "$DYNB_TOKEN") ]]; then
errorMessage "Hostname supplied does not exist under specified account, enter new login credentials before performing an additional request." errorMessage "Hostname supplied does not exist under specified account, enter new login credentials before performing an additional request."
return 1 return 1
else rm "$_statusFile" else delete_status_file
fi fi
return 0 return 0
;; ;;
@@ -488,7 +665,7 @@ function checkStatus
if [[ "$_statusUsername" == "$DYNB_USERNAME" && ("$_statusPassword" == "$DYNB_PASSWORD" || $_statusPassword == "$DYNB_TOKEN") ]]; then if [[ "$_statusUsername" == "$DYNB_USERNAME" && ("$_statusPassword" == "$DYNB_PASSWORD" || $_statusPassword == "$DYNB_TOKEN") ]]; then
errorMessage "Invalid username password combination." errorMessage "Invalid username password combination."
return 1 return 1
else rm "$_statusFile" else delete_status_file
fi fi
return 0 return 0
;; ;;
@@ -509,102 +686,88 @@ function checkStatus
echo "If it still fails file an issue at github or try another client :)" echo "If it still fails file an issue at github or try another client :)"
return 1 return 1
;; ;;
911 | 5* | *'Too Many Requests'*) servererror | 911 | 5* | *'Too Many Requests'*)
delta=$(($(date +%s) - _eventTime)) if check_delay 30
if [[ $delta -lt 1800 ]]
then then
errorMessage "$_status: The provider currently has an fatal error. DynB will wait for next update until 30 minutes have passed since last request, $(date --date=@$delta -u +%M) minutes already passed." delete_status_file
return 0
else
errorMessage "$_previous_response_was: The provider currently has a fatal error."
return 1 return 1
else rm "$_statusFile"
fi fi
return 0
;; ;;
*'Bad Request'*) *'Bad Request'*)
if [[ "$_statusUsername" == "$DYNB_USERNAME" && ("$_statusPassword" == "$DYNB_PASSWORD" || $_statusPassword == "$DYNB_TOKEN") ]]; then if [[ "$_statusUsername" == "$DYNB_USERNAME" && ("$_statusPassword" == "$DYNB_PASSWORD" || $_statusPassword == "$DYNB_TOKEN") ]]
then
errorMessage "Bad Request: Please check your credentials, maybe your token is invalid." errorMessage "Bad Request: Please check your credentials, maybe your token is invalid."
return 1 return 1
else rm "$_statusFile" else delete_status_file
fi fi
return 0 return 0
;; ;;
*) *)
if [[ _errorCounter -gt 1 ]] if [[ $_errorCounter -gt 1 ]] && [[ "$_curl_exit_code_was" -eq 0 ]]
then then
errorMessage "An unknown response code has repeatedly been received. $_response" errorMessage "An unknown response code has repeatedly been received. $_response"
return 1 return 1
elif [[ $_errorCounter -gt 1 ]] && [[ "$_curl_exit_code_was" -eq 7 ]]
then
if check_delay 15
then
delete_status_file
return 0
else return 1
fi
else return 0 else return 0
fi fi
;; ;;
esac esac
} }
function check_delay
{
local minutes=$1
local seconds=$(( minutes * 60 ))
delta=$(($(date +%s) - _eventTime))
if [[ $delta -lt $seconds ]]
then
errorMessage "DynB only executes an update when $minutes minutes have passed since the last failed request, $(date --date=@$delta -u +%M) minutes have already passed."
if loopMode
then sleep $seconds
else return 1
fi
else return 0
fi
}
# requires parameter # requires parameter
# 1. param: 4 or 6 for IP version # 1. param: 4 or 6 for IP version
function ipHasChanged function ipHasChanged
{ {
local ip_version=$1 local ip_version=$1
getRemoteIP "$ip_version"
if test $? -gt 0
then return 1
fi
case ${ip_version} in case ${ip_version} in
4) 4)
remote_ip=$(getRemoteIP 4 $_ipv4_checker) getDNSIP A
if ! is_IPv4_address "$remote_ip" _remote_IPv4=$_remote_ip
then debugMessage "IPv4 from remote IP check server: $_remote_IPv4, IPv4 from DNS: $_dns_ip"
errorMessage "The response from the IP check server is not an IPv4 address: $remote_ip" ;;
return 1
fi
if [[ $DYNB_UPDATE_METHOD == domrobot ]]
then dns_ip=$(getDNSIP A)
else
if [[ -n $_DNS_checkServer ]]
then dig_response=$(dig @"${_DNS_checkServer}" in a +short "$DYNB_DYN_DOMAIN")
else dig_response=$(dig in a +short "$DYNB_DYN_DOMAIN")
fi
if [[ $dig_response == ";; connection timed out; no servers could be reached" ]]
then
errorMessage "DNS request failed $dig_response"
return 1
fi
# If the dns resolver lists multiple records in the answer section we filter the first line
# using short option "-n" and not "--lines" because of alpines limited BusyBox head command
dns_ip=$(echo "$dig_response" | head -n 1)
fi
_new_IPv4=$remote_ip
debugMessage "IPv4 from remote IP check server: $_new_IPv4, IPv4 from DNS: $dns_ip"
;;
6) 6)
remote_ip=$(getRemoteIP 6 $_ipv6_checker) getDNSIP AAAA
if ! is_IPv6_address "$remote_ip" _remote_IPv6=$_remote_ip
then debugMessage "IPv6 from remote IP check server: $_remote_IPv6, IPv6 from DNS: $_dns_ip"
errorMessage "The response from the IP check server is not an IPv6 address: $remote_ip" ;;
return 1
fi
if [[ $DYNB_UPDATE_METHOD == domrobot ]]
then dns_ip=$(getDNSIP AAAA)
else
if [[ -n $_DNS_checkServer ]]
then dig_response=$(dig @"${_DNS_checkServer}" in aaaa +short "$DYNB_DYN_DOMAIN")
else dig_response=$(dig in aaaa +short "$DYNB_DYN_DOMAIN")
fi
exitcode=$?
if [[ $exitcode -gt 0 ]]
then
errorMessage "DNS request failed with exit code: $exitcode $dig_response"
return 1
fi
# If the dns server lists multiple records in the answer section we filter the first line
dns_ip=$(echo "$dig_response" | head -n 1)
fi
_new_IPv6=$remote_ip
debugMessage "IPv6 from remote IP check server: $_new_IPv6, IPv6 from DNS: $dns_ip"
;;
*) ;;
esac esac
if [[ "$remote_ip" == "$dns_ip" ]] if [[ "$_remote_ip" == "$_dns_ip" ]]
then return 1 then return 1
else else
case ${ip_version} in case ${ip_version} in
4) infoMessage "New IPv4: $_new_IPv4 old was: $dns_ip";; 4) infoMessage "Remote IPv4: $_remote_IPv4 DNS IPv4: $_dns_ip";;
6) infoMessage "New IPv6: $_new_IPv6 old was: $dns_ip";; 6) infoMessage "Remote IPv6: $_remote_IPv6 DNS IPv6: $_dns_ip";;
esac esac
return 0 return 0
fi fi
@@ -626,7 +789,7 @@ function handleParameters
fi fi
# shellcheck disable=SC2154 # shellcheck disable=SC2154
if [[ $_arg_debug == "on" ]] || [[ $DYNB_DEBUG == true ]] if [[ $_arg_debug == "on" ]] || [[ $DYNB_DEBUG == true ]]
then _debug=1 then _debug=true
fi fi
# shellcheck disable=SC2154 # shellcheck disable=SC2154
if [[ $_arg_update_method != "" ]] if [[ $_arg_update_method != "" ]]
@@ -666,8 +829,8 @@ function handleParameters
elif [[ $DYNB_INTERVAL -lt _minimum_looptime ]] elif [[ $DYNB_INTERVAL -lt _minimum_looptime ]]
then then
DYNB_INTERVAL=$_minimum_looptime DYNB_INTERVAL=$_minimum_looptime
_loopMode=1 _loopMode=true
else _loopMode=1 else _loopMode=true
fi fi
if [[ $_network_interface != "" ]] if [[ $_network_interface != "" ]]
then _interface_str="--interface $_network_interface" then _interface_str="--interface $_network_interface"
@@ -734,8 +897,8 @@ function doUnsets
unset _is_IPv4_enabled unset _is_IPv4_enabled
unset _is_IPv6_enabled unset _is_IPv6_enabled
unset _main_domain unset _main_domain
unset _new_IPv4 unset _remote_IPv4
unset _new_IPv6 unset _remote_IPv6
unset _version unset _version
unset DYNB_DYN_DOMAIN unset DYNB_DYN_DOMAIN
unset DYNB_USERNAME unset DYNB_USERNAME
@@ -776,11 +939,19 @@ function delete_status_file
then then
debugMessage "Delete status file with previous errors" debugMessage "Delete status file with previous errors"
rm "$_statusFile" rm "$_statusFile"
unset _curl_exit_code_was
unset _previous_response_was
unset _eventTime
unset _errorCounter
unset _statusHostname
unset _statusUsername
unset _statusPassword
fi fi
} }
function doDynDNS2Updates function doDynDNS2Updates
{ {
prepare_request_parameters
changed=0 changed=0
if [[ $_is_IPv4_enabled == true ]] && ipHasChanged 4 if [[ $_is_IPv4_enabled == true ]] && ipHasChanged 4
then ((changed += 1)) then ((changed += 1))
@@ -799,11 +970,11 @@ function doDynDNS2Updates
delete_status_file delete_status_file
else else
debugMessage "Save new status after dynupdate has failed" debugMessage "Save new status after dynupdate has failed"
setStatus "$_response" "$(date +%s)" $((_errorCounter += 1)) "$DYNB_DYN_DOMAIN" "${DYNB_USERNAME}" "${DYNB_PASSWORD}${DYNB_TOKEN}" setStatus "$_curl_exit_code" "$_response" "$(date +%s)" $((_errorCounter += 1)) "$DYNB_DYN_DOMAIN" "${DYNB_USERNAME}" "${DYNB_PASSWORD}${DYNB_TOKEN}"
fi fi
else debugMessage "Skip DynDNS2 update, checkStatus fetched previous error." else debugMessage "Skip DynDNS2 update, checkStatus fetched previous error."
fi fi
else debugMessage "Skip DynDNS2 update, IPs are up to date." else debugMessage "Skip DynDNS2 update"
fi fi
} }
@@ -818,17 +989,20 @@ function doUpdates
function ipv6_is_not_working function ipv6_is_not_working
{ {
curl --ipv6 --head --silent --max-time 5 $_internet_connectivity_test_server > /dev/null execute_connectivity_check 6
status_code=$? return $?
if test $status_code -gt 0
then return 0
else return 1
fi
} }
function ipv4_is_not_working function ipv4_is_not_working
{ {
curl --ipv4 --head --silent --max-time 5 $_internet_connectivity_test_server > /dev/null execute_connectivity_check 4
return $?
}
function execute_connectivity_check
{
local ip_version=$1
curl --ipv"$ip_version" --head --silent --max-time 5 $_internet_connectivity_test_server > /dev/null
status_code=$? status_code=$?
if test $status_code -gt 0 if test $status_code -gt 0
then return 0 then return 0
@@ -898,7 +1072,7 @@ function main
do do
doUpdates doUpdates
debugMessage "wait $DYNB_INTERVAL seconds until next check" debugMessage "wait $DYNB_INTERVAL seconds until next check"
sleep $DYNB_INTERVAL sleep "$DYNB_INTERVAL"
done done
else doUpdates else doUpdates
fi fi