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

29 Commits
0.3.1 ... 0.4.0

Author SHA1 Message Date
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
c83b893fc8 minor: 📝 update CHANGELOG 2022-11-03 13:11:54 +01:00
c9794f7c22 minor: increase version 2022-11-03 13:03:40 +01:00
b8bbce7ec2 doc: 📝 change TTL to 60 for IPv64.net 2022-11-03 12:01:32 +01:00
bd6f1336b2 fix: delete status file after success 2022-11-03 10:44:10 +01:00
260bc7172f doc: 📝 add IPv64.net as DynDNS2 provider 2022-11-03 07:11:19 +01:00
a85b2bf911 fix: more status file issues 2022-11-02 21:21:01 +01:00
a496acbebf fix: incorrect status code handling
no persistent status file was written in case of an error as the return status code was always 0
2022-11-02 19:07:52 +01:00
c81da8846e feat: add support for IPv64.net as DynDNS2 provider 2022-11-02 18:09:20 +01:00
122e08a42a refactor: combine if statements 2022-11-02 15:03:44 +01:00
2352d655a3 refactor(extract method): read_config_file 2022-06-30 14:28:08 +02:00
341d535f21 minor: 📝 update CHANGELOG 2022-06-30 13:57:27 +02:00
fc2c499daa minor: increase version 2022-06-30 13:52:48 +02:00
39d5143108 minor: lint tables in README 2022-06-30 13:51:01 +02:00
e58d87cf94 doc: 📝 update providers in README 2022-06-30 13:20:49 +02:00
9d41354695 feat: add support for ddnss.de as DynDNS2 provider 2022-06-30 09:46:40 +02:00
d8a298f58a minor: change .gitignore 2022-06-30 09:30:24 +02:00
3e89610e22 minor: response output as debug message 2022-06-30 09:30:24 +02:00
faa9c371c1 refactor: extract methods 2022-06-30 09:30:09 +02:00
11fccf2f0e minor: fix typo 2022-06-12 18:41:27 +02:00
75715cb0a8 minor: 📝 update CHANGELOG 2022-06-07 10:00:10 +02:00
6313ee4347 minor: set version 0.3.3 2022-06-07 09:58:10 +02:00
f37230401f fix: wrong parameter name for DuckDNS
Co-authored-by: Qrrbrbirlbel <Qrrbrbirlbel@users.noreply.github.com>
2022-06-06 22:26:40 +02:00
d6c98c04ad doc: 📝 add comments to docker-compose.yml example 2022-05-28 15:20:16 +02:00
64a5a76b6d minor: 📝 update CHANGELOG 2022-05-26 16:15:12 +02:00
05120c25e4 feat: add tzdata to Dockerfile for timezone config
You can now set your timezone with the environment variable
`TZ="Europe/Berlin"`
2022-05-26 16:10:47 +02:00
5 changed files with 429 additions and 211 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
.env* .env*
docker-compose* docker-compose*
.vscode

View File

@@ -1,6 +1,82 @@
# Changelog # Changelog
## 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)
### Features
* ✨ add support for IPv64.net as DynDNS2 provider. [Eduard Veit]
### Fix
* Delete status file after success. [Eduard Veit]
* More status file issues. [Eduard Veit]
* Incorrect status code handling. [Eduard Veit]
no persistent status file was written in case of an error as the return status code was always 0
### Documentation
* 📝 change TTL to 60 for IPv64.net. [Eduard Veit]
* 📝 add IPv64.net as DynDNS2 provider. [Eduard Veit]
## 0.3.4 (2022-06-30)
### Features
* ✨ add support for ddnss.de as DynDNS2 provider. [Eduard Veit]
### Documentation
* 📝 update providers in README. [Eduard Veit]
## 0.3.3 (2022-06-07)
### Fix
* Wrong parameter name for DuckDNS. [Eduard Veit]
### Documentation
* 📝 add comments to docker-compose.yml example. [Eduard Veit]
## 0.3.2 (2022-05-26)
### Features
* ✨ add tzdata to Dockerfile for timezone config. [Eduard Veit]
You can now set your timezone with the environment variable
`TZ="Europe/Berlin"`
## 0.3.1 (2022-05-26)
### Fix
* 🐛 curl/libcurl doesn't support dns-server option. [Eduard Veit]
the latest alpine we are using for the docker image
does also drops the support for that option like debian/ubuntu/etc
## 0.3.0 (2022-05-26) ## 0.3.0 (2022-05-26)
### Features ### Features

View File

@@ -7,7 +7,8 @@ apk add \
bash \ bash \
curl \ curl \
jq \ jq \
bind-tools bind-tools \
tzdata
WORKDIR /usr/src/app WORKDIR /usr/src/app

View File

@@ -7,8 +7,6 @@ IPv4 (A) and IPv6 (AAAA) record updates are supported.
<!-- TOC --> <!-- TOC -->
- [✨ Update Methods](#-update-methods) - [✨ Update Methods](#-update-methods)
- [APIs](#apis)
- [DynDNS2](#dyndns2)
- [📦 Requirements](#-requirements) - [📦 Requirements](#-requirements)
- [🚀 Installation](#-installation) - [🚀 Installation](#-installation)
- [⚙ Configuration](#-configuration) - [⚙ Configuration](#-configuration)
@@ -25,18 +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:
### APIs | Provider | API | TTL in seconds | Credentials | own domain via NS record | free (sub-) domain |
|---------------------|-----------------------|----------------|----------------------------------------------------------------------------------------------|--------------------------|------------------------------------------------|
- INWX.com Domrobot JSON-RPC-API\ | 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 |
Limitations: | INWX.com | DynDNS2 | 60 | specific dyndns `username` & `password` | ✔️ | ⛔ choose one of your owned domains per account |
- minimum TTL is 300 (5 minutes) | deSEC.io (dedyn.io) | DynDNS2 | 60 | `token` | ✔️ | ✔️ |
| DuckDNS.org | DynDNS2 | 60 | `token` | ⛔ | ✔️ |
### DynDNS2 | dynv6.com | DynDNS2 | 60 | `token` | ✔️ | ✔️ |
| ddnss.de | DynDNS2 | 10 | `update key` as `token` | ⛔ | ✔️ |
- INWX.com | IPv64.net | DynDNS2 | 60 | `DynDNS Updatehash` as `token` | ⛔ | ✔️ |
- deSEC.io (dedyn.io) | dynu.com | DynDNS2 | 30 | `username` & `password` | ✔️ | ✔️ |
- DuckDNS.org
- dynv6.com
## 📦 Requirements ## 📦 Requirements
@@ -57,7 +53,7 @@ or simply clone this repo
git clone https://github.com/EV21/dynb.git git clone https://github.com/EV21/dynb.git
``` ```
If you want to add the script to you PATH, run :point_down: If you want to add the script to you PATH, run 👇
```shell ```shell
bash dynb.sh --link bash dynb.sh --link
@@ -79,7 +75,7 @@ DYNB_SERVICE_PROVIDER=inwx
## update method options: domrobot, dyndns ## update method options: domrobot, dyndns
DYNB_UPDATE_METHOD=domrobot DYNB_UPDATE_METHOD=domrobot
## ip mode could be either: 4, 6 or dual for dualstack ## ip mode could be either: 4, 6 or 64 for dualstack
DYNB_IP_MODE=64 DYNB_IP_MODE=64
## 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
@@ -92,14 +88,14 @@ DYNB_TOKEN=
## 🏃 Run ## 🏃 Run
If you have a config file just run :point_down: If you have a config file just run 👇
```bash ```bash
dynb dynb
``` ```
Alternatively you can use parameters if your system meets the relevant requirements. This example shows the long form parameter, there are also short ones.\ Alternatively you can use parameters if your system meets the relevant requirements. This example shows the long form parameter, there are also short ones.\
Call the help function :point_down: Call the help function 👇
```bash ```bash
dynb --help dynb --help
@@ -123,13 +119,13 @@ Just use the parameter `--interval 60` or the environment variable `DYNB_INTERVA
### crontab ### crontab
execute :point_down: execute 👇
```bash ```bash
crontab -e crontab -e
``` ```
then enter :point_down: to run dynb every five minutes. then add the following line 👇 to run dynb every five minutes.
```bash ```bash
*/5 * * * * $HOME/.local/bin/dynb >> $HOME/.local/share/dynb/dynb-cron.log */5 * * * * $HOME/.local/bin/dynb >> $HOME/.local/share/dynb/dynb-cron.log
@@ -155,25 +151,33 @@ services:
image: ev21/dynb image: ev21/dynb
container_name: dynb container_name: dynb
network_mode: host network_mode: host
build: stdin_open: true
context: . tty: true
dockerfile: ./Dockerfile
environment: environment:
- DYNB_DYN_DOMAIN=dyndns.example.com - DYNB_DYN_DOMAIN=dyndns.example.com
- DYNB_SERVICE_PROVIDER=inwx # Providers: deSec, DuckDNS, dynv6, inwx, ddnss, ipv64
- DYNB_SERVICE_PROVIDER=desec
# Possible update methods are: dyndns, domrobot
- DYNB_UPDATE_METHOD=dyndns - DYNB_UPDATE_METHOD=dyndns
# IP modes: 4 (IPv4 only), 6 (IPv6 only), 64 both
- DYNB_IP_MODE=64 - DYNB_IP_MODE=64
# If your provider uses tokens use DYNB_TOKEN instead of DYNB_USERNAME and DYNB_PASSWORD
- DYNB_USERNAME=User42 - DYNB_USERNAME=User42
- DYNB_PASSWORD=SuperSecretPassword - DYNB_PASSWORD=SuperSecretPassword
- DYNB_INTERVAL=60 # The interval in seconds is the time the script waits before executing it again
- DYNB_INTERVAL=300
# TZ: Timezone setting for correct log time
- TZ=Europe/Berlin
# TERM: For colorful console output (attached mode)
- TERM=xterm-256color
``` ```
## 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` | | 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 |

502
dynb.sh
View File

@@ -12,46 +12,50 @@
#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
_ipv4_checker=
_ipv6_checker=
_DNS_checkServer=
_new_IPv4= _new_IPv4=
_new_IPv6= _new_IPv6=
_dns_records= _dns_records=
@@ -66,13 +70,13 @@ _response=
_statusHostname= _statusHostname=
_statusUsername= _statusUsername=
_statusPassword= _statusPassword=
_version=0.3.1 _version=0.4.0
_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
# Ansi color code variables # Ansi color code variables
yellow_color="\e[0;33m" yellow_color="\e[0;33m"
@@ -103,9 +107,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 +138,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,6 +211,7 @@ 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
@@ -194,29 +220,94 @@ function getRecordID
jq "select(.type == \"${1}\") | .id" jq "select(.type == \"${1}\") | .id"
} }
## getDNSIP A
# requires parameter A or AAAA # requires parameter A or AAAA
# result to stdout # result to stdout
function getDNSIP() { function getDNSIP() {
echo "$_dns_records" | local record_type=$1
jq --raw-output "select(.type == \"${1}\") | .content" if [[ $DYNB_UPDATE_METHOD == domrobot ]]
then
echo "$_dns_records" |
jq --raw-output "select(.type == \"${record_type}\") | .content"
else
if [[ -n $_DNS_checkServer ]]
then dig_response=$(dig @"${_DNS_checkServer}" in "$record_type" +short "$DYNB_DYN_DOMAIN")
else dig_response=$(dig in "$record_type" +short "$DYNB_DYN_DOMAIN")
fi
dig_exitcode=$?
if [[ $dig_exitcode -gt 0 ]]
then
errorMessage "DNS request failed with exit code: $dig_exitcode $dig_response"
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 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
echo "$dig_response" | head -n 1
fi
} }
_has_remote_ip_error=false
# 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" \ default_ip_check_servers=("ip64.ev21.de" "api64.ipify.org" "api.my-ip.io/ip" "ip.anysrc.net/plain")
--ipv"${ip_version}" --location "${ip_check_server}" case $ip_version in
local curls_status_code=$? 4)
# shellcheck disable=2181 if test -n "$_ipv4_checker"
if [[ $curls_status_code -gt 0 ]]; then then ip_check_servers=("$_ipv4_checker" "${default_ip_check_servers[@]}")
errorMessage "IPCheck (getRemoteIP $ip_version) request failed" else ip_check_servers=("${default_ip_check_servers[@]}")
exit 1 fi
fi ;;
6)
if test -n "$_ipv6_checker"
then ip_check_servers=("$_ipv6_checker" "${default_ip_check_servers[@]}")
else ip_check_servers=("${default_ip_check_servers[@]}")
fi
;;
esac
for current_check_server in "${ip_check_servers[@]}"
do
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 failed with ${current_check_server}"
_has_remote_ip_error=true
return_value=1
else
if is_ip_address "$ip_version" "$response"
then
_has_remote_ip_error=false
echo "$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
return $return_value
} }
# requires parameter # requires parameter
@@ -258,102 +349,150 @@ function updateRecord
fi fi
} }
# using DynDNS2 protocol function prepare_request_parameters
function dynupdate
{ {
# 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&"
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}" ;;
myip_str=ipv4 [Dd][Yy][Nn][Uu]*)
myipv6_str=ipv6 curl_parameters+=("--user" "$DYNB_USERNAME:$DYNB_PASSWORD")
;; curl_parameters+=("--get")
DuckDNS* | duckdns*) dyndns_update_url="https://api.dynu.com/nic/update"
dyndns_update_url="${DUCKDNS_DYNDNS_UPDATE_URL}" ;;
myip_str=ipv4 [Dd][Ee][Ss][Ee][Cc]* | [Dd][Ee][Dd][Yy][Nn]* )
myipv6_str=ipv6 # deSEC.de / dedyn.io
;; curl_parameters+=("--header" "Authorization: Token $DYNB_TOKEN")
curl_parameters+=("--get")
curl_parameters+=("--data-urlencode" "hostname=$DYNB_DYN_DOMAIN")
dyndns_update_url="https://update.dedyn.io"
;;
[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"
;;
[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"
;;
[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"
;;
[Ii][Pp][Vv]64*)
# IPv64.net
ipv4_parameter_name=ip
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"
;;
*) *)
errorMessage "$DYNB_SERVICE_PROVIDER is not supported" errorMessage "$DYNB_SERVICE_PROVIDER is not supported"
exit 1 exit 1
;; ;;
esac esac
# pre encode ip parameters prepare_ip_flag_parameters
if [[ $_is_IPv4_enabled == true ]] && [[ $_is_IPv6_enabled == true ]]; then curl_parameters+=("${ip_flag_parameters[@]}")
dyndns_update_url="${dyndns_update_url}${myip_str}=${_new_IPv4}&${myipv6_str}=${_new_IPv6}" }
fi
if [[ $_is_IPv4_enabled == true ]] && [[ $_is_IPv6_enabled == false ]]; then
dyndns_update_url="${dyndns_update_url}${myip_str}=${_new_IPv4}"
fi
if [[ $_is_IPv4_enabled == false ]] && [[ $_is_IPv6_enabled == true ]]; then
dyndns_update_url="${dyndns_update_url}${myipv6_str}=${_new_IPv6}"
fi
debugMessage "Update URL was: $dyndns_update_url"
## request ## function prepare_ip_flag_parameters
case $DYNB_SERVICE_PROVIDER in {
inwx* | INWX*) if [[ $_is_IPv4_enabled == true ]] && [[ $_is_IPv6_enabled == true ]]
_response=$(curl --silent "$_interface_str" \ then
--user-agent "$_userAgent" \ ip_flag_parameters=("--data-urlencode" "${ipv4_parameter_name}=${_new_IPv4}" "--data-urlencode" "${ipv6_parameter_name}=${_new_IPv6}")
--user "$DYNB_USERNAME":"$DYNB_PASSWORD" \ fi
"${dyndns_update_url}") if [[ $_is_IPv4_enabled == true ]] && [[ $_is_IPv6_enabled == false ]]
;; then
deSEC* | desec* | dedyn*) ip_flag_parameters=("--data-urlencode" "${ipv4_parameter_name}=${_new_IPv4}")
_response=$(curl --silent "$_interface_str" \ fi
--user-agent "$_userAgent" \ if [[ $_is_IPv4_enabled == false ]] && [[ $_is_IPv6_enabled == true ]]
--header "Authorization: Token $DYNB_TOKEN" \ then
--get --data-urlencode "hostname=$DYNB_DYN_DOMAIN" \ ip_flag_parameters=("--data-urlencode" "${ipv6_parameter_name}=${_new_IPv6}")
"${dyndns_update_url}") fi
;; }
dynv6* | duckDNS* | duckdns*)
_response=$(
curl --silent "$_interface_str" \
--user-agent "$_userAgent" \
"${dyndns_update_url}"
)
;;
esac
case $_response in function send_request
good* | OK* | "addresses updated") {
local _response
debugMessage "curl parameters: ${curl_parameters[*]} ${dyndns_update_url}"
_response=$(
curl --silent "$_interface_str" \
"${curl_parameters[@]}" \
"${dyndns_update_url}")
analyse_response
status_code=$?
return $status_code
}
function analyse_response
{
case $_response in
good* | OK* | "addresses updated" | *Updated*hostname* | *'"info":"good"'*)
if [[ $_response == "good 127.0.0.1" ]]; then if [[ $_response == "good 127.0.0.1" ]]; then
errorMessage "$_response: Request ignored." errorMessage "$_response: Request ignored."
return 1 return 1
else else
infoMessage "$_response: The DynDNS update has been executed." infoMessage "The DynDNS update has been executed."
_errorCounter=0 debugMessage "Response: $_response"
return 0 return 0
fi fi
;; ;;
nochg*) *nochg*)
infoMessage "$_response: Nothing has changed, IP addresses are still up to date." infoMessage "Nothing has changed, IP addresses are still up to date."
debugMessage "Response: $_response"
return 1
;;
400* | *'Bad Request'*)
errorMessage "Bad Request."
debugMessage "Response: $_response"
return 1
;;
*'Too Many Requests'*)
errorMessage "Too Many Request."
debugMessage "Response: $_response"
return 1 return 1
;; ;;
abuse) abuse)
errorMessage "$_response: Username is blocked due to abuse." errorMessage "Username is blocked due to abuse."
debugMessage "Response: $_response"
return 1 return 1
;; ;;
badauth | 401) *badauth* | 401 | *Unauthorized*)
errorMessage "$_response: Invalid username password combination." errorMessage "Invalid token or username password combination."
debugMessage "Response: $_response"
return 1 return 1
;; ;;
badagent) badagent)
errorMessage "$_response: Client disabled. Something is very wrong!" errorMessage "Client disabled. Something is very wrong!"
debugMessage "Response: $_response"
return 1 return 1
;; ;;
!donator) !donator)
@@ -368,8 +507,9 @@ function dynupdate
errorMessage "$_response: Hostname $DYNB_DYN_DOMAIN is invalid" errorMessage "$_response: Hostname $DYNB_DYN_DOMAIN is invalid"
return 1 return 1
;; ;;
nohost) *nohost*)
errorMessage "$_response: 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."
debugMessage "Response: $_response"
return 1 return 1
;; ;;
numhost) numhost)
@@ -380,13 +520,13 @@ function dynupdate
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 [[ "$_response" == "$_status" ]]; 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
errorMessage "unknown respnse code: $_response" errorMessage "unknown respnse code: $_response"
@@ -396,6 +536,15 @@ function dynupdate
esac esac
} }
# using DynDNS2 protocol
function dynupdate
{
prepare_request_parameters
send_request
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 "_status=$1; _eventTime=$2; _errorCounter=$3; _statusHostname=$4; _statusUsername=$5; _statusPassword=$6" >/tmp/dynb.status
@@ -405,10 +554,10 @@ function setStatus
function checkStatus function checkStatus
{ {
case $_status in case $_status 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 an 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
fi fi
;; ;;
@@ -420,8 +569,8 @@ function checkStatus
fi fi
return 0 return 0
;; ;;
badauth | 401) *badauth* | 401 | *Unauthorized*)
if [[ "$_statusUsername" == "$DYNB_USERNAME" && "$_statusPassword" == "$DYNB_PASSWORD" ]]; 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 rm "$_statusFile"
@@ -430,8 +579,7 @@ function checkStatus
;; ;;
badagent) badagent)
errorMessage "Client is deactivated by provider." errorMessage "Client is deactivated by provider."
echo "Fix your config and then manually remove $_statusFile to reset the client blockade." echo "Please 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
;; ;;
!donator) !donator)
@@ -446,7 +594,7 @@ 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*) servererror | 911 | 5* | *'Too Many Requests'*)
delta=$(($(date +%s) - _eventTime)) delta=$(($(date +%s) - _eventTime))
if [[ $delta -lt 1800 ]] if [[ $delta -lt 1800 ]]
then then
@@ -456,6 +604,14 @@ function checkStatus
fi fi
return 0 return 0
;; ;;
*'Bad Request'*)
if [[ "$_statusUsername" == "$DYNB_USERNAME" && ("$_statusPassword" == "$DYNB_PASSWORD" || $_statusPassword == "$DYNB_TOKEN") ]]; then
errorMessage "Bad Request: Please check your credentials, maybe your token is invalid."
return 1
else rm "$_statusFile"
fi
return 0
;;
*) *)
if [[ _errorCounter -gt 1 ]] if [[ _errorCounter -gt 1 ]]
then then
@@ -472,58 +628,20 @@ function checkStatus
function ipHasChanged function ipHasChanged
{ {
local ip_version=$1 local ip_version=$1
remote_ip=$(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) dns_ip=$(getDNSIP A)
if ! is_IPv4_address "$remote_ip"
then
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 _new_IPv4=$remote_ip
debugMessage "IPv4 from remote IP check server: $_new_IPv4, IPv4 from DNS: $dns_ip" debugMessage "IPv4 from remote IP check server: $_new_IPv4, IPv4 from DNS: $dns_ip"
;; ;;
6) 6)
remote_ip=$(getRemoteIP 6 $_ipv6_checker) dns_ip=$(getDNSIP AAAA)
if ! is_IPv6_address "$remote_ip"
then
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 _new_IPv6=$remote_ip
debugMessage "IPv6 from remote IP check server: $_new_IPv6, IPv4 from DNS: $dns_ip" debugMessage "IPv6 from remote IP check server: $_new_IPv6, IPv6 from DNS: $dns_ip"
;; ;;
*) ;; *) ;;
esac esac
@@ -554,8 +672,8 @@ function handleParameters
then rm --verbose "$_statusFile"; exit 0 then rm --verbose "$_statusFile"; exit 0
fi fi
# shellcheck disable=SC2154 # shellcheck disable=SC2154
if [[ $_arg_debug == "on" ]] 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 != "" ]]
@@ -595,8 +713,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"
@@ -614,9 +732,6 @@ function handleParameters
then _is_IPv6_enabled=true then _is_IPv6_enabled=true
fi fi
if [[ $DYNB_DEBUG == true ]]
then _debug=1
fi
# shellcheck disable=SC2154 # shellcheck disable=SC2154
if [[ -n $DYNB_IPv4_CHECK_SITE ]] if [[ -n $DYNB_IPv4_CHECK_SITE ]]
then _ipv4_checker=$DYNB_IPv4_CHECK_SITE then _ipv4_checker=$DYNB_IPv4_CHECK_SITE
@@ -702,6 +817,15 @@ function doDomrobotUpdates
fi fi
} }
function delete_status_file
{
if test -f "$_statusFile"
then
debugMessage "Delete status file with previous errors"
rm "$_statusFile"
fi
}
function doDynDNS2Updates function doDynDNS2Updates
{ {
changed=0 changed=0
@@ -711,20 +835,22 @@ function doDynDNS2Updates
if [[ $_is_IPv6_enabled == true ]] && ipHasChanged 6 if [[ $_is_IPv6_enabled == true ]] && ipHasChanged 6
then ((changed += 1)) then ((changed += 1))
fi fi
if [[ $changed -gt 0 ]] if [[ $changed -gt 0 ]] && [[ $_has_remote_ip_error == false ]]
then then
if checkStatus if checkStatus
then then
debugMessage "checkStatus has no errors, try update" debugMessage "checkStatus has no errors, try update"
if dynupdate if dynupdate
then debugMessage "DynDNS2 update success" then
debugMessage "DynDNS2 update success"
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 "$_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 or there is a connection problem" else debugMessage "Skip DynDNS2 update"
fi fi
} }
@@ -739,17 +865,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
@@ -777,11 +906,8 @@ function check_internet_connection
fi fi
} }
function main function read_config_file
{ {
# shellcheck disable=SC1091,SC1090
source "$(dirname "$(realpath "$0")")/dynb-parsing.sh"
# shellcheck source=.env # shellcheck source=.env
if test -f "$_configFile" if test -f "$_configFile"
then then
@@ -795,6 +921,15 @@ function main
source "$alternativeConfig" source "$alternativeConfig"
fi fi
fi fi
}
function main
{
# shellcheck disable=SC1091,SC1090
source "$(dirname "$(realpath "$0")")/dynb-parsing.sh"
read_config_file
if test -f "$_statusFile" if test -f "$_statusFile"
then then
debugMessage "read previous status file" debugMessage "read previous status file"
@@ -812,6 +947,7 @@ function main
while : while :
do do
doUpdates doUpdates
debugMessage "wait $DYNB_INTERVAL seconds until next check"
sleep $DYNB_INTERVAL sleep $DYNB_INTERVAL
done done
else doUpdates else doUpdates