diff --git a/advanced/Scripts/utils.sh b/advanced/Scripts/utils.sh index cf24c098..a9e05692 100755 --- a/advanced/Scripts/utils.sh +++ b/advanced/Scripts/utils.sh @@ -71,28 +71,87 @@ removeKey() { } ####################### -# returns FTL's current telnet API port +# returns path of FTL's port file +####################### +getFTLAPIPortFile() { + local FTLCONFFILE="/etc/pihole/pihole-FTL.conf" + local DEFAULT_PORT_FILE="/run/pihole-FTL.port" + local FTL_APIPORT_FILE + + if [ -s "${FTLCONFFILE}" ]; then + # if PORTFILE is not set in pihole-FTL.conf, use the default path + FTL_APIPORT_FILE="$({ grep '^PORTFILE=' "${FTLCONFFILE}" || echo "${DEFAULT_PORT_FILE}"; } | cut -d'=' -f2-)" + else + # if there is no pihole-FTL.conf, use the default path + FTL_APIPORT_FILE="${DEFAULT_PORT_FILE}" + fi + + echo "${FTL_APIPORT_FILE}" +} + + +####################### +# returns FTL's current telnet API port based on the content of the pihole-FTL.port file +# +# Takes one argument: path to pihole-FTL.port +# Example getFTLAPIPort "/run/pihole-FTL.port" ####################### getFTLAPIPort(){ - local FTLCONFFILE="/etc/pihole/pihole-FTL.conf" - local DEFAULT_PORT_FILE="/run/pihole-FTL.port" - local DEFAULT_FTL_PORT=4711 - local PORTFILE - local ftl_api_port + local PORTFILE="${1}" + local DEFAULT_FTL_PORT=4711 + local ftl_api_port - if [ -f "$FTLCONFFILE" ]; then - # if PORTFILE is not set in pihole-FTL.conf, use the default path - PORTFILE="$( (grep "^PORTFILE=" $FTLCONFFILE || echo "$DEFAULT_PORT_FILE") | cut -d"=" -f2-)" - fi + if [ -s "$PORTFILE" ]; then + # -s: FILE exists and has a size greater than zero + ftl_api_port=$(cat "${PORTFILE}") + # Exploit prevention: unset the variable if there is malicious content + # Verify that the value read from the file is numeric + expr "$ftl_api_port" : "[^[:digit:]]" > /dev/null && unset ftl_api_port + fi - if [ -s "$PORTFILE" ]; then - # -s: FILE exists and has a size greater than zero - ftl_api_port=$(cat "${PORTFILE}") - # Exploit prevention: unset the variable if there is malicious content - # Verify that the value read from the file is numeric - expr "$ftl_api_port" : "[^[:digit:]]" > /dev/null && unset ftl_api_port - fi - - # echo the port found in the portfile or default to the default port - echo "${ftl_api_port:=$DEFAULT_FTL_PORT}" + # echo the port found in the portfile or default to the default port + echo "${ftl_api_port:=$DEFAULT_FTL_PORT}" +} + +####################### +# returns path of FTL's PID file +####################### +getFTLPIDFile() { + local FTLCONFFILE="/etc/pihole/pihole-FTL.conf" + local DEFAULT_PID_FILE="/run/pihole-FTL.pid" + local FTL_PID_FILE + + if [ -s "${FTLCONFFILE}" ]; then + # if PIDFILE is not set in pihole-FTL.conf, use the default path + FTL_PID_FILE="$({ grep '^PIDFILE=' "${FTLCONFFILE}" || echo "${DEFAULT_PID_FILE}"; } | cut -d'=' -f2-)" + else + # if there is no pihole-FTL.conf, use the default path + FTL_PID_FILE="${DEFAULT_PID_FILE}" + fi + + echo "${FTL_PID_FILE}" +} + +####################### +# returns FTL's PID based on the content of the pihole-FTL.pid file +# +# Takes one argument: path to pihole-FTL.pid +# Example getFTLPID "/run/pihole-FTL.pid" +####################### +getFTLPID() { + local FTL_PID_FILE="${1}" + local FTL_PID + + if [ -s "${FTL_PID_FILE}" ]; then + # -s: FILE exists and has a size greater than zero + FTL_PID="$(cat "${FTL_PID_FILE}")" + # Exploit prevention: unset the variable if there is malicious content + # Verify that the value read from the file is numeric + expr "${FTL_PID}" : "[^[:digit:]]" > /dev/null && unset FTL_PID + fi + + # If FTL is not running, or the PID file contains malicious stuff, substitute + # negative PID to signal this + FTL_PID=${FTL_PID:=-1} + echo "${FTL_PID}" } diff --git a/advanced/Templates/pihole-FTL.service b/advanced/Templates/pihole-FTL.service index f5abfcea..7346dc20 100644 --- a/advanced/Templates/pihole-FTL.service +++ b/advanced/Templates/pihole-FTL.service @@ -9,48 +9,10 @@ # Description: Enable service provided by pihole-FTL daemon ### END INIT INFO -# Global variables -FTLCONFFILE="/etc/pihole/pihole-FTL.conf" -DEFAULT_PID_FILE="/run/pihole-FTL.pid" -DEFAULT_PORT_FILE="/run/pihole-FTL.port" -FTL_PID='' - -# Get the file path of the pihole-FTL.pid file -getFTLPIDFile() { - if [ -s "${FTLCONFFILE}" ]; then - # if PIDFILE is not set in pihole-FTL.conf, use the default path - FTL_PID_FILE="$({ grep '^PIDFILE=' "${FTLCONFFILE}" || echo "${DEFAULT_PID_FILE}"; } | cut -d'=' -f2-)" - else - # if there is no pihole-FTL.conf, use the default path - FTL_PID_FILE="${DEFAULT_PID_FILE}" - fi -} - -# Get the PID of the FTL process based on the content of the pihole-FTL.pid file -getFTLPID() { - if [ -s "${FTL_PID_FILE}" ]; then - # -s: FILE exists and has a size greater than zero - FTL_PID="$(cat "${FTL_PID_FILE}")" - # Exploit prevention: unset the variable if there is malicious content - # Verify that the value read from the file is numeric - expr "${FTL_PID}" : "[^[:digit:]]" > /dev/null && unset FTL_PID - fi - - # If FTL is not running, or the PID file contains malicious stuff, substitute - # negative PID to signal this - FTL_PID=${FTL_PID:=-1} -} - -# Get the file path of the pihole-FTL.port file -getFTLPortFile() { - if [ -s "${FTLCONFFILE}" ]; then - # if PORTFILE is not set in pihole-FTL.conf, use the default path - FTL_PORT_FILE="$({ grep '^PORTFILE=' "${FTLCONFFILE}" || echo "${DEFAULT_PORT_FILE}"; } | cut -d'=' -f2-)" - else - # if there is no pihole-FTL.conf, use the default path - FTL_PORT_FILE="${DEFAULT_PORT_FILE}" -fi -} +#source utils.sh for getFTLPIDFile(), getFTLPID (), getFTLAPIPortFile() +PI_HOLE_SCRIPT_DIR="/opt/pihole" +utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh" +. "${utilsfile}" is_running() { @@ -148,11 +110,11 @@ status() { ### main logic ### # Get file paths -getFTLPIDFile -getFTLPortFile +FTL_PID_FILE="$(getFTLPIDFile)" +FTL_PORT_FILE="$(getFTLAPIPortFile)" # Get FTL's current PID -getFTLPID +FTL_PID="$(getFTLPID ${FTL_PID_FILE})" case "$1" in stop) diff --git a/pihole b/pihole index 35f6c07b..eb825965 100755 --- a/pihole +++ b/pihole @@ -16,7 +16,6 @@ readonly PI_HOLE_SCRIPT_DIR="/opt/pihole" # error due to modifying a readonly variable. setupVars="/etc/pihole/setupVars.conf" PI_HOLE_BIN_DIR="/usr/local/bin" -readonly FTL_PID_FILE="/run/pihole-FTL.pid" readonly colfile="${PI_HOLE_SCRIPT_DIR}/COL_TABLE" source "${colfile}" @@ -101,25 +100,8 @@ versionFunc() { exec "${PI_HOLE_SCRIPT_DIR}"/version.sh "$@" } -# Get PID of main pihole-FTL process -getFTLPID() { - local pid - - if [ -s "${FTL_PID_FILE}" ]; then - # -s: FILE exists and has a size greater than zero - pid="$(<"$FTL_PID_FILE")" - # Exploit prevention: unset the variable if there is malicious content - # Verify that the value read from the file is numeric - [[ "$pid" =~ [^[:digit:]] ]] && unset pid - fi - - # If FTL is not running, or the PID file contains malicious stuff, substitute - # negative PID to signal this to the caller - echo "${pid:=-1}" -} - restartDNS() { - local svcOption svc str output status pid icon + local svcOption svc str output status pid icon FTL_PID_FILE svcOption="${1:-restart}" # Determine if we should reload or restart @@ -128,7 +110,11 @@ restartDNS() { # Note 1: This will NOT re-read any *.conf files # Note 2: We cannot use killall here as it does # not know about real-time signals - pid="$(getFTLPID)" + + # get the current path to the pihole-FTL.pid + FTL_PID_FILE="$(getFTLPIDFile)" + + pid="$(getFTLPID ${FTL_PID_FILE})" if [[ "$pid" -eq "-1" ]]; then svc="true" str="FTL is not running" @@ -141,7 +127,7 @@ restartDNS() { elif [[ "${svcOption}" =~ "reload" ]]; then # Reloading of the DNS cache has been requested # Note: This will NOT re-read any *.conf files - pid="$(getFTLPID)" + pid="$(getFTLPID ${FTL_PID_FILE})" if [[ "$pid" -eq "-1" ]]; then svc="true" str="FTL is not running" @@ -316,33 +302,37 @@ analyze_ports() { } statusFunc() { - # Determine if there is pihole-FTL service is listening - local pid port ftl_api_port + # Determine if there is pihole-FTL service is listening + local pid port ftl_api_port ftl_pid_file ftl_apiport_file - pid="$(getFTLPID)" - ftl_api_port="$(getFTLAPIPort)" - if [[ "$pid" -eq "-1" ]]; then - case "${1}" in - "web") echo "-1";; - *) echo -e " ${CROSS} DNS service is NOT running";; - esac - return 0 - else - #get the DNS port pihole-FTL is listening on by using FTL's telnet API - port="$(echo ">dns-port >quit" | nc 127.0.0.1 "$ftl_api_port")" - if [[ "${port}" == "0" ]]; then - case "${1}" in - "web") echo "-1";; - *) echo -e " ${CROSS} DNS service is NOT listening";; - esac - return 0 + ftl_pid_file="$(getFTLPIDFile)" + + pid="$(getFTLPID ${ftl_pid_file})" + + ftl_apiport_file="${getFTLAPIPortFile}" + ftl_api_port="$(getFTLAPIPort ${ftl_apiport_file})" + if [[ "$pid" -eq "-1" ]]; then + case "${1}" in + "web") echo "-1";; + *) echo -e " ${CROSS} DNS service is NOT running";; + esac + return 0 else - if [[ "${1}" != "web" ]]; then - echo -e " ${TICK} FTL is listening on port ${port}" - analyze_ports "${port}" - fi + #get the DNS port pihole-FTL is listening on by using FTL's telnet API + port="$(echo ">dns-port >quit" | nc 127.0.0.1 "$ftl_api_port")" + if [[ "${port}" == "0" ]]; then + case "${1}" in + "web") echo "-1";; + *) echo -e " ${CROSS} DNS service is NOT listening";; + esac + return 0 + else + if [[ "${1}" != "web" ]]; then + echo -e " ${TICK} FTL is listening on port ${port}" + analyze_ports "${port}" + fi + fi fi - fi # Determine if Pi-hole's blocking is enabled if grep -q "BLOCKING_ENABLED=false" /etc/pihole/setupVars.conf; then diff --git a/test/test_any_utils.py b/test/test_any_utils.py index b30ff7fd..5126f263 100644 --- a/test/test_any_utils.py +++ b/test/test_any_utils.py @@ -50,25 +50,73 @@ def test_key_removal_works(host): assert expected_stdout == output.stdout +def test_getFTLAPIPortFile_default(host): + ''' Confirms getFTLAPIPortFile returns the default API port file path ''' + output = host.run(''' + source /opt/pihole/utils.sh + getFTLAPIPortFile + ''') + expected_stdout = '/run/pihole-FTL.port\n' + assert expected_stdout == output.stdout + + def test_getFTLAPIPort_default(host): ''' Confirms getFTLAPIPort returns the default API port ''' output = host.run(''' source /opt/pihole/utils.sh - getFTLAPIPort + getFTLAPIPort "/run/pihole-FTL.port" ''') expected_stdout = '4711\n' assert expected_stdout == output.stdout -def test_getFTLAPIPort_custom(host): +def test_getFTLAPIPortFile_and_getFTLAPIPort_custom(host): ''' Confirms getFTLAPIPort returns a custom API port in a custom PORTFILE location ''' host.run(''' - echo "PORTFILE=/tmp/port.file" > /etc/pihole/pihole-FTL.conf - echo "1234" > /tmp/port.file + tmpfile=$(mktemp) + echo "PORTFILE=${tmpfile}" > /etc/pihole/pihole-FTL.conf + echo "1234" > ${tmpfile} ''') output = host.run(''' source /opt/pihole/utils.sh - getFTLAPIPort + FTL_API_PORT_FILE=$(getFTLAPIPortFile) + getFTLAPIPort "${FTL_API_PORT_FILE}" + ''') + expected_stdout = '1234\n' + assert expected_stdout == output.stdout + + +def test_getFTLPIDFile_default(host): + ''' Confirms getFTLPIDFile returns the default PID file path ''' + output = host.run(''' + source /opt/pihole/utils.sh + getFTLPIDFile + ''') + expected_stdout = '/run/pihole-FTL.pid\n' + assert expected_stdout == output.stdout + + +def test_getFTLPID_default(host): + ''' Confirms getFTLPID returns the default value if FTL is not running ''' + output = host.run(''' + source /opt/pihole/utils.sh + getFTLPID + ''') + expected_stdout = '-1\n' + assert expected_stdout == output.stdout + + +def test_getFTLPIDFile_and_getFTLPID_custom(host): + ''' Confirms getFTLPIDFile returns a custom PID file path ''' + host.run(''' + tmpfile=$(mktemp) + echo "PIDFILE=${tmpfile}" > /etc/pihole/pihole-FTL.conf + echo "1234" > ${tmpfile} + ''') + output = host.run(''' + source /opt/pihole/utils.sh + FTL_PID_FILE=$(getFTLPIDFile) + getFTLPID "${FTL_PID_FILE}" ''') expected_stdout = '1234\n' assert expected_stdout == output.stdout