= 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 = trim($_REQUEST['ip']); if(isset($_REQUEST['domain'])) $domain = trim($_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() { if (isset($customDNSFile)) { $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 errorJsonResponse($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."
"; return true; } } function returnError($message = "", $json = true) { if ($json) { return [ "success" => false, "message" => $message ]; } else { echo $msg."
"; return false; } } ?>