2016-10-26 08:36:02 +00:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
# 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.
|
|
|
|
#
|
2017-05-14 01:11:44 +00:00
|
|
|
# Whitelist and blacklist 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.
|
|
|
|
|
2017-05-14 01:11:44 +00:00
|
|
|
# Globals
|
2016-10-26 08:36:02 +00:00
|
|
|
basename=pihole
|
2018-02-18 20:18:52 +00:00
|
|
|
piholeDir=/etc/"${basename}"
|
2019-04-25 10:10:42 +00:00
|
|
|
gravityDBfile="${piholeDir}/gravity.db"
|
2018-08-13 16:17:14 +00:00
|
|
|
|
2016-10-26 08:36:02 +00:00
|
|
|
reload=false
|
|
|
|
addmode=true
|
|
|
|
verbose=true
|
2018-07-08 18:37:33 +00:00
|
|
|
wildcard=false
|
2019-04-25 13:01:13 +00:00
|
|
|
web=false
|
2016-10-26 08:36:02 +00:00
|
|
|
|
|
|
|
domList=()
|
|
|
|
|
2019-11-30 14:18:12 +00:00
|
|
|
typeId=""
|
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
|
|
|
GetListnameFromTypeId() {
|
2019-11-30 13:13:26 +00:00
|
|
|
if [[ "$1" == "0" ]]; then
|
|
|
|
echo "whitelist"
|
|
|
|
elif [[ "$1" == "1" ]]; then
|
|
|
|
echo "blacklist"
|
|
|
|
elif [[ "$1" == "2" ]]; then
|
|
|
|
echo "regex_whitelist"
|
|
|
|
elif [[ "$1" == "3" ]]; then
|
|
|
|
echo "regex_blacklist"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2019-11-30 16:26:26 +00:00
|
|
|
GetListParamFromTypeId() {
|
2019-11-30 14:18:12 +00:00
|
|
|
if [[ "${typeId}" == "0" ]]; then
|
2019-11-30 16:26:26 +00:00
|
|
|
echo "w"
|
|
|
|
elif [[ "${typeId}" == "1" ]]; then
|
|
|
|
echo "b"
|
|
|
|
elif [[ "${typeId}" == "2" && "${wildcard}" == true ]]; then
|
|
|
|
echo "-white-wild"
|
|
|
|
elif [[ "${typeId}" == "2" ]]; then
|
|
|
|
echo "regex_blacklist"
|
|
|
|
elif [[ "${typeId}" == "3" && "${wildcard}" == true ]]; then
|
|
|
|
echo "-regex"
|
|
|
|
elif [[ "${typeId}" == "3" ]]; then
|
|
|
|
echo "-wild"
|
2018-07-20 19:42:11 +00:00
|
|
|
fi
|
2019-11-30 16:26:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
helpFunc() {
|
|
|
|
local listname param
|
|
|
|
|
|
|
|
listname="$(GetListnameFromTypeId "${typeId}")"
|
|
|
|
param="$(GetListParamFromTypeId)"
|
2017-05-14 01:11:44 +00:00
|
|
|
|
2017-05-16 00:18:32 +00:00
|
|
|
echo "Usage: pihole -${param} [options] <domain> <domain2 ...>
|
|
|
|
Example: 'pihole -${param} site.com', or 'pihole -${param} site1.com site2.com'
|
2019-11-30 16:26:26 +00:00
|
|
|
${listname^} one or more domains
|
2017-05-14 01:11:44 +00:00
|
|
|
|
2017-05-16 00:23:53 +00:00
|
|
|
Options:
|
2019-11-30 16:26:26 +00:00
|
|
|
-d, --delmode Remove domain(s) from the ${listname}
|
|
|
|
-nr, --noreload Update ${listname} without reloading the DNS server
|
2017-05-14 01:11:44 +00:00
|
|
|
-q, --quiet Make output less verbose
|
|
|
|
-h, --help Show this help dialog
|
2019-11-30 16:26:26 +00:00
|
|
|
-l, --list Display all your ${listname}listed domains
|
2017-10-07 15:29:47 +00:00
|
|
|
--nuke Removes all entries in a list"
|
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
|
|
|
}
|
|
|
|
|
2019-11-30 16:26:26 +00:00
|
|
|
ValidateDomain() {
|
2018-07-20 19:42:11 +00:00
|
|
|
# Convert to lowercase
|
|
|
|
domain="${1,,}"
|
|
|
|
|
|
|
|
# Check validity of domain (don't check for regex entries)
|
|
|
|
if [[ "${#domain}" -le 253 ]]; then
|
2019-11-30 14:18:12 +00:00
|
|
|
if [[ ( "${typeId}" == "3" || "${typeId}" == "2" ) && "${wildcard}" == false ]]; then
|
2018-07-20 19:42:11 +00:00
|
|
|
validDomain="${domain}"
|
|
|
|
else
|
2019-11-30 17:45:07 +00:00
|
|
|
# Use regex to check the validity of the passed domain. see https://regexr.com/3abjr
|
|
|
|
validDomain=$(grep -P "^((?!-))(xn--)?[a-z0-9][a-z0-9-_]{0,61}[a-z0-9]{0,1}\.(xn--)?([a-z0-9\-]{1,61}|[a-z0-9-]{1,30}\.[a-z]{2,})$" <<< "${domain}")
|
2018-07-20 19:42:11 +00:00
|
|
|
fi
|
|
|
|
fi
|
2017-05-14 01:11:44 +00:00
|
|
|
|
2018-07-20 19:42:11 +00:00
|
|
|
if [[ -n "${validDomain}" ]]; then
|
|
|
|
domList=("${domList[@]}" ${validDomain})
|
2018-06-29 03:21:01 +00:00
|
|
|
else
|
2018-07-20 19:42:11 +00:00
|
|
|
echo -e " ${CROSS} ${domain} is not a valid argument or domain name!"
|
2018-06-29 03:21:01 +00:00
|
|
|
fi
|
2016-10-26 08:36:02 +00:00
|
|
|
}
|
|
|
|
|
2019-04-25 10:10:42 +00:00
|
|
|
ProcessDomainList() {
|
2018-07-20 19:42:11 +00:00
|
|
|
for dom in "${domList[@]}"; do
|
2019-04-25 10:30:38 +00:00
|
|
|
# Format domain into regex filter if requested
|
|
|
|
if [[ "${wildcard}" == true ]]; then
|
|
|
|
dom="(^|\\.)${dom//\./\\.}$"
|
|
|
|
fi
|
|
|
|
|
2019-04-26 15:06:01 +00:00
|
|
|
# Logic: If addmode then add to desired list and remove from the other;
|
|
|
|
# if delmode then remove from desired list but do not add to the other
|
2018-07-20 19:42:11 +00:00
|
|
|
if ${addmode}; then
|
2019-11-30 14:18:12 +00:00
|
|
|
AddDomain "${dom}"
|
2018-07-20 19:42:11 +00:00
|
|
|
else
|
2019-11-30 14:18:12 +00:00
|
|
|
RemoveDomain "${dom}"
|
2018-07-20 19:42:11 +00:00
|
|
|
fi
|
2017-05-14 01:11:44 +00:00
|
|
|
done
|
2016-10-26 08:36:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
AddDomain() {
|
2019-11-30 14:18:12 +00:00
|
|
|
local domain num requestedListname existingTypeId existingListname
|
2019-04-26 18:54:01 +00:00
|
|
|
domain="$1"
|
2018-07-20 19:42:11 +00:00
|
|
|
|
2019-04-25 10:10:42 +00:00
|
|
|
# Is the domain in the list we want to add it to?
|
2019-11-30 13:13:26 +00:00
|
|
|
num="$(sqlite3 "${gravityDBfile}" "SELECT COUNT(*) FROM domainlist WHERE domain = '${domain}';")"
|
2019-11-30 16:26:26 +00:00
|
|
|
requestedListname="$(GetListnameFromTypeId "${typeId}")"
|
2018-07-20 19:42:11 +00:00
|
|
|
|
2019-04-26 15:06:01 +00:00
|
|
|
if [[ "${num}" -ne 0 ]]; then
|
2019-11-30 14:18:12 +00:00
|
|
|
existingTypeId="$(sqlite3 "${gravityDBfile}" "SELECT type FROM domainlist WHERE domain = '${domain}';")"
|
|
|
|
if [[ "${existingTypeId}" == "${typeId}" ]]; then
|
2019-11-30 13:13:26 +00:00
|
|
|
if [[ "${verbose}" == true ]]; then
|
2019-11-30 14:18:12 +00:00
|
|
|
echo -e " ${INFO} ${1} already exists in ${requestedListname}, no need to add!"
|
2019-11-30 13:13:26 +00:00
|
|
|
fi
|
|
|
|
else
|
2019-11-30 16:26:26 +00:00
|
|
|
existingListname="$(GetListnameFromTypeId "${existingTypeId}")"
|
2019-11-30 14:18:12 +00:00
|
|
|
sqlite3 "${gravityDBfile}" "UPDATE domainlist SET type = ${typeId} WHERE domain='${domain}';"
|
2019-11-30 13:13:26 +00:00
|
|
|
if [[ "${verbose}" == true ]]; then
|
2019-11-30 14:18:12 +00:00
|
|
|
echo -e " ${INFO} ${1} already exists in ${existingListname}, it has been moved to ${requestedListname}!"
|
2019-11-30 13:13:26 +00:00
|
|
|
fi
|
2019-04-26 15:06:01 +00:00
|
|
|
fi
|
|
|
|
return
|
2017-05-14 01:11:44 +00:00
|
|
|
fi
|
2019-04-26 15:06:01 +00:00
|
|
|
|
|
|
|
# Domain not found in the table, add it!
|
|
|
|
if [[ "${verbose}" == true ]]; then
|
2019-11-30 14:18:12 +00:00
|
|
|
echo -e " ${INFO} Adding ${domain} to the ${requestedListname}..."
|
2019-04-26 15:06:01 +00:00
|
|
|
fi
|
|
|
|
reload=true
|
2019-04-26 16:15:53 +00:00
|
|
|
# Insert only the domain here. The enabled and date_added fields will be filled
|
|
|
|
# with their default values (enabled = true, date_added = current timestamp)
|
2019-11-30 14:18:12 +00:00
|
|
|
sqlite3 "${gravityDBfile}" "INSERT INTO domainlist (domain,type) VALUES ('${domain}',${typeId});"
|
2016-10-26 08:36:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
RemoveDomain() {
|
2019-11-30 14:18:12 +00:00
|
|
|
local domain num requestedListname
|
2019-04-26 18:54:01 +00:00
|
|
|
domain="$1"
|
2019-04-25 10:10:42 +00:00
|
|
|
|
|
|
|
# Is the domain in the list we want to remove it from?
|
2019-11-30 14:18:12 +00:00
|
|
|
num="$(sqlite3 "${gravityDBfile}" "SELECT COUNT(*) FROM domainlist WHERE domain = '${domain}' AND type = ${typeId};")"
|
|
|
|
|
2019-11-30 16:26:26 +00:00
|
|
|
requestedListname="$(GetListnameFromTypeId "${typeId}")"
|
2019-04-25 10:10:42 +00:00
|
|
|
|
2019-04-26 15:06:01 +00:00
|
|
|
if [[ "${num}" -eq 0 ]]; then
|
|
|
|
if [[ "${verbose}" == true ]]; then
|
2019-11-30 14:18:12 +00:00
|
|
|
echo -e " ${INFO} ${domain} does not exist in ${requestedListname}, no need to remove!"
|
2019-04-26 15:06:01 +00:00
|
|
|
fi
|
|
|
|
return
|
2019-04-25 10:10:42 +00:00
|
|
|
fi
|
2019-04-26 15:06:01 +00:00
|
|
|
|
|
|
|
# Domain found in the table, remove it!
|
|
|
|
if [[ "${verbose}" == true ]]; then
|
2019-11-30 14:18:12 +00:00
|
|
|
echo -e " ${INFO} Removing ${domain} from the ${requestedListname}..."
|
2019-04-26 15:06:01 +00:00
|
|
|
fi
|
|
|
|
reload=true
|
|
|
|
# Remove it from the current list
|
2019-11-30 14:18:12 +00:00
|
|
|
sqlite3 "${gravityDBfile}" "DELETE FROM domainlist WHERE domain = '${domain}' AND type = ${typeId};"
|
2016-10-26 08:36:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Displaylist() {
|
2019-11-30 14:18:12 +00:00
|
|
|
local count num_pipes domain enabled status nicedate requestedListname
|
2019-04-25 10:10:42 +00:00
|
|
|
|
2019-11-30 16:26:26 +00:00
|
|
|
requestedListname="$(GetListnameFromTypeId "${typeId}")"
|
2019-11-30 14:18:12 +00:00
|
|
|
data="$(sqlite3 "${gravityDBfile}" "SELECT domain,enabled,date_modified FROM domainlist WHERE type = ${typeId};" 2> /dev/null)"
|
2019-04-25 10:10:42 +00:00
|
|
|
|
|
|
|
if [[ -z $data ]]; then
|
2019-07-22 17:35:16 +00:00
|
|
|
echo -e "Not showing empty list"
|
2019-04-25 10:10:42 +00:00
|
|
|
else
|
2019-11-30 14:18:12 +00:00
|
|
|
echo -e "Displaying ${requestedListname}:"
|
2018-07-20 19:42:11 +00:00
|
|
|
count=1
|
2019-04-25 10:10:42 +00:00
|
|
|
while IFS= read -r line
|
|
|
|
do
|
2019-04-25 10:30:38 +00:00
|
|
|
# Count number of pipes seen in this line
|
|
|
|
# This is necessary because we can only detect the pipe separating the fields
|
|
|
|
# from the end backwards as the domain (which is the first field) may contain
|
|
|
|
# pipe symbols as they are perfectly valid regex filter control characters
|
|
|
|
num_pipes="$(grep -c "^" <<< "$(grep -o "|" <<< "${line}")")"
|
|
|
|
|
2019-04-25 10:45:08 +00:00
|
|
|
# Extract domain and enabled status based on the obtained number of pipe characters
|
2019-05-02 15:11:39 +00:00
|
|
|
domain="$(cut -d'|' -f"-$((num_pipes-1))" <<< "${line}")"
|
|
|
|
enabled="$(cut -d'|' -f"$((num_pipes))" <<< "${line}")"
|
|
|
|
datemod="$(cut -d'|' -f"$((num_pipes+1))" <<< "${line}")"
|
2019-04-25 10:45:08 +00:00
|
|
|
|
|
|
|
# Translate boolean status into human readable string
|
2019-04-25 10:10:42 +00:00
|
|
|
if [[ "${enabled}" -eq 1 ]]; then
|
|
|
|
status="enabled"
|
|
|
|
else
|
|
|
|
status="disabled"
|
|
|
|
fi
|
2019-04-25 10:30:38 +00:00
|
|
|
|
2019-04-26 15:07:16 +00:00
|
|
|
# Get nice representation of numerical date stored in database
|
2019-05-02 15:11:39 +00:00
|
|
|
nicedate=$(date --rfc-2822 -d "@${datemod}")
|
2019-04-26 15:07:16 +00:00
|
|
|
|
2019-05-02 15:11:39 +00:00
|
|
|
echo " ${count}: ${domain} (${status}, last modified ${nicedate})"
|
2018-07-20 19:42:11 +00:00
|
|
|
count=$((count+1))
|
2019-04-25 10:10:42 +00:00
|
|
|
done <<< "${data}"
|
2017-06-21 11:49:05 +00:00
|
|
|
fi
|
2019-04-26 15:13:36 +00:00
|
|
|
exit 0;
|
2016-10-26 08:36:02 +00:00
|
|
|
}
|
|
|
|
|
2017-10-07 15:29:47 +00:00
|
|
|
NukeList() {
|
2019-11-30 14:18:12 +00:00
|
|
|
sqlite3 "${gravityDBfile}" "DELETE FROM domainlist WHERE type = ${typeId};"
|
2017-10-07 15:29:47 +00:00
|
|
|
}
|
|
|
|
|
2016-10-26 08:36:02 +00:00
|
|
|
for var in "$@"; do
|
2018-07-20 19:42:11 +00:00
|
|
|
case "${var}" in
|
2019-11-30 14:18:12 +00:00
|
|
|
"-w" | "whitelist" ) typeId=0;;
|
|
|
|
"-b" | "blacklist" ) typeId=1;;
|
|
|
|
"--white-regex" | "white-regex" ) typeId=2;;
|
|
|
|
"--white-wild" | "white-wild" ) typeId=2; wildcard=true;;
|
|
|
|
"--wild" | "wildcard" ) typeId=3; wildcard=true;;
|
|
|
|
"--regex" | "regex" ) typeId=3;;
|
2018-07-20 19:42:11 +00:00
|
|
|
"-nr"| "--noreload" ) reload=false;;
|
|
|
|
"-d" | "--delmode" ) addmode=false;;
|
|
|
|
"-q" | "--quiet" ) verbose=false;;
|
|
|
|
"-h" | "--help" ) helpFunc;;
|
|
|
|
"-l" | "--list" ) Displaylist;;
|
|
|
|
"--nuke" ) NukeList;;
|
2019-04-25 13:01:13 +00:00
|
|
|
"--web" ) web=true;;
|
2019-11-30 16:26:26 +00:00
|
|
|
* ) ValidateDomain "${var}";;
|
2018-07-20 19:42:11 +00:00
|
|
|
esac
|
2016-10-26 08:36:02 +00:00
|
|
|
done
|
|
|
|
|
|
|
|
shift
|
|
|
|
|
|
|
|
if [[ $# = 0 ]]; then
|
2018-07-20 19:42:11 +00:00
|
|
|
helpFunc
|
2016-10-26 08:36:02 +00:00
|
|
|
fi
|
|
|
|
|
2019-04-25 10:10:42 +00:00
|
|
|
ProcessDomainList
|
2016-10-26 08:36:02 +00:00
|
|
|
|
2019-04-25 13:01:13 +00:00
|
|
|
# Used on web interface
|
|
|
|
if $web; then
|
|
|
|
echo "DONE"
|
|
|
|
fi
|
|
|
|
|
2017-07-24 11:26:39 +00:00
|
|
|
if [[ "${reload}" != false ]]; then
|
2019-04-25 10:10:42 +00:00
|
|
|
pihole restartdns reload
|
2016-10-26 08:36:02 +00:00
|
|
|
fi
|