From 551add5f447b25e7c5c3e54c325b287212dfd9c8 Mon Sep 17 00:00:00 2001 From: WaLLy3K Date: Tue, 2 May 2017 17:06:31 +1000 Subject: [PATCH] Update index.php * An "About Pi-hole" link on the block page provides an ELI5 explanation to those not familiar with Pi-hole * An email contact link on the block page provides users of your Pi-hole with a means to easily get in touch with you * Browsing to your Pi-hole's address will show a simple "landing page", which can be replaced by adding "landing.php" within "/var/www/html" * Users manually browsing to file/image based content (i.e: non HTML based content) on blocked sites will be greeted with a small "Blocked by Pi-hole" image * Sites that are manually blacklisted will display a notice of this on the block page * Sites that aren't directly blocked, but have a CNAME record, will show a notification on the block page (e.g: If raw.githubusercontent.com is not blocked, but github.map.fastly.net is) * On the block page, "Back to Safety" now directs the user to "about:home" if Javascript is disabled * Whitelisting is disabled for installs without a password, or if a client does not have Javascript * Known issues: * Admin Console needs a text field under "Web User Interface" where the admin can enter a preferred contact email when a site needs to be whitelisted, to be saved to setupVars.conf with the key "ADMIN_EMAIL" * Admin Console needs a text field under "Networking" where the admin can enter their Pi-hole's externally contactable FQDN, allowing access to their landing page when browsing to mypi.duckdns.org, to be saved to setupVars.conf with the key "FQDN" * I am not aware of expected output of `$_SERVER["VIRTUAL_HOST"]`, so I have assumed it should be filtered as if it's a domain --- advanced/index.php | 487 ++++++++++++++++++++++++++++----------------- 1 file changed, 299 insertions(+), 188 deletions(-) diff --git a/advanced/index.php b/advanced/index.php index bfc44a1d..aeb607ca 100644 --- a/advanced/index.php +++ b/advanced/index.php @@ -1,224 +1,335 @@ SERVER_NAME header output does not appear to be valid: ".$_SERVER["SERVER_NAME"].""); } -// Retrieve server URI extension (EG: jpg, exe, php) -ini_set('pcre.recursion_limit',100); -$uriExt = pathinfo($uri, PATHINFO_EXTENSION); - -// Define which URL extensions get rendered as "Website Blocked" -$webExt = array('asp', 'htm', 'html', 'php', 'rss', 'xml'); - -// Get IPv4 and IPv6 addresses from setupVars.conf (if available) +// Get values from setupVars.conf $setupVars = parse_ini_file("/etc/pihole/setupVars.conf"); -$ipv4 = isset($setupVars["IPV4_ADDRESS"]) ? explode("/", $setupVars["IPV4_ADDRESS"])[0] : $_SERVER['SERVER_ADDR']; -$ipv6 = isset($setupVars["IPV6_ADDRESS"]) ? explode("/", $setupVars["IPV6_ADDRESS"])[0] : $_SERVER['SERVER_ADDR']; +$svFQDN = (!empty($setupVars["FQDN"]) && validate_server_name($setupVars["FQDN"]) === TRUE) ? $setupVars["FQDN"] : ""; +$svPasswd = !empty($setupVars["WEBPASSWORD"]); +$svEmail = (!empty($setupVars["ADMIN_EMAIL"]) && filter_var($setupVars["ADMIN_EMAIL"], FILTER_VALIDATE_EMAIL)) ? $setupVars["ADMIN_EMAIL"] : ""; +unset($setupVars); -$AUTHORIZED_HOSTNAMES = array( - $ipv4, - $ipv6, - str_replace(array("[","]"), array("",""), $_SERVER["SERVER_ADDR"]), - "pi.hole", - "localhost"); -// Allow user set virtual hostnames -$virtual_host = getenv('VIRTUAL_HOST'); -if (!empty($virtual_host)) - array_push($AUTHORIZED_HOSTNAMES, $virtual_host); +// Set landing page name, found within /var/www/html/ +$landPage = "landing.php"; -// Immediately quit since we didn't block this page (the IP address or pi.hole is explicitly requested) -if(validIP($serverName) || in_array($serverName,$AUTHORIZED_HOSTNAMES)) -{ - http_response_code(404); - die(); +// Set empty array for hostnames to be accepted as self address for splash page +$authorizedHosts = []; + +// Append FQDN to $authorizedHosts +if (!empty($svFQDN)) array_push($authorizedHosts, $svFQDN); + +// Append virtual hostname to $authorizedHosts +if (!empty($_SERVER["VIRTUAL_HOST"])) { + if (validate_server_name($_SERVER["VIRTUAL_HOST"]) === TRUE) { + array_push($authorizedHosts, $_SERVER["VIRTUAL_HOST"]); + } else { + die("[ERROR]: VIRTUAL_HOST header output does not appear to be valid: ".$_SERVER["VIRTUAL_HOST"].""); + } } -if(in_array($uriExt, $webExt) || empty($uriExt)) -{ - // Requested resource has an extension listed in $webExt - // or no extension (index access to some folder incl. the root dir) - $showPage = true; -} -else -{ - // Something else - $showPage = false; +// Set which extension types get rendered as "Website Blocked" (Including "" for index file extensions) +$validExtTypes = array("asp", "htm", "html", "php", "rss", "xml", ""); + +// Get extension of current URL +$currentUrlExt = pathinfo($_SERVER["REQUEST_URI"], PATHINFO_EXTENSION); + +// Set mobile friendly viewport +$viewPort = ''; + +// Set response header +function setHeader($type = "x") { + header("X-Pi-hole: A black hole for Internet advertisements."); + if (isset($type) && $type === "js") header("Content-Type: application/javascript"); } -// Handle incoming URI types -if (!$showPage) -{ -?> - - - - - - - - + $viewPort + +
Pi-hole: Your black hole for Internet advertisements + "; + $pageType = is_file(getcwd()."/$landPage") ? include $landPage : "$splashPage"; + unset($serverName, $svFQDN, $svPasswd, $svEmail, $authorizedHosts, $validExtTypes, $currentUrlExt, $viewPort); + exit($pageType); +} elseif ($currentUrlExt === "js") { + // Set Javascript redirect for blocked sources + exit(setHeader("js").'var x = "Pi-hole: A black hole for Internet advertisements."'); +} elseif (strpos($_SERVER["REQUEST_URI"], "?") !== FALSE && isset($_SERVER["HTTP_REFERER"])) { + // Set blank image upon receiving REQUEST_URI w/ query string & HTTP_REFERRER (Presumably from iframe) + exit(setHeader().' + + + '); +} elseif (!in_array($currentUrlExt, $validExtTypes) || substr_count($_SERVER["REQUEST_URI"], "?")) { + // Set svg image upon receiving non $validExtTypes URL extension or query string (Presumably not from an iframe) + $blockImg = 'Blocked by Pi-hole'; + exit(setHeader()." + $viewPort + $blockImg + "); } -// Get Pi-hole version -$piHoleVersion = exec('cd /etc/.pihole/ && git describe --tags --abbrev=0'); +/* Start processing block page from here */ -// Don't show the URI if it is the root directory -if($uri == "/") -{ - $uri = ""; +// Get Pi-hole core branch name +$phBranch = exec("cd /etc/.pihole/ && git rev-parse --abbrev-ref HEAD"); +if ($phBranch !== "master") { + error_reporting(E_ALL); + ini_set("display_errors", 1); + ini_set("display_startup_errors", 1); } +// Validate SERVER_IP output +if (filter_var($_SERVER['SERVER_ADDR'], FILTER_VALIDATE_IP)) { + $serverAddr = $_SERVER["SERVER_ADDR"]; +} else { + die("[ERROR]: SERVER_IP header output does not appear to be valid: ".$_SERVER["SERVER_ADDR"].""); +} + +// Determine placeholder text based off $svPasswd presence +$wlPlaceHolder = empty($svPasswd) ? "No admin password set" : "Javascript disabled"; + +// Get admin email address +$bpAskAdmin = !empty($svEmail) ? '' : ""; + +// Determine if at least one block list has been generated +if (empty(glob("/etc/pihole/list.0.*.domains"))) die("[ERROR]: There are no domain lists generated lists within /etc/pihole/! Please update gravity by running pihole -g, or repair Pi-hole using pihole -r."); + +// Get contents of adlist.list +$adLists = is_file("/etc/pihole/adlists.list") ? "/etc/pihole/adlists.list" : "/etc/pihole/adlists.default"; +if (!is_file($adLists)) die("[ERROR]: Unable to find file: $adLists"); + +// Get all URLs starting with "http" or "www" from $adLists and re-index array numerically +$adlistsUrls = array_values(preg_grep("/(^http)|(^www)/i", file($adLists, FILE_IGNORE_NEW_LINES))); +if (empty($adlistsUrls)) die("[ERROR]: There are no adlist URL's found within $adLists"); +$adlistsCount = count($adlistsUrls) + 3; // +1 because array starts at 0, +2 for Blacklist & Wildcard lists + +// Get results of queryads.php exact search +ini_set("default_socket_timeout", 3); +function queryAds($serverName) { + $preQueryTime = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"]; + $queryAds = file("http://127.0.0.1/admin/scripts/pi-hole/php/queryads.php?domain=$serverName&exact", FILE_IGNORE_NEW_LINES); + $queryTime = sprintf("%.0f", (microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"]) - $preQueryTime); + try { + if ($queryTime >= ini_get("default_socket_timeout")) { + throw new Exception ("Connection timeout (".ini_get("default_socket_timeout")."s)"); + } elseif ($queryAds[0][0] === ":") { + if (strpos($queryAds[0], "Invalid") !== FALSE) throw new Exception ("Invalid Domain ($serverName)"); + if (strpos($queryAds[0], "No exact") !== FALSE) return array("0" => "none"); + throw new Exception ("Unhandled error message ($queryAds[0])"); + } elseif ($queryAds[0][0] !== "/") { + throw new Exception ("Unexpected output ($queryAds[0])"); + } + return $queryAds; + } catch (Exception $e) { + return array("0" => "error", "1" => $e->getMessage()); + } +} +$queryAds = queryAds($serverName); + +if ($queryAds[0] === "error") { + die("[ERROR]: Unable to parse results from queryads.php: ".$queryAds[1].""); +} + +// Filter, sort, and count $queryAds array +if ($queryAds[0] !== "none") { + $queryAds = preg_replace("/(\/etc\/pihole\/)|(\/etc\/dnsmasq\.d\/)/", "", $queryAds); + $queryAds = preg_replace("/(^list\.)|(\..*domains)/", "", $queryAds); + $featuredTotal = count($queryAds); +} + +// Determine if domain has been blacklisted or wildcarded +if ($queryAds[0] === "blacklist.txt") { + $intBlacklist = array("π" => $queryAds[0]); + $queryAds[0] = "π"; // Manually blacklisted sites do not have a number + $notableFlagClass = "blacklist"; +} elseif ($queryAds[0] === "03-pihole-wildcard.conf") { + $intBlacklist = array("π" => $queryAds[0]); + $queryAds[0] = "π"; + $notableFlagClass = "wildcard"; +} elseif ($queryAds[0] === "none") { + $featuredTotal = "0"; + $notableFlagClass = "noblock"; + + // Determine appropriate info message if CNAME exists + $dnsRecord = dns_get_record("$serverName")[0]; + if (array_key_exists("target", $dnsRecord)) { + $wlInfo = $dnsRecord['target']; + } else { + $wlInfo = "recentwl"; + } +} + +// Merge $intBlacklist with $adlistsUrls if domain has been blacklisted or wildcarded +if (isset($intBlacklist)) $adlistsUrls = array_merge($intBlacklist, $adlistsUrls); + +// Set #bpOutput notification +$wlOutputClass = (isset($wlInfo) && $wlInfo === "recentwl") ? $wlInfo : "hidden"; +$wlOutput = (isset($wlInfo) && $wlInfo !== "recentwl") ? "$wlInfo" : ""; + +// Get Pi-hole core version +if ($phBranch !== "master") { + $phVersion = exec("cd /etc/.pihole/ && git describe --long --dirty --tags"); + $execTime = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"]; +} else { + $phVersion = exec("cd /etc/.pihole/ && git describe --tags --abbrev=0"); +} ?> + - - Website Blocked - - - - + + + + + + + + ● <?=$serverName ?> + + - +
-

Website Blocked

+

+ +

+
+ + +
+
+
+

Open Source Ad Blocker + Designed for Raspberry Pi +

+
+ +
+ +
+ +
+
-
Access to the following site has been blocked:
-
-
If you have an ongoing use for this website, please ask the owner of the Pi-hole in your network to have it whitelisted.
- - - - This page is blocked because it is explicitly contained within the following block list(s): -
- - +
+
+

+
+ +
+

+
+ +
+
+ + 0) echo ''; ?> +
+ +
+ +
 0) foreach ($queryAds as $num) { echo "[$num]:$adlistsUrls[$num]\n"; } ?>
+
+ + +
+
-
Generated by Pi-hole
- - - +
. Pi-hole ()
+
+ - - + +