Merge branch 'release/v5.2' into new/mac_clients

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER
2020-07-30 21:56:21 +02:00
30 changed files with 1169 additions and 1119 deletions

25
.github/workflows/codeql.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: "Code Scanning - Action"
on:
push:
schedule:
- cron: "0 0 * * 0"
jobs:
CodeQL-Build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: javascript
- name: Autobuild
uses: github/codeql-action/autobuild@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

10
api.php
View File

@@ -52,7 +52,10 @@ elseif (isset($_GET['enable']) && $auth)
} }
pihole_execute('enable'); pihole_execute('enable');
$data = array_merge($data, array("status" => "enabled")); $data = array_merge($data, array("status" => "enabled"));
unlink("../custom_disable_timer"); if (file_exists("../custom_disable_timer"))
{
unlink("../custom_disable_timer");
}
} }
elseif (isset($_GET['disable']) && $auth) elseif (isset($_GET['disable']) && $auth)
{ {
@@ -77,7 +80,10 @@ elseif (isset($_GET['disable']) && $auth)
else else
{ {
pihole_execute('disable'); pihole_execute('disable');
unlink("../custom_disable_timer"); if (file_exists("../custom_disable_timer"))
{
unlink("../custom_disable_timer");
}
} }
$data = array_merge($data, array("status" => "disabled")); $data = array_merge($data, array("status" => "disabled"));
} }

View File

@@ -159,7 +159,7 @@
</div> </div>
</div> </div>
<!-- /.row --> <!-- /.row -->
<script src="scripts/pi-hole/js/ip-address-sorting.js"></script>
<script src="scripts/vendor/daterangepicker.min.js"></script> <script src="scripts/vendor/daterangepicker.min.js"></script>
<script src="scripts/pi-hole/js/db_queries.js"></script> <script src="scripts/pi-hole/js/db_queries.js"></script>

View File

@@ -89,6 +89,7 @@
</div> </div>
</div> </div>
<script src="scripts/pi-hole/js/utils.js"></script>
<script src="scripts/pi-hole/js/ip-address-sorting.js"></script> <script src="scripts/pi-hole/js/ip-address-sorting.js"></script>
<script src="scripts/pi-hole/js/customdns.js"></script> <script src="scripts/pi-hole/js/customdns.js"></script>

1398
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -23,11 +23,11 @@
"test": "npm run prettier:check && npm run xo" "test": "npm run prettier:check && npm run xo"
}, },
"devDependencies": { "devDependencies": {
"autoprefixer": "^9.8.0", "autoprefixer": "^9.8.5",
"eslint-plugin-compat": "^3.7.0", "eslint-plugin-compat": "^3.8.0",
"postcss-cli": "^7.1.1", "postcss-cli": "^7.1.1",
"prettier": "2.0.4", "prettier": "2.0.4",
"xo": "^0.32.0" "xo": "^0.32.1"
}, },
"browserslist": [ "browserslist": [
"defaults", "defaults",

View File

@@ -138,11 +138,11 @@ if(strlen($showing) > 0)
</tr> </tr>
</tfoot> </tfoot>
</table> </table>
<div> <p><strong>Filtering options:</strong></p>
<input type="checkbox" id="autofilter"> <ul>
<label for="autofilter">Apply filtering on click on Type, Domain, and Clients</label> <li>Use <kbd>Ctrl</kbd> or <kbd>&#8984;</kbd> + <i class="fas fa-mouse-pointer"></i> to add columns to the current filter</li>
</div> <li>Use <kbd>Shift</kbd> + <i class="fas fa-mouse-pointer"></i> to remove columns from the current filter</li>
<button type="button" id="resetButton" class="btn btn-default btn-sm text-red hidden">Clear Filters</button> </ul><br/><button type="button" id="resetButton" class="btn btn-default btn-sm text-red hidden">Clear filters</button>
</div> </div>
<!-- /.box-body --> <!-- /.box-body -->
</div> </div>
@@ -150,7 +150,7 @@ if(strlen($showing) > 0)
</div> </div>
</div> </div>
<!-- /.row --> <!-- /.row -->
<script src="scripts/pi-hole/js/ip-address-sorting.js"></script>
<script src="scripts/pi-hole/js/utils.js"></script> <script src="scripts/pi-hole/js/utils.js"></script>
<script src="scripts/pi-hole/js/queries.js"></script> <script src="scripts/pi-hole/js/queries.js"></script>

View File

@@ -99,7 +99,7 @@ function add(domain, list) {
domain: domain, domain: domain,
list: list, list: list,
token: token, token: token,
action: "add_domain", action: list === "audit" ? "add_audit" : "add_domain",
comment: "Added from Audit Log" comment: "Added from Audit Log"
}, },
success: function () { success: function () {

View File

@@ -5,6 +5,8 @@
* This file is copyright under the latest version of the EUPL. * This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */ * Please see LICENSE file for your rights under this license. */
/* global utils:false */
var table; var table;
var token = $("#token").text(); var token = $("#token").text();
@@ -76,8 +78,8 @@ $(function () {
}); });
function addCustomDNS() { function addCustomDNS() {
var ip = $("#ip").val(); var ip = utils.escapeHtml($("#ip").val());
var domain = $("#domain").val(); var domain = utils.escapeHtml($("#domain").val());
showAlert("info"); showAlert("info");
$.ajax({ $.ajax({

View File

@@ -373,7 +373,7 @@ $(function () {
}, },
{ width: "10%" }, { width: "10%" },
{ width: "40%" }, { width: "40%" },
{ width: "20%" }, { width: "20%", type: "ip-address" },
{ width: "10%" }, { width: "10%" },
{ width: "5%" } { width: "5%" }
], ],

View File

@@ -4,7 +4,6 @@
* *
* This file is copyright under the latest version of the EUPL. * This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */ * Please see LICENSE file for your rights under this license. */
/* global initpage:false */
//The following functions allow us to display time until pi-hole is enabled after disabling. //The following functions allow us to display time until pi-hole is enabled after disabling.
//Works between all pages //Works between all pages
@@ -221,10 +220,6 @@ $(function () {
initCheckboxRadioStyle(); initCheckboxRadioStyle();
initCPUtemp(); initCPUtemp();
if (typeof initpage === "function") {
setTimeout(initpage, 100);
}
// Run check immediately after page loading ... // Run check immediately after page loading ...
checkMessages(); checkMessages();
// ... and once again with five seconds delay // ... and once again with five seconds delay

View File

@@ -55,9 +55,9 @@ function initTable() {
$(row).attr("data-id", data.id); $(row).attr("data-id", data.id);
var tooltip = var tooltip =
"Added: " + "Added: " +
utils.datetime(data.date_added) + utils.datetime(data.date_added, false) +
"\nLast modified: " + "\nLast modified: " +
utils.datetime(data.date_modified) + utils.datetime(data.date_modified, false) +
"\nDatabase ID: " + "\nDatabase ID: " +
data.id; data.id;
$("td:eq(0)", row).html( $("td:eq(0)", row).html(
@@ -212,8 +212,8 @@ function initTable() {
} }
function addAdlist() { function addAdlist() {
var address = $("#new_address").val(); var address = utils.escapeHtml($("#new_address").val());
var comment = $("#new_comment").val(); var comment = utils.escapeHtml($("#new_comment").val());
utils.disableAll(); utils.disableAll();
utils.showAlert("info", "", "Adding adlist...", address); utils.showAlert("info", "", "Adding adlist...", address);
@@ -258,9 +258,9 @@ function editAdlist() {
var tr = $(this).closest("tr"); var tr = $(this).closest("tr");
var id = tr.attr("data-id"); var id = tr.attr("data-id");
var status = tr.find("#status_" + id).is(":checked") ? 1 : 0; var status = tr.find("#status_" + id).is(":checked") ? 1 : 0;
var comment = tr.find("#comment_" + id).val(); var comment = utils.escapeHtml(tr.find("#comment_" + id).val());
var groups = tr.find("#multiselect_" + id).val(); var groups = tr.find("#multiselect_" + id).val();
var address = tr.find("#address_" + id).text(); var address = utils.escapeHtml(tr.find("#address_" + id).text());
var done = "edited"; var done = "edited";
var notDone = "editing"; var notDone = "editing";
@@ -338,7 +338,7 @@ function editAdlist() {
function deleteAdlist() { function deleteAdlist() {
var tr = $(this).closest("tr"); var tr = $(this).closest("tr");
var id = tr.attr("data-id"); var id = tr.attr("data-id");
var address = tr.find("#address_" + id).text(); var address = utils.escapeHtml(tr.find("#address_" + id).text());
utils.disableAll(); utils.disableAll();
utils.showAlert("info", "", "Deleting adlist...", address); utils.showAlert("info", "", "Deleting adlist...", address);

View File

@@ -106,9 +106,9 @@ function initTable() {
$(row).attr("data-id", data.id); $(row).attr("data-id", data.id);
var tooltip = var tooltip =
"Added: " + "Added: " +
utils.datetime(data.date_added) + utils.datetime(data.date_added, false) +
"\nLast modified: " + "\nLast modified: " +
utils.datetime(data.date_modified) + utils.datetime(data.date_modified, false) +
"\nDatabase ID: " + "\nDatabase ID: " +
data.id; data.id;
var ipName = var ipName =
@@ -260,7 +260,7 @@ function initTable() {
function addClient() { function addClient() {
var ip = $("#select").val().trim(); var ip = $("#select").val().trim();
var comment = $("#new_comment").val(); var comment = utils.escapeHtml($("#new_comment").val());
utils.disableAll(); utils.disableAll();
utils.showAlert("info", "", "Adding client...", ip); utils.showAlert("info", "", "Adding client...", ip);
@@ -318,9 +318,9 @@ function editClient() {
var tr = $(this).closest("tr"); var tr = $(this).closest("tr");
var id = tr.attr("data-id"); var id = tr.attr("data-id");
var groups = tr.find("#multiselect_" + id).val(); var groups = tr.find("#multiselect_" + id).val();
var ip = tr.find("#ip_" + id).text(); var ip = utils.escapeHtml(tr.find("#ip_" + id).text());
var name = tr.find("#name_" + id).text(); var name = utils.escapeHtml(tr.find("#name_" + id).text());
var comment = tr.find("#comment_" + id).val(); var comment = utils.escapeHtml(tr.find("#comment_" + id).val());
var done = "edited"; var done = "edited";
var notDone = "editing"; var notDone = "editing";
@@ -385,7 +385,7 @@ function deleteClient() {
var tr = $(this).closest("tr"); var tr = $(this).closest("tr");
var id = tr.attr("data-id"); var id = tr.attr("data-id");
var ip = tr.find("#ip_" + id).text(); var ip = tr.find("#ip_" + id).text();
var name = tr.find("#name_" + id).text(); var name = utils.escapeHtml(tr.find("#name_" + id).text());
if (name.length > 0) { if (name.length > 0) {
ip += " (" + name + ")"; ip += " (" + name + ")";

View File

@@ -85,9 +85,9 @@ function initTable() {
$(row).attr("data-id", data.id); $(row).attr("data-id", data.id);
var tooltip = var tooltip =
"Added: " + "Added: " +
utils.datetime(data.date_added) + utils.datetime(data.date_added, false) +
"\nLast modified: " + "\nLast modified: " +
utils.datetime(data.date_modified) + utils.datetime(data.date_modified, false) +
"\nDatabase ID: " + "\nDatabase ID: " +
data.id; data.id;
$("td:eq(0)", row).html( $("td:eq(0)", row).html(
@@ -318,8 +318,8 @@ function addDomain() {
commentEl = $("#new_regex_comment"); commentEl = $("#new_regex_comment");
} }
var domain = domainEl.val(); var domain = utils.escapeHtml(domainEl.val());
var comment = commentEl.val(); var comment = utils.escapeHtml(commentEl.val());
utils.disableAll(); utils.disableAll();
utils.showAlert("info", "", "Adding " + domainRegex + "...", domain); utils.showAlert("info", "", "Adding " + domainRegex + "...", domain);
@@ -385,10 +385,10 @@ function editDomain() {
var elem = $(this).attr("id"); var elem = $(this).attr("id");
var tr = $(this).closest("tr"); var tr = $(this).closest("tr");
var id = tr.attr("data-id"); var id = tr.attr("data-id");
var domain = tr.find("#domain_" + id).text(); var domain = utils.escapeHtml(tr.find("#domain_" + id).text());
var type = tr.find("#type_" + id).val(); var type = tr.find("#type_" + id).val();
var status = tr.find("#status_" + id).is(":checked") ? 1 : 0; var status = tr.find("#status_" + id).is(":checked") ? 1 : 0;
var comment = tr.find("#comment_" + id).val(); var comment = utils.escapeHtml(tr.find("#comment_" + id).val());
// Show group assignment field only if in full domain management mode // Show group assignment field only if in full domain management mode
// if not included, just use the row data. // if not included, just use the row data.
@@ -485,7 +485,7 @@ function editDomain() {
function deleteDomain() { function deleteDomain() {
var tr = $(this).closest("tr"); var tr = $(this).closest("tr");
var id = tr.attr("data-id"); var id = tr.attr("data-id");
var domain = tr.find("#domain_" + id).text(); var domain = utils.escapeHtml(tr.find("#domain_" + id).text());
var type = tr.find("#type_" + id).val(); var type = tr.find("#type_" + id).val();
var domainRegex; var domainRegex;

View File

@@ -34,9 +34,9 @@ $(function () {
$(row).attr("data-id", data.id); $(row).attr("data-id", data.id);
var tooltip = var tooltip =
"Added: " + "Added: " +
utils.datetime(data.date_added) + utils.datetime(data.date_added, false) +
"\nLast modified: " + "\nLast modified: " +
utils.datetime(data.date_modified) + utils.datetime(data.date_modified, false) +
"\nDatabase ID: " + "\nDatabase ID: " +
data.id; data.id;
$("td:eq(0)", row).html( $("td:eq(0)", row).html(
@@ -127,8 +127,8 @@ $(function () {
}); });
function addGroup() { function addGroup() {
var name = $("#new_name").val(); var name = utils.escapeHtml($("#new_name").val());
var desc = $("#new_desc").val(); var desc = utils.escapeHtml($("#new_desc").val());
utils.disableAll(); utils.disableAll();
utils.showAlert("info", "", "Adding group...", name); utils.showAlert("info", "", "Adding group...", name);
@@ -166,9 +166,9 @@ function editGroup() {
var elem = $(this).attr("id"); var elem = $(this).attr("id");
var tr = $(this).closest("tr"); var tr = $(this).closest("tr");
var id = tr.attr("data-id"); var id = tr.attr("data-id");
var name = tr.find("#name_" + id).val(); var name = utils.escapeHtml(tr.find("#name_" + id).val());
var status = tr.find("#status_" + id).is(":checked") ? 1 : 0; var status = tr.find("#status_" + id).is(":checked") ? 1 : 0;
var desc = tr.find("#desc_" + id).val(); var desc = utils.escapeHtml(tr.find("#desc_" + id).val());
var done = "edited"; var done = "edited";
var notDone = "editing"; var notDone = "editing";
@@ -239,7 +239,7 @@ function editGroup() {
function deleteGroup() { function deleteGroup() {
var tr = $(this).closest("tr"); var tr = $(this).closest("tr");
var id = tr.attr("data-id"); var id = tr.attr("data-id");
var name = tr.find("#name_" + id).val(); var name = utils.escapeHtml(tr.find("#name_" + id).val());
utils.disableAll(); utils.disableAll();
utils.showAlert("info", "", "Deleting group...", name); utils.showAlert("info", "", "Deleting group...", name);

View File

@@ -287,6 +287,7 @@ function updateQueriesOverTime() {
}); });
} }
var querytypeids = [];
function updateQueryTypesPie() { function updateQueryTypesPie() {
$.getJSON("api.php?getQueryTypes", function (data) { $.getJSON("api.php?getQueryTypes", function (data) {
if ("FTLnotrunning" in data) { if ("FTLnotrunning" in data) {
@@ -305,10 +306,16 @@ function updateQueryTypesPie() {
iter = data; iter = data;
} }
querytypeids = [];
Object.keys(iter).forEach(function (key) { Object.keys(iter).forEach(function (key) {
v.push(iter[key]); if (iter[key] > 0) {
c.push(THEME_COLORS[i++ % THEME_COLORS.length]); v.push(iter[key]);
k.push(key); c.push(THEME_COLORS[i % THEME_COLORS.length]);
k.push(key);
querytypeids.push(i + 1);
}
i++;
}); });
// Build a single dataset with the data to be pushed // Build a single dataset with the data to be pushed
@@ -340,7 +347,7 @@ function updateQueryTypesPie() {
ci.update(); ci.update();
} else if (e.which === 1) { } else if (e.which === 1) {
// which == 1 is left mouse button // which == 1 is left mouse button
window.open("queries.php?querytype=" + ($(this).index() + 1), "_self"); window.open("queries.php?querytype=" + querytypeids[$(this).index()], "_self");
} }
}); });
}).done(function () { }).done(function () {

View File

@@ -5,7 +5,7 @@
* This file is copyright under the latest version of the EUPL. * This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */ * Please see LICENSE file for your rights under this license. */
/* global moment:false, utils:false */ /* global utils:false */
var tableApi; var tableApi;
@@ -196,7 +196,7 @@ $(function () {
width: "8%", width: "8%",
render: function (data, type) { render: function (data, type) {
if (type === "display") { if (type === "display") {
return moment.unix(data).format("Y-MM-DD [<br class='hidden-lg'>]HH:mm:ss z"); return utils.datetime(data);
} }
return data; return data;
@@ -207,7 +207,7 @@ $(function () {
width: "8%", width: "8%",
render: function (data, type) { render: function (data, type) {
if (type === "display") { if (type === "display") {
return moment.unix(data).format("Y-MM-DD [<br class='hidden-lg'>]HH:mm:ss z"); return utils.datetime(data);
} }
return data; return data;

View File

@@ -8,6 +8,22 @@
/* global moment:false, utils:false */ /* global moment:false, utils:false */
var tableApi; var tableApi;
var tableFilters = [];
var replyTypes = [
"N/A",
"NODATA",
"NXDOMAIN",
"CNAME",
"IP",
"DOMAIN",
"RRNAME",
"SERVFAIL",
"REFUSED",
"NOTIMP",
"upstream error"
];
var colTypes = ["time", "query type", "domain", "client", "status", "reply type"];
function add(domain, list) { function add(domain, list) {
var token = $("#token").text(); var token = $("#token").text();
@@ -103,10 +119,6 @@ function handleAjaxError(xhr, textStatus) {
tableApi.draw(); tableApi.draw();
} }
function autofilter() {
return $("#autofilter").prop("checked");
}
$(function () { $(function () {
// Do we want to filter queries? // Do we want to filter queries?
var GETDict = {}; var GETDict = {};
@@ -140,7 +152,7 @@ $(function () {
rowCallback: function (row, data) { rowCallback: function (row, data) {
// DNSSEC status // DNSSEC status
var dnssecStatus; var dnssecStatus;
switch (data[5]) { switch (data[6]) {
case "1": case "1":
dnssecStatus = '<br><span class="text-green">SECURE</span>'; dnssecStatus = '<br><span class="text-green">SECURE</span>';
break; break;
@@ -162,37 +174,32 @@ $(function () {
} }
// Query status // Query status
var blocked, var fieldtext,
fieldtext,
buttontext, buttontext,
colorClass, colorClass = false,
isCNAME = false, isCNAME = false,
regexLink = false; regexLink = false;
switch (data[4]) { switch (data[4]) {
case "1": case "1":
blocked = true;
colorClass = "text-red"; colorClass = "text-red";
fieldtext = "Blocked (gravity)"; fieldtext = "Blocked (gravity)";
buttontext = buttontext =
'<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>'; '<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>';
break; break;
case "2": case "2":
blocked = false;
colorClass = "text-green"; colorClass = "text-green";
fieldtext = "OK <br class='hidden-lg'>(forwarded)" + dnssecStatus; fieldtext = "OK <br class='hidden-lg'>(forwarded)" + dnssecStatus;
buttontext = buttontext =
'<button type="button" class="btn btn-default btn-sm text-red"><i class="fa fa-ban"></i> Blacklist</button>'; '<button type="button" class="btn btn-default btn-sm text-red"><i class="fa fa-ban"></i> Blacklist</button>';
break; break;
case "3": case "3":
blocked = false;
colorClass = "text-green"; colorClass = "text-green";
fieldtext = "OK <br class='hidden-lg'>(cached)" + dnssecStatus; fieldtext = "OK <br class='hidden-lg'>(cached)" + dnssecStatus;
buttontext = buttontext =
'<button type="button" class="btn btn-default btn-sm text-red"><i class="fa fa-ban"></i> Blacklist</button>'; '<button type="button" class="btn btn-default btn-sm text-red"><i class="fa fa-ban"></i> Blacklist</button>';
break; break;
case "4": case "4":
blocked = true;
colorClass = "text-red"; colorClass = "text-red";
fieldtext = "Blocked <br class='hidden-lg'>(regex blacklist)"; fieldtext = "Blocked <br class='hidden-lg'>(regex blacklist)";
@@ -204,32 +211,27 @@ $(function () {
'<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>'; '<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>';
break; break;
case "5": case "5":
blocked = true;
colorClass = "text-red"; colorClass = "text-red";
fieldtext = "Blocked <br class='hidden-lg'>(exact blacklist)"; fieldtext = "Blocked <br class='hidden-lg'>(exact blacklist)";
buttontext = buttontext =
'<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>'; '<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>';
break; break;
case "6": case "6":
blocked = true;
colorClass = "text-red"; colorClass = "text-red";
fieldtext = "Blocked <br class='hidden-lg'>(external, IP)"; fieldtext = "Blocked <br class='hidden-lg'>(external, IP)";
buttontext = ""; buttontext = "";
break; break;
case "7": case "7":
blocked = true;
colorClass = "text-red"; colorClass = "text-red";
fieldtext = "Blocked <br class='hidden-lg'>(external, NULL)"; fieldtext = "Blocked <br class='hidden-lg'>(external, NULL)";
buttontext = ""; buttontext = "";
break; break;
case "8": case "8":
blocked = true;
colorClass = "text-red"; colorClass = "text-red";
fieldtext = "Blocked <br class='hidden-lg'>(external, NXRA)"; fieldtext = "Blocked <br class='hidden-lg'>(external, NXRA)";
buttontext = ""; buttontext = "";
break; break;
case "9": case "9":
blocked = true;
colorClass = "text-red"; colorClass = "text-red";
fieldtext = "Blocked (gravity, CNAME)"; fieldtext = "Blocked (gravity, CNAME)";
buttontext = buttontext =
@@ -237,7 +239,6 @@ $(function () {
isCNAME = true; isCNAME = true;
break; break;
case "10": case "10":
blocked = true;
colorClass = "text-red"; colorClass = "text-red";
fieldtext = "Blocked <br class='hidden-lg'>(regex blacklist, CNAME)"; fieldtext = "Blocked <br class='hidden-lg'>(regex blacklist, CNAME)";
@@ -250,7 +251,6 @@ $(function () {
isCNAME = true; isCNAME = true;
break; break;
case "11": case "11":
blocked = true;
colorClass = "text-red"; colorClass = "text-red";
fieldtext = "Blocked <br class='hidden-lg'>(exact blacklist, CNAME)"; fieldtext = "Blocked <br class='hidden-lg'>(exact blacklist, CNAME)";
buttontext = buttontext =
@@ -258,12 +258,13 @@ $(function () {
isCNAME = true; isCNAME = true;
break; break;
default: default:
blocked = false;
colorClass = false; colorClass = false;
fieldtext = "Unknown (" + parseInt(data[4], 10) + ")"; fieldtext = "Unknown (" + parseInt(data[4], 10) + ")";
buttontext = ""; buttontext = "";
} }
fieldtext += '<input type="hidden" name="id" value="' + data[4] + '">';
if (colorClass !== false) { if (colorClass !== false) {
$(row).addClass(colorClass); $(row).addClass(colorClass);
} }
@@ -306,49 +307,17 @@ $(function () {
} }
// Check for existence of sixth column and display only if not Pi-holed // Check for existence of sixth column and display only if not Pi-holed
var replytext; var replytext,
if (data.length > 6 && !blocked) { replyid = data[5];
switch (data[6]) {
case "0": if (replyid >= 0 && replyid < replyTypes.length) {
replytext = "N/A"; replytext = replyTypes[replyid];
break;
case "1":
replytext = "NODATA";
break;
case "2":
replytext = "NXDOMAIN";
break;
case "3":
replytext = "CNAME";
break;
case "4":
replytext = "IP";
break;
case "5":
replytext = "DOMAIN";
break;
case "6":
replytext = "RRNAME";
break;
case "7":
replytext = "SERVFAIL";
break;
case "8":
replytext = "REFUSED";
break;
case "9":
replytext = "NOTIMP";
break;
case "10":
replytext = "upstream error";
break;
default:
replytext = "? (" + parseInt(data[6], 10) + ")";
}
} else { } else {
replytext = "-"; replytext = "? (" + replyid + ")";
} }
replytext += '<input type="hidden" name="id" value="' + replyid + '">';
$("td:eq(5)", row).html(replytext); $("td:eq(5)", row).html(replytext);
if (data.length > 7) { if (data.length > 7) {
@@ -368,6 +337,10 @@ $(function () {
var dataIndex = 0; var dataIndex = 0;
return data.data.map(function (x) { return data.data.map(function (x) {
x[0] = x[0] * 1e6 + dataIndex++; x[0] = x[0] * 1e6 + dataIndex++;
var dnssec = x[5];
var reply = x[6];
x[5] = reply;
x[6] = dnssec;
return x; return x;
}); });
} }
@@ -390,7 +363,7 @@ $(function () {
}, },
{ width: "4%" }, { width: "4%" },
{ width: "36%", render: $.fn.dataTable.render.text() }, { width: "36%", render: $.fn.dataTable.render.text() },
{ width: "8%", render: $.fn.dataTable.render.text() }, { width: "8%", type: "ip-address", render: $.fn.dataTable.render.text() },
{ width: "14%", orderData: 4 }, { width: "14%", orderData: 4 },
{ width: "8%", orderData: 6 }, { width: "8%", orderData: 6 },
{ width: "10%", orderData: 4 } { width: "10%", orderData: 4 }
@@ -415,77 +388,100 @@ $(function () {
], ],
initComplete: function () { initComplete: function () {
var api = this.api(); var api = this.api();
// Query type IPv4 / IPv6 // Query type IPv4 / IPv6
api.$("td:eq(1)").click(function () { api
if (autofilter()) { .$("td:eq(1)")
api.search(this.textContent).draw(); .click(function (event) {
$("#resetButton").removeClass("hidden"); addColumnFilter(event, 1, this.textContent);
} })
}); .hover(
api.$("td:eq(1)").hover( function () {
function () { $(this).addClass("pointer").attr("title", tooltipText(1, this.textContent));
if (autofilter()) { },
this.title = "Click to show only " + this.textContent + " queries"; function () {
this.style.color = "#72afd2"; $(this).removeClass("pointer");
} else {
this.title = "";
this.style.color = "";
} }
}, );
function () {
this.style.color = "";
}
);
api.$("td:eq(1)").addClass("pointer");
// Domain // Domain
api.$("td:eq(2)").click(function () { api
if (autofilter()) { .$("td:eq(2)")
var domain = this.textContent.split("\n")[0]; .click(function (event) {
api.search(domain).draw(); addColumnFilter(event, 2, this.textContent.split("\n")[0]);
$("#resetButton").removeClass("hidden"); })
} .hover(
}); function () {
api.$("td:eq(2)").hover( $(this).addClass("pointer").attr("title", tooltipText(2, this.textContent));
function () { },
if (autofilter()) { function () {
var domain = this.textContent.split("\n")[0]; $(this).removeClass("pointer");
this.title = "Click to show only queries with domain " + domain;
this.style.color = "#72afd2";
} else {
this.title = "";
this.style.color = "";
} }
}, );
function () {
this.style.color = "";
}
);
api.$("td:eq(2)").addClass("pointer");
// Client // Client
api.$("td:eq(3)").click(function () { api
if (autofilter()) { .$("td:eq(3)")
api.search(this.textContent).draw(); .click(function (event) {
$("#resetButton").removeClass("hidden"); addColumnFilter(event, 3, this.textContent);
} })
}); .hover(
api.$("td:eq(3)").hover( function () {
function () { $(this).addClass("pointer").attr("title", tooltipText(3, this.textContent));
if (autofilter()) { },
this.title = "Click to show only queries made by " + this.textContent; function () {
this.style.color = "#72afd2"; $(this).removeClass("pointer");
} else {
this.title = "";
this.style.color = "";
} }
}, );
function () {
this.style.color = ""; // Status
} api
); .$("td:eq(4)")
api.$("td:eq(3)").addClass("pointer"); .click(function (event) {
var id = this.children.id.value;
var text = this.textContent;
addColumnFilter(event, 4, id + "#" + text);
})
.hover(
function () {
$(this).addClass("pointer").attr("title", tooltipText(4, this.textContent));
},
function () {
$(this).removeClass("pointer");
}
);
// Reply type
api
.$("td:eq(5)")
.click(function (event) {
var id = this.children.id.value;
var text = this.textContent.split(" ")[0];
addColumnFilter(event, 5, id + "#" + text);
})
.hover(
function () {
$(this).addClass("pointer").attr("title", tooltipText(5, this.textContent));
},
function () {
$(this).removeClass("pointer");
}
);
// Disable autocorrect in the search box
var input = $("input[type=search]");
if (input !== null) {
input.attr("autocomplete", "off");
input.attr("autocorrect", "off");
input.attr("autocapitalize", "off");
input.attr("spellcheck", false);
input.attr("placeholder", "Type / Domain / Client");
}
} }
}); });
resetColumnsFilters();
$("#all-queries tbody").on("click", "button", function () { $("#all-queries tbody").on("click", "button", function () {
var data = tableApi.row($(this).parents("tr")).data(); var data = tableApi.row($(this).parents("tr")).data();
if (data[4] === "2" || data[4] === "3") { if (data[4] === "2" || data[4] === "3") {
@@ -496,28 +492,84 @@ $(function () {
}); });
$("#resetButton").click(function () { $("#resetButton").click(function () {
tableApi.search("").draw(); tableApi.search("");
$("#resetButton").addClass("hidden"); resetColumnsFilters();
});
// 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);
var chkboxData = localStorage.getItem("query_log_filter_chkbox");
if (chkboxData !== null) {
// Restore checkbox state
$("#autofilter").prop("checked", chkboxData === "true");
} else {
// Initialize checkbox
$("#autofilter").prop("checked", true);
localStorage.setItem("query_log_filter_chkbox", true);
}
$("#autofilter").click(function () {
localStorage.setItem("query_log_filter_chkbox", $("#autofilter").prop("checked"));
}); });
}); });
function tooltipText(index, text) {
if (index === 5) {
// Strip reply time from tooltip text
text = text.split(" ")[0];
}
if (index in tableFilters && tableFilters[index].length > 0) {
return "Clear filter on " + colTypes[index] + ' "' + text + '" using Shift + Click.';
}
return "Add filter on " + colTypes[index] + ' "' + text + '" using Ctrl + Click.';
}
function addColumnFilter(event, colID, filterstring) {
// Don't do anything when NOT explicitly requesting multi-selection functions
if (!event.ctrlKey && !event.metaKey && !event.shiftKey) {
return;
}
if (event.shiftKey) {
filterstring = "";
}
tableFilters[colID] = filterstring;
applyColumnFiltering();
}
function resetColumnsFilters() {
tableFilters.forEach(function (value, index) {
tableFilters[index] = "";
});
// Clear filter reset button
applyColumnFiltering();
}
function applyColumnFiltering() {
var showReset = false;
tableFilters.forEach(function (value, index) {
// Prepare regex filter string
var regex = "";
// Split filter string if we received a combined ID#Name column
var valArr = value.split("#");
if (valArr.length > 0) {
value = valArr[0];
}
if (value.length > 0) {
// Exact matching
regex = "^" + value + "$";
// Add background color
tableApi.$("td:eq(" + index + ")").addClass("highlight");
// Remember to show reset button
showReset = true;
} else {
// Clear background color
tableApi.$("td:eq(" + index + ")").removeClass("highlight");
}
// Apply filtering on this column (regex may be empty -> no filtering)
tableApi.column(index).search(regex, true, true);
});
if (showReset) {
$("#resetButton").removeClass("hidden");
} else {
$("#resetButton").addClass("hidden");
}
// Trigger table update
tableApi.draw();
}

View File

@@ -229,17 +229,6 @@ $(function () {
$('[data-toggle="tooltip"]').tooltip({ html: true, container: "body" }); $('[data-toggle="tooltip"]').tooltip({ html: true, container: "body" });
}); });
// Change "?tab=" parameter in URL for save and reload
$(".nav-tabs a").on("shown.bs.tab", function (e) {
var tab = e.target.hash.substring(1);
window.history.pushState("", "", "?tab=" + tab);
if (tab === "piholedhcp") {
window.location.reload();
}
window.scrollTo(0, 0);
});
// Auto dismissal for info notifications // Auto dismissal for info notifications
$(function () { $(function () {
var alInfo = $("#alInfo"); var alInfo = $("#alInfo");
@@ -255,6 +244,25 @@ $(function () {
input.setAttribute("autocorrect", "off"); input.setAttribute("autocorrect", "off");
input.setAttribute("autocapitalize", "off"); input.setAttribute("autocapitalize", "off");
input.setAttribute("spellcheck", false); input.setAttribute("spellcheck", false);
// En-/disable conditional forwarding input fields based
// on the checkbox state
$('input[name="rev_server"]').click(function () {
$('input[name="rev_server_cidr"]').prop("disabled", !this.checked);
$('input[name="rev_server_target"]').prop("disabled", !this.checked);
$('input[name="rev_server_domain"]').prop("disabled", !this.checked);
});
});
// Change "?tab=" parameter in URL for save and reload
$(".nav-tabs a").on("shown.bs.tab", function (e) {
var tab = e.target.hash.substring(1);
window.history.pushState("", "", "?tab=" + tab);
if (tab === "piholedhcp") {
window.location.reload();
}
window.scrollTo(0, 0);
}); });
// Bar/Smooth chart toggle // Bar/Smooth chart toggle

View File

@@ -104,8 +104,9 @@ function showAlert(type, icon, title, message) {
} }
} }
function datetime(date) { function datetime(date, html) {
return moment.unix(Math.floor(date)).format("Y-MM-DD [<br class='hidden-lg'>]HH:mm:ss z"); var format = html === false ? "Y-MM-DD HH:mm:ss z" : "Y-MM-DD [<br class='hidden-lg'>]HH:mm:ss z";
return moment.unix(Math.floor(date)).format(format);
} }
function disableAll() { function disableAll() {
@@ -208,7 +209,14 @@ function stateLoadCallback(itemName) {
return null; return null;
} }
// Parse JSON string
data = JSON.parse(data); data = JSON.parse(data);
// Clear possible filtering settings
data.columns.forEach(function (value, index) {
data.columns[index].search.search = "";
});
// Always start on the first page to show most recent queries // Always start on the first page to show most recent queries
data.start = 0; data.start = 0;
// Always start with empty search field // Always start with empty search field

View File

@@ -161,9 +161,9 @@ function add_to_table($db, $table, $domains, $comment=null, $wildcardstyle=false
if($wildcardstyle) if($wildcardstyle)
$domain = "(\\.|^)".str_replace(".","\\.",$domain)."$"; $domain = "(\\.|^)".str_replace(".","\\.",$domain)."$";
$stmt->bindValue(":$field", $domain, SQLITE3_TEXT); $stmt->bindValue(":$field", htmlentities($domain), SQLITE3_TEXT);
if($bindcomment) { if($bindcomment) {
$stmt->bindValue(":comment", $comment, SQLITE3_TEXT); $stmt->bindValue(":comment", htmlentities($comment), SQLITE3_TEXT);
} }
if($stmt->execute() && $stmt->reset()) if($stmt->execute() && $stmt->reset())

View File

@@ -214,31 +214,34 @@ function deleteCustomDNSEntry()
function deleteAllCustomDNSEntries() function deleteAllCustomDNSEntries()
{ {
$handle = fopen($customDNSFile, "r"); if (isset($customDNSFile))
if ($handle)
{ {
try $handle = fopen($customDNSFile, "r");
if ($handle)
{ {
while (($line = fgets($handle)) !== false) { try
$line = str_replace("\r","", $line); {
$line = str_replace("\n","", $line); while (($line = fgets($handle)) !== false) {
$explodedLine = explode (" ", $line); $line = str_replace("\r","", $line);
$line = str_replace("\n","", $line);
$explodedLine = explode (" ", $line);
if (count($explodedLine) != 2) if (count($explodedLine) != 2)
continue; continue;
$ip = $explodedLine[0]; $ip = $explodedLine[0];
$domain = $explodedLine[1]; $domain = $explodedLine[1];
pihole_execute("-a removecustomdns ".$ip." ".$domain); pihole_execute("-a removecustomdns ".$ip." ".$domain);
}
}
catch (\Exception $ex)
{
return errorJsonResponse($ex->getMessage());
} }
}
catch (\Exception $ex)
{
return errorJsonResponse($ex->getMessage());
}
fclose($handle); fclose($handle);
}
} }
return successJsonResponse(); return successJsonResponse();

View File

@@ -58,7 +58,8 @@ if ($_POST['action'] == 'get_groups') {
} elseif ($_POST['action'] == 'add_group') { } elseif ($_POST['action'] == 'add_group') {
// Add new group // Add new group
try { try {
$names = str_getcsv(trim($_POST['name']), ' '); $input = html_entity_decode(trim($_POST['name']));
$names = str_getcsv($input, ' ');
$total = count($names); $total = count($names);
$added = 0; $added = 0;
$stmt = $db->prepare('INSERT INTO "group" (name,description) VALUES (:name,:desc)'); $stmt = $db->prepare('INSERT INTO "group" (name,description) VALUES (:name,:desc)');
@@ -96,6 +97,9 @@ if ($_POST['action'] == 'get_groups') {
} elseif ($_POST['action'] == 'edit_group') { } elseif ($_POST['action'] == 'edit_group') {
// Edit group identified by ID // Edit group identified by ID
try { try {
$name = html_entity_decode($_POST['name']);
$desc = html_entity_decode($_POST['desc']);
$stmt = $db->prepare('UPDATE "group" SET enabled=:enabled, name=:name, description=:desc WHERE id = :id'); $stmt = $db->prepare('UPDATE "group" SET enabled=:enabled, name=:name, description=:desc WHERE id = :id');
if (!$stmt) { if (!$stmt) {
throw new Exception('While preparing statement: ' . $db->lastErrorMsg()); throw new Exception('While preparing statement: ' . $db->lastErrorMsg());
@@ -106,11 +110,10 @@ if ($_POST['action'] == 'get_groups') {
throw new Exception('While binding enabled: ' . $db->lastErrorMsg()); throw new Exception('While binding enabled: ' . $db->lastErrorMsg());
} }
if (!$stmt->bindValue(':name', $_POST['name'], SQLITE3_TEXT)) { if (!$stmt->bindValue(':name', $name, SQLITE3_TEXT)) {
throw new Exception('While binding name: ' . $db->lastErrorMsg()); throw new Exception('While binding name: ' . $db->lastErrorMsg());
} }
$desc = $_POST['desc'];
if (strlen($desc) === 0) { if (strlen($desc) === 0) {
// Store NULL in database for empty descriptions // Store NULL in database for empty descriptions
$desc = null; $desc = null;
@@ -307,7 +310,7 @@ if ($_POST['action'] == 'get_groups') {
throw new Exception('While binding ip: ' . $db->lastErrorMsg()); throw new Exception('While binding ip: ' . $db->lastErrorMsg());
} }
$comment = $_POST['comment']; $comment = html_entity_decode($_POST['comment']);
if (strlen($comment) === 0) { if (strlen($comment) === 0) {
// Store NULL in database for empty comments // Store NULL in database for empty comments
$comment = null; $comment = null;
@@ -337,7 +340,7 @@ if ($_POST['action'] == 'get_groups') {
throw new Exception('While preparing statement: ' . $db->lastErrorMsg()); throw new Exception('While preparing statement: ' . $db->lastErrorMsg());
} }
$comment = $_POST['comment']; $comment = html_entity_decode($_POST['comment']);
if (strlen($comment) === 0) { if (strlen($comment) === 0) {
// Store NULL in database for empty comments // Store NULL in database for empty comments
$comment = null; $comment = null;
@@ -497,7 +500,7 @@ if ($_POST['action'] == 'get_groups') {
} elseif ($_POST['action'] == 'add_domain') { } elseif ($_POST['action'] == 'add_domain') {
// Add new domain // Add new domain
try { try {
$domains = explode(' ', trim($_POST['domain'])); $domains = explode(' ', html_entity_decode(trim($_POST['domain'])));
$before = intval($db->querySingle("SELECT COUNT(*) FROM domainlist;")); $before = intval($db->querySingle("SELECT COUNT(*) FROM domainlist;"));
$total = count($domains); $total = count($domains);
$added = 0; $added = 0;
@@ -518,7 +521,7 @@ if ($_POST['action'] == 'get_groups') {
throw new Exception('While binding type: ' . $db->lastErrorMsg()); throw new Exception('While binding type: ' . $db->lastErrorMsg());
} }
$comment = $_POST['comment']; $comment = html_entity_decode($_POST['comment']);
if (strlen($comment) === 0) { if (strlen($comment) === 0) {
// Store NULL in database for empty comments // Store NULL in database for empty comments
$comment = null; $comment = null;
@@ -617,7 +620,7 @@ if ($_POST['action'] == 'get_groups') {
throw new Exception('While binding enabled: ' . $db->lastErrorMsg()); throw new Exception('While binding enabled: ' . $db->lastErrorMsg());
} }
$comment = $_POST['comment']; $comment = html_entity_decode($_POST['comment']);
if (strlen($comment) === 0) { if (strlen($comment) === 0) {
// Store NULL in database for empty comments // Store NULL in database for empty comments
$comment = null; $comment = null;
@@ -786,16 +789,16 @@ if ($_POST['action'] == 'get_groups') {
} elseif ($_POST['action'] == 'add_adlist') { } elseif ($_POST['action'] == 'add_adlist') {
// Add new adlist // Add new adlist
try { try {
$addresses = explode(' ', trim($_POST['address'])); $addresses = explode(' ', html_entity_decode(trim($_POST['address'])));
$total = count($addresses); $total = count($addresses);
$added = 0; $added = 0;
$stmt = $db->prepare('INSERT INTO adlist (address,comment) VALUES (:address,:comment)'); $stmt = $db->prepare('INSERT OR IGNORE INTO adlist (address,comment) VALUES (:address,:comment)');
if (!$stmt) { if (!$stmt) {
throw new Exception('While preparing statement: ' . $db->lastErrorMsg()); throw new Exception('While preparing statement: ' . $db->lastErrorMsg());
} }
$comment = $_POST['comment']; $comment = html_entity_decode($_POST['comment']);
if (strlen($comment) === 0) { if (strlen($comment) === 0) {
// Store NULL in database for empty comments // Store NULL in database for empty comments
$comment = null; $comment = null;
@@ -844,7 +847,7 @@ if ($_POST['action'] == 'get_groups') {
throw new Exception('While binding enabled: ' . $db->lastErrorMsg()); throw new Exception('While binding enabled: ' . $db->lastErrorMsg());
} }
$comment = $_POST['comment']; $comment = html_entity_decode($_POST['comment']);
if (strlen($comment) === 0) { if (strlen($comment) === 0) {
// Store NULL in database for empty comments // Store NULL in database for empty comments
$comment = null; $comment = null;
@@ -934,6 +937,53 @@ if ($_POST['action'] == 'get_groups') {
} catch (\Exception $ex) { } catch (\Exception $ex) {
JSON_error($ex->getMessage()); JSON_error($ex->getMessage());
} }
} elseif ($_POST['action'] == 'add_audit') {
// Add new domain
try {
$domains = explode(' ', html_entity_decode(trim($_POST['domain'])));
$before = intval($db->querySingle("SELECT COUNT(*) FROM domain_audit;"));
$total = count($domains);
$added = 0;
$stmt = $db->prepare('REPLACE INTO domain_audit (domain) VALUES (:domain)');
if (!$stmt) {
throw new Exception('While preparing statement: ' . $db->lastErrorMsg());
}
foreach ($domains as $domain) {
$input = $domain;
if (!$stmt->bindValue(':domain', $domain, SQLITE3_TEXT)) {
throw new Exception('While binding domain: <strong>' . $db->lastErrorMsg() . '</strong><br>'.
'Added ' . $added . " out of ". $total . " domains");
}
if (!$stmt->execute()) {
throw new Exception('While executing: <strong>' . $db->lastErrorMsg() . '</strong><br>'.
'Added ' . $added . " out of ". $total . " domains");
}
$added++;
}
$after = intval($db->querySingle("SELECT COUNT(*) FROM domain_audit;"));
$difference = $after - $before;
if($total === 1) {
if($difference !== 1) {
$msg = "Not adding ". htmlentities(utf8_encode($domain)) . " as it is already on the list";
} else {
$msg = "Added " . htmlentities(utf8_encode($domain));
}
} else {
if($difference !== $total) {
$msg = "Added " . ($after-$before) . " out of ". $total . " domains (skipped duplicates)";
} else {
$msg = "Added " . $total . " domains";
}
}
$reload = true;
JSON_success($msg);
} catch (\Exception $ex) {
JSON_error($ex->getMessage());
}
} else { } else {
log_and_die('Requested action not supported!'); log_and_die('Requested action not supported!');
} }

View File

@@ -162,7 +162,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; base-uri 'none'; child-src 'self'; form-action 'self'; frame-src 'self'; img-src 'self'; manifest-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"> <meta http-equiv="Content-Security-Policy" content="default-src 'none'; base-uri 'none'; child-src 'self'; form-action 'self'; frame-src 'self'; font-src 'self'; connect-src 'self'; img-src 'self'; manifest-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'">
<!-- Usually browsers proactively perform domain name resolution on links that the user may choose to follow. We disable DNS prefetching here --> <!-- Usually browsers proactively perform domain name resolution on links that the user may choose to follow. We disable DNS prefetching here -->
<meta http-equiv="x-dns-prefetch-control" content="off"> <meta http-equiv="x-dns-prefetch-control" content="off">
<meta http-equiv="cache-control" content="max-age=60,private"> <meta http-equiv="cache-control" content="max-age=60,private">

View File

@@ -19,6 +19,30 @@ function validIP($address){
return !filter_var($address, FILTER_VALIDATE_IP) === 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 // Check for existance of variable
// and test it only if it exists // and test it only if it exists
function istrue(&$argument) { function istrue(&$argument) {
@@ -263,25 +287,27 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
{ {
$exploded = explode("#", $_POST["custom".$i."val"], 2); $exploded = explode("#", $_POST["custom".$i."val"], 2);
$IP = trim($exploded[0]); $IP = trim($exploded[0]);
if(count($exploded) > 1)
{
$port = trim($exploded[1]);
}
else
{
$port = "53";
}
if(!validIP($IP)) if(!validIP($IP))
{ {
$error .= "IP (".htmlspecialchars($IP).") is invalid!<br>"; $error .= "IP (".htmlspecialchars($IP).") is invalid!<br>";
} }
elseif(!is_numeric($port))
{
$error .= "Port (".htmlspecialchars($port).") is invalid!<br>";
}
else else
{ {
array_push($DNSservers,$IP."#".$port); if(count($exploded) > 1)
{
$port = trim($exploded[1]);
if(!is_numeric($port))
{
$error .= "Port (".htmlspecialchars($port).") is invalid!<br>";
}
else
{
$IP .= "#".$port;
}
}
array_push($DNSservers,$IP);
} }
} }
} }
@@ -323,28 +349,35 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
$extra .= "no-dnssec"; $extra .= "no-dnssec";
} }
// Check if Conditional Forwarding is requested // Check if rev-server is requested
if(isset($_POST["conditionalForwarding"])) if(isset($_POST["rev_server"]))
{ {
$conditionalForwardingIP = trim($_POST["conditionalForwardingIP"]); // Validate CIDR IP
$conditionalForwardingDomain = trim($_POST["conditionalForwardingDomain"]); $cidr = trim($_POST["rev_server_cidr"]);
if (!validCIDRIP($cidr))
// Validate conditional forwarding IP
if (!validIP($conditionalForwardingIP))
{ {
$error .= "Conditional forwarding IP (".htmlspecialchars($conditionalForwardingIP).") is invalid!<br>"; $error .= "Conditional forwarding subnet (\"".htmlspecialchars($cidr)."\") is invalid!<br>".
"This field requires CIDR notation for local subnets (e.g., 192.168.0.0/16).<br>".
"Please use only subnets /8, /16, /24, and /32.<br>";
} }
// Validate conditional forwarding domain name // Validate target IP
if(!validDomain($conditionalForwardingDomain)) $target = trim($_POST["rev_server_target"]);
if (!validIP($target))
{ {
$error .= "Conditional forwarding domain name (".htmlspecialchars($conditionalForwardingDomain).") is invalid!<br>"; $error .= "Conditional forwarding target IP (\"".htmlspecialchars($target)."\") is invalid!<br>";
} }
// Validate conditional forwarding domain name (empty is okay)
$domain = trim($_POST["rev_server_domain"]);
if(strlen($domain) > 0 && !validDomain($domain))
{
$error .= "Conditional forwarding domain name (\"".htmlspecialchars($domain)."\") is invalid!<br>";
}
if(!$error) if(!$error)
{ {
$addressArray = explode(".", $conditionalForwardingIP); $extra .= " rev-server ".$cidr." ".$target." ".$domain;
$reverseAddress = $addressArray[2].".".$addressArray[1].".".$addressArray[0].".in-addr.arpa";
$extra .= " conditional_forwarding ".$conditionalForwardingIP." ".$conditionalForwardingDomain." $reverseAddress";
} }
} }
@@ -376,16 +409,8 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
{ {
$IPs = implode (",", $DNSservers); $IPs = implode (",", $DNSservers);
$return = pihole_execute("-a setdns \"".$IPs."\" ".$extra); $return = pihole_execute("-a setdns \"".$IPs."\" ".$extra);
if(!empty($return)) $success .= htmlspecialchars(end($return))."<br>";
{ $success .= "The DNS settings have been updated (using ".$DNSservercount." DNS servers)";
$success .= htmlspecialchars(end($return))."<br>";
$success .= "The DNS settings have been updated (using ".$DNSservercount." DNS servers)";
}
else
{
$success .= "Updating DNS settings failed. Result:";
$success .= implode($return);
}
} }
else else
{ {
@@ -712,7 +737,8 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
break; break;
// Flush network table // Flush network table
case "flusharp": case "flusharp":
pihole_execute("arpflush quiet", $output); $output = pihole_execute("arpflush quiet");
$error = "";
if(is_array($output)) if(is_array($output))
{ {
$error = implode("<br>", $output); $error = implode("<br>", $output);

View File

@@ -173,7 +173,7 @@ function archive_restore_table($file, $table, $flush=false)
foreach($contents as $row) foreach($contents as $row)
{ {
// Limit max length for a domain entry to 253 chars // Limit max length for a domain entry to 253 chars
if(strlen($row[$field]) > 253) if(isset($field) && strlen($row[$field]) > 253)
continue; continue;
// Bind properties from JSON data // Bind properties from JSON data
@@ -196,7 +196,7 @@ function archive_restore_table($file, $table, $flush=false)
default: default:
$sqltype = "UNK"; $sqltype = "UNK";
} }
$stmt->bindValue(":".$key, $value, $sqltype); $stmt->bindValue(":".$key, htmlentities($value), $sqltype);
} }
if($stmt->execute() && $stmt->reset() && $stmt->clear()) if($stmt->execute() && $stmt->reset() && $stmt->clear())
@@ -540,7 +540,7 @@ if(isset($_POST["action"]))
} }
else else
{ {
$hostname = gethostname() ? gethostname()."-" : ""; $hostname = gethostname() ? str_replace(".", "_", gethostname())."-" : "";
$tarname = "pi-hole-".$hostname."teleporter_".date("Y-m-d_H-i-s").".tar"; $tarname = "pi-hole-".$hostname."teleporter_".date("Y-m-d_H-i-s").".tar";
$filename = $tarname.".gz"; $filename = $tarname.".gz";
$archive_file_name = sys_get_temp_dir() ."/". $tarname; $archive_file_name = sys_get_temp_dir() ."/". $tarname;

View File

@@ -1,19 +0,0 @@
/*! iCheck v2.0.0 rc1 - http://git.io/arlzeA, (c) Damir Sultanov - http://fronteed.com */
(function(r,x,m){r.ichecked||(r.ichecked=function(){m=r.jQuery||r.Zepto;var A={autoInit:!0,autoAjax:!1,tap:!0,checkboxClass:"icheckbox",radioClass:"iradio",checkedClass:"checked",disabledClass:"disabled",indeterminateClass:"indeterminate",hoverClass:"hover",callbacks:{ifCreated:!1},classes:{base:"icheck",div:"#-item",area:"#-area-",input:"#-input",label:"#-label"}};r.icheck=m.extend(A,r.icheck);var l=r.navigator.userAgent,ca=/MSIE [5-8]/.test(l)||9>x.documentMode,D=/Opera Mini/.test(l),E=A.classes.base,
O=A.classes.div.replace("#",E),da=A.classes.area.replace("#",E),P=A.classes.input.replace("#",E),Q=A.classes.label.replace("#",E);delete A.classes;var ea={},p={},la=new RegExp(E+"\\[(.*?)\\]"),I=function(a,c,e){a&&(c=la.exec(a))&&p[c[1]]&&(e=c[1]);return e},fa=r.getComputedStyle,X=r.PointerEvent||r.MSPointerEvent,y="ontouchend"in r,F=/mobile|tablet|phone|ip(ad|od)|android|silk|webos/i.test(l),l=["mouse","down","up","over","out"],u=r.PointerEvent?["pointer",l[1],l[2],l[3],l[4]]:["MSPointer","Down",
"Up","Over","Out"],R=["touch","start","end"],S=y&&F||X,T=S?y?R[0]+R[1]:u[0]+u[3]:l[0]+l[3],Y=S?y?R[0]+R[2]:u[0]+u[4]:l[0]+l[4],M=S?y?!1:u[0]+u[1]:l[0]+l[1],Z=S?y?!1:u[0]+u[2]:l[0]+l[2],l=D?"":T+".i "+Y+".i ",u=!D&&M?M+".i "+Z+".i":"",J,U,ga=!1!==A.areaStyle?'position:absolute;display:block;content:"";top:#;bottom:#;left:#;right:#;':0,V=function(a,c,e){J||(J=x.createElement("style"),(x.head||x.getElementsByTagName("head")[0]).appendChild(J),r.createPopup||J.appendChild(x.createTextNode("")),U=J.sheet||
J.styleSheet);c||(c="div."+(e?da+e+":after":O+" input."+P));a=a.replace(/!/g," !important");U.addRule?U.addRule(c,a,0):U.insertRule(c+"{"+a+"}",0)};V("position:absolute!;display:block!;outline:none!;"+(A.debug?"":"opacity:0!;z-index:-99!;clip:rect(0 0 0 0)!;"));(y&&F||D)&&V("cursor:pointer!;","label."+Q+",div."+O);V("display:none!","iframe.icheck-frame");var G=function(a,c,e,b,f,d,k){if(b=a.className)return f=" "+b+" ",1===e?d=c:0===e?k=c:(d=c[0],k=c[1]),d&&0>f.indexOf(" "+d+" ")&&(f+=d+" "),k&&~f.indexOf(" "+
k+" ")&&(f=f.replace(" "+k+" "," ")),f=f.replace(/^\s+|\s+$/g,""),f!==b&&(a.className=f),f},ha=function(a,c,e,b,f,d){p[c]&&(b=p[c],f=b.className,d=m(K(a,"div",f)),d.length&&(m(a).removeClass(P+" "+f).attr("style",b.style),m("label."+b.esc).removeClass(Q+" "+f),m(d).replaceWith(m(a)),e&&N(a,c,e)),p[c]=!1)},ia=function(a,c,e,b,f){e=[];for(b=a.length;b--;)if(c=a[b],c.type)~"input[type=checkbox],input[type=radio]".indexOf(c.type)&&e.push(c);else for(c=m(c).find("input[type=checkbox],input[type=radio]"),
f=c.length;f--;)e.push(c[f]);return e},K=function(a,c,e,b){for(;a&&9!==a.nodeType;)if((a=a.parentNode)&&a.tagName==c.toUpperCase()&&~a.className.indexOf(e)){b=a;break}return b},N=function(a,c,e){e="if"+e;if(p[c].callbacks&&!1!==p[c].callbacks[e]&&(m(a).trigger(e),"function"==typeof p[c].callbacks[e]))p[c].callbacks[e](a,p[c])},ja=function(a,c,e,b){a=ia(a);for(var f=a.length;f--;){var d=a[f],k=d.attributes,l={},g=k.length,h,n,u={},y={},t,q=d.id,w=d.className,z,D=d.type,aa=m.cache?m.cache[d[m.expando]]:
0,B=I(w),L,v,C="",H=!1;v=[];for(var F=r.FastClick?" needsclick":"";g--;)h=k[g].name,n=k[g].value,~h.indexOf("data-")&&(u[h.substr(5)]=n),"style"==h&&(z=n),l[h]=n;aa&&aa.data&&(u=m.extend(u,aa.data));for(t in u){n=u[t];if("true"==n||"false"==n)n="true"==n;y[t.replace(/checkbox|radio|class|id|label/g,function(a,b){return 0===b?a:a.charAt(0).toUpperCase()+a.slice(1)})]=n}k=m.extend({},A,r.icheck,y,c);g=k.handle;"checkbox"!==g&&"radio"!==g&&(g="input[type=checkbox],input[type=radio]");if(!1!==k.init&&
~g.indexOf(D)){for(B&&ha(d,B);!p[B];)if(B=Math.random().toString(36).substr(2,5),!p[B]){L=E+"["+B+"]";break}delete k.autoInit;delete k.autoAjax;k.style=z||"";k.className=L;k.esc=L.replace(/(\[|\])/g,"\\$1");p[B]=k;if(g=K(d,"label",""))!g.htmlFor&&q&&(g.htmlFor=q),v.push(g);if(q)for(h=m('label[for="'+q+'"]');h.length--;)q=h[h.length],q!==g&&v.push(q);for(n=v.length;n--;)q=v[n],h=q.className,h=(g=I(h))?G(q,E+"["+g+"]",0):(h?h+" ":"")+Q,q.className=h+" "+L+F;v=x.createElement("div");if(k.inherit)for(q=
k.inherit.split(/\s*,\s*/),h=q.length;h--;)g=q[h],void 0!==l[g]&&("class"==g?C+=l[g]+" ":v.setAttribute(g,"id"==g?E+"-"+l[g]:l[g]));C+=k[D+"Class"];C+=" "+O+" "+L;k.area&&ga&&(H=(""+k.area).replace(/%|px|em|\+|-/g,"")|0)&&(ea[H]||(V(ga.replace(/#/g,"-"+H+"%"),!1,H),ea[H]=!0),C+=" "+da+H);v.className=C+F;d.className=(w?w+" ":"")+P+" "+L;d.parentNode.replaceChild(v,d);v.appendChild(d);k.insert&&m(v).append(k.insert);H&&(l=fa?fa(v,null).getPropertyValue("position"):v.currentStyle.position,"static"==
l&&(v.style.position="relative"));W(d,v,B,"updated",!0,!1,e);p[B].done=!0;b||N(d,B,"Created")}}},W=function(a,c,e,b,f,d,k){var m=p[e],g={},h={};g.checked=[a.checked,"Checked","Unchecked"];d&&!k||"click"===b||(g.disabled=[a.disabled,"Disabled","Enabled"],g.indeterminate=["true"==a.getAttribute("indeterminate")||!!a.indeterminate,"Indeterminate","Determinate"]);"updated"==b||"click"==b?(h.checked=d?!g.checked[0]:g.checked[0],d&&!k||"click"===b||(h.disabled=g.disabled[0],h.indeterminate=g.indeterminate[0])):
"checked"==b||"unchecked"==b?h.checked="checked"==b:"disabled"==b||"enabled"==b?h.disabled="disabled"==b:"indeterminate"==b||"determinate"==b?h.indeterminate="determinate"!==b:h.checked=!g.checked[0];ka(a,c,g,h,e,m,b,f,d,k)},ka=function(a,c,e,b,f,d,k,l,g,h,n){var r=a.type,u="radio"==r?"Radio":"Checkbox",t,q,w,z,y,x,B,A,v,C;c||(c=K(a,"div",d.className));if(c){for(t in b)if(q=b[t],e[t][0]!==q&&"updated"!==k&&"click"!==k&&(a[t]=q),h&&(q?a.setAttribute(t,t):a.removeAttribute(t)),d[t]!==q){d[t]=q;v=!0;
if("checked"==t&&(C=!0,!n&&q&&(p[f].done||h)&&"radio"==r&&a.name))for(z=K(a,"form",""),w='input[name="'+a.name+'"]',w=z&&!h?m(z).find(w):m(w),z=w.length;z--;)y=w[z],x=I(y.className),a!==y&&p[x]&&p[x].checked&&(B={checked:[!0,"Checked","Unchecked"]},A={checked:!1},ka(y,!1,B,A,x,p[x],"updated",l,g,h,!0));w=[d[t+"Class"],d[t+u+"Class"],d[e[t][1]+"Class"],d[e[t][1]+u+"Class"],d[t+"LabelClass"]];z=[w[3]||w[2],w[1]||w[0]];q&&z.reverse();G(c,z);if(d.mirror&&w[4])for(z=m("label."+d.esc);z.length--;)G(z[z.length],
w[4],q?1:0);l&&!n||N(a,f,e[t][q?1:2])}if(!l||n)v&&N(a,f,"Changed"),C&&N(a,f,"Toggled");d.cursor&&!F&&(d.disabled||d.pointer?d.disabled&&d.pointer&&(c.style.cursor="default",d.pointer=!1):(c.style.cursor="pointer",d.pointer=!0));p[f]=d}};m.fn.icheck=function(a,c){if(/^(checked|unchecked|indeterminate|determinate|disabled|enabled|updated|toggle|destroy|data|styler)$/.test(a))for(var e=ia(this),b=e.length;b--;){var f=e[b],d=I(f.className);if(d){if("data"==a)return p[d];if("styler"==a)return K(f,"div",
p[d].className);"destroy"==a?ha(f,d,"Destroyed"):W(f,!1,d,a);"function"==typeof c&&c(f)}}else"object"!=typeof a&&a||ja(this,a||{});return this};var ba;m(x).on("click.i "+l+u,"label."+Q+",div."+O,function(a){var c=this,e=I(c.className);if(e){var b=a.type,f=p[e],d=f.esc,e="DIV"==c.tagName,k,l,g,h,n=[["label",f.activeLabelClass,f.hoverLabelClass],["div",f.activeClass,f.hoverClass]];e&&n.reverse();if(b==M||b==Z){n[0][1]&&G(c,n[0][1],b==M?1:0);if(f.mirror&&n[1][1])for(g=m(n[1][0]+"."+d);g.length--;)G(g[g.length],
n[1][1],b==M?1:0);e&&b==Z&&f.tap&&F&&X&&!D&&(h=!0)}else if(b==T||b==Y){n[0][2]&&G(c,n[0][2],b==T?1:0);if(f.mirror&&n[1][2])for(g=m(n[1][0]+"."+d);g.length--;)G(g[g.length],n[1][2],b==T?1:0);e&&b==Y&&f.tap&&F&&y&&!D&&(h=!0)}else e&&(F&&(y||X)&&f.tap&&!D||(h=!0));h&&setTimeout(function(){l=a.currentTarget||{};"LABEL"!==l.tagName&&(!f.change||100<+new Date-f.change)&&(k=m(c).find("input."+d).click(),(ca||D)&&k.change())},2)}}).on("click.i change.i focusin.i focusout.i keyup.i keydown.i","input."+P,function(a){var c=
I(this.className);if(c){var e=a.type,b=p[c],f=b.esc,d="click"==e?!1:K(this,"div",b.className);if("click"==e)p[c].change=+new Date,a.stopPropagation();else if("change"==e)d&&!this.disabled&&W(this,d,c,"click");else if(~e.indexOf("focus")){if(a=[b.focusClass,b.focusLabelClass],a[0]&&d&&G(d,a[0],"focusin"==e?1:0),b.mirror&&a[1])for(b=m("label."+f);b.length--;)G(b[b.length],a[1],"focusin"==e?1:0)}else d&&!this.disabled&&("keyup"==e?(("checkbox"==this.type&&32==a.keyCode&&b.keydown||"radio"==this.type&&
!this.checked)&&W(this,d,c,"click",!1,!0),p[c].keydown=!1,p[ba]&&(p[ba].keydown=!1)):(ba=c,p[c].keydown=!0))}}).ready(function(){r.icheck.autoInit&&m("."+E).icheck();if(r.jQuery){var a=x.body||x.getElementsByTagName("body")[0];m.ajaxSetup({converters:{"text html":function(c){if(r.icheck.autoAjax&&a){var e=x.createElement("iframe"),b;ca||(e.style="display:none");e.className="iframe.icheck-frame";e.src="about:blank";a.appendChild(e);b=e.contentDocument?e.contentDocument:e.contentWindow.document;b.open();
b.write(c);b.close();a.removeChild(e);b=m(b);ja(b.find("."+E),{},!0);b=b[0];c=(b.body||b.getElementsByTagName("body")[0]).innerHTML}return c}}})}})},"function"==typeof define&&define.amd?define("icheck",[r.jQuery?"jquery":"zepto"],r.ichecked):r.ichecked())})(window,document);

View File

@@ -164,12 +164,13 @@ if (isset($setupVars["DNSMASQ_LISTENING"])) {
} else { } else {
$DNSinterface = "single"; $DNSinterface = "single";
} }
if (isset($setupVars["CONDITIONAL_FORWARDING"]) && ($setupVars["CONDITIONAL_FORWARDING"] == 1)) { if (isset($setupVars["REV_SERVER"]) && ($setupVars["REV_SERVER"] == 1)) {
$conditionalForwarding = true; $rev_server = true;
$conditionalForwardingDomain = $setupVars["CONDITIONAL_FORWARDING_DOMAIN"]; $rev_server_cidr = $setupVars["REV_SERVER_CIDR"];
$conditionalForwardingIP = $setupVars["CONDITIONAL_FORWARDING_IP"]; $rev_server_target = $setupVars["REV_SERVER_TARGET"];
$rev_server_domain = $setupVars["REV_SERVER_DOMAIN"];
} else { } else {
$conditionalForwarding = false; $rev_server = false;
} }
?> ?>
@@ -714,7 +715,7 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "adlists", "
title="Lease type: IPv<?php echo $lease["type"]; ?><br/>Remaining lease time: <?php echo $lease["TIME"]; ?><br/>DHCP UID: <?php echo $lease["clid"]; ?>"> title="Lease type: IPv<?php echo $lease["type"]; ?><br/>Remaining lease time: <?php echo $lease["TIME"]; ?><br/>DHCP UID: <?php echo $lease["clid"]; ?>">
<td id="MAC"><?php echo $lease["hwaddr"]; ?></td> <td id="MAC"><?php echo $lease["hwaddr"]; ?></td>
<td id="IP" data-order="<?php echo bin2hex(inet_pton($lease["IP"])); ?>"><?php echo $lease["IP"]; ?></td> <td id="IP" data-order="<?php echo bin2hex(inet_pton($lease["IP"])); ?>"><?php echo $lease["IP"]; ?></td>
<td id="HOST"><?php echo $lease["host"]; ?></td> <td id="HOST"><?php echo htmlentities($lease["host"]); ?></td>
<td> <td>
<button type="button" id="button" class="btn btn-warning btn-xs" data-static="alert"> <button type="button" id="button" class="btn btn-warning btn-xs" data-static="alert">
<span class="fas fas fa-file-import"></span> <span class="fas fas fa-file-import"></span>
@@ -742,9 +743,9 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "adlists", "
<tr> <tr>
<td><?php echo $lease["hwaddr"]; ?></td> <td><?php echo $lease["hwaddr"]; ?></td>
<td data-order="<?php echo bin2hex(inet_pton($lease["IP"])); ?>"><?php echo $lease["IP"]; ?></td> <td data-order="<?php echo bin2hex(inet_pton($lease["IP"])); ?>"><?php echo $lease["IP"]; ?></td>
<td><?php echo $lease["host"]; ?></td> <td><?php echo htmlentities($lease["host"]); ?></td>
<td><?php if (strlen($lease["hwaddr"]) > 0) { ?> <td><?php if (strlen($lease["hwaddr"]) > 0) { ?>
<button type="button" class="btn btn-danger btn-xs" name="removestatic" <button type="submit" class="btn btn-danger btn-xs" name="removestatic"
value="<?php echo $lease["hwaddr"]; ?>"> value="<?php echo $lease["hwaddr"]; ?>">
<span class="far fa-trash-alt"></span> <span class="far fa-trash-alt"></span>
</button> </button>
@@ -975,38 +976,69 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "adlists", "
when enabling DNSSEC. A DNSSEC resolver test can be found when enabling DNSSEC. A DNSSEC resolver test can be found
<a href="https://dnssec.vs.uni-due.de/" rel="noopener" target="_blank">here</a>.</p> <a href="https://dnssec.vs.uni-due.de/" rel="noopener" target="_blank">here</a>.</p>
</div> </div>
<strong>Conditional Forwarding</strong> <p>Validate DNS replies and cache DNSSEC data. When forwarding DNS
<p>If not configured as your DHCP server, Pi-hole won't be able to queries, Pi-hole requests the DNSSEC records needed to validate
the replies. If a domain fails validation or the upstream does not
support DNSSEC, this setting can cause issues resolving domains.
Use Google, Cloudflare, DNS.WATCH, Quad9, or another DNS
server which supports DNSSEC when activating DNSSEC. Note that
the size of your log might increase significantly
when enabling DNSSEC. A DNSSEC resolver test can be found
<a href="https://dnssec.vs.uni-due.de/" rel="noopener" target="_blank">here</a>.</p>
<br>
<h4>Conditional forwarding</h4>
<p>If not configured as your DHCP server, Pi-hole typically won't be able to
determine the names of devices on your local network. As a determine the names of devices on your local network. As a
result, tables such as Top Clients will only show IP addresses.</p> result, tables such as Top Clients will only show IP addresses.</p>
<p>One solution for this is to configure Pi-hole to forward these <p>One solution for this is to configure Pi-hole to forward these
requests to your DHCP server (most likely your router), but only for devices on your requests to your DHCP server (most likely your router), but only for devices on your
home network. To configure this we will need to know the IP home network. To configure this we will need to know the IP
address of your DHCP server and the name of your local network.</p> address of your DHCP server and which addresses belong to your local network.
<p>Note: The local domain name must match the domain name specified Exemplary inout is given below as placeholder in the text boxes (if empty).</p>
in your DHCP server, likely found within the DHCP settings.</p> <p>If your local network spans 192.168.0.1 - 192.168.0.255, then you will have to input
<div> <code>192.168.0.0/24</code>. If your local network is 192.168.47.1 - 192.168.47.255, it will
<input type="checkbox" name="conditionalForwarding" id="conditionalForwarding" value="conditionalForwarding" <?php if(isset($conditionalForwarding) && ($conditionalForwarding == true)){ ?>checked<?php } ?>> be <code>192.168.47.0/24</code> and similar. If your network is larger, the CIDR has to be
<label for="conditionalForwarding"><strong>Use Conditional Forwarding</strong></label> different, for instance a range of 10.8.0.1 - 10.8.255.255 results in <code>10.8.0.0/16</code>,
</div> whereas an even wider network of 10.0.0.1 - 10.255.255.255 results in <code>10.0.0.0/8</code>.
<div class="input-group"> Setting up IPv6 ranges is exactly similar to setting up IPv4 here and fully supported.
<table class="table table-bordered"> Feel free to reach out to us on our
<tr> <a href="https://discourse.pi-hole.net" target="_blank">Discourse forum</a>
<th>IP of your router</th> in case you need any assistance setting up local host name resolution for your particular system.</p>
<th>Local domain name</th> <p>You can also specify a local domain name (like <code>fritz.box</code>) to ensure queries to
</tr> devices ending in your local domain name will not leave your network, however, this is optional.
<tr> The local domain name must match the domain name specified
<div class="input-group"> in your DHCP server for this to work. You can likely find it within the DHCP settings.</p>
<td> <div class="form-group">
<input type="text" name="conditionalForwardingIP" class="form-control" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off" <div>
<?php if(isset($conditionalForwardingIP)){ ?>value="<?php echo $conditionalForwardingIP; ?>"<?php } ?>> <input type="checkbox" name="rev_server" id="rev_server" value="rev_server" <?php if(isset($rev_server) && ($rev_server == true)){ ?>checked<?php } ?>>
</td> <label for="rev_server"><strong>Use Conditional Forwarding</strong></label>
<td><input type="text" name="conditionalForwardingDomain" class="form-control" data-mask autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off" </div>
<?php if(isset($conditionalForwardingDomain)){ ?>value="<?php echo $conditionalForwardingDomain; ?>"<?php } ?>> <div class="input-group">
</td> <table class="table table-bordered">
</div> <tr>
</tr> <th>Local network in <a href="https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing" target="_blank">CIDR notation</a></th>
</table> <th>IP address of your DHCP server (router)</th>
<th>Local domain name (optional)</th>
</tr>
<tr>
<td>
<input type="text" name="rev_server_cidr" placeholder="192.168.0.0/16" class="form-control" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off"
<?php if(isset($rev_server_cidr)){ ?>value="<?php echo $rev_server_cidr; ?>"<?php } ?>
<?php if(!isset($rev_server) || !$rev_server){ ?>disabled<?php } ?>>
</td>
<td>
<input type="text" name="rev_server_target" placeholder="192.168.0.1" class="form-control" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off"
<?php if(isset($rev_server_target)){ ?>value="<?php echo $rev_server_target; ?>"<?php } ?>
<?php if(!isset($rev_server) || !$rev_server){ ?>disabled<?php } ?>>
</td>
<td>
<input type="text" name="rev_server_domain" placeholder="local" class="form-control" data-mask autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off"
<?php if(isset($rev_server_domain)){ ?>value="<?php echo $rev_server_domain; ?>"<?php } ?>
<?php if(!isset($rev_server) || !$rev_server){ ?>disabled<?php } ?>>
</td>
</tr>
</table>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -385,7 +385,7 @@ pre {
color: #007997 !important; color: #007997 !important;
} }
td.highlight { td.highlight {
background-color: yellow; background-color: rgba(255, 204, 0, 0.333);
} }
.btn-default { .btn-default {
box-shadow: none; box-shadow: none;

View File

@@ -192,7 +192,7 @@
} }
td.highlight { td.highlight {
background-color: #ff0 !important; background-color: rgba(255, 204, 0, 0.333);
} }
.network-never { .network-never {