diff --git a/api_FTL.php b/api_FTL.php index 3e1b350a..ed6741dd 100644 --- a/api_FTL.php +++ b/api_FTL.php @@ -321,7 +321,7 @@ else $allQueries = array(); foreach($return as $line) { - $tmp = explode(" ",$line); + $tmp = str_getcsv($line," "); // UTF-8 encode domain $tmp[2] = utf8_encode(str_replace("~"," ",$tmp[2])); // UTF-8 encode client host name diff --git a/api_db.php b/api_db.php index e40ac160..427fc3cc 100644 --- a/api_db.php +++ b/api_db.php @@ -11,6 +11,7 @@ header('Content-type: application/json'); require("scripts/pi-hole/php/database.php"); require("scripts/pi-hole/php/password.php"); require("scripts/pi-hole/php/auth.php"); +require_once("scripts/pi-hole/php/func.php"); check_cors(); // Set maximum execution time to 10 minutes @@ -31,7 +32,7 @@ function resolveHostname($clientip, $printIP) return $clientname; } - else if(filter_var($clientip, FILTER_VALIDATE_IP)) + else if(validIP($clientip)) { // Get host name of client and convert to lower case $clientname = strtolower(gethostbyaddr($ipaddr)); diff --git a/index.php b/index.php index fd0c7ba7..ff8e71ea 100644 --- a/index.php +++ b/index.php @@ -150,7 +150,7 @@
-

Queries answered by

+

Upstream servers

diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index e8e787ef..80344564 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -75,29 +75,37 @@ $(function () { tableApi = $("#all-queries").DataTable({ rowCallback: function (row, data) { + var replyid = parseInt(data[5], 10); // DNSSEC status var dnssecStatus; + var ede = data[11]; switch (data[6]) { case "1": - dnssecStatus = '
SECURE'; + dnssecStatus = '
SECURE'; break; case "2": - dnssecStatus = '
INSECURE'; + dnssecStatus = '
INSECURE'; break; case "3": - dnssecStatus = '
BOGUS'; + dnssecStatus = '
BOGUS'; break; case "4": - dnssecStatus = '
ABANDONED'; + dnssecStatus = '
ABANDONED'; break; case "5": - dnssecStatus = '
UNKNOWN'; + dnssecStatus = '
UNKNOWN'; break; default: // No DNSSEC dnssecStatus = ""; } + if (dnssecStatus.length > 0) { + if (ede.length > 0) dnssecStatus += " (" + ede + ")"; + else if (replyid === 7) dnssecStatus += " (refused upstream)"; + dnssecStatus += ""; + } + // Query status var fieldtext, buttontext, @@ -114,10 +122,10 @@ $(function () { break; case "2": colorClass = "text-green"; - fieldtext = - "OK (forwarded to " + + fieldtext = replyid === 0 ? "OK, sent to " : "OK, answered by "; + fieldtext += + "" + (data.length > 10 && data[10] !== "N/A" ? data[10] : "") + - ")" + dnssecStatus; buttontext = ''; @@ -208,6 +216,12 @@ $(function () { buttontext = ""; } + // Add EDE here if available and not included in dnssecStatus + if (ede.length > 0 && dnssecStatus.length === 0) fieldtext += " (" + ede + ")"; + + // Cannot block internal queries of this type + if ((data[1] === "DNSKEY" || data[1] === "DS") && data[3] === "pi.hole") buttontext = ""; + fieldtext += ''; if (colorClass !== false) { @@ -252,7 +266,6 @@ $(function () { } // Check for existence of sixth column and display only if not Pi-holed - var replyid = data[5]; var replytext = replyid >= 0 && replyid < replyTypes.length ? replyTypes[replyid] : "? (" + replyid + ")"; diff --git a/scripts/pi-hole/php/auth.php b/scripts/pi-hole/php/auth.php index 83e51995..16a7390e 100644 --- a/scripts/pi-hole/php/auth.php +++ b/scripts/pi-hole/php/auth.php @@ -126,7 +126,7 @@ function check_csrf($token) { function check_domain(&$domains) { foreach($domains as &$domain) { - $validDomain = is_valid_domain_name($domain); + $validDomain = validDomain($domain); if(!$validDomain){ log_and_die(htmlspecialchars($domain. ' is not a valid domain')); } diff --git a/scripts/pi-hole/php/func.php b/scripts/pi-hole/php/func.php index a3e82eb1..c2e42b48 100644 --- a/scripts/pi-hole/php/func.php +++ b/scripts/pi-hole/php/func.php @@ -6,11 +6,86 @@ * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license. */ -function is_valid_domain_name($domain_name) +// Credit: http://stackoverflow.com/a/4694816/2087442 +// Modified because of https://github.com/pi-hole/AdminLTE/pull/533 +ini_set("pcre.recursion_limit", 1500); +function validDomain($domain_name, &$message = NULL) { - return (preg_match("/^((-|_)*[a-z\d]((-|_)*[a-z\d])*(-|_)*)(\.(-|_)*([a-z\d]((-|_)*[a-z\d])*))*$/i", $domain_name) && // Valid chars check - preg_match("/^.{1,253}$/", $domain_name) && // Overall length check - preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name)); // Length of each label + if(!preg_match("/^((-|_)*[a-z\d]((-|_)*[a-z\d])*(-|_)*)(\.(-|_)*([a-z\d]((-|_)*[a-z\d])*))*$/i", $domain_name)) { + if($message !== NULL) + $message = "it contains invalid characters"; + return false; + } + if(!preg_match("/^.{1,253}$/", $domain_name)) { + if($message !== NULL) + $message = "its length is invalid"; + return false; + } + if(!preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name)) { + if($message !== NULL) + $message = "at least one label is of invalid length"; + return false; + } + + // everything is okay + return true; +} + +function validDomainWildcard($domain_name) +{ + // There has to be either no or at most one "*" at the beginning of a line + $validChars = preg_match("/^((\*.)?[_a-z\d](-*[_a-z\d])*)(\.([_a-z\d](-*[a-z\d])*))*(\.([_a-z\d])*)*$/i", $domain_name); + $lengthCheck = preg_match("/^.{1,253}$/", $domain_name); + $labelLengthCheck = preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name); + return ( $validChars && $lengthCheck && $labelLengthCheck ); //length of each label +} + +function validIP($address){ + if (preg_match('/[.:0]/', $address) && !preg_match('/[1-9a-f]/', $address)) { + // Test if address contains either `:` or `0` but not 1-9 or a-f + return false; + } + return !filter_var($address, FILTER_VALIDATE_IP) === false; +} + +function validCIDRIP($address){ + // This validation strategy has been taken from ../js/groups-common.js + $isIPv6 = strpos($address, ":") !== false; + if($isIPv6) { + // One IPv6 element is 16bit: 0000 - FFFF + $v6elem = "[0-9A-Fa-f]{1,4}"; + // CIDR for IPv6 is any multiple of 4 from 4 up to 128 bit + $v6cidr = "(4"; + for ($i=8; $i <= 128; $i+=4) { + $v6cidr .= "|$i"; + } + $v6cidr .= ")"; + $validator = "/^(((?:$v6elem))((?::$v6elem))*::((?:$v6elem))((?::$v6elem))*|((?:$v6elem))((?::$v6elem)){7})\/$v6cidr$/"; + return preg_match($validator, $address); + } else { + // One IPv4 element is 8bit: 0 - 256 + $v4elem = "(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)"; + // Note that rev-server accepts only /8, /16, /24, and /32 + $allowedv4cidr = "(8|16|24|32)"; + $validator = "/^$v4elem\.$v4elem\.$v4elem\.$v4elem\/$allowedv4cidr$/"; + return preg_match($validator, $address); + } +} + +function validMAC($mac_addr) +{ + // Accepted input format: 00:01:02:1A:5F:FF (characters may be lower case) + return !filter_var($mac_addr, FILTER_VALIDATE_MAC) === false; +} + +function validEmail($email) +{ + return filter_var($email, FILTER_VALIDATE_EMAIL) + // Make sure that the email does not contain special characters which + // may be used to execute shell commands, even though they may be valid + // in an email address. If the escaped email does not equal the original + // email, it is not safe to store in setupVars. + && escapeshellcmd($email) === $email; } function get_ip_type($ip) @@ -145,7 +220,7 @@ function addCustomDNSEntry($ip="", $domain="", $json=true) if (empty($domain)) return returnError("Domain must be set", $json); - if (!is_valid_domain_name($domain)) + if (!validDomain($domain)) return returnError("Domain must be valid", $json); // Only check for duplicates if adding new records from the web UI (not through Teleporter) @@ -302,13 +377,13 @@ function addCustomCNAMEEntry($domain="", $target="", $json=true) if (empty($target)) return returnError("Target must be set", $json); - if (!is_valid_domain_name($target)) + if (!validDomain($target)) return returnError("Target must be valid", $json); // Check if each submitted domain is valid $domains = array_map('trim', explode(",", $domain)); foreach ($domains as $d) { - if (!is_valid_domain_name($d)) + if (!validDomain($d)) return returnError("Domain '$d' is not valid", $json); } diff --git a/scripts/pi-hole/php/groups.php b/scripts/pi-hole/php/groups.php index 6bc423e6..49a8d51c 100644 --- a/scripts/pi-hole/php/groups.php +++ b/scripts/pi-hole/php/groups.php @@ -649,15 +649,16 @@ if ($_POST['action'] == 'get_groups') { { // If adding to the exact lists, we convert the domain lower case and check whether it is valid $domain = strtolower($domain); - if(filter_var($domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) === false) + $msg = ""; + if(!validDomain($domain, $msg)) { // This is the case when idn_to_ascii() modified the string if($input !== $domain && strlen($domain) > 0) - $errormsg = 'Domain ' . htmlentities($input) . ' (converted to "' . htmlentities(utf8_encode($domain)) . '") is not a valid domain.'; + $errormsg = 'Domain ' . htmlentities($input) . ' (converted to "' . htmlentities(utf8_encode($domain)) . '") is not a valid domain because ' . $msg . '.'; elseif($input !== $domain) - $errormsg = 'Domain ' . htmlentities($input) . ' is not a valid domain.'; + $errormsg = 'Domain ' . htmlentities($input) . ' is not a valid domain because ' . $msg . '.'; else - $errormsg = 'Domain ' . htmlentities(utf8_encode($domain)) . ' is not a valid domain.'; + $errormsg = 'Domain ' . htmlentities(utf8_encode($domain)) . ' is not a valid domain because ' . $msg . '.'; throw new Exception($errormsg . '
Added ' . $added . " out of ". $total . " domains"); } } diff --git a/scripts/pi-hole/php/queryads.php b/scripts/pi-hole/php/queryads.php index f247c87a..38d4e8d2 100644 --- a/scripts/pi-hole/php/queryads.php +++ b/scripts/pi-hole/php/queryads.php @@ -9,6 +9,7 @@ while (ob_get_level() > 0) { ob_end_flush(); } +require_once("func.php"); ini_set("output_buffering", "0"); ob_implicit_flush(true); header('Content-Type: text/event-stream'); @@ -21,22 +22,12 @@ function echoEvent($datatext) { echo $datatext; } -// Credit: http://stackoverflow.com/a/4694816/2087442 -ini_set("pcre.recursion_limit", 1500); -function is_valid_domain_name($domain_name) -{ - return ($domain_name[0] !== '-' // Don't allow domains to appear as command line options - && preg_match("/^((-|_)*[a-z\d]((-|_)*[a-z\d])*(-|_)*)(\.(-|_)*([a-z\d]((-|_)*[a-z\d])*))*$/i", $domain_name) // Valid chars check - && preg_match("/^.{1,253}$/", $domain_name) // Overall length check - && preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name) ); // Length of each label -} - // Test if domain is set if(isset($_GET["domain"])) { // Is this a valid domain? $url = $_GET["domain"]; - if(!is_valid_domain_name($url)) + if(!validDomain($url)) { echoEvent("Invalid domain!"); die(); diff --git a/scripts/pi-hole/php/savesettings.php b/scripts/pi-hole/php/savesettings.php index 36f8def4..e987e27f 100644 --- a/scripts/pi-hole/php/savesettings.php +++ b/scripts/pi-hole/php/savesettings.php @@ -6,43 +6,13 @@ * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license. */ +require_once("func.php"); + if(!in_array(basename($_SERVER['SCRIPT_FILENAME']), ["settings.php", "teleporter.php"], true)) { die("Direct access to this script is forbidden!"); } -function validIP($address){ - if (preg_match('/[.:0]/', $address) && !preg_match('/[1-9a-f]/', $address)) { - // Test if address contains either `:` or `0` but not 1-9 or a-f - return false; - } - return !filter_var($address, FILTER_VALIDATE_IP) === false; -} - -function validCIDRIP($address){ - // This validation strategy has been taken from ../js/groups-common.js - $isIPv6 = strpos($address, ":") !== false; - if($isIPv6) { - // One IPv6 element is 16bit: 0000 - FFFF - $v6elem = "[0-9A-Fa-f]{1,4}"; - // CIDR for IPv6 is any multiple of 4 from 4 up to 128 bit - $v6cidr = "(4"; - for ($i=8; $i <= 128; $i+=4) { - $v6cidr .= "|$i"; - } - $v6cidr .= ")"; - $validator = "/^(((?:$v6elem))((?::$v6elem))*::((?:$v6elem))((?::$v6elem))*|((?:$v6elem))((?::$v6elem)){7})\/$v6cidr$/"; - return preg_match($validator, $address); - } else { - // One IPv4 element is 8bit: 0 - 256 - $v4elem = "(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)"; - // Note that rev-server accepts only /8, /16, /24, and /32 - $allowedv4cidr = "(8|16|24|32)"; - $validator = "/^$v4elem\.$v4elem\.$v4elem\.$v4elem\/$allowedv4cidr$/"; - return preg_match($validator, $address); - } -} - // Check for existance of variable // and test it only if it exists function istrue(&$argument) { @@ -56,30 +26,6 @@ function istrue(&$argument) { return false; } -// Credit: http://stackoverflow.com/a/4694816/2087442 -function validDomain($domain_name) -{ - $validChars = preg_match("/^([_a-z\d](-*[_a-z\d])*)(\.([_a-z\d](-*[a-z\d])*))*(\.([_a-z\d])*)*$/i", $domain_name); - $lengthCheck = preg_match("/^.{1,253}$/", $domain_name); - $labelLengthCheck = preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name); - return ( $validChars && $lengthCheck && $labelLengthCheck ); //length of each label -} - -function validDomainWildcard($domain_name) -{ - // There has to be either no or at most one "*" at the beginning of a line - $validChars = preg_match("/^((\*\.)?[_a-z\d](-*[_a-z\d])*)(\.([_a-z\d](-*[a-z\d])*))*(\.([_a-z\d])*)*$/i", $domain_name); - $lengthCheck = preg_match("/^.{1,253}$/", $domain_name); - $labelLengthCheck = preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name); - return ( $validChars && $lengthCheck && $labelLengthCheck ); //length of each label -} - -function validMAC($mac_addr) -{ - // Accepted input format: 00:01:02:1A:5F:FF (characters may be lower case) - return !filter_var($mac_addr, FILTER_VALIDATE_MAC) === false; -} - function formatMAC($mac_addr) { preg_match("/([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})/", $mac_addr, $matches); @@ -88,16 +34,6 @@ function formatMAC($mac_addr) return null; } -function validEmail($email) -{ - return filter_var($email, FILTER_VALIDATE_EMAIL) - // Make sure that the email does not contain special characters which - // may be used to execute shell commands, even though they may be valid - // in an email address. If the escaped email does not equal the original - // email, it is not safe to store in setupVars. - && escapeshellcmd($email) === $email; -} - $dhcp_static_leases = array(); function readStaticLeasesFile($origin_file="/etc/dnsmasq.d/04-pihole-static-dhcp.conf") { diff --git a/settings.php b/settings.php index a59fe423..d08d17fb 100644 --- a/settings.php +++ b/settings.php @@ -63,9 +63,7 @@ if (isset($_POST["submit"])) {
- -
in active">
-
-
-
-

Network Information

-
-
-
-
- - - - - - - - - - - - - - - - - - - -
Pi-hole Ethernet Interface:
Pi-hole IPv4 address:
Pi-hole IPv6 address:
Pi-hole hostname:
-
-
-
-
-
-
+

FTL Information

@@ -822,14 +765,14 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "adlists", " "> -
" id="DNS6server" value="true" checked disabled >
+
" id="DNS6server" value="true" checked>
"> -
" id="DNS6server" value="true" checked disabled >
+
" id="DNS6server" value="true" checked>