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

♻️ refactor, fix and debug error handling

This commit is contained in:
2021-01-27 15:23:09 +01:00
parent 522a0f99bb
commit 5210c35400

215
dynb.sh
View File

@@ -40,6 +40,7 @@ TTL=300
_ipv4_checker=api64.ipify.org _ipv4_checker=api64.ipify.org
_ipv6_checker=api64.ipify.org _ipv6_checker=api64.ipify.org
## An exernal DNS check server prevents wrong info from local DNS servers/resolvers
_DNS_checkServer=1.1.1.1 _DNS_checkServer=1.1.1.1
## 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
@@ -60,7 +61,8 @@ _is_IPv6_enabled=false
_interface_str= _interface_str=
_status= _status=
_eventTime=0 _eventTime=0
_counter=0 _errorCounter=0
_response=
_statusHostname= _statusHostname=
_statusUsername= _statusUsername=
_statusPassword= _statusPassword=
@@ -68,18 +70,7 @@ _version=0.0.1
_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=1
function echoerr() { printf "%s\n" "$*" >&2; }
for i in curl jq date; do
if ! command -v $i >/dev/null 2>&1; then
echoerr "Error: could not find \"$i\", DynB depends on it. "
exit 1
fi
done
[[ -x $(command -v getopt 2> /dev/null) ]] || {
_has_getopt=false
}
_help_message="$(cat << 'EOF' _help_message="$(cat << 'EOF'
dynb - dynamic DNS update script for bash dynb - dynamic DNS update script for bash
@@ -109,6 +100,22 @@ dynb --ip-mode dual --update-method dyndns --service-provider inwx --domain dynd
EOF EOF
)" )"
function debugMode() {
if [[ $_debug -eq 1 ]]; then
return 0
else
return 1
fi
}
function debugMessage() {
if debugMode; then
echo "Debug: ${1}"
fi
}
function echoerr() { printf "%s\n" "$*" >&2; }
# The main domain as an identifier for the dns zone is required for the updateRecord call # The main domain as an identifier for the dns zone is required for the updateRecord call
function getMainDomain() { function getMainDomain() {
request=$( echo "{}" | \ request=$( echo "{}" | \
@@ -117,14 +124,14 @@ function getMainDomain() {
jq "(.params.pass=\"$_password\")" jq "(.params.pass=\"$_password\")"
) )
response=$(curl --silent \ _response=$(curl --silent \
"$_interface_str" \ "$_interface_str" \
--user-agent "$_userAgent" \ --user-agent "$_userAgent" \
--header "Content-Type: application/json" \ --header "Content-Type: application/json" \
--request POST $_INWX_JSON_API_URL \ --request POST $_INWX_JSON_API_URL \
--data "$request" | jq ".resData.domains[] | select(inside(.domain=\"$_dyn_domain\"))" --data "$request" | jq ".resData.domains[] | select(inside(.domain=\"$_dyn_domain\"))"
) )
_main_domain=$( echo "$response" | jq --raw-output '.domain' ) _main_domain=$( echo "$_response" | jq --raw-output '.domain' )
} }
function fetchDNSRecords() { function fetchDNSRecords() {
@@ -136,7 +143,7 @@ function fetchDNSRecords() {
jq "(.params.name=\"$_dyn_domain\")" jq "(.params.name=\"$_dyn_domain\")"
) )
response=$( curl --silent \ _response=$( curl --silent \
"$_interface_str" \ "$_interface_str" \
--user-agent "$_userAgent" \ --user-agent "$_userAgent" \
--header "Content-Type: application/json" \ --header "Content-Type: application/json" \
@@ -144,7 +151,7 @@ function fetchDNSRecords() {
--data "$request" --data "$request"
) )
_dns_records=$( echo "$response" | jq '.resData.record[]' ) _dns_records=$( echo "$_response" | jq '.resData.record[]' )
} }
# requires parameter A or AAAA # requires parameter A or AAAA
@@ -189,14 +196,14 @@ function updateRecord() {
jq "(.params.ttl=\"$TTL\")" jq "(.params.ttl=\"$TTL\")"
) )
response=$(curl --silent \ _response=$(curl --silent \
"$_interface_str" \ "$_interface_str" \
--user-agent "$_userAgent" \ --user-agent "$_userAgent" \
--header "Content-Type: application/json" \ --header "Content-Type: application/json" \
--request POST $_INWX_JSON_API_URL \ --request POST $_INWX_JSON_API_URL \
--data "$request" --data "$request"
) )
echo -e "$(echo "$response" | jq --raw-output '.msg')\n Domain: $_dyn_domain\n new IPv${1}: $IP" echo -e "$(echo "$_response" | jq --raw-output '.msg')\n Domain: $_dyn_domain\n new IPv${1}: $IP"
fi fi
} }
@@ -227,136 +234,155 @@ function dynupdate() {
if [[ $_is_IPv4_enabled == false ]] && [[ $_is_IPv6_enabled == true ]]; then if [[ $_is_IPv4_enabled == false ]] && [[ $_is_IPv6_enabled == true ]]; then
dyndns_update_url="${dyndns_update_url}${myipv6_str}=${_new_IPv6}" dyndns_update_url="${dyndns_update_url}${myipv6_str}=${_new_IPv6}"
fi fi
debugMessage "Update URL was: $dyndns_update_url"
## request ## ## request ##
if [[ $_serviceProvider == "dynv6" ]]; then if [[ $_serviceProvider == "dynv6" ]]; then
response=$(curl --silent "$_interface_str" \ _response=$(curl --silent "$_interface_str" \
--user-agent "$_userAgent" \ --user-agent "$_userAgent" \
"${dyndns_update_url}" "${dyndns_update_url}"
) )
fi fi
if [[ $_serviceProvider == "inwx" ]]; then if [[ $_serviceProvider == "inwx" ]]; then
response=$(curl --silent "$_interface_str" \ _response=$(curl --silent "$_interface_str" \
--user-agent "$_userAgent" \ --user-agent "$_userAgent" \
--user "$_username":"$_password" \ --user "$_username":"$_password" \
"${dyndns_update_url}" ) "${dyndns_update_url}" )
fi fi
case $response in case $_response in
good* ) good* )
if [[ $response == "good 127.0.0.1" ]]; then if [[ $_response == "good 127.0.0.1" ]]; then
echoerr "Error: $response: Request ignored." echoerr "Error: $_response: Request ignored."
return 1
else else
echo "$response: The DynDNS update has been executed." echo "$_response: The DynDNS update has been executed."
_errorCounter=0
return 0
fi fi
return
;; ;;
nochg* ) nochg* )
echo "$response: Nothing has changed, IP addresses are still up to date." echo "$_response: Nothing has changed, IP addresses are still up to date."
return return 1
;; ;;
abuse ) abuse )
echoerr "Error: $response: Username is blocked due to abuse." echoerr "Error: $_response: Username is blocked due to abuse."
return return 1
;; ;;
badauth ) badauth | 401 )
echoerr "Error: $response: Invalid username password combination." echoerr "Error: $_response: Invalid username password combination."
return return 1
;; ;;
badagent ) badagent )
echoerr "Error: $response: Client disabled. Something is very wrong!" echoerr "Error: $_response: Client disabled. Something is very wrong!"
return return 1
;; ;;
!donator ) !donator )
echoerr "Error: $response: An update request was sent, including a feature that is not available to that particular user such as offline options." echoerr "Error: $_response: An update request was sent, including a feature that is not available to that particular user such as offline options."
return return 1
;; ;;
!yours ) !yours )
echoerr "Error: $response: The domain does not belong to your user account" echoerr "Error: $_response: The domain does not belong to your user account"
return return 1
;; ;;
notfqdn ) notfqdn )
echoerr "Error: $response: Hostname $_dyn_domain is invalid" echoerr "Error: $_response: Hostname $_dyn_domain is invalid"
return return 1
;; ;;
nohost ) nohost )
echoerr "Error: $response: Hostname supplied does not exist under specified account, enter new login credentials before performing an additional request." echoerr "Error: $_response: Hostname supplied does not exist under specified account, enter new login credentials before performing an additional request."
return return 1
;; ;;
numhost ) numhost )
echoerr "Error: $response: Too many hostnames have been specified for this update" echoerr "Error: $_response: Too many hostnames have been specified for this update"
return return 1
;; ;;
dnserr ) dnserr )
echoerr "Error: $response: There is an internal error in the dyndns update system" echoerr "Error: $_response: There is an internal error in the dyndns update system. Retry update no sooner than 30 minutes."
return return 1
;; ;;
911 ) 911 | 5* )
echoerr "Error: $response: A fatal error on provider side such as a database outage. Retry the update no sooner than 30 minutes." echoerr "Error: $_response: A fatal error on provider side such as a database outage. Retry update no sooner than 30 minutes."
return return 1
;; ;;
* ) * )
echo "$response" if [[ "$_response" == "$_status" ]]; then
return echoerr "Error: An unknown response code has been received. $_response"
return 1
else
echoerr "Error: unknown respnse code: $_response"
return 0
fi
;; ;;
esac esac
if [[ $response != good ]]; then
setStatus "$response" "$(date +%s)" $(( _counter += 1 )) "$_dyn_domain" "${_username}${_token}"
fi
} }
function setStatus() { function setStatus() {
echo "_status=$1; _eventTime=$2; _counter=$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
} }
# handle errors from past update requests # handle errors from past update requests
function checkStatus() { function checkStatus() {
case $_status in case $_status in
nohost ) nochg* )
if [[ _errorCounter -gt 1 ]]; then
echoerr "Error: The update client was spamming unnecessary update requests, something might be wrong with your IP-Check site."
echoerr "Fix your config an then delete $_statusFile"
return 1
fi
;;
nohost | !yours )
if [[ "$_statusHostname" == "$_dyn_domain" && ( "$_statusUsername" == "$_username" || $_statusUsername == "$_token" ) ]]; then if [[ "$_statusHostname" == "$_dyn_domain" && ( "$_statusUsername" == "$_username" || $_statusUsername == "$_token" ) ]]; then
echoerr "Error: Hostname supplied does not exist under specified account, enter new login credentials before performing an additional request." echoerr "Error: Hostname supplied does not exist under specified account, enter new login credentials before performing an additional request."
exit 1 return 1
else else
rm "$_statusFile" rm "$_statusFile"
fi fi
return return 0
;; ;;
badauth ) badauth | 401 )
if [[ "$_statusUsername" == "$_username" && "$_statusPassword" == "$_password" ]]; then if [[ "$_statusUsername" == "$_username" && "$_statusPassword" == "$_password" ]]; then
echoerr "Error: Invalid username password combination." echoerr "Error: Invalid username password combination."
exit 1 return 1
else else
rm "$_statusFile" rm "$_statusFile"
fi fi
return return 0
;; ;;
badagent ) badagent )
echoerr "Error: Client is deactivated by provider." echoerr "Error: Client is deactivated by provider."
echo "Fix your config and then manually remove $_statusFile to reset the client blockade" echo "Fix your config and then manually remove $_statusFile to reset the client blockade."
exit 1 echo "If it still fails file an issue at github or try another client :)"
return return 1
;; ;;
!donator ) !donator )
echoerr "Error: An update request was sent, including a feature that is not available to that particular user such as offline options." echoerr "Error: An update request was sent, including a feature that is not available to that particular user such as offline options."
echo "Fix your config and then manually remove $_statusFile to reset the client blockade" echo "Fix your config and then manually remove $_statusFile to reset the client blockade"
exit 1 echo "If it still fails file an issue at github or try another client :)"
return return 1
;; ;;
abuse ) abuse )
echoerr "Error: Username is blocked due to abuse." echoerr "Error: Username is blocked due to abuse."
echo "Fix your config and then manually remove $_statusFile to reset the client blockade" echo "Fix your config and then manually remove $_statusFile to reset the client blockade"
exit 1 echo "If it still fails file an issue at github or try another client :)"
return return 1
;; ;;
911 ) 911 | 5* )
delta=$(( $(date +%s) - _eventTime )) delta=$(( $(date +%s) - _eventTime ))
if [[ $delta -lt 1800 ]]; then if [[ $delta -lt 1800 ]]; then
echoerr "$_status: The provider currently has an fatal error. DynB will wait $(date --date=@$delta -u +%M) minutes for next update until 30 minutes have passed since last request" echoerr "$_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."
exit 1 return 1
else else
rm "$_statusFile" rm "$_statusFile"
fi fi
return return 0
;;
* )
if [[ _errorCounter -gt 1 ]]; then
echoerr "Error: An unknown response code has repeatedly been received. $_response"
return 1
else
return 0
fi
;; ;;
esac esac
} }
@@ -381,9 +407,6 @@ function ipHasChanged() {
fi fi
fi fi
if [[ "$remote_ip" == "$dns_ip" ]]; then
return 0
else
if [[ ${1} == 4 ]]; then if [[ ${1} == 4 ]]; then
_new_IPv4=$remote_ip _new_IPv4=$remote_ip
#echo "New IPv4: $_new_IPv4 old was: $dns_ip" #echo "New IPv4: $_new_IPv4 old was: $dns_ip"
@@ -391,6 +414,10 @@ function ipHasChanged() {
_new_IPv6=$remote_ip _new_IPv6=$remote_ip
#echo "New IPv6: $_new_IPv6 old was: $dns_ip" #echo "New IPv6: $_new_IPv6 old was: $dns_ip"
fi fi
if [[ "$remote_ip" == "$dns_ip" ]]; then
return 0
else
return 1 return 1
fi fi
} }
@@ -465,16 +492,20 @@ function processParameters() {
################## ##################
function checkDependencies() { function checkDependencies() {
[[ -x $(command -v curl 2> /dev/null) ]] || { ## If there will be more general dependencies use a loop
echo "This script depends on curl and it is not available." >&2 # for i in curl and some other stuff; do
exit 1 # if ! command -v $i >/dev/null 2>&1; then
} # echoerr "Error: could not find \"$i\", DynB depends on it. "
# exit 1
# fi
# done
[[ -x $(command -v jq 2> /dev/null) ]] || { [[ -x $(command -v jq 2> /dev/null) ]] || {
if [[ $_update_method != dyndns* ]]; then if [[ $_update_method != dyndns* ]]; then
echo "This script depends on jq and it is not available." >&2 echo "This script depends on jq and it is not available." >&2
exit 1 exit 1
fi fi
} }
# maybe replace this with matejak/argbash
[[ -x $(command -v getopt 2> /dev/null) ]] || { [[ -x $(command -v getopt 2> /dev/null) ]] || {
_has_getopt=false _has_getopt=false
} }
@@ -502,6 +533,9 @@ function doUnsets() {
unset _version unset _version
} }
#################
## MAIN method ##
#################
function dynb() { function dynb() {
## parameters and checks ## parameters and checks
checkDependencies checkDependencies
@@ -518,6 +552,7 @@ function dynb() {
fi fi
fi fi
if test -f "$_statusFile"; then if test -f "$_statusFile"; then
debugMessage "read previous status file"
# shellcheck disable=SC1090 # shellcheck disable=SC1090
source "$_statusFile" source "$_statusFile"
fi fi
@@ -571,15 +606,25 @@ function dynb() {
(( changed += $? )) (( changed += $? ))
fi fi
if [[ $changed -gt 0 ]]; then if [[ $changed -gt 0 ]]; then
dynupdate if checkStatus; then
debugMessage "checkStatus has no errors"
if dynupdate; then
debugMessage "DynDNS2 update success"
else
debugMessage "Save new status after dynupdate has failed"
setStatus "$_response" "$(date +%s)" $(( _errorCounter += 1 )) "$_dyn_domain" "${_username}${_token}"
fi
else
debugMessage "Skip DynDNS2 update, checkStatus fetched previous error."
fi
fi fi
fi fi
doUnsets doUnsets
return 0 return 0
} }
################## ######################
## MAIN section ## ## END MAIN section ##
################## ######################
dynb "${@}" dynb "${@}"
exit $? exit $?