Files
web/scripts/pi-hole/php/savesettings.php
mcattle edb93db3ce Add Quad9 DNS Servers to DNS Servers List
I've added the Quad9 DNS servers (IPv4 and IPv6) to the DNS Servers list.  (https://www.quad9.net/)  I've opted not to also add their non-secure alternate servers, as those mainly exist for diagnostics of false positives, and are not the primary focus of the Quad9 service.  They do not offer secondary servers, as their primary servers are already geographically distributed and redundant.
2017-11-16 23:08:35 -07:00

646 lines
16 KiB
PHP

<?php
/* Pi-hole: A black hole for Internet advertisements
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */ ?>
<?php
if(basename($_SERVER['SCRIPT_FILENAME']) !== "settings.php")
{
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;
}
// Check for existance of variable
// and test it only if it exists
function istrue(&$argument) {
if(isset($argument))
{
if($argument)
{
return true;
}
}
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 (preg_match('/([a-fA-F0-9]{2}[:]?){6}/', $mac_addr) == 1);
}
$dhcp_static_leases = array();
function readStaticLeasesFile()
{
global $dhcp_static_leases;
$dhcp_static_leases = array();
$dhcpstatic = @fopen('/etc/dnsmasq.d/04-pihole-static-dhcp.conf', 'r');
if(!is_resource($dhcpstatic))
return false;
while(!feof($dhcpstatic))
{
// Remove any possibly existing variable with this name
$mac = ""; $one = ""; $two = "";
sscanf(trim(fgets($dhcpstatic)),"dhcp-host=%[^,],%[^,],%[^,]",$mac,$one,$two);
if(strlen($mac) > 0 && validMAC($mac))
{
if(validIP($one) && strlen($two) == 0)
// dhcp-host=mac,IP - no HOST
array_push($dhcp_static_leases,["hwaddr"=>$mac, "IP"=>$one, "host"=>""]);
elseif(strlen($two) == 0)
// dhcp-host=mac,hostname - no IP
array_push($dhcp_static_leases,["hwaddr"=>$mac, "IP"=>"", "host"=>$one]);
else
// dhcp-host=mac,IP,hostname
array_push($dhcp_static_leases,["hwaddr"=>$mac, "IP"=>$one, "host"=>$two]);
}
else if(validIP($one) && validDomain($mac))
{
// dhcp-host=hostname,IP - no MAC
array_push($dhcp_static_leases,["hwaddr"=>"", "IP"=>$one, "host"=>$mac]);
}
}
return true;
}
function isequal(&$argument, &$compareto) {
if(isset($argument))
{
if($argument === $compareto)
{
return true;
}
}
return false;
}
function isinserverlist($addr) {
global $DNSserverslist;
foreach ($DNSserverslist as $key => $value) {
if (isequal($value['v4_1'],$addr) || isequal($value['v4_2'],$addr))
return true;
if (isequal($value['v6_1'],$addr) || isequal($value['v6_2'],$addr))
return true;
}
return false;
}
$DNSserverslist = [
"Google" => ["v4_1" => "8.8.8.8","v4_2" => "8.8.4.4", "v6_1" => "2001:4860:4860:0:0:0:0:8888", "v6_2" => "2001:4860:4860:0:0:0:0:8844"],
"OpenDNS" => ["v4_1" => "208.67.222.222", "v4_2" => "208.67.220.220", "v6_1" => "2620:0:ccc::2", "v6_2" => "2620:0:ccd::2"],
"Level3" => ["v4_1" => "4.2.2.1", "v4_2" => "4.2.2.2"],
"Norton" => ["v4_1" => "199.85.126.10", "v4_2" => "199.85.127.10"],
"Comodo" => ["v4_1" => "8.26.56.26", "v4_2" => "8.20.247.20"],
"DNS.WATCH" => ["v4_1" => "84.200.69.80", "v4_2" => "84.200.70.40", "v6_1" => "2001:1608:10:25:0:0:1c04:b12f", "v6_2" => "2001:1608:10:25:0:0:9249:d69b"],
"Quad9" => ["v4_1" => "9.9.9.9", "v6_1" => "2620:fe::fe"]
];
$adlist = [];
function readAdlists()
{
// Reset list
$list = [];
$handle = @fopen("/etc/pihole/adlists.list", "r");
if ($handle)
{
while (($line = fgets($handle)) !== false)
{
if(substr($line, 0, 5) === "#http")
{
// Commented list
array_push($list, [false,rtrim(substr($line, 1))]);
}
elseif(substr($line, 0, 4) === "http")
{
// Active list
array_push($list, [true,rtrim($line)]);
}
}
fclose($handle);
}
return $list;
}
// Read available adlists
$adlist = readAdlists();
$error = "";
$success = "";
if(isset($_POST["field"]))
{
// Handle CSRF
check_csrf(isset($_POST["token"]) ? $_POST["token"] : "");
// Process request
switch ($_POST["field"]) {
// Set DNS server
case "DNS":
$DNSservers = [];
// Add selected predefined servers to list
foreach ($DNSserverslist as $key => $value)
{
foreach(["v4_1", "v4_2", "v6_1", "v6_2"] as $type)
{
if(@array_key_exists("DNSserver".str_replace(".","_",$value[$type]),$_POST))
{
array_push($DNSservers,$value[$type]);
}
}
}
// Test custom server fields
for($i=1;$i<=4;$i++)
{
if(array_key_exists("custom".$i,$_POST))
{
$IP = $_POST["custom".$i."val"];
if(validIP($IP))
{
array_push($DNSservers,$IP);
}
else
{
$error .= "IP (".htmlspecialchars($IP).") is invalid!<br>";
}
}
}
// Check if at least one DNS server has been added
if(count($DNSservers) < 1)
{
$error .= "No DNS server has been selected.<br>";
}
// Check if domain-needed is requested
if(isset($_POST["DNSrequiresFQDN"]))
{
$extra = "domain-needed ";
}
else
{
$extra = "domain-not-needed ";
}
// Check if domain-needed is requested
if(isset($_POST["DNSbogusPriv"]))
{
$extra .= "bogus-priv ";
}
else
{
$extra .= "no-bogus-priv ";
}
// Check if DNSSEC is requested
if(isset($_POST["DNSSEC"]))
{
$extra .= "dnssec";
}
else
{
$extra .= "no-dnssec";
}
// Check if DNSinterface is set
if(isset($_POST["DNSinterface"]))
{
if($_POST["DNSinterface"] === "single")
{
$DNSinterface = "single";
}
elseif($_POST["DNSinterface"] === "all")
{
$DNSinterface = "all";
}
else
{
$DNSinterface = "local";
}
}
else
{
// Fallback
$DNSinterface = "local";
}
exec("sudo pihole -a -i ".$DNSinterface." -web");
// If there has been no error we can save the new DNS server IPs
if(!strlen($error))
{
$IPs = implode (",", $DNSservers);
$return = exec("sudo pihole -a setdns ".$IPs." ".$extra);
$success .= htmlspecialchars($return)."<br>";
$success .= "The DNS settings have been updated (using ".count($DNSservers)." DNS servers)";
}
else
{
$error .= "The settings have been reset to their previous values";
}
break;
// Set query logging
case "Logging":
if($_POST["action"] === "Disable")
{
exec("sudo pihole -l off");
$success .= "Logging has been disabled";
}
else
{
exec("sudo pihole -l on");
$success .= "Logging has been enabled";
}
break;
// Set domains to be excluded from being shown in Top Domains (or Ads) and Top Clients
case "API":
// Explode the contents of the textareas into PHP arrays
// \n (Unix) and \r\n (Win) will be considered as newline
// array_filter( ... ) will remove any empty lines
$domains = array_filter(preg_split('/\r\n|[\r\n]/', $_POST["domains"]));
$clients = array_filter(preg_split('/\r\n|[\r\n]/', $_POST["clients"]));
$domainlist = "";
$first = true;
foreach($domains as $domain)
{
if(!validDomainWildcard($domain) || validIP($domain))
{
$error .= "Top Domains/Ads entry ".htmlspecialchars($domain)." is invalid (use only domains)!<br>";
}
if(!$first)
{
$domainlist .= ",";
}
else
{
$first = false;
}
$domainlist .= $domain;
}
$clientlist = "";
$first = true;
foreach($clients as $client)
{
if(!validDomainWildcard($client))
{
$error .= "Top Clients entry ".htmlspecialchars($client)." is invalid (use only IP addresses)!<br>";
}
if(!$first)
{
$clientlist .= ",";
}
else
{
$first = false;
}
$clientlist .= $client;
}
// Set Top Lists options
if(!strlen($error))
{
// All entries are okay
exec("sudo pihole -a setexcludedomains ".$domainlist);
exec("sudo pihole -a setexcludeclients ".$clientlist);
$success .= "The API settings have been updated<br>";
}
else
{
$error .= "The settings have been reset to their previous values";
}
// Set query log options
if(isset($_POST["querylog-permitted"]) && isset($_POST["querylog-blocked"]))
{
exec("sudo pihole -a setquerylog all");
if(!isset($_POST["privacyMode"]))
{
$success .= "All entries will be shown in Query Log";
}
else
{
$success .= "Only blocked entries will be shown in Query Log";
}
}
elseif(isset($_POST["querylog-permitted"]))
{
exec("sudo pihole -a setquerylog permittedonly");
if(!isset($_POST["privacyMode"]))
{
$success .= "Only permitted will be shown in Query Log";
}
else
{
$success .= "No entries will be shown in Query Log";
}
}
elseif(isset($_POST["querylog-blocked"]))
{
exec("sudo pihole -a setquerylog blockedonly");
$success .= "Only blocked entries will be shown in Query Log";
}
else
{
exec("sudo pihole -a setquerylog nothing");
$success .= "No entries will be shown in Query Log";
}
if(isset($_POST["privacyMode"]))
{
exec("sudo pihole -a privacymode true");
$success .= " (privacy mode enabled)";
}
else
{
exec("sudo pihole -a privacymode false");
}
break;
case "webUI":
if($_POST["tempunit"] == "F")
{
exec('sudo pihole -a -f');
}
elseif($_POST["tempunit"] == "K")
{
exec('sudo pihole -a -k');
}
else
{
exec('sudo pihole -a -c');
}
if(isset($_POST["boxedlayout"]))
{
exec('sudo pihole -a layout boxed');
}
else
{
exec('sudo pihole -a layout traditional');
}
$success .= "The webUI settings have been updated";
break;
case "reboot":
exec("sudo pihole -a reboot");
$success = "The system will reboot in 5 seconds...";
break;
case "restartdns":
exec("sudo pihole -a restartdns");
$success = "The DNS server has been restarted";
break;
case "flushlogs":
exec("sudo pihole -f");
$success = "The Pi-hole log file has been flushed";
break;
case "DHCP":
if(isset($_POST["addstatic"]))
{
$mac = $_POST["AddMAC"];
$ip = $_POST["AddIP"];
$hostname = $_POST["AddHostname"];
if(!validMAC($mac))
{
$error .= "MAC address (".htmlspecialchars($mac).") is invalid!<br>";
}
$mac = strtoupper($mac);
if(!validIP($ip) && strlen($ip) > 0)
{
$error .= "IP address (".htmlspecialchars($ip).") is invalid!<br>";
}
if(!validDomain($hostname) && strlen($hostname) > 0)
{
$error .= "Host name (".htmlspecialchars($hostname).") is invalid!<br>";
}
if(strlen($hostname) == 0 && strlen($ip) == 0)
{
$error .= "You can not omit both the IP address and the host name!<br>";
}
if(strlen($hostname) == 0)
$hostname = "nohost";
if(strlen($ip) == 0)
$ip = "noip";
// Test if this MAC address is already included
readStaticLeasesFile();
foreach($dhcp_static_leases as $lease) {
if($lease["hwaddr"] === $mac)
{
$error .= "Static release for MAC address (".htmlspecialchars($mac).") already defined!<br>";
break;
}
}
if(!strlen($error))
{
exec("sudo pihole -a addstaticdhcp ".$mac." ".$ip." ".$hostname);
$success .= "A new static address has been added";
}
break;
}
if(isset($_POST["removestatic"]))
{
$mac = $_POST["removestatic"];
if(!validMAC($mac))
{
$error .= "MAC address (".htmlspecialchars($mac).") is invalid!<br>";
}
$mac = strtoupper($mac);
if(!strlen($error))
{
exec("sudo pihole -a removestaticdhcp ".$mac);
$success .= "The static address with MAC address ".htmlspecialchars($mac)." has been removed";
}
break;
}
if(isset($_POST["active"]))
{
// Validate from IP
$from = $_POST["from"];
if (!validIP($from))
{
$error .= "From IP (".htmlspecialchars($from).") is invalid!<br>";
}
// Validate to IP
$to = $_POST["to"];
if (!validIP($to))
{
$error .= "To IP (".htmlspecialchars($to).") is invalid!<br>";
}
// Validate router IP
$router = $_POST["router"];
if (!validIP($router))
{
$error .= "Router IP (".htmlspecialchars($router).") is invalid!<br>";
}
$domain = $_POST["domain"];
// Validate Domain name
if(!validDomain($domain))
{
$error .= "Domain name ".htmlspecialchars($domain)." is invalid!<br>";
}
$leasetime = $_POST["leasetime"];
// Validate Lease time length
if(!is_numeric($leasetime) || intval($leasetime) < 0)
{
$error .= "Lease time ".htmlspecialchars($leasetime)." is invalid!<br>";
}
if(isset($_POST["useIPv6"]))
{
$ipv6 = "true";
$type = "(IPv4 + IPv6)";
}
else
{
$ipv6 = "false";
$type = "(IPv4)";
}
if(!strlen($error))
{
exec("sudo pihole -a enabledhcp ".$from." ".$to." ".$router." ".$leasetime." ".$domain." ".$ipv6);
$success .= "The DHCP server has been activated ".htmlspecialchars($type);
}
}
else
{
exec("sudo pihole -a disabledhcp");
$success = "The DHCP server has been deactivated";
}
break;
case "adlists":
foreach ($adlist as $key => $value)
{
if(isset($_POST["adlist-del-".$key]))
{
// Delete list
exec("sudo pihole -a adlist del ".escapeshellcmd($value[1]));
}
elseif(isset($_POST["adlist-enable-".$key]) && !$value[0])
{
// Is not enabled, but should be
exec("sudo pihole -a adlist enable ".escapeshellcmd($value[1]));
}
elseif(!isset($_POST["adlist-enable-".$key]) && $value[0])
{
// Is enabled, but shouldn't be
exec("sudo pihole -a adlist disable ".escapeshellcmd($value[1]));
}
}
if(strlen($_POST["newuserlists"]) > 1)
{
$domains = array_filter(preg_split('/\r\n|[\r\n]/', $_POST["newuserlists"]));
foreach($domains as $domain)
{
exec("sudo pihole -a adlist add ".escapeshellcmd($domain));
}
}
// Reread available adlists
$adlist = readAdlists();
break;
default:
// Option not found
$debug = true;
break;
}
}
// Credit: http://stackoverflow.com/a/5501447/2087442
function formatSizeUnits($bytes)
{
if ($bytes >= 1073741824)
{
$bytes = number_format($bytes / 1073741824, 2) . ' GB';
}
elseif ($bytes >= 1048576)
{
$bytes = number_format($bytes / 1048576, 2) . ' MB';
}
elseif ($bytes >= 1024)
{
$bytes = number_format($bytes / 1024, 2) . ' kB';
}
elseif ($bytes > 1)
{
$bytes = $bytes . ' bytes';
}
elseif ($bytes == 1)
{
$bytes = $bytes . ' byte';
}
else
{
$bytes = '0 bytes';
}
return $bytes;
}
?>