237 lines
6 KiB
Bash
Executable file
237 lines
6 KiB
Bash
Executable file
#!/bin/bash
|
|
#
|
|
## petrified - bash client to update dynamic DNS at freedns.afraid.org
|
|
## Copyright (c) 2015 Troy Engel
|
|
## Version: 1.0.5
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
#
|
|
|
|
## One of these must exist - '-c <config>' ignores global/local entirely
|
|
CONF_GLOBAL=/etc/petrified.conf
|
|
CONF_LOCAL=~/.petrifiedrc
|
|
CONF_NAMED=""
|
|
|
|
# Check our arguments for a config file
|
|
while getopts ":c:" opt; do
|
|
case $opt in
|
|
c)
|
|
CONF_NAMED="$OPTARG"
|
|
;;
|
|
:)
|
|
echo "Option -$OPTARG requires the name of a config file." >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
## How to update DNS - DDNS_KEY is unique per domain
|
|
DDNS_URL=https://freedns.afraid.org/dynamic/update.php
|
|
DDNS_KEY=""
|
|
|
|
## Which URL to use to get an IP
|
|
# IPv4 icanhazip (http://major.io/icanhazip-com-faq/)
|
|
DDNS_CHECK="http://4.icanhazip.com"
|
|
# IPv6 icanhazip (http://major.io/icanhazip-com-faq/)
|
|
#DDNS_CHECK="http://6.icanhazip.com"
|
|
|
|
## How many seconds should curl wait when either checking your IP or
|
|
## trying to update the remote DNS
|
|
CURL_WAIT=10
|
|
|
|
## Report an internal IP instead of the public IP - useful if you're
|
|
## using dynamic DNS for machines on the internal network
|
|
LOCAL_MODE=0
|
|
# Specify which interface
|
|
LOCAL_IF=eth0
|
|
# Should we use IPv4 or IPv6
|
|
LOCAL_IV=4
|
|
|
|
## How to log - multiple supported
|
|
USE_JOURNAL=1
|
|
USE_SYSLOG=0
|
|
USE_STDOUT=0
|
|
USE_LOGFILE=0
|
|
|
|
## If USE_LOGFILE is 1, where to log
|
|
PET_LOG=/var/log/petrified.log
|
|
|
|
## Prevent race conditions, i.e. stuck crons piling up
|
|
USE_PID=1
|
|
PET_PID=/run/petrified.pid
|
|
|
|
## Save the IP from our last check
|
|
USE_LIP=1
|
|
PET_LIP=/var/cache/petrified/lastip.dat
|
|
|
|
## Dependencies:
|
|
# logger (util-linux)
|
|
# kill (util-linux)
|
|
# printf (coreutils)
|
|
# touch (coreutils)
|
|
# date (coreutils)
|
|
# stat (coreutils)
|
|
# cat (coreutils)
|
|
# rm (coreutils)
|
|
# bash (bash)
|
|
# ip (iproute2)
|
|
# curl (curl)
|
|
|
|
####
|
|
|
|
# Read in our configs
|
|
if [[ -z "${CONF_NAMED}" ]]; then
|
|
[[ -r "${CONF_GLOBAL}" ]] && source "${CONF_GLOBAL}"
|
|
[[ -r "${CONF_LOCAL}" ]] && source "${CONF_LOCAL}"
|
|
else
|
|
[[ -r "${CONF_NAMED}" ]] && source "${CONF_NAMED}"
|
|
fi
|
|
|
|
# Check that we have all the needed variables
|
|
if [[ -z "${DDNS_KEY}" ]]; then
|
|
echo "DDNS_KEY must be configured, exiting."
|
|
exit 1
|
|
fi
|
|
|
|
# Make sure that PET_LOG will work if required
|
|
if (( ${USE_LOGFILE} == 1 )); then
|
|
if [[ -z "${PET_LOG}" ]]; then
|
|
echo "USE_LOGFILE=1 but PET_LOG is an empty string, exiting."
|
|
exit 1
|
|
elif (( $(touch "${PET_LOG}" 2>/dev/null; echo $?;) != 0 )); then
|
|
echo "USE_LOGFILE=1 but cannot write to ${PET_LOG}, exiting."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Logging
|
|
logmsg () {
|
|
LMSG=$1
|
|
if (( ${USE_JOURNAL} == 1 )); then
|
|
printf "%s\n%s\n" "SYSLOG_IDENTIFIER=petrified" "MESSAGE=${LMSG}" | \
|
|
logger --journald
|
|
fi
|
|
if (( ${USE_SYSLOG} == 1 )); then
|
|
echo "${LMSG}" | logger -t petrified
|
|
fi
|
|
if (( ${USE_STDOUT} == 1 )); then
|
|
DTS=$(date +"%Y-%m-%d %H:%M:%S")
|
|
echo "[${DTS}] [petrified] ${LMSG}"
|
|
fi
|
|
if (( ${USE_LOGFILE} == 1 )); then
|
|
DTS=$(date +"%Y-%m-%d %H:%M:%S")
|
|
echo "[${DTS}] [petrified] ${LMSG}" >> "${PET_LOG}"
|
|
fi
|
|
return
|
|
}
|
|
|
|
# Security warning
|
|
statchk () {
|
|
_CFILE=$1
|
|
if [[ -f "${_CFILE}" ]] && [[ -r "${_CFILE}" ]]; then
|
|
if [[ $(stat -c "%a" "${_CFILE}") != 600 ]]; then
|
|
logmsg "Security warning: ${_CFILE} is readable but not mode 0600"
|
|
fi
|
|
fi
|
|
}
|
|
[[ -n "${CONF_GLOBAL}" ]] && statchk "${CONF_GLOBAL}"
|
|
[[ -n "${CONF_LOCAL}" ]] && statchk "${CONF_LOCAL}"
|
|
[[ -n "${CONF_NAMED}" ]] && statchk "${CONF_NAMED}"
|
|
|
|
# Make sure that PET_PID will work if required
|
|
if (( ${USE_PID} == 1 )); then
|
|
if [[ -z "${PET_PID}" ]]; then
|
|
logmsg "USE_PID=1 but PET_PID is an empty string, exiting."
|
|
exit 1
|
|
elif (( $(touch "${PET_PID}" 2>/dev/null; echo $?;) != 0 )); then
|
|
logmsg "USE_PID=1 but cannot write to ${PET_PID}, exiting."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# PID actions
|
|
if (( ${USE_PID} == 1 )); then
|
|
PIDNUM=$(cat "${PET_PID}" 2>/dev/null)
|
|
# check if we have a number
|
|
if [[ ${PIDMUM} =~ ^-?[0-9]+$ ]]; then
|
|
kill -0 ${PIDNUM}
|
|
if (( $? == 0 )); then
|
|
logmsg "Detected a running process ${PIDNUM}, exiting."
|
|
exit 1
|
|
else
|
|
logmsg "Stale PID ${PIDNUM} detected, overwriting."
|
|
fi
|
|
fi
|
|
echo ${BASHPID} > "${PET_PID}"
|
|
fi
|
|
|
|
# Cleanup actions
|
|
cleanup () {
|
|
(( ${USE_PID} == 1 )) && (rm -f "${PET_PID}" 1>/dev/null 2>&1)
|
|
(( ${USE_LIP} == 0 )) && (rm -f "${PET_LIP}" 1>/dev/null 2>&1)
|
|
return
|
|
}
|
|
|
|
# Trap the basic signals
|
|
sigexit () {
|
|
logmsg "Trapped a kill signal, exiting."
|
|
cleanup
|
|
exit 3
|
|
}
|
|
trap sigexit SIGHUP SIGINT SIGTERM
|
|
|
|
# Get our IP address
|
|
NEWIP=""
|
|
if (( ${LOCAL_MODE} == 1 )); then
|
|
NEWIP=$(ip -${LOCAL_IV} -o addr show dev ${LOCAL_IF} primary 2>/dev/null)
|
|
NEWIP=${NEWIP%%/*}
|
|
NEWIP=${NEWIP##* }
|
|
else
|
|
NEWIP=$(curl -m ${CURL_WAIT} -s ${DDNS_CHECK} 2>/dev/null)
|
|
fi
|
|
if [[ -z "${NEWIP}" ]]; then
|
|
logmsg "Error getting an IP address, exiting."
|
|
cleanup
|
|
exit 1
|
|
fi
|
|
|
|
# Check our saved IP against the new IP
|
|
OLDIP="0"
|
|
if (( ${USE_LIP} == 1 )); then
|
|
OLDIP=$(cat "${PET_LIP}" 2>/dev/null)
|
|
fi
|
|
|
|
# If they don't match, tell upstream
|
|
if [[ "${OLDIP}" != "${NEWIP}" ]]; then
|
|
UPDURL="${DDNS_URL}?${DDNS_KEY}&address=${NEWIP}"
|
|
RESULT=$(curl -m ${CURL_WAIT} -sk "${UPDURL}" 2>/dev/null)
|
|
logmsg "${RESULT}"
|
|
else
|
|
logmsg "IP ${NEWIP} hasn't changed, not updating."
|
|
fi
|
|
|
|
# Save the new IP if configured
|
|
if (( ${USE_LIP} == 1 )); then
|
|
if (( $(touch "${PET_LIP}" 2>/dev/null; echo $?;) != 0 )); then
|
|
logmsg "USE_LIP=1 but cannot write to ${PET_LIP}."
|
|
else
|
|
echo "${NEWIP}" > "${PET_LIP}"
|
|
fi
|
|
fi
|
|
|
|
# Take out the trash
|
|
cleanup
|
|
|
|
exit 0
|