diff --git a/nsupdate.sh b/nsupdate.sh
index 9d43955..2608c79 100755
--- a/nsupdate.sh
+++ b/nsupdate.sh
@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+#!/usr/bin/env sh
# Update a nameserver entry at inwx with the current WAN IP (DynDNS)
@@ -24,228 +24,286 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-# check required tools
-command -v curl &> /dev/null || { echo >&2 "I require curl but it's not installed. Note: all needed items are listed in the README.md file."; exit 1; }
-command -v awk &> /dev/null || { echo >&2 "I require awk but it's not installed. Note: all needed items are listed in the README.md file."; exit 1; }
-command -v drill &> /dev/null || command -v nslookup &> /dev/null || { echo >&2 "I need drill or nslookup installed. Note: all needed items are listed in the README.md file."; exit 1; }
-command -v xmllint &> /dev/null || { echo >&2 "I recommend xmllint but it's not installed. Note: all needed items are listed in the README.md file."; NO_XMLLINT="true";}
+# Print and log messages when verbose mode is on
+# Usage: chat [0|1|2|3] MESSAGE
+## 0 = regular output
+## 1 = error messages
+## 2 = verbose messages
+## 3 = debug messages
-LOG=$0.log
-SILENT=NO
+chat () {
+ messagetype=$1
+ message=$2
+ log=$nsupdate_log_dir/$nsupdate_log_file
+ log_date=$(date "+$log_date_format")
+
+ if [ $messagetype = 0 ]; then
+ echo "[$log_date] [INFO] $message" | tee -a $log ;
+ fi
+ #
+ if [ $messagetype = 1 ]; then
+ echo "[$log_date] [ERROR] $message" | tee -a $log ; exit 1;
+ fi
+
+ if [ $messagetype = 2 ] && [ "$VERBOSE" = true ]; then
+ echo "[$log_date] [INFO] $message" | tee -a $log
+ fi
+
+ if [ $messagetype = 3 ] && [ "$DEBUG" = true ]; then
+ echo "[$log_date] [DEBUG] $message" | tee -a $log
+ fi
+}
+
+# Load config file, set default variables and get wan IP
+# Usage: init
+init () {
+
+ nsupdate_conf_file="nsupdate.conf"
+ basedir="${BASEDIR:-/usr/local/etc}"
+ nsupdate_conf_dir="${NSUPDATE_CONF_DIR:-$basedir/nsupdate}"
+
+ ## Try to load nsupdate.conf
+ [ -f "./$nsupdate_conf_file" ] && . ./$nsupdate_conf_file
+ [ -f "$nsupdate_conf_dir/$nsupdate_conf_file" ] && . $nsupdate_conf_dir/$nsupdate_conf_file
+
+ VERBOSE="${VERBOSE:-false}"
+ DEBUG="${DEBUG:-false}"
+
+ if [ "$DEBUG" = true ]; then
+ set -x
+ fi
+
+ nsupdate_confd_dir="${NSUPDATE_CONFD_DIR:-$nsupdate_conf_dir/conf.d}"
+ log_date_format="${LOG_DATE_FORMAT:-%Y-%m-%d %H:%M:%S}"
+ nsupdate_log_dir="${NSUPDATE_LOG_DIR:-/var/log/nsupdate}"
+ nsupdate_log_file="${NSUPDATE_LOG_FILE:-nsupdate.log}"
+ tmp_dir="${NSUPDATE_TMP_DIR:-/tmp}"
+ nsupdate_conf_extension="${NSUPDATE_CONF_EXTENSION:-.conf}"
+
+ inwx_api="https://api.domrobot.com/xmlrpc/"
+ inwx_api_xpath_ip='string(/methodResponse/params/param/value/struct/member[name="resData"]/value/struct/member[name="record"]/value/array/data/value/struct/member[name="content"]/value/string)'
+ inwx_api_xpath_id='string(/methodResponse/params/param/value/struct/member[name="resData"]/value/struct/member[name="record"]/value/array/data/value/struct/member[name="id"]/value/int)'
+ inwx_nameserver="ns.inwx.de"
+ ip_check_site="${NSUPDATE_IP_CHECK_SITE:-https://api64.ipify.org}"
+
+ ## Get WAN IP 4
+ wan_ip4="$(curl -s -4 ${ip_check_site})"
+ chat 2 "WAN IP 4: ${wan_ip4}"
+
+ ## Get WAN IP 6
+ wan_ip6="$(curl -s -6 ${ip_check_site})"
+ chat 2 "WAN IP 6: ${wan_ip6}"
+}
+
+# Get the data for a given domain
+# Usage: get_inwx_domain_id
+get_domain_info () {
+
+ ## Check if xmllint is installed and use it.
+ if command -v xmllint > /dev/null 2>&1; then
+
+ ## File name for tempory file to store the XML from API
+ tmp_file="${domain}_${record_type}_$(date +%s).xml"
+
+ ## Get connection type by record type
+ if [ "${record_type}" = "AAAA" ]; then
+ wan_ip="${wan_ip6}"
+ else
+ wan_ip="${wan_ip4}"
+ fi
+
+ chat 3 "Found xmllint. Using curl for retrieving data from INWX API."
+
+ inwx_api_xml_info="
+
+ nameserver.info
+
+
+
+
+
+ user
+
+ ${inwx_user}
+
+
+
+ lang
+
+ en
+
+
+
+ pass
+
+ ${inwx_password}
+
+
+
+ domain
+
+ ${main_domain}
+
+
+
+ name
+
+ ${domain}
+
+
+
+ type
+
+ ${record_type}
+
+
+
+
+
+
+ "
+
+ ## Get domain info from INWX API and save it to a temporary file
+ curl -s -X POST ${inwx_api} -H "Content-Type: application/xml" -d "${inwx_api_xml_info}" -o ${tmp_dir}/${tmp_file}
+
+ ## Extract ID and IP from INWX data
+ inwx_domain_ip="$(xmllint --xpath ${inwx_api_xpath_ip} ${tmp_dir}/${tmp_file})"
+ inwx_domain_id="$(xmllint --xpath ${inwx_api_xpath_id} ${tmp_dir}/${tmp_file})"
+
+ ## Remove domain info tmp file
+ rm ${tmp_dir}/${tmp_file}
+ else
+ ## Check if nslookup is installed and use it to get the IP
+ if command -v nslookup > /dev/null 2>&1; then
+ chat 3 "Found nslookup. Using it for IP from INWX nameserver."
+ inwx_domain_ip=$(nslookup -sil -type=${record_type} ${domain} - ${inwx_nameserver} | tail -2 | head -1 | cut -d' ' -f2)
+ ## Check if drill is installed and use it to get the IP
+ else command -v drill > /dev/null 2>&1;
+ chat 3 "Found drill. Using it for IP from INWX nameserver."
+ inwx_domain_ip=$(drill ${domain} @${inwx_nameserver} ${record_type} | head -7 | tail -1 | cut -f2 -d$'\t' -f5)
+ fi
+
+ ## Set domain ID from config file
+ chat 3 "Trying to get domain ID from config file."
+ inwx_domain_id="${INWX_DOMAIN_ID}"
+ fi
+
+ if [ -z "$inwx_domain_ip" ]; then
+ chat 1 "Couldn't get current IP. please check the installation instructions."
+ fi
+
+ if [ -z "$inwx_domain_id" ]; then
+ chat 1 "Couldn't find domain ID. please check the installation instructions."
+ fi
+}
+
+# Update a dns record
+# Usage: update_record
+update_record () {
+ chat 3 "Using curl to update the DNS record with INWX API."
+ inwx_api_xml_update_record="
+
+ nameserver.updateRecord
+
+
+
+
+
+ user
+
+ ${inwx_user}
+
+
+
+ lang
+
+ en
+
+
+
+ pass
+
+ ${inwx_password}
+
+
+
+ id
+
+ ${inwx_domain_id}
+
+
+
+ content
+
+ ${wan_ip}
+
+
+
+ ttl
+
+ ${record_ttl}
+
+
+
+
+
+
+ "
+
+ curl -s -X POST "${inwx_api}" -H "Content-Type: application/xml" -d "${inwx_api_xml_update_record}"
+}
+
+## Initalize nsupdate
+init
# Check if there are any usable config files
-if ls $(dirname $0)/nsupdate.d/*.config &> /dev/null; then
- # Loop through configs
- for f in $(dirname $0)/nsupdate.d/*.config
- do
- # Config files could be much cleaner by containing only relevant settings.
- # If your User and Password is always the same just set it here once and delete it in the config files.
- #INWX_USER="Username"
- #INWX_PASS="Password"
- # Resets previous set variables to catch wrong or not configured settings and set defaults.
- MAIN_DOMAIN=
- DOMAIN=
- INWX_DOMAIN_ID="unset"
- TTL=300
- TYPE=A
- CONNECTION_TYPE=4
- # For backward compatability the following options remain in this script
- IPV6="NO"
- MX="NO"
+if ls ${nsupdate_confd_dir}/*${nsupdate_conf_extension} > /dev/null 2>&1; then
+ # Loop through config files
+ for f in ${nsupdate_confd_dir}/*${nsupdate_conf_extension}
+ do
+ . ${f}
+ chat 2 "Loading config file ${f}"
- source $f
+ ## Get variables from config file
+ inwx_user="${INWX_USER:-$NSUPDATE_INWX_USER}"
+ inwx_password="${INWX_PASSWORD:-$NSUPDATE_INWX_PASSWORD}"
+ main_domain="${MAIN_DOMAIN}"
+ domain="${DOMAIN}"
+ RECORD_TYPE="${TYPE:-$RECORD_TYPE}" ## For backwards compatibility in config files
+ RECORD_TTL="${TTL:-$RECORD_TTL}" ## For backwards compatibility in config files
+ record_type="${RECORD_TYPE:-$NSUPDATE_RECORD_TYPE}"
+ record_ttl="${RECORD_TTL:-$NSUPDATE_RECORD_TTL}"
- if [[ "$SILENT" == "NO" ]]; then
- echo "Starting nameserver update with config file $f ($LOG)"
- fi
+ ## Get domain info
+ get_domain_info
- if [[ "$TYPE" == "A" ]]; then
- CONNECTION_TYPE=4
- fi
+ ## Verbose output
+ chat 2 "DOMAIN: ${domain}"
+ chat 2 "RECORD TYPE: ${record_type}"
+ chat 2 "RECORD TTL: ${record_ttl}"
+ chat 2 "INWX DOMAIN ID: ${inwx_domain_id}"
+ chat 2 "INWX IP: ${inwx_domain_ip}"
- ## Set record type to MX
- if [[ "$MX" == "YES" ]]; then
- TYPE=MX
- fi
+ ## Check if record needs an update and do it
+ if [ "${inwx_domain_ip}" != "${wan_ip}" ]; then
+ chat 0 "Updating DNS record for ${domain} [${record_type}]. Old IP: ${inwx_domain_ip}. New IP: ${wan_ip}."
+ update_record ${inwx_user} ${inwx_password} ${inwx_domain_id} ${wan_ip} ${record_ttl}
+ else
+ chat 0 "No update required for ${domain} [${record_type}]."
+ fi
- ## Set record type to IPv6
- if [[ "$IPV6" == "YES" ]]; then
- TYPE=AAAA
- fi
-
- if [[ "$TYPE" == "AAAA" ]]; then
- CONNECTION_TYPE=6
- fi
-
- if [[ "$USE_DRILL" == "YES" ]]; then
- if [[ "$TYPE" == "MX" ]]; then
- echo looking up MX records with drill currently not supported!
- exit 1
- else
- NSLOOKUP=$(drill $DOMAIN @ns.inwx.de $TYPE | head -7 | tail -1 | awk '{print $5}')
- fi
- else
- if [[ "$TYPE" == "MX" ]]; then
- PART_NSLOOKUP=$(nslookup -sil -type=$TYPE $DOMAIN - ns.inwx.de | tail -2 | head -1 | cut -d' ' -f5)
- NSLOOKUP=${PART_NSLOOKUP%"."}
- else
- NSLOOKUP=$(nslookup -sil -type=$TYPE $DOMAIN - ns.inwx.de | tail -2 | head -1 | cut -d' ' -f2)
- fi
- fi
-
- # WAN_IP=`curl -s -$CONNECTION_TYPE ${IP_CHECK_SITE}| grep -Eo '\<[[:digit:]]{1,3}(\.[[:digit:]]{1,3}){3}\>'`
- if [[ -z ${IPCOMMAND} ]]; then
- WAN_IP=$(curl -s -$CONNECTION_TYPE ${IP_CHECK_SITE})
- else
- WAN_IP=$($IPCOMMAND)
- fi
-
- # This is relevant for getting the specific domain record id.
- API_XML_INFO="
-
- nameserver.info
-
-
-
-
-
- user
-
- $INWX_USER
-
-
-
- lang
-
- en
-
-
-
- pass
-
- $INWX_PASS
-
-
-
- domain
-
- $MAIN_DOMAIN
-
-
-
- name
-
- $DOMAIN
-
-
-
- type
-
- $TYPE
-
-
-
-
-
-
- "
-
- # The full xpath is
- # XPATH='string(/methodResponse/params/param/value/struct/member[name="resData"]/value/struct/member[name="record"]/value/array/data/value/struct/member[name="id"]/value/int)'
- # A short version of the xpath
- XPATH='string(//member[name="id"]/value/int/text())'
- if [[ "$NO_XMLLINT" != "true" ]]; then
- if [[ "$NSLOOKUP" != "$WAN_IP" ]]; then
- if [[ "$INWX_DOMAIN_ID" == "unset" ]]; then
- INWX_DOMAIN_ID=$(curl -s -X POST https://api.domrobot.com/xmlrpc/ \
- -H "Content-Type: application/xml" \
- -d "$API_XML_INFO" \
- | xmllint --xpath $XPATH -)
- if [[ "$SILENT" == "NO" ]]; then
- echo $(printf "%s - The %s-Type Record-ID of %s is: %s" "$(date)" "$TYPE" "$DOMAIN" "$INWX_DOMAIN_ID")>>$LOG
- fi
- fi
- fi
- fi
-
- API_XML_UPDATE_RECORD="
-
- nameserver.updateRecord
-
-
-
-
-
- user
-
- $INWX_USER
-
-
-
- lang
-
- en
-
-
-
- pass
-
- $INWX_PASS
-
-
-
- id
-
- $INWX_DOMAIN_ID
-
-
-
- content
-
- $WAN_IP
-
-
-
- ttl
-
- $TTL
-
-
-
-
-
-
- "
-
- if [[ "$NSLOOKUP" != "$WAN_IP" ]]; then
- curl -s -X POST https://api.domrobot.com/xmlrpc/ \
- -H "Content-Type: application/xml" \
- -d "$API_XML_UPDATE_RECORD"
-
- if [[ "$SILENT" == "NO" ]]; then
- echo "$(date) - $DOMAIN updated. Old IP: "$NSLOOKUP "New IP: "$WAN_IP >> $LOG
- fi
- else
- if [[ "$SILENT" == "NO" ]]; then
- echo "$(date) - No update needed for $DOMAIN. Current IP: "$NSLOOKUP >> $LOG
- fi
- fi
-
- unset TYPE
- unset MAIN_DOMAIN
- unset DOMAIN
- unset IPV6
- unset MX
- unset WAN_IP
- unset TTL
- unset NSLOOKUP
- unset INWX_PASS
- unset INWX_USER
- unset INWX_DOMAIN_ID
- unset API_XML_UPDATE_RECORD
- unset API_XML_INFO
- done
+ ## Clean up variables for a fresh start
+ unset INWX_USER
+ unset INWX_PASSWORD
+ unset MAIN_DOMAIN
+ unset DOMAIN
+ unset RECORD_TYPE
+ unset RECORD_TTL
+ unset tmp_file
+ unset inwx_domain_id
+ unset inwx_domain_ip
+ unset wan_ip
+ done
else
- echo "There does not seem to be any config file available in $(dirname $0)/nsupdate.d/."
- exit 1
-fi
+ chat 1 "Couldn't find any usable config files. Check installation instructions."
+fi
\ No newline at end of file