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

1 Commits

Author SHA1 Message Date
1f31d943ca add DynDNS2 support for noip.com 2021-11-03 23:24:39 +01:00
7 changed files with 347 additions and 516 deletions

View File

@@ -158,7 +158,7 @@ body_process = ReSub(r'((^|\n)[A-Z]\w+(-\w+)*: .*(\n\s+.*)*)+$', r'') | strip
##
## Available constructs are those listed in ``body_process`` doc.
subject_process = (strip |
ReSub(r'^([cC]hg|[fF]ix|[nN]ew|[dD]oc|[fF]eat|[aA]dd|[cC]hange)\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n@]*)(@[a-z]+\s+)*$', r'\4') |
ReSub(r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n@]*)(@[a-z]+\s+)*$', r'\4') |
SetIfEmpty("No commit message.") | ucfirst | final_dot)

View File

@@ -1,16 +0,0 @@
name: Run shellcheck with reviewdog
on: [pull_request]
jobs:
shellcheck:
name: runner / shellcheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: shellcheck
uses: reviewdog/action-shellcheck@v1.9.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
reporter: github-pr-review
path: "."
pattern: "*.sh"
exclude: "./.git/*"

1
.gitignore vendored
View File

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

View File

@@ -1,179 +1,127 @@
# Changelog
## 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)
### Features
* ✨ validate ip address respons from ip check web service. [Eduard Veit]
* ✨ check internet connection for selected ip versions. [Eduard Veit]
* 🎨 colorful info, debug and error messages. [Eduard Veit]
### Fix
* Abort on all dig errors. [Eduard Veit]
### Documentation
* 📝 add labels to Dockerfile. [Eduard Veit]
## 0.2.0 (2021-09-24)
### Features
### New
* enable parameter extensions for `docker run --interactive` [Eduard Veit]
* :sparkles: enable parameter extensions for `docker run --interactive` [Eduard Veit]
### Changed
* :sparkles: add pre-commit hooks for markdown. [Eduard Veit]
* Handle dns server selection. [Eduard Veit]
* :sparkles: change git hooks and add shfmt. [Eduard Veit]
* :sparkles: add pre-commit git hook for shellcheck. [Eduard Veit]
### Changes
* :wrench: remove md-toc from pre-commit. [Eduard Veit]
* :wrench: handle dns server selection. [Eduard Veit]
### Documentation
* 📝 update CHANGELOG.md. [Eduard Veit]
* :memo: update CHANGELOG.md. [Eduard Veit]
* 📝 document docker parameters. [Eduard Veit]
* :memo: document docker parameters. [Eduard Veit]
* 📝 change default dns server setting. [Eduard Veit]
* :memo: change default dns server setting. [Eduard Veit]
* 📝 update CHANGELOG. [Eduard Veit]
* :memo: update CHANGELOG. [Eduard Veit]
* 📝 update README.md. [Eduard Veit]
* :memo: update README.md. [Eduard Veit]
### Other
* :art: lint README.md. [Eduard Veit]
* :art: add shfmt params to pre-commit check. [Eduard Veit]
* :art: fix shell style. [Eduard Veit]
* :recycle: seperate parsing logic. [Eduard Veit]
* :recycle: normalizing pre-commit configuration to a top-level map. [Eduard Veit]
* :recycle: refactorings. [Eduard Veit]
* Refactor: :recycle: remove unused help message. [Eduard Veit]
## 0.1.2 (2021-04-23)
### Documentation
* 📝 document environment variables. [Eduard Veit]
* :memo: document environment variables. [Eduard Veit]
### Other
* :recycle: refactor: method extractions and other beautifications. [Eduard Veit]
## 0.1.1 (2021-04-23)
### Fix
* 🐛 fix loop and error handling in case of connection issues. [Eduard Veit]
* :bug: fix loop and error handling in case of connection issues. [Eduard Veit]
## 0.1.0 (2021-04-22)
### Features
### New
* add Dockerfile. [Eduard Veit]
* :sparkles: add Dockerfile. [Eduard Veit]
* add loop mode. [Eduard Veit]
* :sparkles: add loop mode. [Eduard Veit]
* add support for Duck DNS as DynDNS2 provider. [Eduard Veit]
* :sparkles: add support for Duck DNS as DynDNS2 provider. [Eduard Veit]
* add support for deSEC as DynDNS2 provider. [Eduard Veit]
* :sparkles: add support for deSEC as DynDNS2 provider. [Eduard Veit]
* add completion. [Eduard Veit]
* :sparkles: add completion. [Eduard Veit]
add man page
:sparkles: add man page
* 🔃 replace getopt with argbash. [Eduard Veit]
* :sparkles: replace getopt with argbash. [Eduard Veit]
* add interpretaton of status codes and act accordingly. [Eduard Veit]
* :sparkles: add interpretaton of status codes and act accordingly. [Eduard Veit]
* make network interface configurable. [Eduard Veit]
* :sparkles: make network interface configurable. [Eduard Veit]
* add DynDNS2 support for dynv6.com. [Eduard Veit]
* :sparkles: add DynDNS2 support for dynv6.com. [Eduard Veit]
### Added
* :sparkles: add .gitchangelog.rc. [Eduard Veit]
* 📝 README.md. [Eduard Veit]
* ✨ dynb.sh. [Eduard Veit]
### Changed
* 🔃 rename environment variables. [Eduard Veit]
* :sparkles: add dynb.sh. [Eduard Veit]
### Fix
* 🐛 fix error handling. [Eduard Veit]
* :bug: fix sourcing of config file. [Eduard Veit]
* 🐛 fix sourcing of config file. [Eduard Veit]
♻️ do some shellcheck fixes
:recycle: do some shellcheck fixes
### Documentation
* 📝 document example of an docker-compose.yml file. [Eduard Veit]
* :memo: document example of an docker-compose.yml file. [Eduard Veit]
* 📝 document `loop mode` and mention `dig` as requirement. [Eduard Veit]
* :memo: document loop mode and dig as requirement. [Eduard Veit]
* 📝 update example of .env in README.md. [Eduard Veit]
* :memo: update example of .env in README.md. [Eduard Veit]
* 📝 CHANGELOG.md. [Eduard Veit]
* :memo: add CHANGELOG.md. [Eduard Veit]
* 📝 add example.env. [Eduard Veit]
* :memo: add example.env. [Eduard Veit]
* :memo: write README.md. [Eduard Veit]
### Other
* :recycle: refactor: rename environment variables. [Eduard Veit]
* :recycle: refactor, fix and debug error handling. [Eduard Veit]
* :recycle: refactor main code. [Eduard Veit]
* Initial commit. [EV21]

View File

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

View File

@@ -7,6 +7,8 @@ IPv4 (A) and IPv6 (AAAA) record updates are supported.
<!-- TOC -->
- [✨ Update Methods](#-update-methods)
- [APIs](#apis)
- [DynDNS2](#dyndns2)
- [📦 Requirements](#-requirements)
- [🚀 Installation](#-installation)
- [⚙ Configuration](#-configuration)
@@ -23,15 +25,18 @@ IPv4 (A) and IPv6 (AAAA) record updates are supported.
The following update methods are currently implemented:
| 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 | DynDNS2 | 60 | specific dyndns `username` & `password` | ✔️ | ⛔ choose one of your owned domains per account |
| deSEC.io (dedyn.io) | DynDNS2 | 60 | `token` | ✔️ | ✔️ |
| DuckDNS.org | DynDNS2 | 60 | `token` | ⛔ | ✔️ |
| dynv6.com | DynDNS2 | 60 | `token` | ✔️ | ✔️ |
| ddnss.de | DynDNS2 | 10 | update key as `token` | ⛔ | ✔️ |
| IPv64.net | DynDNS2 | 60 | `DynDNS Updatehash` as `token` | ⛔ | ✔️ |
### APIs
- INWX.com Domrobot JSON-RPC-API\
Limitations:
- minimum TTL is 300 (5 minutes)
### DynDNS2
- INWX.com
- deSEC.io (dedyn.io)
- DuckDNS.org
- dynv6.com
## 📦 Requirements
@@ -52,7 +57,7 @@ or simply clone this repo
git clone https://github.com/EV21/dynb.git
```
If you want to add the script to you PATH, run 👇
If you want to add the script to you PATH, run :point_down:
```shell
bash dynb.sh --link
@@ -74,7 +79,7 @@ DYNB_SERVICE_PROVIDER=inwx
## update method options: domrobot, dyndns
DYNB_UPDATE_METHOD=domrobot
## ip mode could be either: 4, 6 or 64 for dualstack
## ip mode could be either: 4, 6 or dual for dualstack
DYNB_IP_MODE=64
## If you are using the DomRobot RPC-API enter your credentials for the web interface login here
@@ -87,14 +92,14 @@ DYNB_TOKEN=
## 🏃 Run
If you have a config file just run 👇
If you have a config file just run :point_down:
```bash
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.\
Call the help function 👇
Call the help function :point_down:
```bash
dynb --help
@@ -118,13 +123,13 @@ Just use the parameter `--interval 60` or the environment variable `DYNB_INTERVA
### crontab
execute 👇
execute :point_down:
```bash
crontab -e
```
then add the following line 👇 to run dynb every five minutes.
then enter :point_down: to run dynb every five minutes.
```bash
*/5 * * * * $HOME/.local/bin/dynb >> $HOME/.local/share/dynb/dynb-cron.log
@@ -150,33 +155,25 @@ services:
image: ev21/dynb
container_name: dynb
network_mode: host
stdin_open: true
tty: true
build:
context: .
dockerfile: ./Dockerfile
environment:
- DYNB_DYN_DOMAIN=dyndns.example.com
# Providers: deSec, DuckDNS, dynv6, inwx, ddnss, ipv64
- DYNB_SERVICE_PROVIDER=desec
# Possible update methods are: dyndns, domrobot
- DYNB_SERVICE_PROVIDER=inwx
- DYNB_UPDATE_METHOD=dyndns
# IP modes: 4 (IPv4 only), 6 (IPv6 only), 64 both
- DYNB_IP_MODE=64
# If your provider uses tokens use DYNB_TOKEN instead of DYNB_USERNAME and DYNB_PASSWORD
- DYNB_USERNAME=User42
- DYNB_PASSWORD=SuperSecretPassword
# 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
- DYNB_INTERVAL=60
```
## environment variables
| variable | default value | description |
|-----------------------|-----------------|----------------------------------------------------------------------------------------------------------------|
| --------------------- | --------------- | -------------------------------------------------------------------------------------------------------------- |
| 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` |
| DYNB_UPDATE_METHOD | undefined | required; `dyndns` or `domrobot` (with inwx) |
| DYNB_IP_MODE | undefined | required; `4`, `6` or `64` for both |
| DYNB_USERNAME | undefined | the requirement depends on your provider and the update method |

590
dynb.sh
View File

@@ -66,7 +66,7 @@ _response=
_statusHostname=
_statusUsername=
_statusPassword=
_version=0.3.5
_version=0.3.0
_userAgent="DynB/$_version github.com/EV21/dynb"
_configFile=$HOME/.local/share/dynb/.env
_statusFile=/tmp/dynb.status
@@ -85,66 +85,57 @@ reset_color_modification="\e[0m"
REGEX_IPv4="^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$"
REGEX_IPv6="^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$"
function is_IPv4_address
{
local ip=$1
if [[ $ip =~ $REGEX_IPv4 ]]
function is_IPv4_address() {
if [[ $1 =~ $REGEX_IPv4 ]]
then return 0
else return 1
fi
}
function is_IPv6_address
{
local ip=$1
if [[ $ip =~ $REGEX_IPv6 ]]
function is_IPv6_address() {
if [[ $1 =~ $REGEX_IPv6 ]]
then return 0
else return 1
fi
}
function loopMode
{
if [[ $_loopMode -eq 1 ]]
then return 0
else return 1
function loopMode() {
if [[ $_loopMode -eq 1 ]]; then
return 0
else
return 1
fi
}
function debugMode
{
if [[ $_debug -eq 1 ]]
then return 0
else return 1
function debugMode() {
if [[ $_debug -eq 1 ]]; then
return 0
else
return 1
fi
}
function infoMessage
{
function infoMessage() {
echo -e "${green_color}$(logtime) INFO: $*${reset_color_modification}"
}
function debugMessage
{
if debugMode
then echo -e "${yellow_color}$(logtime) DEBUG: ${*}${reset_color_modification}"
function debugMessage() {
if debugMode; then
echo -e "${yellow_color}$(logtime) DEBUG: ${*}${reset_color_modification}"
fi
}
function errorMessage
{
function errorMessage() {
echo -e "${red_color_bg}${bold}$(logtime) ERROR: $*${reset_color_modification}" >&2
}
function logtime
{
function logtime() {
LOGTIME=$(date "+%Y-%m-%d %H:%M:%S")
echo "[$LOGTIME]"
}
# The main domain as an identifier for the dns zone is required for the updateRecord call
function getMainDomain
{
function getMainDomain() {
request=$(
echo "{}" |
jq '(.method="nameserver.list")' |
@@ -163,8 +154,7 @@ function getMainDomain
_main_domain=$(echo "$_response" | jq --raw-output '.domain')
}
function fetchDNSRecords
{
function fetchDNSRecords() {
request=$(
echo "{}" |
jq '(.method="'nameserver.info'")' |
@@ -188,54 +178,47 @@ function fetchDNSRecords
# requires parameter A or AAAA
# result to stdout
function getRecordID
{
echo "$_dns_records" |
jq "select(.type == \"${1}\") | .id"
function getRecordID() {
echo "$_dns_records" | jq "select(.type == \"${1}\") | .id"
}
# requires parameter A or AAAA
# result to stdout
function getDNSIP() {
echo "$_dns_records" |
jq --raw-output "select(.type == \"${1}\") | .content"
echo "$_dns_records" | jq --raw-output "select(.type == \"${1}\") | .content"
}
# requires parameter
# 1. param: 4 or 6 for ip version
# 2. param: IP check server address
# result to stdout
function getRemoteIP
{
local ip_version=$1
local ip_check_server=$2
curl --silent "$_interface_str" --user-agent "$_userAgent" \
--ipv"${ip_version}" --location "${ip_check_server}"
local curls_status_code=$?
function getRemoteIP() {
if [[ -n $_DNS_checkServer ]]; then
curl --silent "$_interface_str" --user-agent "$_userAgent" \
--ipv"${1}" --dns-servers "$_DNS_checkServer" --location "${2}"
else
curl --silent "$_interface_str" --user-agent "$_userAgent" \
--ipv"${1}" --location "${2}"
fi
# shellcheck disable=2181
if [[ $curls_status_code -gt 0 ]]; then
errorMessage "IPCheck (getRemoteIP $ip_version) request failed"
if [[ $? -gt 0 ]]; then
errorMessage "IPCheck (getRemoteIP ${1}) request failed"
exit 1
fi
}
# requires parameter
# 1. param: 4 or 6 as ip version
function updateRecord
{
local ip_version=$1
if [[ ${ip_version} == 4 ]]
then
function updateRecord() {
if [[ ${1} == 4 ]]; then
ID=$(getRecordID A)
IP=$_new_IPv4
fi
if [[ ${ip_version} == 6 ]]
then
if [[ ${1} == 6 ]]; then
ID=$(getRecordID AAAA)
IP=$_new_IPv6
fi
if [[ $IP != "" ]]
then
if [[ $IP != "" ]]; then
request=$(
echo "{}" |
jq '(.method="nameserver.updateRecord")' |
@@ -258,8 +241,8 @@ function updateRecord
fi
}
function select_update_base_url
{
# using DynDNS2 protocol
function dynupdate() {
# default parameter values
myip_str=myip
myipv6_str=myipv6
@@ -268,8 +251,7 @@ function select_update_base_url
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?"
NOIP_DYNDNS_UPDATE_URL="https://dynupdate.no-ip.com/nic/update?hostname=$DYNB_DYN_DOMAIN&"
case $DYNB_SERVICE_PROVIDER in
inwx* | INWX*)
@@ -278,6 +260,9 @@ function select_update_base_url
deSEC* | desec* | dedyn*)
dyndns_update_url="${DESEC_DYNDNS_UPDATE_URL}"
;;
noip* | no-ip*)
dyndns_update_url="$NOIP_DYNDNS_UPDATE_URL"
;;
dynv6*)
dyndns_update_url="${DYNV6_DYNDNS_UPDATE_URL}"
myip_str=ipv4
@@ -285,37 +270,34 @@ function select_update_base_url
;;
DuckDNS* | duckdns*)
dyndns_update_url="${DUCKDNS_DYNDNS_UPDATE_URL}"
myip_str=ip
myip_str=ipv4
myipv6_str=ipv6
;;
ddnss*)
dyndns_update_url="${DDNSS_DYNDNS_UPDATE_URL}"
## we are currently not using the syntax with ip auto detection
myip_str=ip
myipv6_str=ip6
;;
[Ii][Pp][Vv]64*)
dyndns_update_url="${IPV64NET_DYNDNS_UPDATE_URL}"
myip_str=ip
myipv6_str=ip6
;;
*)
errorMessage "$DYNB_SERVICE_PROVIDER is not supported"
exit 1
;;
esac
}
function send_request
{
case $DYNB_SERVICE_PROVIDER in
inwx* | INWX*)
# pre encode ip parameters
if [[ $_is_IPv4_enabled == true ]] && [[ $_is_IPv6_enabled == true ]]; then
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 ##
case $DYNB_SERVICE_PROVIDER in
inwx* | INWX* | noip* | no-ip*)
_response=$(curl --silent "$_interface_str" \
--user-agent "$_userAgent" \
--user "$DYNB_USERNAME":"$DYNB_PASSWORD" \
"${dyndns_update_url}")
analyse_response
status_code=$?
;;
deSEC* | desec* | dedyn*)
_response=$(curl --silent "$_interface_str" \
@@ -323,72 +305,42 @@ function send_request
--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*)
dynv6* | duckDNS* | duckdns*)
_response=$(
curl --silent "$_interface_str" \
--user-agent "$_userAgent" \
"${dyndns_update_url}")
analyse_response
status_code=$?
"${dyndns_update_url}"
)
;;
esac
return $status_code
}
function analyse_response
{
case $_response in
good* | OK* | "addresses updated" | *Updated*hostname* | *'"info":"good"'*)
case $_response in
good* | OK* | "addresses updated")
if [[ $_response == "good 127.0.0.1" ]]; then
errorMessage "$_response: Request ignored."
return 1
else
infoMessage "The DynDNS update has been executed."
debugMessage "Response: $_response"
infoMessage "$_response: The DynDNS update has been executed."
_errorCounter=0
return 0
fi
;;
*nochg*)
infoMessage "Nothing has changed, IP addresses are still up to date."
debugMessage "Response: $_response"
return 1
;;
*'Bad Request'*)
errorMessage "Bad Request."
debugMessage "Response: $_response"
return 1
;;
*'Too Many Requests'*)
errorMessage "Too Many Request."
debugMessage "Response: $_response"
nochg*)
infoMessage "$_response: Nothing has changed, IP addresses are still up to date."
return 1
;;
abuse)
errorMessage "Username is blocked due to abuse."
debugMessage "Response: $_response"
errorMessage "$_response: Username is blocked due to abuse."
return 1
;;
*badauth* | 401 | *Unauthorized*)
errorMessage "Invalid token or username password combination."
debugMessage "Response: $_response"
badauth | 401)
errorMessage "$_response: Invalid username password combination."
return 1
;;
badagent)
errorMessage "Client disabled. Something is very wrong!"
debugMessage "Response: $_response"
errorMessage "$_response: Client disabled. Something is very wrong!"
return 1
;;
!donator)
@@ -403,9 +355,8 @@ function analyse_response
errorMessage "$_response: Hostname $DYNB_DYN_DOMAIN is invalid"
return 1
;;
*nohost*)
errorMessage "Hostname supplied does not exist under specified account, enter new login credentials before performing an additional request."
debugMessage "Response: $_response"
nohost)
errorMessage "$_response: Hostname supplied does not exist under specified account, enter new login credentials before performing an additional request."
return 1
;;
numhost)
@@ -422,7 +373,7 @@ function analyse_response
;;
*)
if [[ "$_response" == "$_status" ]]; then
errorMessage "An unknown response code has been received: $_response"
errorMessage "An unknown response code has been received. $_response"
return 1
else
errorMessage "unknown respnse code: $_response"
@@ -432,47 +383,17 @@ function analyse_response
esac
}
# using DynDNS2 protocol
function dynupdate
{
select_update_base_url
# pre encode ip parameters
if [[ $_is_IPv4_enabled == true ]] && [[ $_is_IPv6_enabled == true ]]
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
}
function setStatus
{
function setStatus() {
echo "_status=$1; _eventTime=$2; _errorCounter=$3; _statusHostname=$4; _statusUsername=$5; _statusPassword=$6" >/tmp/dynb.status
}
# handle errors from past update requests
function checkStatus
{
function checkStatus() {
case $_status in
*nochg*)
nochg*)
if [[ _errorCounter -gt 1 ]]; then
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 an then delete $_statusFile or restart your docker container"
return 1
fi
;;
@@ -480,21 +401,24 @@ function checkStatus
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."
return 1
else rm "$_statusFile"
else
rm "$_statusFile"
fi
return 0
;;
*badauth* | 401 | *Unauthorized*)
if [[ "$_statusUsername" == "$DYNB_USERNAME" && ("$_statusPassword" == "$DYNB_PASSWORD" || $_statusPassword == "$DYNB_TOKEN") ]]; then
badauth | 401)
if [[ "$_statusUsername" == "$DYNB_USERNAME" && "$_statusPassword" == "$DYNB_PASSWORD" ]]; then
errorMessage "Invalid username password combination."
return 1
else rm "$_statusFile"
else
rm "$_statusFile"
fi
return 0
;;
badagent)
errorMessage "Client is deactivated by provider."
echo "Please file an issue at GitHub or try another client :)"
echo "Fix your config and then manually remove $_statusFile to reset the client blockade."
echo "If it still fails file an issue at github or try another client :)"
return 1
;;
!donator)
@@ -509,30 +433,22 @@ function checkStatus
echo "If it still fails file an issue at github or try another client :)"
return 1
;;
911 | 5* | *'Too Many Requests'*)
911 | 5*)
delta=$(($(date +%s) - _eventTime))
if [[ $delta -lt 1800 ]]
then
if [[ $delta -lt 1800 ]]; 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."
return 1
else rm "$_statusFile"
fi
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"
else
rm "$_statusFile"
fi
return 0
;;
*)
if [[ _errorCounter -gt 1 ]]
then
if [[ _errorCounter -gt 1 ]]; then
errorMessage "An unknown response code has repeatedly been received. $_response"
return 1
else return 0
else
return 0
fi
;;
esac
@@ -540,28 +456,26 @@ function checkStatus
# requires parameter
# 1. param: 4 or 6 for IP version
function ipHasChanged
{
local ip_version=$1
case ${ip_version} in
function ipHasChanged() {
case ${1} in
4)
remote_ip=$(getRemoteIP 4 $_ipv4_checker)
if ! is_IPv4_address "$remote_ip"
then
errorMessage "The response from the IP check server is not an IPv4 address: $remote_ip"
return 1
return 0
fi
if [[ $DYNB_UPDATE_METHOD == domrobot ]]
then dns_ip=$(getDNSIP A)
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")
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
if [[ $dig_response == ";; connection timed out; no servers could be reached" ]]; then
errorMessage "DNS request failed $dig_response"
return 1
return 0
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
@@ -575,154 +489,161 @@ function ipHasChanged
if ! is_IPv6_address "$remote_ip"
then
errorMessage "The response from the IP check server is not an IPv6 address: $remote_ip"
return 1
return 0
fi
if [[ $DYNB_UPDATE_METHOD == domrobot ]]
then dns_ip=$(getDNSIP AAAA)
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")
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
if [[ $dig_response == ";; connection timed out; no servers could be reached" ]]; then
errorMessage "DNS request failed $dig_response"
return 0
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"
debugMessage "IPv6 from remote IP check server: $_new_IPv6, IPv4 from DNS: $dns_ip"
;;
*) ;;
esac
if [[ "$remote_ip" == "$dns_ip" ]]
then return 1
if [[ "$remote_ip" == "$dns_ip" ]]; then
return 0
else
case ${ip_version} in
case ${1} in
4) infoMessage "New IPv4: $_new_IPv4 old was: $dns_ip";;
6) infoMessage "New IPv6: $_new_IPv6 old was: $dns_ip";;
esac
return 0
return 1
fi
}
function handleParameters
{
################
## parameters ##
################
function handleParameters() {
# shellcheck disable=SC2154
if [[ $_arg_version == "on" ]]
then echo $_version; exit 0
if [[ $_arg_version == "on" ]]; then
echo $_version
exit 0
fi
# shellcheck disable=SC2154
if [[ $_arg_link == "on" ]]
then ln --verbose --symbolic "$(realpath "$0")" "$HOME/.local/bin/dynb"; exit 0
if [[ $_arg_link == "on" ]]; then
ln --verbose --symbolic "$(realpath "$0")" "$HOME/.local/bin/dynb"
exit 0
fi
# shellcheck disable=SC2154
if [[ $_arg_reset == "on" ]] && test -f "$_statusFile"
then rm --verbose "$_statusFile"; exit 0
if [[ $_arg_reset == "on" ]]; then
rm --verbose "$_statusFile"
exit 0
fi
# shellcheck disable=SC2154
if [[ $_arg_debug == "on" ]] || [[ $DYNB_DEBUG == true ]]
then _debug=1
if [[ $_arg_debug == "on" ]]; then
_debug=1
fi
# shellcheck disable=SC2154
if [[ $_arg_update_method != "" ]]
then DYNB_UPDATE_METHOD=$_arg_update_method
if [[ $_arg_update_method != "" ]]; then
DYNB_UPDATE_METHOD=$_arg_update_method
fi
# shellcheck disable=SC2154
if [[ $_arg_ip_mode != "" ]]
then DYNB_IP_MODE=$_arg_ip_mode
if [[ $_arg_ip_mode != "" ]]; then
DYNB_IP_MODE=$_arg_ip_mode
fi
# shellcheck disable=SC2154
if [[ $_arg_domain != "" ]]
then DYNB_DYN_DOMAIN=$_arg_domain
if [[ $_arg_domain != "" ]]; then
DYNB_DYN_DOMAIN=$_arg_domain
fi
# shellcheck disable=SC2154
if [[ $_arg_service_provider != "" ]]
then DYNB_SERVICE_PROVIDER=$_arg_service_provider
if [[ $_arg_service_provider != "" ]]; then
DYNB_SERVICE_PROVIDER=$_arg_service_provider
fi
# shellcheck disable=SC2154
if [[ $_arg_username != "" ]]
then DYNB_USERNAME=$_arg_username
if [[ $_arg_username != "" ]]; then
DYNB_USERNAME=$_arg_username
fi
# shellcheck disable=SC2154
if [[ $_arg_password != "" ]]
then DYNB_PASSWORD=$_arg_password
if [[ $_arg_password != "" ]]; then
DYNB_PASSWORD=$_arg_password
fi
# shellcheck disable=SC2154
if [[ $_arg_token != "" ]]
then DYNB_TOKEN=$_arg_token
if [[ $_arg_token != "" ]]; then
DYNB_TOKEN=$_arg_token
fi
# shellcheck disable=SC2154
if [[ $_arg_interval != "" ]]
then DYNB_INTERVAL=$_arg_interval
if [[ $_arg_interval != "" ]]; then
DYNB_INTERVAL=$_arg_interval
fi
if [[ -z $DYNB_INTERVAL ]]
then _loopMode=0
elif [[ $DYNB_INTERVAL -lt _minimum_looptime ]]
then
if [[ -z $DYNB_INTERVAL ]]; then
_loopMode=0
elif [[ $DYNB_INTERVAL -lt _minimum_looptime ]]; then
DYNB_INTERVAL=$_minimum_looptime
_loopMode=1
else _loopMode=1
else
_loopMode=1
fi
if [[ $_network_interface != "" ]]
then _interface_str="--interface $_network_interface"
if [[ $_network_interface != "" ]]; then
_interface_str="--interface $_network_interface"
fi
if [[ $DYNB_IP_MODE == d* ]]
then
if [[ $DYNB_IP_MODE == d* ]]; then
_is_IPv4_enabled=true
_is_IPv6_enabled=true
fi
if [[ $DYNB_IP_MODE == *4* ]]
then _is_IPv4_enabled=true
if [[ $DYNB_IP_MODE == *4* ]]; then
_is_IPv4_enabled=true
fi
if [[ $DYNB_IP_MODE == *6* ]]
then _is_IPv6_enabled=true
if [[ $DYNB_IP_MODE == *6* ]]; then
_is_IPv6_enabled=true
fi
# shellcheck disable=SC2154
if [[ -n $DYNB_IPv4_CHECK_SITE ]]
then _ipv4_checker=$DYNB_IPv4_CHECK_SITE
if [[ $DYNB_DEBUG == true ]]; then
_debug=1
fi
# shellcheck disable=SC2154
if [[ -n $DYNB_IPv6_CHECK_SITE ]]
then _ipv6_checker=$DYNB_IPv6_CHECK_SITE
if [[ -n $DYNB_IPv4_CHECK_SITE ]]; then
_ipv4_checker=$DYNB_IPv4_CHECK_SITE
fi
if [[ -n $DYNB_DNS_CHECK_SERVER ]]
then _DNS_checkServer=$DYNB_DNS_CHECK_SERVER
# shellcheck disable=SC2154
if [[ -n $DYNB_IPv6_CHECK_SITE ]]; then
_ipv6_checker=$DYNB_IPv6_CHECK_SITE
fi
if [[ -n $DYNB_DNS_CHECK_SERVER ]]; then
_DNS_checkServer=$DYNB_DNS_CHECK_SERVER
fi
return 0
}
function checkDependencies
{
##################
## dependencies ##
##################
function checkDependencies() {
failCounter=0
for i in curl dig; do
if ! command -v $i >/dev/null 2>&1
then
if ! command -v $i >/dev/null 2>&1; then
errorMessage "could not find \"$i\", DynB depends on it. "
((failCounter++))
fi
done
[[ -x $(command -v jq 2>/dev/null) ]] || {
if [[ $DYNB_UPDATE_METHOD != dyndns* ]]
then
if [[ $DYNB_UPDATE_METHOD != dyndns* ]]; then
echo "This script depends on jq and it is not available." >&2
((failCounter++))
fi
}
if [[ failCounter -gt 0 ]]
then exit 1
if [[ failCounter -gt 0 ]]; then
exit 1
fi
}
function doUnsets
{
function doUnsets() {
unset _network_interface
unset _DNS_checkServer
unset _dns_records
@@ -750,94 +671,83 @@ function doUnsets
unset DYNB_DEBUG
}
function doDomrobotUpdates
{
function doDomrobotUpdates() {
getMainDomain
fetchDNSRecords
if [[ $_is_IPv4_enabled == true ]]
then
if ipHasChanged 4
then updateRecord 4
else debugMessage "Skip IPv4 record update, it is already up to date"
if [[ $_is_IPv4_enabled == true ]]; then
ipHasChanged 4
if [[ $? == 1 ]]; then
updateRecord 4
else
debugMessage "Skip IPv4 record update, it is already up to date"
fi
fi
if [[ $_is_IPv6_enabled == true ]]
then
if ipHasChanged 6
then updateRecord 6
else debugMessage "Skip IPv6 record update, it is already up to date"
if [[ $_is_IPv6_enabled == true ]]; then
ipHasChanged 6
if [[ $? == 1 ]]; then
updateRecord 6
else
debugMessage "Skip IPv6 record update, it is already up to date"
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
if [[ $_is_IPv4_enabled == true ]] && ipHasChanged 4
then ((changed += 1))
if [[ $_is_IPv4_enabled == true ]]; then
ipHasChanged 4
((changed += $?))
fi
if [[ $_is_IPv6_enabled == true ]] && ipHasChanged 6
then ((changed += 1))
if [[ $_is_IPv6_enabled == true ]]; then
ipHasChanged 6
((changed += $?))
fi
if [[ $changed -gt 0 ]]
then
if checkStatus
then
if [[ $changed -gt 0 ]]; then
if checkStatus; then
debugMessage "checkStatus has no errors, try update"
if dynupdate
then
if dynupdate; then
debugMessage "DynDNS2 update success"
delete_status_file
else
debugMessage "Save new status after dynupdate has failed"
setStatus "$_response" "$(date +%s)" $((_errorCounter += 1)) "$DYNB_DYN_DOMAIN" "${DYNB_USERNAME}" "${DYNB_PASSWORD}${DYNB_TOKEN}"
fi
else debugMessage "Skip DynDNS2 update, checkStatus fetched previous error."
else
debugMessage "Skip DynDNS2 update, checkStatus fetched previous error."
fi
else debugMessage "Skip DynDNS2 update, IPs are up to date."
else
debugMessage "Skip DynDNS2 update, IPs are up to date or there is a connection problem"
fi
}
function doUpdates
{
if [[ $DYNB_UPDATE_METHOD == "domrobot" ]]
then doDomrobotUpdates
elif [[ $DYNB_UPDATE_METHOD == "dyndns" ]]
then doDynDNS2Updates
function doUpdates() {
if [[ $DYNB_UPDATE_METHOD == "domrobot" ]]; then
doDomrobotUpdates
fi
if [[ $DYNB_UPDATE_METHOD == "dyndns" ]]; then
doDynDNS2Updates
fi
}
function ipv6_is_not_working
{
curl --ipv6 --head --silent --max-time 5 $_internet_connectivity_test_server > /dev/null
function ipv6_is_not_working() {
curl --ipv6 --head --silent --max-time 1 "$_internet_connectivity_test_server" > /dev/null
status_code=$?
if test $status_code -gt 0
if [[ $status_code -gt 0 ]]
then return 0
else return 1
fi
}
function ipv4_is_not_working
{
curl --ipv4 --head --silent --max-time 5 $_internet_connectivity_test_server > /dev/null
function ipv4_is_not_working() {
curl --ipv4 --head --silent --max-time 1 "$_internet_connectivity_test_server" > /dev/null
status_code=$?
if test $status_code -gt 0
if [[ $status_code -gt 0 ]]
then return 0
else return 1
fi
}
function check_internet_connection
{
function check_internet_connection() {
if [[ $_is_IPv4_enabled == true ]]
then
if ipv4_is_not_working
@@ -856,32 +766,25 @@ function check_internet_connection
fi
}
function read_config_file
{
#################
## MAIN method ##
#################
function dynb() {
# shellcheck disable=SC1091,SC1090
source "$(dirname "$(realpath "$0")")/dynb-parsing.sh"
# shellcheck source=.env
if test -f "$_configFile"
then
if test -f "$_configFile"; then
# shellcheck disable=SC1091
source "$_configFile"
else
alternativeConfig="$(dirname "$(realpath "$0")")/.env"
if test -f "$alternativeConfig"
then
if test -f "$alternativeConfig"; then
# shellcheck disable=SC1091
source "$alternativeConfig"
fi
fi
}
function main
{
# shellcheck disable=SC1091,SC1090
source "$(dirname "$(realpath "$0")")/dynb-parsing.sh"
read_config_file
if test -f "$_statusFile"
then
if test -f "$_statusFile"; then
debugMessage "read previous status file"
# shellcheck disable=SC1090
source "$_statusFile"
@@ -892,20 +795,21 @@ function main
checkDependencies
check_internet_connection
if loopMode
then
while :
do
if loopMode; then
while :; do
doUpdates
debugMessage "wait $DYNB_INTERVAL seconds until next check"
sleep $DYNB_INTERVAL
done
else doUpdates
else
doUpdates
fi
doUnsets
return 0
}
######################
## END MAIN section ##
######################
main "${@}"
dynb "${@}"
exit $?