@ -175,7 +175,7 @@ While quite outdated at this point, [this original blog post about Pi-hole](http
-----
-----
## Coverage
## Coverage
- [Lifehacker: Turn A Raspberry Pi Into An Ad Blocker With A Single Command](https://www.lifehacker.com.au/2015/02/turn-a-raspberry-pi-into-an-ad-blocker-with-a-single-command/) (Feburary, 2015)
- [Lifehacker: Turn A Raspberry Pi Into An Ad Blocker With A Single Command](https://www.lifehacker.com.au/2015/02/turn-a-raspberry-pi-into-an-ad-blocker-with-a-single-command/) (February, 2015)
- [MakeUseOf: Adblock Everywhere: The Raspberry Pi-Hole Way](http://www.makeuseof.com/tag/adblock-everywhere-raspberry-pi-hole-way/) (March, 2015)
- [MakeUseOf: Adblock Everywhere: The Raspberry Pi-Hole Way](http://www.makeuseof.com/tag/adblock-everywhere-raspberry-pi-hole-way/) (March, 2015)
- [Catchpoint: Ad-Blocking on Apple iOS9: Valuing the End User Experience](http://blog.catchpoint.com/2015/09/14/ad-blocking-apple/) (September, 2015)
- [Catchpoint: Ad-Blocking on Apple iOS9: Valuing the End User Experience](http://blog.catchpoint.com/2015/09/14/ad-blocking-apple/) (September, 2015)
- [Security Now Netcast: Pi-hole](https://www.youtube.com/watch?v=p7-osq_y8i8&t=100m26s) (October, 2015)
- [Security Now Netcast: Pi-hole](https://www.youtube.com/watch?v=p7-osq_y8i8&t=100m26s) (October, 2015)
# Put the current Internal Field Separator into another variable so it can be restored later
echo_current_diagnostic "${title}"
OLD_IFS="$IFS"
OLD_IFS="$IFS"
# Get the lines that are in the file(s) and store them in an array for parsing later
IFS=$'\r\n'
IFS=$'\r\n'
localentries=()
mapfile -t entries < <(\
sqlite3 "${PIHOLE_GRAVITY_DB_FILE}"\
-cmd ".headers on"\
-cmd ".mode column"\
-cmd ".width ${widths}"\
"${query}"\
)
for line in "${entries[@]}";do
log_write "${line}"
done
IFS="$OLD_IFS"
}
show_groups(){
show_db_entries "Groups""SELECT id,CASE enabled WHEN '0' THEN ' 0' WHEN '1' THEN ' 1' ELSE enabled END enabled,name,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,description FROM \"group\"""4 7 50 19 19 50"
}
show_adlists(){
show_db_entries "Adlists""SELECT id,CASE enabled WHEN '0' THEN ' 0' WHEN '1' THEN ' 1' ELSE enabled END enabled,GROUP_CONCAT(adlist_by_group.group_id) group_ids,address,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,comment FROM adlist LEFT JOIN adlist_by_group ON adlist.id = adlist_by_group.adlist_id GROUP BY id;""4 7 12 100 19 19 50"
}
show_domainlist(){
show_db_entries "Domainlist (0/1 = exact white-/blacklist, 2/3 = regex white-/blacklist)""SELECT id,CASE type WHEN '0' THEN '0 ' WHEN '1' THEN ' 1 ' WHEN '2' THEN ' 2 ' WHEN '3' THEN ' 3' ELSE type END type,CASE enabled WHEN '0' THEN ' 0' WHEN '1' THEN ' 1' ELSE enabled END enabled,GROUP_CONCAT(domainlist_by_group.group_id) group_ids,domain,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,comment FROM domainlist LEFT JOIN domainlist_by_group ON domainlist.id = domainlist_by_group.domainlist_id GROUP BY id;""4 4 7 12 100 19 19 50"
}
show_clients(){
show_db_entries "Clients""SELECT id,GROUP_CONCAT(client_by_group.group_id) group_ids,ip,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,comment FROM client LEFT JOIN client_by_group ON client.id = client_by_group.client_id GROUP BY id;""4 12 100 19 19 50"
}
analyze_gravity_list(){
echo_current_diagnostic "Gravity List and Database"
# As underscores are legitimate parts of domains, we escape them when using the LIKE operator.
# Underscores are SQLite wildcards matching exactly one character. We obviously want to suppress this
# behavior. The "ESCAPE '\'" clause specifies that an underscore preceded by an '\' should be matched
# as a literal underscore character. We pretreat the $domain variable accordingly to escape underscores.
if[["${table}"=="gravity"]];then
case"${exact}" in
"exact")querystr="SELECT gravity.domain,adlist.address,adlist.enabled FROM gravity LEFT JOIN adlist ON adlist.id = gravity.adlist_id WHERE domain = '${domain}'";;
* )querystr="SELECT gravity.domain,adlist.address,adlist.enabled FROM gravity LEFT JOIN adlist ON adlist.id = gravity.adlist_id WHERE domain LIKE '%${domain//_/\\_}%' ESCAPE '\\'";;
esac
else
case"${exact}" in
"exact")querystr="SELECT domain,enabled FROM domainlist WHERE type = '${type}' AND domain = '${domain}'";;
* )querystr="SELECT domain,enabled FROM domainlist WHERE type = '${type}' AND domain LIKE '%${domain//_/\\_}%' ESCAPE '\\'";;
</head><bodyid='splashpage'><imgsrc='/admin/img/logo.svg'/><br/>Pi-<b>hole</b>: Your black hole for Internet advertisements<br><ahref='/admin'>Did you mean to go to the admin panel?</a></body></html>
</head>
<bodyid='splashpage'>
<imgsrc='admin/img/logo.svg'/><br/>
Pi-<b>hole</b>: Your black hole for Internet advertisements<br/>
<ahref='/admin'>Did you mean to go to the admin panel?</a>
</body>
</html>
";
";
// Set splash/landing page based off presence of $landPage
// Set splash/landing page based off presence of $landPage
@ -68,7 +76,7 @@ if ($serverName === "pi.hole") {
// Unset variables so as to not be included in $landPage
// Unset variables so as to not be included in $landPage
die("[ERROR] There are no domain lists generated lists within <code>/etc/pihole/</code>! Please update gravity by running <code>pihole -g</code>, or repair Pi-hole using <code>pihole -r</code>.");
$gravityDBFile = $FTLsettings["GRAVITYDB"];
} else {
$gravityDBFile = "/etc/pihole/gravity.db";
}
}
// Set location of adlists file
// Connect to gravity.db
if (is_file("/etc/pihole/adlists.list")) {
try {
$adLists = "/etc/pihole/adlists.list";
$db = new SQLite3($gravityDBFile, SQLITE3_OPEN_READONLY);
# Some distros vary slightly so these fixes for dependencies may apply
# Some distros vary slightly so these fixes for dependencies may apply
# on Ubuntu 18.04.1 LTS we need to add the universe repository to gain access to dialog and dhcpcd5
# on Ubuntu 18.04.1 LTS we need to add the universe repository to gain access to dhcpcd5
APT_SOURCES="/etc/apt/sources.list"
APT_SOURCES="/etc/apt/sources.list"
if awk 'BEGIN{a=1;b=0}/bionic main/{a=0}/bionic.*universe/{b=1}END{exit a + b}'${APT_SOURCES};then
if awk 'BEGIN{a=1;b=0}/bionic main/{a=0}/bionic.*universe/{b=1}END{exit a + b}'${APT_SOURCES};then
if ! whiptail --defaultno --title "Dependencies Require Update to Allowed Repositories" --yesno "Would you like to enable 'universe' repository?\\n\\nThis repository is required by the following packages:\\n\\n- dhcpcd5\\n- dialog" ${r}${c};then
if ! whiptail --defaultno --title "Dependencies Require Update to Allowed Repositories" --yesno "Would you like to enable 'universe' repository?\\n\\nThis repository is required by the following packages:\\n\\n- dhcpcd5" "${r}""${c}";then
printf" %b Aborting installation: dependencies could not be installed.\\n""${CROSS}"
printf" %b Aborting installation: dependencies could not be installed.\\n""${CROSS}"
exit# exit the installer
exit# exit the installer
else
else
printf" %b Enabling universe package repository for Ubuntu Bionic\\n""${INFO}"
printf" %b Enabling universe package repository for Ubuntu Bionic\\n""${INFO}"
cp ${APT_SOURCES}${APT_SOURCES}.backup # Backup current repo list
cp -p ${APT_SOURCES}${APT_SOURCES}.backup # Backup current repo list
printf" %b Backed up current configuration to %s\\n""${TICK}""${APT_SOURCES}.backup"
printf" %b Backed up current configuration to %s\\n""${TICK}""${APT_SOURCES}.backup"
# Since PHP 7 is available by default, install via default PHP package names
# Since PHP 7 is available by default, install via default PHP package names
: # do nothing as PHP is current
: # do nothing as PHP is current
@ -332,7 +344,7 @@ elif is_command rpm ; then
rpm -q ${REMI_PKG}&> /dev/null ||rc=$?
rpm -q ${REMI_PKG}&> /dev/null ||rc=$?
if[[$rc -ne 0]];then
if[[$rc -ne 0]];then
# The PHP version available via default repositories is older than version 7
# The PHP version available via default repositories is older than version 7
if ! whiptail --defaultno --title "PHP 7 Update (recommended)" --yesno "PHP 7.x is recommended for both security and language features.\\nWould you like to install PHP7 via Remi's RPM repository?\\n\\nSee: https://rpms.remirepo.net for more information"${r}${c};then
if ! whiptail --defaultno --title "PHP 7 Update (recommended)" --yesno "PHP 7.x is recommended for both security and language features.\\nWould you like to install PHP7 via Remi's RPM repository?\\n\\nSee: https://rpms.remirepo.net for more information""${r}""${c}";then
# User decided to NOT update PHP from REMI, attempt to install the default available PHP version
# User decided to NOT update PHP from REMI, attempt to install the default available PHP version
printf" %b User opt-out of PHP 7 upgrade on CentOS. Deprecated PHP may be in use.\\n""${INFO}"
printf" %b User opt-out of PHP 7 upgrade on CentOS. Deprecated PHP may be in use.\\n""${INFO}"
: # continue with unsupported php version
: # continue with unsupported php version
@ -355,7 +367,7 @@ elif is_command rpm ; then
fi
fi
else
else
# Warn user of unsupported version of Fedora or CentOS
# Warn user of unsupported version of Fedora or CentOS
if ! whiptail --defaultno --title "Unsupported RPM based distribution" --yesno "Would you like to continue installation on an unsupported RPM based distribution?\\n\\nPlease ensure the following packages have been installed manually:\\n\\n- lighttpd\\n- lighttpd-fastcgi\\n- PHP version 7+"${r}${c};then
if ! whiptail --defaultno --title "Unsupported RPM based distribution" --yesno "Would you like to continue installation on an unsupported RPM based distribution?\\n\\nPlease ensure the following packages have been installed manually:\\n\\n- lighttpd\\n- lighttpd-fastcgi\\n- PHP version 7+""${r}""${c}";then
printf" %b Aborting installation due to unsupported RPM based distribution\\n""${CROSS}"
printf" %b Aborting installation due to unsupported RPM based distribution\\n""${CROSS}"
exit# exit the installer
exit# exit the installer
else
else
@ -377,16 +389,12 @@ is_repo() {
# Use a named, local variable instead of the vague $1, which is the first argument passed to this function
# Use a named, local variable instead of the vague $1, which is the first argument passed to this function
# These local variables should always be lowercase
# These local variables should always be lowercase
localdirectory="${1}"
localdirectory="${1}"
# A local variable for the current directory
local curdir
# A variable to store the return code
# A variable to store the return code
local rc
local rc
# Assign the current directory variable by using pwd
curdir="${PWD}"
# If the first argument passed to this function is a directory,
# If the first argument passed to this function is a directory,
if[[ -d "${directory}"]];then
if[[ -d "${directory}"]];then
# move into the directory
# move into the directory
cd "${directory}"
pushd "${directory}"&> /dev/null ||return1
# Use git to check if the directory is a repo
# Use git to check if the directory is a repo
# git -C is not used here to support git versions older than 1.8.4
# git -C is not used here to support git versions older than 1.8.4
git status --short &> /dev/null ||rc=$?
git status --short &> /dev/null ||rc=$?
@ -396,7 +404,7 @@ is_repo() {
rc=1
rc=1
fi
fi
# Move back into the directory the user started in
# Move back into the directory the user started in
cd "${curdir}"
popd &> /dev/null ||return1
# Return the code; if one is not set, return 0
# Return the code; if one is not set, return 0
return"${rc:-0}"
return"${rc:-0}"
}
}
@ -406,6 +414,7 @@ make_repo() {
# Set named variables for better readability
# Set named variables for better readability
localdirectory="${1}"
localdirectory="${1}"
localremoteRepo="${2}"
localremoteRepo="${2}"
# The message to display when this function is running
# The message to display when this function is running
str="Clone ${remoteRepo} into ${directory}"
str="Clone ${remoteRepo} into ${directory}"
# Display the message and use the color table to preface the message with an "info" indicator
# Display the message and use the color table to preface the message with an "info" indicator
@ -417,9 +426,21 @@ make_repo() {
fi
fi
# Clone the repo and return the return code from this command
# Clone the repo and return the return code from this command
# Data in the repositories is public anyway so we can make it readable by everyone (+r to keep executable permission if already set by git)
chmod -R a+rX "${directory}"
# Move back into the original directory
# Move back into the original directory
cd"${curdir}"&> /dev/null ||return1
popd&> /dev/null ||return1
return0
return0
}
}
@ -490,15 +516,19 @@ resetRepo() {
# Use named variables for arguments
# Use named variables for arguments
localdirectory="${1}"
localdirectory="${1}"
# Move into the directory
# Move into the directory
cd "${directory}"&> /dev/null ||return1
pushd "${directory}"&> /dev/null ||return1
# Store the message in a variable
# Store the message in a variable
str="Resetting repository within ${1}..."
str="Resetting repository within ${1}..."
# Show the message
# Show the message
printf" %b %s...""${INFO}""${str}"
printf" %b %s...""${INFO}""${str}"
# Use git to remove the local changes
# Use git to remove the local changes
git reset --hard &> /dev/null ||return$?
git reset --hard &> /dev/null ||return$?
# Data in the repositories is public anyway so we can make it readable by everyone (+r to keep executable permission if already set by git)
chmod -R a+rX "${directory}"
# And show the status
# And show the status
printf"%b %b %s\\n""${OVER}""${TICK}""${str}"
printf"%b %b %s\\n""${OVER}""${TICK}""${str}"
# Return to where we came from
popd&> /dev/null ||return1
# Returning success anyway?
# Returning success anyway?
return0
return0
}
}
@ -540,15 +570,15 @@ get_available_interfaces() {
# A function for displaying the dialogs the user sees when first running the installer
# A function for displaying the dialogs the user sees when first running the installer
welcomeDialogs(){
welcomeDialogs(){
# Display the welcome dialog using an appropriately sized window via the calculation conducted earlier in the script
# Display the welcome dialog using an appropriately sized window via the calculation conducted earlier in the script
whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer""\\n\\nThis installer will transform your device into a network-wide ad blocker!"${r}${c}
whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer""\\n\\nThis installer will transform your device into a network-wide ad blocker!""${r}""${c}"
# Request that users donate if they enjoy the software since we all work on it in our free time
# Request that users donate if they enjoy the software since we all work on it in our free time
whiptail --msgbox --backtitle "Plea" --title "Free and open source""\\n\\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate"${r}${c}
whiptail --msgbox --backtitle "Plea" --title "Free and open source""\\n\\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate""${r}""${c}"
# Explain the need for a static address
# Explain the need for a static address
whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed""\\n\\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly.
whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed""\\n\\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly.
In the next section, you can choose to use your current network settings (DHCP) or to manually edit them."${r}${c}
In the next section, you can choose to use your current network settings (DHCP) or to manually edit them.""${r}""${c}"
}
}
# We need to make sure there is enough space before installing, so there is a function to check this
# We need to make sure there is enough space before installing, so there is a function to check this
@ -635,7 +665,7 @@ chooseInterface() {
# Feed the available interfaces into this while loop
# Feed the available interfaces into this while loop
done<<<"${availableInterfaces}"
done<<<"${availableInterfaces}"
# The whiptail command that will be run, stored in a variable
# The whiptail command that will be run, stored in a variable
chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)"${r}${c}${interfaceCount})
chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)""${r}""${c}""${interfaceCount}")
# Now run the command using the interfaces saved into the array
# Now run the command using the interfaces saved into the array
whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported""$IPV6_ADDRESS will be used to block ads."${r}${c}
whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported""$IPV6_ADDRESS will be used to block ads.""${r}""${c}"
fi
fi
}
}
@ -726,7 +756,7 @@ use4andor6() {
local useIPv4
local useIPv4
local useIPv6
local useIPv6
# Let use select IPv4 and/or IPv6 via a checklist
# Let use select IPv4 and/or IPv6 via a checklist
cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)"${r}${c} 2)
cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)""${r}""${c}" 2)
# In an array, show the options available:
# In an array, show the options available:
# IPv4 (on by default)
# IPv4 (on by default)
options=(IPv4 "Block ads over IPv4" on
options=(IPv4 "Block ads over IPv4" on
@ -775,11 +805,11 @@ getStaticIPv4Settings() {
# This is useful for users that are using DHCP reservations; then we can just use the information gathered via our functions
# This is useful for users that are using DHCP reservations; then we can just use the information gathered via our functions
if whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address?
if whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address?
IP address: ${IPV4_ADDRESS}
IP address: ${IPV4_ADDRESS}
Gateway: ${IPv4gw}"${r}${c}; then
Gateway: ${IPv4gw}""${r}""${c}"; then
# If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict.
# If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict.
whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict""It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that.
whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict""It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that.
If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want.
If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want.
It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address."${r}${c}
It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address.""${r}""${c}"
# Nothing else to do since the variables are already set above
# Nothing else to do since the variables are already set above
else
else
# Otherwise, we need to ask the user to input their desired settings.
# Otherwise, we need to ask the user to input their desired settings.
@ -788,13 +818,13 @@ It is also possible to use a DHCP reservation, but if you are going to do that,
{ipSettingsCorrect=False;echo -e "${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}";exit 1;}
{ipSettingsCorrect=False;echo -e "${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}";exit 1;}
printf" %b Your static IPv4 gateway: %s\\n""${INFO}""${IPv4gw}"
printf" %b Your static IPv4 gateway: %s\\n""${INFO}""${IPv4gw}"
@ -802,7 +832,7 @@ It is also possible to use a DHCP reservation, but if you are going to do that,
# Give the user a chance to review their settings before moving on
# Give the user a chance to review their settings before moving on
if whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct?
if whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct?
IP address: ${IPV4_ADDRESS}
IP address: ${IPV4_ADDRESS}
Gateway: ${IPv4gw}"${r}${c}; then
Gateway: ${IPv4gw}""${r}""${c}"; then
# After that's done, the loop ends and we move on
# After that's done, the loop ends and we move on
ipSettingsCorrect=True
ipSettingsCorrect=True
else
else
@ -825,11 +855,12 @@ setDHCPCD() {
echo"interface ${PIHOLE_INTERFACE}
echo"interface ${PIHOLE_INTERFACE}
static ip_address=${IPV4_ADDRESS}
static ip_address=${IPV4_ADDRESS}
static routers=${IPv4gw}
static routers=${IPv4gw}
static domain_name_servers=127.0.0.1" | tee -a /etc/dhcpcd.conf >/dev/null
static domain_name_servers=${PIHOLE_DNS_1}${PIHOLE_DNS_2}" | tee -a /etc/dhcpcd.conf >/dev/null
# Then use the ip command to immediately set the new address
# Then use the ip command to immediately set the new address
ip addr replace dev "${PIHOLE_INTERFACE}""${IPV4_ADDRESS}"
ip addr replace dev "${PIHOLE_INTERFACE}""${IPV4_ADDRESS}"
# Also give a warning that the user may need to reboot their system
# Also give a warning that the user may need to reboot their system
printf" %b Set IP address to %s \\n You may need to restart after the install is complete\\n""${TICK}""${IPV4_ADDRESS%/*}"
printf" %b Set IP address to %s\\n""${TICK}""${IPV4_ADDRESS%/*}"
printf" %b You may need to restart after the install is complete\\n""${INFO}"
fi
fi
}
}
@ -850,7 +881,7 @@ setIFCFG() {
# Put the IP in variables without the CIDR notation
# Put the IP in variables without the CIDR notation
printf -v CIDR "%s""${IPV4_ADDRESS##*/}"
printf -v CIDR "%s""${IPV4_ADDRESS##*/}"
# Backup existing interface configuration:
# Backup existing interface configuration:
cp "${IFCFG_FILE}""${IFCFG_FILE}".pihole.orig
cp -p "${IFCFG_FILE}""${IFCFG_FILE}".pihole.orig
# Build Interface configuration file using the GLOBAL variables we have
# Build Interface configuration file using the GLOBAL variables we have
{
{
echo"# Configured via Pi-hole installer"
echo"# Configured via Pi-hole installer"
@ -864,6 +895,8 @@ setIFCFG() {
echo"DNS2=$PIHOLE_DNS_2"
echo"DNS2=$PIHOLE_DNS_2"
echo"USERCTL=no"
echo"USERCTL=no"
}> "${IFCFG_FILE}"
}> "${IFCFG_FILE}"
chmod 644"${IFCFG_FILE}"
chown root:root "${IFCFG_FILE}"
# Use ip to immediately set the new address
# Use ip to immediately set the new address
ip addr replace dev "${PIHOLE_INTERFACE}""${IPV4_ADDRESS}"
ip addr replace dev "${PIHOLE_INTERFACE}""${IPV4_ADDRESS}"
# If NetworkMangler command line interface exists and ready to mangle,
# If NetworkMangler command line interface exists and ready to mangle,
@ -928,7 +961,7 @@ valid_ip() {
# and set the new one to a dot (period)
# and set the new one to a dot (period)
IFS='.'
IFS='.'
# Put the IP into an array
# Put the IP into an array
ip=(${ip})
read -r -a ip <<<"${ip}"
# Restore the IFS to what it was
# Restore the IFS to what it was
IFS=${OIFS}
IFS=${OIFS}
## Evaluate each octet by checking if it's less than or equal to 255 (the max for each octet)
## Evaluate each octet by checking if it's less than or equal to 255 (the max for each octet)
@ -938,7 +971,7 @@ valid_ip() {
stat=$?
stat=$?
fi
fi
# Return the exit code
# Return the exit code
return${stat}
return"${stat}"
}
}
# A function to choose the upstream DNS provider(s)
# A function to choose the upstream DNS provider(s)
@ -968,13 +1001,11 @@ setDNS() {
# Restore the IFS to what it was
# Restore the IFS to what it was
IFS=${OIFS}
IFS=${OIFS}
# In a whiptail dialog, show the options
# In a whiptail dialog, show the options
DNSchoices=$(whiptail --separate-output --menu "Select Upstream DNS Provider. To use your own, select Custom."${r}${c}7\
DNSchoices=$(whiptail --separate-output --menu "Select Upstream DNS Provider. To use your own, select Custom.""${r}""${c}"7\
"${DNSChooseOptions[@]}" 2>&1 >/dev/tty)||\
"${DNSChooseOptions[@]}" 2>&1 >/dev/tty)||\
# exit if Cancel is selected
# exit if Cancel is selected
{printf" %bCancel was selected, exiting installer%b\\n""${COL_LIGHT_RED}""${COL_NC}";exit 1;}
{printf" %bCancel was selected, exiting installer%b\\n""${COL_LIGHT_RED}""${COL_NC}";exit 1;}
# Display the selection
printf" %b Using ""${INFO}"
# Depending on the user's choice, set the GLOBAl variables to the IP of the respective provider
# Depending on the user's choice, set the GLOBAl variables to the IP of the respective provider
if[["${DNSchoices}"=="Custom"]]
if[["${DNSchoices}"=="Custom"]]
then
then
@ -998,7 +1029,7 @@ setDNS() {
fi
fi
# Dialog for the user to enter custom upstream servers
# Dialog for the user to enter custom upstream servers
piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), separated by a comma.\\n\\nFor example '8.8.8.8, 8.8.4.4'"${r}${c}"${prePopulate}" 3>&1 1>&2 2>&3)||\
piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), separated by a comma.\\n\\nFor example '8.8.8.8, 8.8.4.4'""${r}""${c}""${prePopulate}" 3>&1 1>&2 2>&3)||\
{printf" %bCancel was selected, exiting installer%b\\n""${COL_LIGHT_RED}""${COL_NC}";exit 1;}
{printf" %bCancel was selected, exiting installer%b\\n""${COL_LIGHT_RED}""${COL_NC}";exit 1;}
# Clean user input and replace whitespace with comma.
# Clean user input and replace whitespace with comma.
# Since the settings will not work, stay in the loop
# Since the settings will not work, stay in the loop
DNSSettingsCorrect=False
DNSSettingsCorrect=False
# Otherwise,
# Otherwise,
else
else
# Show the settings
# Show the settings
if(whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\\n DNS Server 1: $PIHOLE_DNS_1\\n DNS Server 2: ${PIHOLE_DNS_2}"${r}${c});then
if(whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\\n DNS Server 1: $PIHOLE_DNS_1\\n DNS Server 2: ${PIHOLE_DNS_2}""${r}""${c}");then
# and break from the loop since the servers are valid
# and break from the loop since the servers are valid
DNSSettingsCorrect=True
DNSSettingsCorrect=True
# Otherwise,
# Otherwise,
else
else
# If the settings are wrong, the loop continues
# If the settings are wrong, the loop continues
@ -1041,7 +1072,7 @@ setDNS() {
fi
fi
fi
fi
done
done
else
else
# Save the old Internal Field Separator in a variable
# Save the old Internal Field Separator in a variable
printf" %b Using upstream DNS: %s (%s)\\n""${INFO}""${DNSchoices}""${DNSIP}"
}
}
# Allow the user to enable/disable logging
# Allow the user to enable/disable logging
@ -1122,7 +1157,7 @@ setAdminFlag() {
local WebChoices
local WebChoices
# Similar to the logging function, ask what the user wants
# Similar to the logging function, ask what the user wants
WebToggleCommand=(whiptail --separate-output --radiolist "Do you wish to install the web admin interface?"${r}${c} 6)
WebToggleCommand=(whiptail --separate-output --radiolist "Do you wish to install the web admin interface?""${r}""${c}" 6)
# with the default being enabled
# with the default being enabled
WebChooseOptions=("On (Recommended)""" on
WebChooseOptions=("On (Recommended)""" on
Off "" off)
Off "" off)
@ -1171,14 +1206,12 @@ chooseBlocklists() {
mv "${adlistFile}""${adlistFile}.old"
mv "${adlistFile}""${adlistFile}.old"
fi
fi
# Let user select (or not) blocklists via a checklist
# Let user select (or not) blocklists via a checklist
cmd=(whiptail --separate-output --checklist "Pi-hole relies on third party lists in order to block ads.\\n\\nYou can use the suggestions below, and/or add your own after installation\\n\\nTo deselect any list, use the arrow keys and spacebar""${r}""${c}"6)
cmd=(whiptail --separate-output --checklist "Pi-hole relies on third party lists in order to block ads.\\n\\nYou can use the suggestions below, and/or add your own after installation\\n\\nTo deselect any list, use the arrow keys and spacebar""${r}""${c}"5)
# In an array, show the options available (all off by default):
# In an array, show the options available (all off by default):
options=(StevenBlack "StevenBlack's Unified Hosts List" on
options=(StevenBlack "StevenBlack's Unified Hosts List" on
MalwareDom "MalwareDomains" on
MalwareDom "MalwareDomains" on
Cameleon "Cameleon" on
DisconTrack "Disconnect.me Tracking" on
DisconTrack "Disconnect.me Tracking" on
DisconAd "Disconnect.me Ads" on
DisconAd "Disconnect.me Ads" on)
HostsFile "Hosts-file.net Ads" on)
# In a variable, show the choices available; exit if Cancel is selected
# In a variable, show the choices available; exit if Cancel is selected
sed -i "s/59 1 /$((1+ RANDOM %58))$((3+ RANDOM %2))/" /etc/cron.d/pihole
sed -i "s/59 1 /$((1+ RANDOM %58))$((3+ RANDOM %2))/" /etc/cron.d/pihole
# Randomize update checker time
# Randomize update checker time
@ -1746,45 +1780,6 @@ create_pihole_user() {
fi
fi
}
}
# Allow HTTP and DNS traffic
configureFirewall(){
printf"\\n"
# If a firewall is running,
if firewall-cmd --state &> /dev/null;then
# ask if the user wants to install Pi-hole's default firewall rules
whiptail --title "Firewall in use" --yesno "We have detected a running firewall\\n\\nPi-hole currently requires HTTP and DNS port access.\\n\\n\\n\\nInstall Pi-hole default firewall rules?"${r}${c}||\
{printf" %b Not installing firewall rulesets.\\n""${INFO}";return 0;}
printf" %b Configuring FirewallD for httpd and pihole-FTL\\n""${TICK}"
whiptail --title "Firewall in use" --yesno "We have detected a running firewall\\n\\nPi-hole currently requires HTTP and DNS port access.\\n\\n\\n\\nInstall Pi-hole default firewall rules?"${r}${c}||\
{printf" %b Not installing firewall rulesets.\\n""${INFO}";return 0;}
printf" %b Installing new IPTables firewall rulesets\\n""${TICK}"
# Check chain first, otherwise a new rule will duplicate old ones
whiptail --defaultno --title "SELinux Enforcing Detected" --yesno "SELinux is being ENFORCED on your system! \\n\\nPi-hole currently does not support SELinux, but you may still continue with the installation.\\n\\nNote: Web Admin will not be fully functional unless you set your policies correctly\\n\\nContinue installing Pi-hole?"${r}${c}||\
@ -1993,7 +2010,7 @@ If you set a new IP address, you should restart the Pi.
The install log is in /etc/pihole.
The install log is in /etc/pihole.
${additional}"${r}${c}
${additional}""${r}""${c}"
}
}
update_dialogs(){
update_dialogs(){
@ -2014,7 +2031,7 @@ update_dialogs() {
opt2b="This will reset your Pi-hole and allow you to enter new settings."
opt2b="This will reset your Pi-hole and allow you to enter new settings."
# Display the information to the user
# Display the information to the user
UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\\n\\nWe have detected an existing install.\\n\\nPlease choose from the following options: \\n($strAdd)"${r}${c}2\
UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\\n\\nWe have detected an existing install.\\n\\nPlease choose from the following options: \\n($strAdd)""${r}""${c}"2\
"${opt1a}""${opt1b}"\
"${opt1a}""${opt1b}"\
"${opt2a}""${opt2b}" 3>&2 2>&1 1>&3)||\
"${opt2a}""${opt2b}" 3>&2 2>&1 1>&3)||\
{printf" %bCancel was selected, exiting installer%b\\n""${COL_LIGHT_RED}""${COL_NC}";exit 1;}
{printf" %bCancel was selected, exiting installer%b\\n""${COL_LIGHT_RED}""${COL_NC}";exit 1;}
@ -2103,6 +2120,8 @@ checkout_pull_branch() {
printf" %b %s""${INFO}""$str"
printf" %b %s""${INFO}""$str"
git checkout "${branch}" --quiet ||return1
git checkout "${branch}" --quiet ||return1
printf"%b %b %s\\n""${OVER}""${TICK}""$str"
printf"%b %b %s\\n""${OVER}""${TICK}""$str"
# Data in the repositories is public anyway so we can make it readable by everyone (+r to keep executable permission if already set by git)
chmod -R a+rX "${directory}"
git_pull=$(git pull ||return 1)
git_pull=$(git pull ||return 1)
@ -2196,6 +2215,8 @@ FTLinstall() {
# Before stopping FTL, we download the macvendor database
# Before stopping FTL, we download the macvendor database
echo -e "\\n ${CROSS} Unable to copy data from ${gravityDBfile} to ${gravityTEMPfile}\\n ${output}"
return1
fi
fi
echo -e "${OVER}${TICK}${str}"
# Swap databases and remove old database
rm "${gravityDBfile}"
mv "${gravityTEMPfile}""${gravityDBfile}"
}
# Update timestamp when the gravity table was last updated successfully
update_gravity_timestamp(){
output=$({printf".timeout 30000\\nINSERT OR REPLACE INTO info (property,value) values ('updated',cast(strftime('%%s', 'now') as int));"| sqlite3 "${gravityDBfile}";} 2>&1)
status="$?"
if[["${status}" -ne 0]];then
echo -e "\\n ${CROSS} Unable to update gravity timestamp in database ${gravityDBfile}\\n ${output}"
return1
fi
return0
}
# Import domains from file and store them in the specified database table
database_table_from_file(){
# Define locals
local table source backup_path backup_file tmpFile type
table="${1}"
source="${2}"
backup_path="${piholeDir}/migration_backup"
backup_file="${backup_path}/$(basename "${2}")"
tmpFile="$(mktemp -p "/tmp" --suffix=".gravity")"
local timestamp
timestamp="$(date --utc +'%s')"
local rowid
declare -i rowid
rowid=1
# Special handling for domains to be imported into the common domainlist table
if[["${table}"=="whitelist"]];then
type="0"
table="domainlist"
elif[["${table}"=="blacklist"]];then
type="1"
table="domainlist"
elif[["${table}"=="regex"]];then
type="3"
table="domainlist"
fi
# Get MAX(id) from domainlist when INSERTing into this table
if[["${table}"=="domainlist"]];then
rowid="$(sqlite3 "${gravityDBfile}""SELECT MAX(id) FROM domainlist;")"