diff --git a/cname_records.php b/cname_records.php new file mode 100644 index 00000000..e6f98197 --- /dev/null +++ b/cname_records.php @@ -0,0 +1,96 @@ + + + + + + +
+
+
+ +
+

+ Add a new CNAME record +

+
+ +
+
+
+ + +
+
+ + +
+
+
+ +
+
+
+ + + + + + +
+
+
+
+

+ List of local CNAME records +

+
+ +
+ + + + + + + + +
DomainTargetAction
+ +
+ +
+ +
+
+ + + + diff --git a/scripts/pi-hole/js/customcname.js b/scripts/pi-hole/js/customcname.js new file mode 100644 index 00000000..49aebab2 --- /dev/null +++ b/scripts/pi-hole/js/customcname.js @@ -0,0 +1,116 @@ +/* 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. */ + +var table; + +function showAlert(type, message) { + var alertElement = null; + var messageElement = null; + + switch (type) { + case "info": + alertElement = $("#alInfo"); + break; + case "success": + alertElement = $("#alSuccess"); + break; + case "warning": + alertElement = $("#alWarning"); + messageElement = $("#warn"); + break; + case "error": + alertElement = $("#alFailure"); + messageElement = $("#err"); + break; + default: + return; + } + + if (messageElement !== null) messageElement.html(message); + + alertElement.fadeIn(200); + alertElement.delay(8000).fadeOut(2000); +} + +$(document).ready(function () { + $("#btnAdd").on("click", addCustomCNAME); + + table = $("#customCNAMETable").DataTable({ + ajax: "scripts/pi-hole/php/customcname.php?action=get", + columns: [{}, {}, { orderable: false, searchable: false }], + columnDefs: [ + { + targets: 2, + render: function (data, type, row) { + return ( + '" + ); + } + } + ], + drawCallback: function () { + $(".deleteCustomCNAME").on("click", deleteCustomCNAME); + } + }); + // Disable autocorrect in the search box + var input = document.querySelector("input[type=search]"); + input.setAttribute("autocomplete", "off"); + input.setAttribute("autocorrect", "off"); + input.setAttribute("autocapitalize", "off"); + input.setAttribute("spellcheck", false); +}); + +function addCustomCNAME() { + var target = $("#target").val(); + var domain = $("#domain").val(); + + showAlert("info"); + $.ajax({ + url: "scripts/pi-hole/php/customcname.php", + method: "post", + dataType: "json", + data: { action: "add", target: target, domain: domain }, + success: function (response) { + if (response.success) { + showAlert("success"); + table.ajax.reload(); + } else showAlert("error", response.message); + }, + error: function () { + showAlert("error", "Error while adding this custom CNAME record"); + } + }); +} + +function deleteCustomCNAME() { + var target = $(this).attr("data-target"); + var domain = $(this).attr("data-domain"); + + showAlert("info"); + $.ajax({ + url: "scripts/pi-hole/php/customcname.php", + method: "post", + dataType: "json", + data: { action: "delete", domain: domain, target: target }, + success: function (response) { + if (response.success) { + showAlert("success"); + table.ajax.reload(); + } else showAlert("error", response.message); + }, + error: function (jqXHR, exception) { + showAlert("error", "Error while deleting this custom CNAME record"); + console.log(exception); + } + }); +} diff --git a/scripts/pi-hole/php/customcname.php b/scripts/pi-hole/php/customcname.php new file mode 100644 index 00000000..fcc5e862 --- /dev/null +++ b/scripts/pi-hole/php/customcname.php @@ -0,0 +1,141 @@ +domain, $entry->target ]; + + return [ "data" => $data ]; + } + + function getCustomCNAMEEntries() + { + global $customCNAMEFile; + + $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() + { + try + { + $target = !empty($_REQUEST['target']) ? $_REQUEST['target']: ""; + $domain = !empty($_REQUEST['domain']) ? $_REQUEST['domain']: ""; + + if (empty($target)) + return errorJsonResponse("Target must be set"); + + if (empty($domain)) + return errorJsonResponse("Domain must be set"); + + // Check if each submitted domain is valid + $domains = array_map('trim', explode(",", $domain)); + foreach ($domains as $d) { + if (!is_valid_domain_name($d)) + return errorJsonResponse("Domain '$d' is not valid"); + } + + $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 errorJsonResponse("There is already a CNAME record for '$d'"); + + exec("sudo pihole -a addcustomcname ".$domain." ".$target); + + return successJsonResponse(); + } + catch (\Exception $ex) + { + return errorJsonResponse($ex->getMessage()); + } + } + + function deleteCustomCNAMEEntry() + { + try + { + $target = !empty($_REQUEST['target']) ? $_REQUEST['target']: ""; + $domain = !empty($_REQUEST['domain']) ? $_REQUEST['domain']: ""; + + if (empty($target)) + return errorJsonResponse("Target must be set"); + + if (empty($domain)) + return errorJsonResponse("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 errorJsonResponse("This domain/ip association does not exist"); + + exec("sudo pihole -a removecustomcname ".$domain." ".$target); + + return successJsonResponse(); + } + catch (\Exception $ex) + { + return errorJsonResponse($ex->getMessage()); + } + } + + function successJsonResponse($message = "") + { + return [ "success" => true, "message" => $message ]; + } + + function errorJsonResponse($message = "") + { + return [ "success" => false, "message" => $message ]; + } +?> diff --git a/scripts/pi-hole/php/header.php b/scripts/pi-hole/php/header.php index 38f920aa..96a88465 100644 --- a/scripts/pi-hole/php/header.php +++ b/scripts/pi-hole/php/header.php @@ -608,10 +608,25 @@ if($auth) { - class="active"> - - Local DNS Records - +
  • active"> + + + + + Local DNS + +