2016-10-26 08:36:02 +00:00
|
|
|
#!/usr/bin/env bash
|
2021-03-18 08:04:09 +00:00
|
|
|
# shellcheck disable=SC1090
|
|
|
|
|
2016-10-26 08:36:02 +00:00
|
|
|
# Pi-hole: A black hole for Internet advertisements
|
2017-02-22 17:55:20 +00:00
|
|
|
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
|
|
|
# Network-wide ad blocking via your own hardware.
|
|
|
|
#
|
2024-06-19 21:04:39 +00:00
|
|
|
# allowlist and denylist domains
|
2016-10-26 08:36:02 +00:00
|
|
|
#
|
2017-02-22 17:55:20 +00:00
|
|
|
# This file is copyright under the latest version of the EUPL.
|
|
|
|
# Please see LICENSE file for your rights under this license.
|
|
|
|
|
2024-06-19 21:04:39 +00:00
|
|
|
readonly PI_HOLE_SCRIPT_DIR="/opt/pihole"
|
|
|
|
readonly utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh"
|
|
|
|
source "${utilsfile}"
|
|
|
|
|
|
|
|
readonly apifile="${PI_HOLE_SCRIPT_DIR}/api.sh"
|
|
|
|
source "${apifile}"
|
|
|
|
|
|
|
|
# Determine database location
|
|
|
|
DBFILE=$(getFTLConfigValue "files.database")
|
|
|
|
if [ -z "$DBFILE" ]; then
|
|
|
|
DBFILE="/etc/pihole/pihole-FTL.db"
|
2021-03-18 08:01:22 +00:00
|
|
|
fi
|
|
|
|
|
2024-06-19 21:04:39 +00:00
|
|
|
# Determine gravity database location
|
|
|
|
GRAVITYDB=$(getFTLConfigValue "files.gravity")
|
|
|
|
if [ -z "$GRAVITYDB" ]; then
|
|
|
|
GRAVITYDB="/etc/pihole/gravity.db"
|
|
|
|
fi
|
2018-08-13 16:17:14 +00:00
|
|
|
|
2016-10-26 08:36:02 +00:00
|
|
|
addmode=true
|
|
|
|
verbose=true
|
2018-07-08 18:37:33 +00:00
|
|
|
wildcard=false
|
2016-10-26 08:36:02 +00:00
|
|
|
|
|
|
|
domList=()
|
|
|
|
|
2019-11-30 14:18:12 +00:00
|
|
|
typeId=""
|
2020-03-14 11:18:43 +00:00
|
|
|
comment=""
|
2016-10-26 08:36:02 +00:00
|
|
|
|
2019-04-25 10:45:08 +00:00
|
|
|
colfile="/opt/pihole/COL_TABLE"
|
|
|
|
source ${colfile}
|
2017-06-21 11:49:05 +00:00
|
|
|
|
2019-11-30 16:26:26 +00:00
|
|
|
helpFunc() {
|
2024-06-19 21:04:39 +00:00
|
|
|
echo "Usage: pihole ${abbrv} [options] <domain> <domain2 ...>
|
|
|
|
Example: 'pihole ${abbrv} site.com', or 'pihole ${abbrv} site1.com site2.com'
|
|
|
|
${typeId^} one or more ${kindId} domains
|
2017-05-14 01:11:44 +00:00
|
|
|
|
2017-05-16 00:23:53 +00:00
|
|
|
Options:
|
2024-06-29 07:32:13 +00:00
|
|
|
remove, delete, -d Remove domain(s)
|
2017-05-14 01:11:44 +00:00
|
|
|
-q, --quiet Make output less verbose
|
|
|
|
-h, --help Show this help dialog
|
2024-06-19 21:04:39 +00:00
|
|
|
-l, --list Display domains
|
2021-12-21 21:10:56 +00:00
|
|
|
--comment \"text\" Add a comment to the domain. If adding multiple domains the same comment will be used for all"
|
2017-05-14 15:43:20 +00:00
|
|
|
|
2017-05-16 00:18:32 +00:00
|
|
|
exit 0
|
2016-10-26 08:36:02 +00:00
|
|
|
}
|
|
|
|
|
2024-06-19 21:04:39 +00:00
|
|
|
CreateDomainList() {
|
|
|
|
# Format domain into regex filter if requested
|
|
|
|
local dom=${1}
|
|
|
|
if [[ "${wildcard}" == true ]]; then
|
|
|
|
dom="(\\.|^)${dom//\./\\.}$"
|
2018-06-29 03:21:01 +00:00
|
|
|
fi
|
2024-06-19 21:04:39 +00:00
|
|
|
domList=("${domList[@]}" "${dom}")
|
2016-10-26 08:36:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
AddDomain() {
|
2024-06-25 12:10:35 +00:00
|
|
|
local json num data
|
2024-06-19 21:04:39 +00:00
|
|
|
|
|
|
|
# Authenticate with the API
|
|
|
|
LoginAPI
|
|
|
|
|
|
|
|
# Prepare request to POST /api/domains/{type}/{kind}
|
|
|
|
# Build JSON object of the following form
|
|
|
|
# {
|
|
|
|
# "domain": [ <domains> ],
|
|
|
|
# "comment": <comment>
|
|
|
|
# }
|
|
|
|
# where <domains> is an array of domain strings and <comment> is a string
|
|
|
|
# We use jq to build the JSON object
|
|
|
|
json=$(jq --null-input --compact-output --arg domains "${domList[*]}" --arg comment "${comment}" '{domain: $domains | split(" "), comment: $comment}')
|
|
|
|
|
|
|
|
# Send the request
|
|
|
|
data=$(PostFTLData "domains/${typeId}/${kindId}" "${json}")
|
|
|
|
|
|
|
|
# Display domain(s) added
|
|
|
|
# (they are listed in .processed.success, use jq)
|
|
|
|
num=$(echo "${data}" | jq '.processed.success | length')
|
|
|
|
if [[ "${num}" -gt 0 ]] && [[ "${verbose}" == true ]]; then
|
|
|
|
echo -e " ${TICK} Added ${num} domain(s):"
|
|
|
|
for i in $(seq 0 $((num-1))); do
|
|
|
|
echo -e " - ${COL_BLUE}$(echo "${data}" | jq --raw-output ".processed.success[$i].item")${COL_NC}"
|
|
|
|
done
|
|
|
|
fi
|
|
|
|
# Display failed domain(s)
|
|
|
|
# (they are listed in .processed.errors, use jq)
|
|
|
|
num=$(echo "${data}" | jq '.processed.errors | length')
|
|
|
|
if [[ "${num}" -gt 0 ]] && [[ "${verbose}" == true ]]; then
|
|
|
|
echo -e " ${CROSS} Failed to add ${num} domain(s):"
|
|
|
|
for i in $(seq 0 $((num-1))); do
|
|
|
|
echo -e " - ${COL_BLUE}$(echo "${data}" | jq --raw-output ".processed.errors[$i].item")${COL_NC}"
|
|
|
|
error=$(echo "${data}" | jq --raw-output ".processed.errors[$i].error")
|
|
|
|
if [[ "${error}" == "UNIQUE constraint failed: domainlist.domain, domainlist.type" ]]; then
|
|
|
|
error="Domain already in the specified list"
|
2021-11-25 06:41:40 +00:00
|
|
|
fi
|
2024-06-19 21:04:39 +00:00
|
|
|
echo -e " ${error}"
|
|
|
|
done
|
2017-05-14 01:11:44 +00:00
|
|
|
fi
|
2019-04-26 15:06:01 +00:00
|
|
|
|
2024-06-19 21:04:39 +00:00
|
|
|
# Log out
|
|
|
|
LogoutAPI
|
2016-10-26 08:36:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
RemoveDomain() {
|
2024-06-25 12:10:35 +00:00
|
|
|
local json num data status
|
2024-06-19 21:04:39 +00:00
|
|
|
|
|
|
|
# Authenticate with the API
|
|
|
|
LoginAPI
|
|
|
|
|
|
|
|
# Prepare request to POST /api/domains:batchDelete
|
|
|
|
# Build JSON object of the following form
|
|
|
|
# [{
|
|
|
|
# "item": <domain>,
|
|
|
|
# "type": "${typeId}",
|
|
|
|
# "kind": "${kindId}",
|
|
|
|
# }]
|
|
|
|
# where <domain> is the domain string and ${typeId} and ${kindId} are the type and kind IDs
|
|
|
|
# We use jq to build the JSON object)
|
|
|
|
json=$(jq --null-input --compact-output --arg domains "${domList[*]}" --arg typeId "${typeId}" --arg kindId "${kindId}" '[ $domains | split(" ")[] as $item | {item: $item, type: $typeId, kind: $kindId} ]')
|
|
|
|
|
|
|
|
# Send the request
|
2024-06-25 12:10:35 +00:00
|
|
|
data=$(PostFTLData "domains:batchDelete" "${json}" "status")
|
|
|
|
# Separate the status from the data
|
|
|
|
status=$(printf %s "${data#"${data%???}"}")
|
|
|
|
data=$(printf %s "${data%???}")
|
2024-06-19 21:04:39 +00:00
|
|
|
|
|
|
|
# If there is an .error object in the returned data, display it
|
|
|
|
local error
|
|
|
|
error=$(jq --compact-output <<< "${data}" '.error')
|
|
|
|
if [[ $error != "null" && $error != "" ]]; then
|
|
|
|
echo -e " ${CROSS} Failed to remove domain(s):"
|
|
|
|
echo -e " $(jq <<< "${data}" '.error')"
|
2024-06-25 12:10:35 +00:00
|
|
|
elif [[ "${verbose}" == true && "${status}" == "204" ]]; then
|
|
|
|
echo -e " ${TICK} Domain(s) removed from the ${kindId} ${typeId}list"
|
|
|
|
elif [[ "${verbose}" == true && "${status}" == "404" ]]; then
|
|
|
|
echo -e " ${TICK} Requested domain(s) not found on ${kindId} ${typeId}list"
|
2019-04-25 10:10:42 +00:00
|
|
|
fi
|
2019-04-26 15:06:01 +00:00
|
|
|
|
2024-06-19 21:04:39 +00:00
|
|
|
# Log out
|
|
|
|
LogoutAPI
|
2016-10-26 08:36:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Displaylist() {
|
2024-06-19 21:04:39 +00:00
|
|
|
local data
|
2019-04-25 10:10:42 +00:00
|
|
|
|
2024-06-19 21:04:39 +00:00
|
|
|
# if either typeId or kindId is empty, we cannot display the list
|
|
|
|
if [[ -z "${typeId}" ]] || [[ -z "${kindId}" ]]; then
|
|
|
|
echo " ${CROSS} Unable to display list. Please specify a list type and kind."
|
|
|
|
exit 1
|
2017-06-21 11:49:05 +00:00
|
|
|
fi
|
2016-10-26 08:36:02 +00:00
|
|
|
|
2024-06-19 21:04:39 +00:00
|
|
|
# Authenticate with the API
|
|
|
|
LoginAPI
|
|
|
|
|
|
|
|
# Send the request
|
|
|
|
data=$(GetFTLData "domains/${typeId}/${kindId}")
|
|
|
|
|
|
|
|
# Display the list
|
|
|
|
num=$(echo "${data}" | jq '.domains | length')
|
|
|
|
if [[ "${num}" -gt 0 ]]; then
|
|
|
|
echo -e " ${TICK} Found ${num} domain(s) in the ${kindId} ${typeId}list:"
|
|
|
|
for i in $(seq 0 $((num-1))); do
|
|
|
|
echo -e " - ${COL_BLUE}$(echo "${data}" | jq --compact-output ".domains[$i].domain")${COL_NC}"
|
|
|
|
echo -e " Comment: $(echo "${data}" | jq --compact-output ".domains[$i].comment")"
|
|
|
|
echo -e " Groups: $(echo "${data}" | jq --compact-output ".domains[$i].groups")"
|
|
|
|
echo -e " Added: $(date -d @"$(echo "${data}" | jq --compact-output ".domains[$i].date_added")")"
|
|
|
|
echo -e " Last modified: $(date -d @"$(echo "${data}" | jq --compact-output ".domains[$i].date_modified")")"
|
|
|
|
done
|
2020-11-16 23:31:35 +00:00
|
|
|
else
|
2024-06-19 21:04:39 +00:00
|
|
|
echo -e " ${INFO} No domains found in the ${kindId} ${typeId}list"
|
2021-10-05 14:52:51 +00:00
|
|
|
fi
|
2024-06-19 21:04:39 +00:00
|
|
|
|
|
|
|
# Log out
|
|
|
|
LogoutAPI
|
|
|
|
|
|
|
|
# Return early without adding/deleting domains
|
|
|
|
exit 0
|
2017-10-07 15:29:47 +00:00
|
|
|
}
|
|
|
|
|
2020-03-14 11:18:43 +00:00
|
|
|
GetComment() {
|
|
|
|
comment="$1"
|
|
|
|
if [[ "${comment}" =~ [^a-zA-Z0-9_\#:/\.,\ -] ]]; then
|
2021-11-25 06:41:40 +00:00
|
|
|
echo " ${CROSS} Found invalid characters in domain comment!"
|
2024-10-20 07:36:28 +00:00
|
|
|
exit 1
|
2020-03-14 11:18:43 +00:00
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
while (( "$#" )); do
|
|
|
|
case "${1}" in
|
2024-06-25 11:56:49 +00:00
|
|
|
"allow" | "allowlist" ) kindId="exact"; typeId="allow"; abbrv="allow";;
|
|
|
|
"deny" | "denylist" ) kindId="exact"; typeId="deny"; abbrv="deny";;
|
2024-06-19 21:04:39 +00:00
|
|
|
"--allow-regex" | "allow-regex" ) kindId="regex"; typeId="allow"; abbrv="--allow-regex";;
|
|
|
|
"--allow-wild" | "allow-wild" ) kindId="regex"; typeId="allow"; wildcard=true; abbrv="--allow-wild";;
|
|
|
|
"--regex" | "regex" ) kindId="regex"; typeId="deny"; abbrv="--regex";;
|
|
|
|
"--wild" | "wildcard" ) kindId="regex"; typeId="deny"; wildcard=true; abbrv="--wild";;
|
2024-06-29 07:32:13 +00:00
|
|
|
"-d" | "remove" | "delete" ) addmode=false;;
|
2018-07-20 19:42:11 +00:00
|
|
|
"-q" | "--quiet" ) verbose=false;;
|
|
|
|
"-h" | "--help" ) helpFunc;;
|
|
|
|
"-l" | "--list" ) Displaylist;;
|
2020-03-14 11:18:43 +00:00
|
|
|
"--comment" ) GetComment "${2}"; shift;;
|
2024-06-19 21:04:39 +00:00
|
|
|
* ) CreateDomainList "${1}";;
|
2018-07-20 19:42:11 +00:00
|
|
|
esac
|
2020-03-14 11:18:43 +00:00
|
|
|
shift
|
2016-10-26 08:36:02 +00:00
|
|
|
done
|
|
|
|
|
|
|
|
shift
|
|
|
|
|
2024-06-19 21:04:39 +00:00
|
|
|
if [[ ${#domList[@]} == 0 ]]; then
|
2018-07-20 19:42:11 +00:00
|
|
|
helpFunc
|
2016-10-26 08:36:02 +00:00
|
|
|
fi
|
|
|
|
|
2024-06-19 21:04:39 +00:00
|
|
|
if ${addmode}; then
|
|
|
|
AddDomain
|
|
|
|
else
|
|
|
|
RemoveDomain
|
2016-10-26 08:36:02 +00:00
|
|
|
fi
|