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

View File

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

View File

@@ -89,6 +89,7 @@
</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/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"
},
"devDependencies": {
"autoprefixer": "^9.8.0",
"eslint-plugin-compat": "^3.7.0",
"autoprefixer": "^9.8.5",
"eslint-plugin-compat": "^3.8.0",
"postcss-cli": "^7.1.1",
"prettier": "2.0.4",
"xo": "^0.32.0"
"xo": "^0.32.1"
},
"browserslist": [
"defaults",

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,7 +4,6 @@
*
* This file is copyright under the latest version of the EUPL.
* 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.
//Works between all pages
@@ -221,10 +220,6 @@ $(function () {
initCheckboxRadioStyle();
initCPUtemp();
if (typeof initpage === "function") {
setTimeout(initpage, 100);
}
// Run check immediately after page loading ...
checkMessages();
// ... and once again with five seconds delay

View File

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

View File

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

View File

@@ -85,9 +85,9 @@ function initTable() {
$(row).attr("data-id", data.id);
var tooltip =
"Added: " +
utils.datetime(data.date_added) +
utils.datetime(data.date_added, false) +
"\nLast modified: " +
utils.datetime(data.date_modified) +
utils.datetime(data.date_modified, false) +
"\nDatabase ID: " +
data.id;
$("td:eq(0)", row).html(
@@ -318,8 +318,8 @@ function addDomain() {
commentEl = $("#new_regex_comment");
}
var domain = domainEl.val();
var comment = commentEl.val();
var domain = utils.escapeHtml(domainEl.val());
var comment = utils.escapeHtml(commentEl.val());
utils.disableAll();
utils.showAlert("info", "", "Adding " + domainRegex + "...", domain);
@@ -385,10 +385,10 @@ function editDomain() {
var elem = $(this).attr("id");
var tr = $(this).closest("tr");
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 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
// if not included, just use the row data.
@@ -485,7 +485,7 @@ function editDomain() {
function deleteDomain() {
var tr = $(this).closest("tr");
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 domainRegex;

View File

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

View File

@@ -287,6 +287,7 @@ function updateQueriesOverTime() {
});
}
var querytypeids = [];
function updateQueryTypesPie() {
$.getJSON("api.php?getQueryTypes", function (data) {
if ("FTLnotrunning" in data) {
@@ -305,10 +306,16 @@ function updateQueryTypesPie() {
iter = data;
}
querytypeids = [];
Object.keys(iter).forEach(function (key) {
v.push(iter[key]);
c.push(THEME_COLORS[i++ % THEME_COLORS.length]);
k.push(key);
if (iter[key] > 0) {
v.push(iter[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
@@ -340,7 +347,7 @@ function updateQueryTypesPie() {
ci.update();
} else if (e.which === 1) {
// 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 () {

View File

@@ -5,7 +5,7 @@
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
/* global moment:false, utils:false */
/* global utils:false */
var tableApi;
@@ -196,7 +196,7 @@ $(function () {
width: "8%",
render: function (data, type) {
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;
@@ -207,7 +207,7 @@ $(function () {
width: "8%",
render: function (data, type) {
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;

View File

@@ -8,6 +8,22 @@
/* global moment:false, utils:false */
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) {
var token = $("#token").text();
@@ -103,10 +119,6 @@ function handleAjaxError(xhr, textStatus) {
tableApi.draw();
}
function autofilter() {
return $("#autofilter").prop("checked");
}
$(function () {
// Do we want to filter queries?
var GETDict = {};
@@ -140,7 +152,7 @@ $(function () {
rowCallback: function (row, data) {
// DNSSEC status
var dnssecStatus;
switch (data[5]) {
switch (data[6]) {
case "1":
dnssecStatus = '<br><span class="text-green">SECURE</span>';
break;
@@ -162,37 +174,32 @@ $(function () {
}
// Query status
var blocked,
fieldtext,
var fieldtext,
buttontext,
colorClass,
colorClass = false,
isCNAME = false,
regexLink = false;
switch (data[4]) {
case "1":
blocked = true;
colorClass = "text-red";
fieldtext = "Blocked (gravity)";
buttontext =
'<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>';
break;
case "2":
blocked = false;
colorClass = "text-green";
fieldtext = "OK <br class='hidden-lg'>(forwarded)" + dnssecStatus;
buttontext =
'<button type="button" class="btn btn-default btn-sm text-red"><i class="fa fa-ban"></i> Blacklist</button>';
break;
case "3":
blocked = false;
colorClass = "text-green";
fieldtext = "OK <br class='hidden-lg'>(cached)" + dnssecStatus;
buttontext =
'<button type="button" class="btn btn-default btn-sm text-red"><i class="fa fa-ban"></i> Blacklist</button>';
break;
case "4":
blocked = true;
colorClass = "text-red";
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>';
break;
case "5":
blocked = true;
colorClass = "text-red";
fieldtext = "Blocked <br class='hidden-lg'>(exact blacklist)";
buttontext =
'<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>';
break;
case "6":
blocked = true;
colorClass = "text-red";
fieldtext = "Blocked <br class='hidden-lg'>(external, IP)";
buttontext = "";
break;
case "7":
blocked = true;
colorClass = "text-red";
fieldtext = "Blocked <br class='hidden-lg'>(external, NULL)";
buttontext = "";
break;
case "8":
blocked = true;
colorClass = "text-red";
fieldtext = "Blocked <br class='hidden-lg'>(external, NXRA)";
buttontext = "";
break;
case "9":
blocked = true;
colorClass = "text-red";
fieldtext = "Blocked (gravity, CNAME)";
buttontext =
@@ -237,7 +239,6 @@ $(function () {
isCNAME = true;
break;
case "10":
blocked = true;
colorClass = "text-red";
fieldtext = "Blocked <br class='hidden-lg'>(regex blacklist, CNAME)";
@@ -250,7 +251,6 @@ $(function () {
isCNAME = true;
break;
case "11":
blocked = true;
colorClass = "text-red";
fieldtext = "Blocked <br class='hidden-lg'>(exact blacklist, CNAME)";
buttontext =
@@ -258,12 +258,13 @@ $(function () {
isCNAME = true;
break;
default:
blocked = false;
colorClass = false;
fieldtext = "Unknown (" + parseInt(data[4], 10) + ")";
buttontext = "";
}
fieldtext += '<input type="hidden" name="id" value="' + data[4] + '">';
if (colorClass !== false) {
$(row).addClass(colorClass);
}
@@ -306,49 +307,17 @@ $(function () {
}
// Check for existence of sixth column and display only if not Pi-holed
var replytext;
if (data.length > 6 && !blocked) {
switch (data[6]) {
case "0":
replytext = "N/A";
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) + ")";
}
var replytext,
replyid = data[5];
if (replyid >= 0 && replyid < replyTypes.length) {
replytext = replyTypes[replyid];
} else {
replytext = "-";
replytext = "? (" + replyid + ")";
}
replytext += '<input type="hidden" name="id" value="' + replyid + '">';
$("td:eq(5)", row).html(replytext);
if (data.length > 7) {
@@ -368,6 +337,10 @@ $(function () {
var dataIndex = 0;
return data.data.map(function (x) {
x[0] = x[0] * 1e6 + dataIndex++;
var dnssec = x[5];
var reply = x[6];
x[5] = reply;
x[6] = dnssec;
return x;
});
}
@@ -390,7 +363,7 @@ $(function () {
},
{ width: "4%" },
{ 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: "8%", orderData: 6 },
{ width: "10%", orderData: 4 }
@@ -415,77 +388,100 @@ $(function () {
],
initComplete: function () {
var api = this.api();
// Query type IPv4 / IPv6
api.$("td:eq(1)").click(function () {
if (autofilter()) {
api.search(this.textContent).draw();
$("#resetButton").removeClass("hidden");
}
});
api.$("td:eq(1)").hover(
function () {
if (autofilter()) {
this.title = "Click to show only " + this.textContent + " queries";
this.style.color = "#72afd2";
} else {
this.title = "";
this.style.color = "";
api
.$("td:eq(1)")
.click(function (event) {
addColumnFilter(event, 1, this.textContent);
})
.hover(
function () {
$(this).addClass("pointer").attr("title", tooltipText(1, this.textContent));
},
function () {
$(this).removeClass("pointer");
}
},
function () {
this.style.color = "";
}
);
api.$("td:eq(1)").addClass("pointer");
);
// Domain
api.$("td:eq(2)").click(function () {
if (autofilter()) {
var domain = this.textContent.split("\n")[0];
api.search(domain).draw();
$("#resetButton").removeClass("hidden");
}
});
api.$("td:eq(2)").hover(
function () {
if (autofilter()) {
var domain = this.textContent.split("\n")[0];
this.title = "Click to show only queries with domain " + domain;
this.style.color = "#72afd2";
} else {
this.title = "";
this.style.color = "";
api
.$("td:eq(2)")
.click(function (event) {
addColumnFilter(event, 2, this.textContent.split("\n")[0]);
})
.hover(
function () {
$(this).addClass("pointer").attr("title", tooltipText(2, this.textContent));
},
function () {
$(this).removeClass("pointer");
}
},
function () {
this.style.color = "";
}
);
api.$("td:eq(2)").addClass("pointer");
);
// Client
api.$("td:eq(3)").click(function () {
if (autofilter()) {
api.search(this.textContent).draw();
$("#resetButton").removeClass("hidden");
}
});
api.$("td:eq(3)").hover(
function () {
if (autofilter()) {
this.title = "Click to show only queries made by " + this.textContent;
this.style.color = "#72afd2";
} else {
this.title = "";
this.style.color = "";
api
.$("td:eq(3)")
.click(function (event) {
addColumnFilter(event, 3, this.textContent);
})
.hover(
function () {
$(this).addClass("pointer").attr("title", tooltipText(3, this.textContent));
},
function () {
$(this).removeClass("pointer");
}
},
function () {
this.style.color = "";
}
);
api.$("td:eq(3)").addClass("pointer");
);
// Status
api
.$("td:eq(4)")
.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 () {
var data = tableApi.row($(this).parents("tr")).data();
if (data[4] === "2" || data[4] === "3") {
@@ -496,28 +492,84 @@ $(function () {
});
$("#resetButton").click(function () {
tableApi.search("").draw();
$("#resetButton").addClass("hidden");
});
// 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"));
tableApi.search("");
resetColumnsFilters();
});
});
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" });
});
// 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
$(function () {
var alInfo = $("#alInfo");
@@ -255,6 +244,25 @@ $(function () {
input.setAttribute("autocorrect", "off");
input.setAttribute("autocapitalize", "off");
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

View File

@@ -104,8 +104,9 @@ function showAlert(type, icon, title, message) {
}
}
function datetime(date) {
return moment.unix(Math.floor(date)).format("Y-MM-DD [<br class='hidden-lg'>]HH:mm:ss z");
function datetime(date, html) {
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() {
@@ -208,7 +209,14 @@ function stateLoadCallback(itemName) {
return null;
}
// Parse JSON string
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
data.start = 0;
// 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)
$domain = "(\\.|^)".str_replace(".","\\.",$domain)."$";
$stmt->bindValue(":$field", $domain, SQLITE3_TEXT);
$stmt->bindValue(":$field", htmlentities($domain), SQLITE3_TEXT);
if($bindcomment) {
$stmt->bindValue(":comment", $comment, SQLITE3_TEXT);
$stmt->bindValue(":comment", htmlentities($comment), SQLITE3_TEXT);
}
if($stmt->execute() && $stmt->reset())

View File

@@ -214,31 +214,34 @@ function deleteCustomDNSEntry()
function deleteAllCustomDNSEntries()
{
$handle = fopen($customDNSFile, "r");
if ($handle)
if (isset($customDNSFile))
{
try
$handle = fopen($customDNSFile, "r");
if ($handle)
{
while (($line = fgets($handle)) !== false) {
$line = str_replace("\r","", $line);
$line = str_replace("\n","", $line);
$explodedLine = explode (" ", $line);
try
{
while (($line = fgets($handle)) !== false) {
$line = str_replace("\r","", $line);
$line = str_replace("\n","", $line);
$explodedLine = explode (" ", $line);
if (count($explodedLine) != 2)
continue;
if (count($explodedLine) != 2)
continue;
$ip = $explodedLine[0];
$domain = $explodedLine[1];
$ip = $explodedLine[0];
$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();

View File

@@ -58,7 +58,8 @@ if ($_POST['action'] == 'get_groups') {
} elseif ($_POST['action'] == 'add_group') {
// Add new group
try {
$names = str_getcsv(trim($_POST['name']), ' ');
$input = html_entity_decode(trim($_POST['name']));
$names = str_getcsv($input, ' ');
$total = count($names);
$added = 0;
$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') {
// Edit group identified by ID
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');
if (!$stmt) {
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());
}
if (!$stmt->bindValue(':name', $_POST['name'], SQLITE3_TEXT)) {
if (!$stmt->bindValue(':name', $name, SQLITE3_TEXT)) {
throw new Exception('While binding name: ' . $db->lastErrorMsg());
}
$desc = $_POST['desc'];
if (strlen($desc) === 0) {
// Store NULL in database for empty descriptions
$desc = null;
@@ -307,7 +310,7 @@ if ($_POST['action'] == 'get_groups') {
throw new Exception('While binding ip: ' . $db->lastErrorMsg());
}
$comment = $_POST['comment'];
$comment = html_entity_decode($_POST['comment']);
if (strlen($comment) === 0) {
// Store NULL in database for empty comments
$comment = null;
@@ -337,7 +340,7 @@ if ($_POST['action'] == 'get_groups') {
throw new Exception('While preparing statement: ' . $db->lastErrorMsg());
}
$comment = $_POST['comment'];
$comment = html_entity_decode($_POST['comment']);
if (strlen($comment) === 0) {
// Store NULL in database for empty comments
$comment = null;
@@ -497,7 +500,7 @@ if ($_POST['action'] == 'get_groups') {
} elseif ($_POST['action'] == 'add_domain') {
// Add new domain
try {
$domains = explode(' ', trim($_POST['domain']));
$domains = explode(' ', html_entity_decode(trim($_POST['domain'])));
$before = intval($db->querySingle("SELECT COUNT(*) FROM domainlist;"));
$total = count($domains);
$added = 0;
@@ -518,7 +521,7 @@ if ($_POST['action'] == 'get_groups') {
throw new Exception('While binding type: ' . $db->lastErrorMsg());
}
$comment = $_POST['comment'];
$comment = html_entity_decode($_POST['comment']);
if (strlen($comment) === 0) {
// Store NULL in database for empty comments
$comment = null;
@@ -617,7 +620,7 @@ if ($_POST['action'] == 'get_groups') {
throw new Exception('While binding enabled: ' . $db->lastErrorMsg());
}
$comment = $_POST['comment'];
$comment = html_entity_decode($_POST['comment']);
if (strlen($comment) === 0) {
// Store NULL in database for empty comments
$comment = null;
@@ -786,16 +789,16 @@ if ($_POST['action'] == 'get_groups') {
} elseif ($_POST['action'] == 'add_adlist') {
// Add new adlist
try {
$addresses = explode(' ', trim($_POST['address']));
$addresses = explode(' ', html_entity_decode(trim($_POST['address'])));
$total = count($addresses);
$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) {
throw new Exception('While preparing statement: ' . $db->lastErrorMsg());
}
$comment = $_POST['comment'];
$comment = html_entity_decode($_POST['comment']);
if (strlen($comment) === 0) {
// Store NULL in database for empty comments
$comment = null;
@@ -844,7 +847,7 @@ if ($_POST['action'] == 'get_groups') {
throw new Exception('While binding enabled: ' . $db->lastErrorMsg());
}
$comment = $_POST['comment'];
$comment = html_entity_decode($_POST['comment']);
if (strlen($comment) === 0) {
// Store NULL in database for empty comments
$comment = null;
@@ -934,6 +937,53 @@ if ($_POST['action'] == 'get_groups') {
} catch (\Exception $ex) {
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 {
log_and_die('Requested action not supported!');
}

View File

@@ -162,7 +162,7 @@
<head>
<meta charset="utf-8">
<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 -->
<meta http-equiv="x-dns-prefetch-control" content="off">
<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;
}
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
// and test it only if it exists
function istrue(&$argument) {
@@ -263,25 +287,27 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
{
$exploded = explode("#", $_POST["custom".$i."val"], 2);
$IP = trim($exploded[0]);
if(count($exploded) > 1)
{
$port = trim($exploded[1]);
}
else
{
$port = "53";
}
if(!validIP($IP))
{
$error .= "IP (".htmlspecialchars($IP).") is invalid!<br>";
}
elseif(!is_numeric($port))
{
$error .= "Port (".htmlspecialchars($port).") is invalid!<br>";
}
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";
}
// Check if Conditional Forwarding is requested
if(isset($_POST["conditionalForwarding"]))
// Check if rev-server is requested
if(isset($_POST["rev_server"]))
{
$conditionalForwardingIP = trim($_POST["conditionalForwardingIP"]);
$conditionalForwardingDomain = trim($_POST["conditionalForwardingDomain"]);
// Validate conditional forwarding IP
if (!validIP($conditionalForwardingIP))
// Validate CIDR IP
$cidr = trim($_POST["rev_server_cidr"]);
if (!validCIDRIP($cidr))
{
$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
if(!validDomain($conditionalForwardingDomain))
// Validate target IP
$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)
{
$addressArray = explode(".", $conditionalForwardingIP);
$reverseAddress = $addressArray[2].".".$addressArray[1].".".$addressArray[0].".in-addr.arpa";
$extra .= " conditional_forwarding ".$conditionalForwardingIP." ".$conditionalForwardingDomain." $reverseAddress";
$extra .= " rev-server ".$cidr." ".$target." ".$domain;
}
}
@@ -376,16 +409,8 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
{
$IPs = implode (",", $DNSservers);
$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)";
}
else
{
$success .= "Updating DNS settings failed. Result:";
$success .= implode($return);
}
$success .= htmlspecialchars(end($return))."<br>";
$success .= "The DNS settings have been updated (using ".$DNSservercount." DNS servers)";
}
else
{
@@ -712,7 +737,8 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
break;
// Flush network table
case "flusharp":
pihole_execute("arpflush quiet", $output);
$output = pihole_execute("arpflush quiet");
$error = "";
if(is_array($output))
{
$error = implode("<br>", $output);

View File

@@ -173,7 +173,7 @@ function archive_restore_table($file, $table, $flush=false)
foreach($contents as $row)
{
// Limit max length for a domain entry to 253 chars
if(strlen($row[$field]) > 253)
if(isset($field) && strlen($row[$field]) > 253)
continue;
// Bind properties from JSON data
@@ -196,7 +196,7 @@ function archive_restore_table($file, $table, $flush=false)
default:
$sqltype = "UNK";
}
$stmt->bindValue(":".$key, $value, $sqltype);
$stmt->bindValue(":".$key, htmlentities($value), $sqltype);
}
if($stmt->execute() && $stmt->reset() && $stmt->clear())
@@ -540,7 +540,7 @@ if(isset($_POST["action"]))
}
else
{
$hostname = gethostname() ? gethostname()."-" : "";
$hostname = gethostname() ? str_replace(".", "_", gethostname())."-" : "";
$tarname = "pi-hole-".$hostname."teleporter_".date("Y-m-d_H-i-s").".tar";
$filename = $tarname.".gz";
$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 {
$DNSinterface = "single";
}
if (isset($setupVars["CONDITIONAL_FORWARDING"]) && ($setupVars["CONDITIONAL_FORWARDING"] == 1)) {
$conditionalForwarding = true;
$conditionalForwardingDomain = $setupVars["CONDITIONAL_FORWARDING_DOMAIN"];
$conditionalForwardingIP = $setupVars["CONDITIONAL_FORWARDING_IP"];
if (isset($setupVars["REV_SERVER"]) && ($setupVars["REV_SERVER"] == 1)) {
$rev_server = true;
$rev_server_cidr = $setupVars["REV_SERVER_CIDR"];
$rev_server_target = $setupVars["REV_SERVER_TARGET"];
$rev_server_domain = $setupVars["REV_SERVER_DOMAIN"];
} 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"]; ?>">
<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="HOST"><?php echo $lease["host"]; ?></td>
<td id="HOST"><?php echo htmlentities($lease["host"]); ?></td>
<td>
<button type="button" id="button" class="btn btn-warning btn-xs" data-static="alert">
<span class="fas fas fa-file-import"></span>
@@ -742,9 +743,9 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "adlists", "
<tr>
<td><?php echo $lease["hwaddr"]; ?></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) { ?>
<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"]; ?>">
<span class="far fa-trash-alt"></span>
</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
<a href="https://dnssec.vs.uni-due.de/" rel="noopener" target="_blank">here</a>.</p>
</div>
<strong>Conditional Forwarding</strong>
<p>If not configured as your DHCP server, Pi-hole won't be able to
<p>Validate DNS replies and cache DNSSEC data. When forwarding DNS
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
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
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
address of your DHCP server and the name of your local network.</p>
<p>Note: The local domain name must match the domain name specified
in your DHCP server, likely found within the DHCP settings.</p>
<div>
<input type="checkbox" name="conditionalForwarding" id="conditionalForwarding" value="conditionalForwarding" <?php if(isset($conditionalForwarding) && ($conditionalForwarding == true)){ ?>checked<?php } ?>>
<label for="conditionalForwarding"><strong>Use Conditional Forwarding</strong></label>
</div>
<div class="input-group">
<table class="table table-bordered">
<tr>
<th>IP of your router</th>
<th>Local domain name</th>
</tr>
<tr>
<div class="input-group">
<td>
<input type="text" name="conditionalForwardingIP" class="form-control" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off"
<?php if(isset($conditionalForwardingIP)){ ?>value="<?php echo $conditionalForwardingIP; ?>"<?php } ?>>
</td>
<td><input type="text" name="conditionalForwardingDomain" class="form-control" data-mask autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off"
<?php if(isset($conditionalForwardingDomain)){ ?>value="<?php echo $conditionalForwardingDomain; ?>"<?php } ?>>
</td>
</div>
</tr>
</table>
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
address of your DHCP server and which addresses belong to your local network.
Exemplary inout is given below as placeholder in the text boxes (if empty).</p>
<p>If your local network spans 192.168.0.1 - 192.168.0.255, then you will have to input
<code>192.168.0.0/24</code>. If your local network is 192.168.47.1 - 192.168.47.255, it will
be <code>192.168.47.0/24</code> and similar. If your network is larger, the CIDR has to be
different, for instance a range of 10.8.0.1 - 10.8.255.255 results in <code>10.8.0.0/16</code>,
whereas an even wider network of 10.0.0.1 - 10.255.255.255 results in <code>10.0.0.0/8</code>.
Setting up IPv6 ranges is exactly similar to setting up IPv4 here and fully supported.
Feel free to reach out to us on our
<a href="https://discourse.pi-hole.net" target="_blank">Discourse forum</a>
in case you need any assistance setting up local host name resolution for your particular system.</p>
<p>You can also specify a local domain name (like <code>fritz.box</code>) to ensure queries to
devices ending in your local domain name will not leave your network, however, this is optional.
The local domain name must match the domain name specified
in your DHCP server for this to work. You can likely find it within the DHCP settings.</p>
<div class="form-group">
<div>
<input type="checkbox" name="rev_server" id="rev_server" value="rev_server" <?php if(isset($rev_server) && ($rev_server == true)){ ?>checked<?php } ?>>
<label for="rev_server"><strong>Use Conditional Forwarding</strong></label>
</div>
<div class="input-group">
<table class="table table-bordered">
<tr>
<th>Local network in <a href="https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing" target="_blank">CIDR notation</a></th>
<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>

View File

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

View File

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