Completely rewritten. POSIX compliant, easier to use and maintain
This commit is contained in:
parent
3030c5375e
commit
802ebd0d12
288
nsupdate.sh
288
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)
|
# Update a nameserver entry at inwx with the current WAN IP (DynDNS)
|
||||||
|
|
||||||
@ -24,84 +24,97 @@
|
|||||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
# check required tools
|
# Print and log messages when verbose mode is on
|
||||||
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; }
|
# Usage: chat [0|1|2|3] MESSAGE
|
||||||
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; }
|
## 0 = regular output
|
||||||
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; }
|
## 1 = error messages
|
||||||
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";}
|
## 2 = verbose messages
|
||||||
|
## 3 = debug messages
|
||||||
|
|
||||||
LOG=$0.log
|
chat () {
|
||||||
SILENT=NO
|
messagetype=$1
|
||||||
|
message=$2
|
||||||
|
log=$nsupdate_log_dir/$nsupdate_log_file
|
||||||
|
log_date=$(date "+$log_date_format")
|
||||||
|
|
||||||
# Check if there are any usable config files
|
if [ $messagetype = 0 ]; then
|
||||||
if ls $(dirname $0)/nsupdate.d/*.config &> /dev/null; then
|
echo "[$log_date] [INFO] $message" | tee -a $log ;
|
||||||
# Loop through configs
|
fi
|
||||||
for f in $(dirname $0)/nsupdate.d/*.config
|
#
|
||||||
do
|
if [ $messagetype = 1 ]; then
|
||||||
# Config files could be much cleaner by containing only relevant settings.
|
echo "[$log_date] [ERROR] $message" | tee -a $log ; exit 1;
|
||||||
# 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"
|
|
||||||
|
|
||||||
source $f
|
|
||||||
|
|
||||||
if [[ "$SILENT" == "NO" ]]; then
|
|
||||||
echo "Starting nameserver update with config file $f ($LOG)"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$TYPE" == "A" ]]; then
|
if [ $messagetype = 2 ] && [ "$VERBOSE" = true ]; then
|
||||||
CONNECTION_TYPE=4
|
echo "[$log_date] [INFO] $message" | tee -a $log
|
||||||
fi
|
fi
|
||||||
|
|
||||||
## Set record type to MX
|
if [ $messagetype = 3 ] && [ "$DEBUG" = true ]; then
|
||||||
if [[ "$MX" == "YES" ]]; then
|
echo "[$log_date] [DEBUG] $message" | tee -a $log
|
||||||
TYPE=MX
|
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
|
fi
|
||||||
|
|
||||||
## Set record type to IPv6
|
nsupdate_confd_dir="${NSUPDATE_CONFD_DIR:-$nsupdate_conf_dir/conf.d}"
|
||||||
if [[ "$IPV6" == "YES" ]]; then
|
log_date_format="${LOG_DATE_FORMAT:-%Y-%m-%d %H:%M:%S}"
|
||||||
TYPE=AAAA
|
nsupdate_log_dir="${NSUPDATE_LOG_DIR:-/var/log/nsupdate}"
|
||||||
fi
|
nsupdate_log_file="${NSUPDATE_LOG_FILE:-nsupdate.log}"
|
||||||
|
tmp_dir="${NSUPDATE_TMP_DIR:-/tmp}"
|
||||||
|
nsupdate_conf_extension="${NSUPDATE_CONF_EXTENSION:-.conf}"
|
||||||
|
|
||||||
if [[ "$TYPE" == "AAAA" ]]; then
|
inwx_api="https://api.domrobot.com/xmlrpc/"
|
||||||
CONNECTION_TYPE=6
|
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)'
|
||||||
fi
|
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}"
|
||||||
|
|
||||||
if [[ "$USE_DRILL" == "YES" ]]; then
|
## Get WAN IP 4
|
||||||
if [[ "$TYPE" == "MX" ]]; then
|
wan_ip4="$(curl -s -4 ${ip_check_site})"
|
||||||
echo looking up MX records with drill currently not supported!
|
chat 2 "WAN IP 4: ${wan_ip4}"
|
||||||
exit 1
|
|
||||||
|
## 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
|
else
|
||||||
NSLOOKUP=$(drill $DOMAIN @ns.inwx.de $TYPE | head -7 | tail -1 | awk '{print $5}')
|
wan_ip="${wan_ip4}"
|
||||||
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
|
fi
|
||||||
|
|
||||||
# WAN_IP=`curl -s -$CONNECTION_TYPE ${IP_CHECK_SITE}| grep -Eo '\<[[:digit:]]{1,3}(\.[[:digit:]]{1,3}){3}\>'`
|
chat 3 "Found xmllint. Using curl for retrieving data from INWX API."
|
||||||
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.
|
inwx_api_xml_info="<?xml version=\"1.0\"?>
|
||||||
API_XML_INFO="<?xml version=\"1.0\"?>
|
|
||||||
<methodCall>
|
<methodCall>
|
||||||
<methodName>nameserver.info</methodName>
|
<methodName>nameserver.info</methodName>
|
||||||
<params>
|
<params>
|
||||||
@ -111,7 +124,7 @@ if ls $(dirname $0)/nsupdate.d/*.config &> /dev/null; then
|
|||||||
<member>
|
<member>
|
||||||
<name>user</name>
|
<name>user</name>
|
||||||
<value>
|
<value>
|
||||||
<string>$INWX_USER</string>
|
<string>${inwx_user}</string>
|
||||||
</value>
|
</value>
|
||||||
</member>
|
</member>
|
||||||
<member>
|
<member>
|
||||||
@ -123,25 +136,25 @@ if ls $(dirname $0)/nsupdate.d/*.config &> /dev/null; then
|
|||||||
<member>
|
<member>
|
||||||
<name>pass</name>
|
<name>pass</name>
|
||||||
<value>
|
<value>
|
||||||
<string>$INWX_PASS</string>
|
<string>${inwx_password}</string>
|
||||||
</value>
|
</value>
|
||||||
</member>
|
</member>
|
||||||
<member>
|
<member>
|
||||||
<name>domain</name>
|
<name>domain</name>
|
||||||
<value>
|
<value>
|
||||||
<string>$MAIN_DOMAIN</string>
|
<string>${main_domain}</string>
|
||||||
</value>
|
</value>
|
||||||
</member>
|
</member>
|
||||||
<member>
|
<member>
|
||||||
<name>name</name>
|
<name>name</name>
|
||||||
<value>
|
<value>
|
||||||
<string>$DOMAIN</string>
|
<string>${domain}</string>
|
||||||
</value>
|
</value>
|
||||||
</member>
|
</member>
|
||||||
<member>
|
<member>
|
||||||
<name>type</name>
|
<name>type</name>
|
||||||
<value>
|
<value>
|
||||||
<string>$TYPE</string>
|
<string>${record_type}</string>
|
||||||
</value>
|
</value>
|
||||||
</member>
|
</member>
|
||||||
</struct>
|
</struct>
|
||||||
@ -150,25 +163,45 @@ if ls $(dirname $0)/nsupdate.d/*.config &> /dev/null; then
|
|||||||
</params>
|
</params>
|
||||||
</methodCall>"
|
</methodCall>"
|
||||||
|
|
||||||
# The full xpath is
|
## Get domain info from INWX API and save it to a temporary file
|
||||||
# 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)'
|
curl -s -X POST ${inwx_api} -H "Content-Type: application/xml" -d "${inwx_api_xml_info}" -o ${tmp_dir}/${tmp_file}
|
||||||
# A short version of the xpath
|
|
||||||
XPATH='string(//member[name="id"]/value/int/text())'
|
## Extract ID and IP from INWX data
|
||||||
if [[ "$NO_XMLLINT" != "true" ]]; then
|
inwx_domain_ip="$(xmllint --xpath ${inwx_api_xpath_ip} ${tmp_dir}/${tmp_file})"
|
||||||
if [[ "$NSLOOKUP" != "$WAN_IP" ]]; then
|
inwx_domain_id="$(xmllint --xpath ${inwx_api_xpath_id} ${tmp_dir}/${tmp_file})"
|
||||||
if [[ "$INWX_DOMAIN_ID" == "unset" ]]; then
|
|
||||||
INWX_DOMAIN_ID=$(curl -s -X POST https://api.domrobot.com/xmlrpc/ \
|
## Remove domain info tmp file
|
||||||
-H "Content-Type: application/xml" \
|
rm ${tmp_dir}/${tmp_file}
|
||||||
-d "$API_XML_INFO" \
|
else
|
||||||
| xmllint --xpath $XPATH -)
|
## Check if nslookup is installed and use it to get the IP
|
||||||
if [[ "$SILENT" == "NO" ]]; then
|
if command -v nslookup > /dev/null 2>&1; then
|
||||||
echo $(printf "%s - The %s-Type Record-ID of %s is: %s" "$(date)" "$TYPE" "$DOMAIN" "$INWX_DOMAIN_ID")>>$LOG
|
chat 3 "Found nslookup. Using it for IP from INWX nameserver."
|
||||||
fi
|
inwx_domain_ip=$(nslookup -sil -type=${record_type} ${domain} - ${inwx_nameserver} | tail -2 | head -1 | cut -d' ' -f2)
|
||||||
fi
|
## Check if drill is installed and use it to get the IP
|
||||||
fi
|
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
|
fi
|
||||||
|
|
||||||
API_XML_UPDATE_RECORD="<?xml version=\"1.0\"?>
|
## 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="<?xml version=\"1.0\"?>
|
||||||
<methodCall>
|
<methodCall>
|
||||||
<methodName>nameserver.updateRecord</methodName>
|
<methodName>nameserver.updateRecord</methodName>
|
||||||
<params>
|
<params>
|
||||||
@ -178,7 +211,7 @@ if ls $(dirname $0)/nsupdate.d/*.config &> /dev/null; then
|
|||||||
<member>
|
<member>
|
||||||
<name>user</name>
|
<name>user</name>
|
||||||
<value>
|
<value>
|
||||||
<string>$INWX_USER</string>
|
<string>${inwx_user}</string>
|
||||||
</value>
|
</value>
|
||||||
</member>
|
</member>
|
||||||
<member>
|
<member>
|
||||||
@ -190,25 +223,25 @@ if ls $(dirname $0)/nsupdate.d/*.config &> /dev/null; then
|
|||||||
<member>
|
<member>
|
||||||
<name>pass</name>
|
<name>pass</name>
|
||||||
<value>
|
<value>
|
||||||
<string>$INWX_PASS</string>
|
<string>${inwx_password}</string>
|
||||||
</value>
|
</value>
|
||||||
</member>
|
</member>
|
||||||
<member>
|
<member>
|
||||||
<name>id</name>
|
<name>id</name>
|
||||||
<value>
|
<value>
|
||||||
<int>$INWX_DOMAIN_ID</int>
|
<int>${inwx_domain_id}</int>
|
||||||
</value>
|
</value>
|
||||||
</member>
|
</member>
|
||||||
<member>
|
<member>
|
||||||
<name>content</name>
|
<name>content</name>
|
||||||
<value>
|
<value>
|
||||||
<string>$WAN_IP</string>
|
<string>${wan_ip}</string>
|
||||||
</value>
|
</value>
|
||||||
</member>
|
</member>
|
||||||
<member>
|
<member>
|
||||||
<name>ttl</name>
|
<name>ttl</name>
|
||||||
<value>
|
<value>
|
||||||
<int>$TTL</int>
|
<int>${record_ttl}</int>
|
||||||
</value>
|
</value>
|
||||||
</member>
|
</member>
|
||||||
</struct>
|
</struct>
|
||||||
@ -217,35 +250,60 @@ if ls $(dirname $0)/nsupdate.d/*.config &> /dev/null; then
|
|||||||
</params>
|
</params>
|
||||||
</methodCall>"
|
</methodCall>"
|
||||||
|
|
||||||
if [[ "$NSLOOKUP" != "$WAN_IP" ]]; then
|
curl -s -X POST "${inwx_api}" -H "Content-Type: application/xml" -d "${inwx_api_xml_update_record}"
|
||||||
curl -s -X POST https://api.domrobot.com/xmlrpc/ \
|
}
|
||||||
-H "Content-Type: application/xml" \
|
|
||||||
-d "$API_XML_UPDATE_RECORD"
|
|
||||||
|
|
||||||
if [[ "$SILENT" == "NO" ]]; then
|
## Initalize nsupdate
|
||||||
echo "$(date) - $DOMAIN updated. Old IP: "$NSLOOKUP "New IP: "$WAN_IP >> $LOG
|
init
|
||||||
fi
|
|
||||||
|
# Check if there are any usable config files
|
||||||
|
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}"
|
||||||
|
|
||||||
|
## 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}"
|
||||||
|
|
||||||
|
## Get domain info
|
||||||
|
get_domain_info
|
||||||
|
|
||||||
|
## 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}"
|
||||||
|
|
||||||
|
## 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
|
else
|
||||||
if [[ "$SILENT" == "NO" ]]; then
|
chat 0 "No update required for ${domain} [${record_type}]."
|
||||||
echo "$(date) - No update needed for $DOMAIN. Current IP: "$NSLOOKUP >> $LOG
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
unset TYPE
|
## Clean up variables for a fresh start
|
||||||
|
unset INWX_USER
|
||||||
|
unset INWX_PASSWORD
|
||||||
unset MAIN_DOMAIN
|
unset MAIN_DOMAIN
|
||||||
unset DOMAIN
|
unset DOMAIN
|
||||||
unset IPV6
|
unset RECORD_TYPE
|
||||||
unset MX
|
unset RECORD_TTL
|
||||||
unset WAN_IP
|
unset tmp_file
|
||||||
unset TTL
|
unset inwx_domain_id
|
||||||
unset NSLOOKUP
|
unset inwx_domain_ip
|
||||||
unset INWX_PASS
|
unset wan_ip
|
||||||
unset INWX_USER
|
|
||||||
unset INWX_DOMAIN_ID
|
|
||||||
unset API_XML_UPDATE_RECORD
|
|
||||||
unset API_XML_INFO
|
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
echo "There does not seem to be any config file available in $(dirname $0)/nsupdate.d/."
|
chat 1 "Couldn't find any usable config files. Check installation instructions."
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
Loading…
x
Reference in New Issue
Block a user