mirror of
https://github.com/pi-hole/web.git
synced 2025-12-27 13:59:14 +00:00
Moved CNAME functions to func.php Added teleporter support Fixed CNAME and DNS file declaration (filename was declarated in seperate file, therefore trying to read those files from func.php failed) Fixed error and success response functions in func.php (calling addCustomDNSEntry and addCustomCNAMEEntry repeatedly from teleporter failed because of error function defined inside of those functions) Signed-off-by: Matthias Rank <development@m-rank.de>
403 lines
10 KiB
PHP
403 lines
10 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. */
|
|
|
|
function is_valid_domain_name($domain_name)
|
|
{
|
|
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
|
|
}
|
|
|
|
function get_ip_type($ip)
|
|
{
|
|
return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ? 4 :
|
|
(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? 6 :
|
|
0);
|
|
}
|
|
|
|
function checkfile($filename) {
|
|
if(is_readable($filename))
|
|
{
|
|
return $filename;
|
|
}
|
|
else
|
|
{
|
|
// substitute dummy file
|
|
return "/dev/null";
|
|
}
|
|
}
|
|
|
|
// Credit: http://php.net/manual/en/function.hash-equals.php#119576
|
|
if(!function_exists('hash_equals')) {
|
|
function hash_equals($known_string, $user_string) {
|
|
$ret = 0;
|
|
|
|
if (strlen($known_string) !== strlen($user_string)) {
|
|
$user_string = $known_string;
|
|
$ret = 1;
|
|
}
|
|
|
|
$res = $known_string ^ $user_string;
|
|
|
|
for ($i = strlen($res) - 1; $i >= 0; --$i) {
|
|
$ret |= ord($res[$i]);
|
|
}
|
|
|
|
return !$ret;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* More safely execute a command with pihole shell script.
|
|
*
|
|
* For example,
|
|
*
|
|
* pihole_execute("-h");
|
|
*
|
|
* would execute command
|
|
*
|
|
* sudo pihole -h
|
|
*
|
|
* and returns output of that command as a string.
|
|
*
|
|
* @param $argument_string String of arguments to run pihole with.
|
|
* @param $error_on_failure If true, a warning is raised if command execution fails. Defaults to true.
|
|
*/
|
|
function pihole_execute($argument_string, $error_on_failure = true) {
|
|
$escaped = escapeshellcmd($argument_string);
|
|
$output = null;
|
|
$return_status = -1;
|
|
$command = "sudo pihole " . $escaped;
|
|
exec($command, $output, $return_status);
|
|
if($return_status !== 0)
|
|
{
|
|
trigger_error("Executing {$command} failed.", E_USER_WARNING);
|
|
}
|
|
return $output;
|
|
}
|
|
|
|
// Custom DNS
|
|
$customDNSFile = "/etc/pihole/custom.list";
|
|
|
|
function echoCustomDNSEntries()
|
|
{
|
|
$entries = getCustomDNSEntries();
|
|
|
|
$data = [];
|
|
foreach ($entries as $entry)
|
|
$data[] = [ $entry->domain, $entry->ip ];
|
|
|
|
return [ "data" => $data ];
|
|
}
|
|
|
|
function getCustomDNSEntries()
|
|
{
|
|
global $customDNSFile;
|
|
|
|
$entries = [];
|
|
|
|
$handle = fopen($customDNSFile, "r");
|
|
if ($handle)
|
|
{
|
|
while (($line = fgets($handle)) !== false) {
|
|
$line = str_replace("\r","", $line);
|
|
$line = str_replace("\n","", $line);
|
|
$explodedLine = explode (" ", $line);
|
|
|
|
if (count($explodedLine) != 2)
|
|
continue;
|
|
|
|
$data = new \stdClass();
|
|
$data->ip = $explodedLine[0];
|
|
$data->domain = $explodedLine[1];
|
|
$entries[] = $data;
|
|
}
|
|
|
|
fclose($handle);
|
|
}
|
|
|
|
return $entries;
|
|
}
|
|
|
|
function addCustomDNSEntry($ip="", $domain="", $json=true)
|
|
{
|
|
try
|
|
{
|
|
if(isset($_REQUEST['ip']))
|
|
$ip = $_REQUEST['ip'];
|
|
|
|
if(isset($_REQUEST['domain']))
|
|
$domain = $_REQUEST['domain'];
|
|
|
|
if (empty($ip))
|
|
return returnError("IP must be set", $json);
|
|
|
|
$ipType = get_ip_type($ip);
|
|
|
|
if (!$ipType)
|
|
return returnError("IP must be valid", $json);
|
|
|
|
if (empty($domain))
|
|
return returnError("Domain must be set", $json);
|
|
|
|
if (!is_valid_domain_name($domain))
|
|
return returnError("Domain must be valid", $json);
|
|
|
|
// Only check for duplicates if adding new records from the web UI (not through Teleporter)
|
|
if(isset($_REQUEST['ip']) || isset($_REQUEST['domain']))
|
|
{
|
|
$existingEntries = getCustomDNSEntries();
|
|
foreach ($existingEntries as $entry)
|
|
if ($entry->domain == $domain && get_ip_type($entry->ip) == $ipType)
|
|
return returnError("This domain already has a custom DNS entry for an IPv" . $ipType, $json);
|
|
}
|
|
|
|
// Add record
|
|
pihole_execute("-a addcustomdns ".$ip." ".$domain);
|
|
|
|
return returnSuccess("", $json);
|
|
}
|
|
catch (\Exception $ex)
|
|
{
|
|
return error($ex->getMessage(), $json);
|
|
}
|
|
}
|
|
|
|
function deleteCustomDNSEntry()
|
|
{
|
|
try
|
|
{
|
|
$ip = !empty($_REQUEST['ip']) ? $_REQUEST['ip']: "";
|
|
$domain = !empty($_REQUEST['domain']) ? $_REQUEST['domain']: "";
|
|
|
|
if (empty($ip))
|
|
return returnError("IP must be set");
|
|
|
|
if (empty($domain))
|
|
return returnError("Domain must be set");
|
|
|
|
$existingEntries = getCustomDNSEntries();
|
|
|
|
$found = false;
|
|
foreach ($existingEntries as $entry)
|
|
if ($entry->domain == $domain)
|
|
if ($entry->ip == $ip) {
|
|
$found = true;
|
|
break;
|
|
}
|
|
|
|
if (!$found)
|
|
return returnError("This domain/ip association does not exist");
|
|
|
|
pihole_execute("-a removecustomdns ".$ip." ".$domain);
|
|
|
|
return returnSuccess();
|
|
}
|
|
catch (\Exception $ex)
|
|
{
|
|
return returnError($ex->getMessage());
|
|
}
|
|
}
|
|
|
|
function deleteAllCustomDNSEntries()
|
|
{
|
|
$handle = fopen($customDNSFile, "r");
|
|
if ($handle)
|
|
{
|
|
try
|
|
{
|
|
while (($line = fgets($handle)) !== false) {
|
|
$line = str_replace("\r","", $line);
|
|
$line = str_replace("\n","", $line);
|
|
$explodedLine = explode (" ", $line);
|
|
|
|
if (count($explodedLine) != 2)
|
|
continue;
|
|
|
|
$ip = $explodedLine[0];
|
|
$domain = $explodedLine[1];
|
|
|
|
pihole_execute("-a removecustomdns ".$ip." ".$domain);
|
|
}
|
|
}
|
|
catch (\Exception $ex)
|
|
{
|
|
return returnError($ex->getMessage());
|
|
}
|
|
|
|
fclose($handle);
|
|
}
|
|
|
|
return returnSuccess();
|
|
}
|
|
|
|
// CNAME
|
|
$customCNAMEFile = "/etc/dnsmasq.d/05-pihole-custom-cname.conf";
|
|
|
|
function echoCustomCNAMEEntries()
|
|
{
|
|
$entries = getCustomCNAMEEntries();
|
|
|
|
$data = [];
|
|
foreach ($entries as $entry)
|
|
$data[] = [ $entry->domain, $entry->target ];
|
|
|
|
return [ "data" => $data ];
|
|
}
|
|
|
|
function getCustomCNAMEEntries()
|
|
{
|
|
global $customCNAMEFile;
|
|
|
|
$entries = [];
|
|
|
|
if (!file_exists($customCNAMEFile)) return $entries;
|
|
|
|
$handle = fopen($customCNAMEFile, "r");
|
|
if ($handle)
|
|
{
|
|
while (($line = fgets($handle)) !== false) {
|
|
$line = str_replace("cname=","", $line);
|
|
$line = str_replace("\r","", $line);
|
|
$line = str_replace("\n","", $line);
|
|
$explodedLine = explode (",", $line);
|
|
|
|
if (count($explodedLine) <= 1)
|
|
continue;
|
|
|
|
$data = new \stdClass();
|
|
$data->domains = array_slice($explodedLine, 0, -1);
|
|
$data->domain = implode(",", $data->domains);
|
|
$data->target = $explodedLine[count($explodedLine)-1];
|
|
$entries[] = $data;
|
|
}
|
|
|
|
fclose($handle);
|
|
}
|
|
|
|
return $entries;
|
|
}
|
|
|
|
function addCustomCNAMEEntry($domain="", $target="", $json=true)
|
|
{
|
|
try
|
|
{
|
|
if(isset($_REQUEST['domain']))
|
|
$domain = $_REQUEST['domain'];
|
|
|
|
if(isset($_REQUEST['target']))
|
|
$target = $_REQUEST['target'];
|
|
|
|
if (empty($domain))
|
|
return returnError("Domain must be set", $json);
|
|
|
|
if (empty($target))
|
|
return returnError("Target must be set", $json);
|
|
|
|
// Check if each submitted domain is valid
|
|
$domains = array_map('trim', explode(",", $domain));
|
|
foreach ($domains as $d) {
|
|
if (!is_valid_domain_name($d))
|
|
return returnError("Domain '$d' is not valid", $json);
|
|
}
|
|
|
|
$existingEntries = getCustomCNAMEEntries();
|
|
|
|
// Check if a record for one of the domains already exists
|
|
foreach ($existingEntries as $entry)
|
|
foreach ($domains as $d)
|
|
if (in_array($d, $entry->domains))
|
|
return returnError("There is already a CNAME record for '$d'", $json);
|
|
|
|
pihole_execute("-a addcustomcname ".$domain." ".$target);
|
|
|
|
return returnSuccess("", $json);
|
|
}
|
|
catch (\Exception $ex)
|
|
{
|
|
return returnError($ex->getMessage(), $json);
|
|
}
|
|
}
|
|
|
|
function deleteCustomCNAMEEntry()
|
|
{
|
|
try
|
|
{
|
|
$target = !empty($_REQUEST['target']) ? $_REQUEST['target']: "";
|
|
$domain = !empty($_REQUEST['domain']) ? $_REQUEST['domain']: "";
|
|
|
|
if (empty($target))
|
|
return returnError("Target must be set");
|
|
|
|
if (empty($domain))
|
|
return returnError("Domain must be set");
|
|
|
|
$existingEntries = getCustomCNAMEEntries();
|
|
|
|
$found = false;
|
|
foreach ($existingEntries as $entry)
|
|
if ($entry->domain == $domain)
|
|
if ($entry->target == $target) {
|
|
$found = true;
|
|
break;
|
|
}
|
|
|
|
if (!$found)
|
|
return returnError("This domain/ip association does not exist");
|
|
|
|
pihole_execute("-a removecustomcname ".$domain." ".$target);
|
|
|
|
return returnSuccess();
|
|
}
|
|
catch (\Exception $ex)
|
|
{
|
|
return returnError($ex->getMessage());
|
|
}
|
|
}
|
|
|
|
function deleteAllCustomCNAMEEntries()
|
|
{
|
|
try
|
|
{
|
|
$existingEntries = getCustomCNAMEEntries();
|
|
|
|
foreach ($existingEntries as $entry) {
|
|
pihole_execute("-a removecustomcname ".$entry->domain." ".$entry->target);
|
|
}
|
|
|
|
}
|
|
catch (\Exception $ex)
|
|
{
|
|
return returnError($ex->getMessage());
|
|
}
|
|
|
|
return returnSuccess();
|
|
}
|
|
|
|
function returnSuccess($message = "", $json = true)
|
|
{
|
|
if ($json) {
|
|
return [ "success" => true, "message" => $message ];
|
|
} else {
|
|
echo $msg."<br>";
|
|
return true;
|
|
}
|
|
}
|
|
|
|
function returnError($message = "", $json = true)
|
|
{
|
|
if ($json) {
|
|
return [ "success" => false, "message" => $message ];
|
|
} else {
|
|
echo $msg."<br>";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
?>
|