#!/bin/bash # Pi-hole: A black hole for Internet advertisements # (c) 2017 Pi-hole, LLC (https://pi-hole.net) # Network-wide ad blocking via your own hardware. # # Controller for all pihole scripts and functions. # # This file is copyright under the latest version of the EUPL. # Please see LICENSE file for your rights under this license. readonly PI_HOLE_SCRIPT_DIR="/opt/pihole" readonly wildcardlist="/etc/dnsmasq.d/03-pihole-wildcard.conf" readonly colfile="${PI_HOLE_SCRIPT_DIR}/COL_TABLE" source "${colfile}" resolver="pihole-FTL" # Must be root to use this tool if [[ ! $EUID -eq 0 ]];then if [[ -x "$(command -v sudo)" ]]; then exec sudo bash "$0" "$@" exit $? else echo -e " ${CROSS} sudo is needed to run pihole commands. Please run this script as root or install sudo." exit 1 fi fi webpageFunc() { source "${PI_HOLE_SCRIPT_DIR}/webpage.sh" main "$@" exit 0 } whitelistFunc() { "${PI_HOLE_SCRIPT_DIR}"/list.sh "$@" exit 0 } blacklistFunc() { "${PI_HOLE_SCRIPT_DIR}"/list.sh "$@" exit 0 } wildcardFunc() { "${PI_HOLE_SCRIPT_DIR}"/list.sh "$@" exit 0 } debugFunc() { local automated local web # Pull off the `debug` leaving passed call augmentation flags in $1 shift if [[ "$@" == *"-a"* ]]; then automated="true" fi if [[ "$@" == *"-w"* ]]; then web="true" fi AUTOMATED=${automated:-} WEBCALL=${web:-} "${PI_HOLE_SCRIPT_DIR}"/piholeDebug.sh exit 0 } flushFunc() { "${PI_HOLE_SCRIPT_DIR}"/piholeLogFlush.sh "$@" exit 0 } updatePiholeFunc() { shift "${PI_HOLE_SCRIPT_DIR}"/update.sh "$@" exit 0 } reconfigurePiholeFunc() { /etc/.pihole/automated\ install/basic-install.sh --reconfigure exit 0; } updateGravityFunc() { "${PI_HOLE_SCRIPT_DIR}"/gravity.sh "$@" exit 0 } # Print each subdomain # e.g: foo.bar.baz.com = "foo.bar.baz.com bar.baz.com baz.com com" processWildcards() { IFS="." read -r -a array <<< "${1}" for (( i=${#array[@]}-1; i>=0; i-- )); do ar="" for (( j=${#array[@]}-1; j>${#array[@]}-i-2; j-- )); do if [[ $j == $((${#array[@]}-1)) ]]; then ar="${array[$j]}" else ar="${array[$j]}.${ar}" fi done echo "${ar}" done } queryFunc() { shift "${PI_HOLE_SCRIPT_DIR}"/query.sh "$@" exit 0 } chronometerFunc() { shift "${PI_HOLE_SCRIPT_DIR}"/chronometer.sh "$@" exit 0 } uninstallFunc() { "${PI_HOLE_SCRIPT_DIR}"/uninstall.sh exit 0 } versionFunc() { shift "${PI_HOLE_SCRIPT_DIR}"/version.sh "$@" exit 0 } restartDNS() { local svcOption svc str output status svcOption="${1:-}" # Determine if we should reload or restart restart if [[ "${svcOption}" =~ "reload" ]]; then # Using SIGHUP will NOT re-read any *.conf files svc="killall -s SIGHUP ${resolver}" else # Get PID of resolver to determine if it needs to start or restart if pidof pihole-FTL &> /dev/null; then svcOption="restart" else svcOption="start" fi svc="service ${resolver} ${svcOption}" fi # Print output to Terminal, but not to Web Admin str="${svcOption^}ing DNS service" [[ -t 1 ]] && echo -ne " ${INFO} ${str}..." output=$( { ${svc}; } 2>&1 ) status="$?" if [[ "${status}" -eq 0 ]]; then [[ -t 1 ]] && echo -e "${OVER} ${TICK} ${str}" else [[ ! -t 1 ]] && local OVER="" echo -e "${OVER} ${CROSS} ${output}" fi } piholeEnable() { if [[ "${2}" == "-h" ]] || [[ "${2}" == "--help" ]]; then echo "Usage: pihole disable [time] Example: 'pihole disable', or 'pihole disable 5m' Disable Pi-hole subsystems Time: #s Disable Pi-hole functionality for # second(s) #m Disable Pi-hole functionality for # minute(s)" exit 0 elif [[ "${1}" == "0" ]]; then # Disable Pi-hole sed -i 's/^addn-hosts=\/etc\/pihole\/gravity.list/#addn-hosts=\/etc\/pihole\/gravity.list/' /etc/dnsmasq.d/01-pihole.conf sed -i 's/^addn-hosts=\/etc\/pihole\/black.list/#addn-hosts=\/etc\/pihole\/black.list/' /etc/dnsmasq.d/01-pihole.conf if [[ -e "$wildcardlist" ]]; then mv "$wildcardlist" "/etc/pihole/wildcard.list" fi if [[ $# > 1 ]]; then local error=false if [[ "${2}" == *"s" ]]; then tt=${2%"s"} if [[ "${tt}" =~ ^-?[0-9]+$ ]];then local str="Disabling blocking for ${tt} seconds" echo -e " ${INFO} ${str}..." local str="Blocking will be re-enabled in ${tt} seconds" nohup bash -c "sleep ${tt}; pihole enable" /dev/null & else local error=true fi elif [[ "${2}" == *"m" ]]; then tt=${2%"m"} if [[ "${tt}" =~ ^-?[0-9]+$ ]];then local str="Disabling blocking for ${tt} minutes" echo -e " ${INFO} ${str}..." local str="Blocking will be re-enabled in ${tt} minutes" tt=$((${tt}*60)) nohup bash -c "sleep ${tt}; pihole enable" /dev/null & else local error=true fi elif [[ -n "${2}" ]]; then local error=true else echo -e " ${INFO} Disabling blocking" fi if [[ ${error} == true ]];then echo -e " ${COL_LIGHT_RED}Unknown format for delayed reactivation of the blocking!${COL_NC}" echo -e " Try 'pihole disable --help' for more information." exit 1 fi local str="Pi-hole Disabled" fi else # Enable Pi-hole echo -e " ${INFO} Enabling blocking" local str="Pi-hole Enabled" sed -i 's/^#addn-hosts/addn-hosts/' /etc/dnsmasq.d/01-pihole.conf if [[ -e "/etc/pihole/wildcard.list" ]]; then mv "/etc/pihole/wildcard.list" "$wildcardlist" fi fi restartDNS echo -e "${OVER} ${TICK} ${str}" } piholeLogging() { shift if [[ "${1}" == "-h" ]] || [[ "${1}" == "--help" ]]; then echo "Usage: pihole logging [options] Example: 'pihole logging on' Specify whether the Pi-hole log should be used Options: on Enable the Pi-hole log at /var/log/pihole.log off Disable and flush the Pi-hole log at /var/log/pihole.log off noflush Disable the Pi-hole log at /var/log/pihole.log" exit 0 elif [[ "${1}" == "off" ]]; then # Disable logging sed -i 's/^log-queries/#log-queries/' /etc/dnsmasq.d/01-pihole.conf sed -i 's/^QUERY_LOGGING=true/QUERY_LOGGING=false/' /etc/pihole/setupVars.conf if [[ "${2}" != "noflush" ]]; then # Flush logs pihole -f fi echo -e " ${INFO} Disabling logging..." local str="Logging has been disabled!" elif [[ "${1}" == "on" ]]; then # Enable logging sed -i 's/^#log-queries/log-queries/' /etc/dnsmasq.d/01-pihole.conf sed -i 's/^QUERY_LOGGING=false/QUERY_LOGGING=true/' /etc/pihole/setupVars.conf echo -e " ${INFO} Enabling logging..." local str="Logging has been enabled!" else echo -e " ${COL_LIGHT_RED}Invalid option${COL_NC} Try 'pihole logging --help' for more information." exit 1 fi restartDNS echo -e "${OVER} ${TICK} ${str}" } statusFunc() { local addnConfigs # Determine if service is running on port 53 (Cr: https://superuser.com/a/806331) if (echo > /dev/tcp/127.0.0.1/53) >/dev/null 2>&1; then if [[ "${1}" != "web" ]]; then echo -e " ${TICK} DNS service is running" fi else case "${1}" in "web") echo "-1";; *) echo -e " ${CROSS} DNS service is NOT running";; esac return 0 fi # Determine if Pi-hole's addn-hosts configs are commented out addnConfigs=$(grep -i "addn-hosts=/" /etc/dnsmasq.d/01-pihole.conf) if [[ "${addnConfigs}" =~ "#" ]]; then # A config is commented out case "${1}" in "web") echo 0;; *) echo -e " ${CROSS} Pi-hole blocking is Disabled";; esac elif [[ -n "${addnConfigs}" ]]; then # Configs are set case "${1}" in "web") echo 1;; *) echo -e " ${TICK} Pi-hole blocking is Enabled";; esac else # No configs were found case "${1}" in "web") echo 99;; *) echo -e " ${INFO} No hosts file linked to dnsmasq, adding it in enabled state";; esac # Add addn-host= to dnsmasq echo "addn-hosts=/etc/pihole/gravity.list" >> /etc/dnsmasq.d/01-pihole.conf restartDNS fi } tailFunc() { # Warn user if Pi-hole's logging is disabled local logging_enabled=$(grep -c "^log-queries" /etc/dnsmasq.d/01-pihole.conf) if [[ "${logging_enabled}" == "0" ]]; then # No "log-queries" lines are found. # Commented out lines (such as "#log-queries") are ignored echo " ${CROSS} Warning: Query logging is disabled" fi echo -e " ${INFO} Press Ctrl-C to exit" # Retrieve IPv4/6 addresses source /etc/pihole/setupVars.conf # Strip date from each line # Colour blocklist/blacklist/wildcard entries as red # Colour A/AAAA/DHCP strings as white # Colour everything else as gray tail -f /var/log/pihole.log | sed -E \ -e "s,($(date +'%b %d ')| dnsmasq[.*[0-9]]),,g" \ -e "s,(.*(gravity.list|black.list| config ).* is (${IPV4_ADDRESS%/*}|${IPV6_ADDRESS:-NULL}).*),${COL_RED}&${COL_NC}," \ -e "s,.*(query\\[A|DHCP).*,${COL_NC}&${COL_NC}," \ -e "s,.*,${COL_GRAY}&${COL_NC}," exit 0 } piholeCheckoutFunc() { if [[ "$2" == "-h" ]] || [[ "$2" == "--help" ]]; then echo "Usage: pihole checkout [repo] [branch] Example: 'pihole checkout master' or 'pihole checkout core dev' Switch Pi-hole subsystems to a different Github branch Repositories: core [branch] Change the branch of Pi-hole's core subsystem web [branch] Change the branch of Web Interface subsystem ftl [branch] Change the branch of Pi-hole's FTL subsystem Branches: master Update subsystems to the latest stable release dev Update subsystems to the latest development release branchname Update subsystems to the specified branchname" exit 0 fi source "${PI_HOLE_SCRIPT_DIR}"/piholeCheckout.sh shift checkout "$@" } tricorderFunc() { if [[ ! -p "/dev/stdin" ]]; then echo -e " ${INFO} Please do not call Tricorder directly" exit 1 fi if ! (echo > /dev/tcp/tricorder.pi-hole.net/9998) >/dev/null 2>&1; then echo -e " ${CROSS} Unable to connect to Pi-hole's Tricorder server" exit 1 fi if command -v openssl &> /dev/null; then openssl s_client -quiet -connect tricorder.pi-hole.net:9998 2> /dev/null < /dev/stdin exit "$?" else echo -e " ${INFO} ${COL_YELLOW}Security Notice${COL_NC}: ${COL_WHITE}openssl${COL_NC} is not installed Your debug log will be transmitted unencrypted via plain-text There is a possibility that this could be intercepted by a third party If you wish to cancel, press Ctrl-C to exit within 10 seconds" secs="10" while [[ "$secs" -gt "0" ]]; do echo -ne "." sleep 1 : $((secs--)) done echo " " nc tricorder.pi-hole.net 9999 < /dev/stdin exit "$?" fi } updateCheckFunc() { "${PI_HOLE_SCRIPT_DIR}"/updatecheck.sh "$@" exit 0 } helpFunc() { echo "Usage: pihole [options] Example: 'pihole -w -h' Add '-h' after specific commands for more information on usage Whitelist/Blacklist Options: -w, whitelist Whitelist domain(s) -b, blacklist Blacklist domain(s) -wild, wildcard Blacklist domain(s), and all its subdomains Add '-h' for more info on whitelist/blacklist usage Debugging Options: -d, debug Start a debugging session Add '-a' to enable automated debugging -f, flush Flush the Pi-hole log -r, reconfigure Reconfigure or Repair Pi-hole subsystems -t, tail View the live output of the Pi-hole log Options: -a, admin Web interface options Add '-h' for more info on Web Interface usage -c, chronometer Calculates stats and displays to an LCD Add '-h' for more info on chronometer usage -g, updateGravity Update the list of ad-serving domains -h, --help, help Show this help dialog -l, logging Specify whether the Pi-hole log should be used Add '-h' for more info on logging usage -q, query Query the adlists for a specified domain Add '-h' for more info on query usage -up, updatePihole Update Pi-hole subsystems Add '--check-only' to exit script before update is performed. -v, version Show installed versions of Pi-hole, Web Interface & FTL Add '-h' for more info on version usage uninstall Uninstall Pi-hole from your system status Display the running status of Pi-hole subsystems enable Enable Pi-hole subsystems disable Disable Pi-hole subsystems Add '-h' for more info on disable usage restartdns Restart Pi-hole subsystems checkout Switch Pi-hole subsystems to a different Github branch Add '-h' for more info on checkout usage"; exit 0 } if [[ $# = 0 ]]; then helpFunc fi # Handle redirecting to specific functions based on arguments case "${1}" in "-w" | "whitelist" ) whitelistFunc "$@";; "-b" | "blacklist" ) blacklistFunc "$@";; "-wild" | "wildcard" ) wildcardFunc "$@";; "-d" | "debug" ) debugFunc "$@";; "-f" | "flush" ) flushFunc "$@";; "-up" | "updatePihole" ) updatePiholeFunc "$@";; "-r" | "reconfigure" ) reconfigurePiholeFunc;; "-g" | "updateGravity" ) updateGravityFunc "$@";; "-c" | "chronometer" ) chronometerFunc "$@";; "-h" | "help" ) helpFunc;; "-v" | "version" ) versionFunc "$@";; "-q" | "query" ) queryFunc "$@";; "-l" | "logging" ) piholeLogging "$@";; "uninstall" ) uninstallFunc;; "enable" ) piholeEnable 1;; "disable" ) piholeEnable 0 "$2";; "status" ) statusFunc "$2";; "restartdns" ) restartDNS "$2";; "-a" | "admin" ) webpageFunc "$@";; "-t" | "tail" ) tailFunc;; "checkout" ) piholeCheckoutFunc "$@";; "tricorder" ) tricorderFunc;; "updatechecker" ) updateCheckFunc "$@";; * ) helpFunc;; esac