show all";
+ $showing .= " up to 100 queries";
+ $showall = true;
}
if(isset($setupVars["API_PRIVACY_MODE"]))
@@ -74,6 +76,8 @@ if(isset($setupVars["API_PRIVACY_MODE"]))
if(strlen($showing) > 0)
{
$showing = "(".$showing.")";
+ if($showall)
+ $showing .= ",
";
}
?>
@@ -135,7 +139,7 @@ if(strlen($showing) > 0)
@@ -146,7 +150,7 @@ if(strlen($showing) > 0)
diff --git a/queryads.php b/queryads.php
index 14bec1dd..0fe5850c 100644
--- a/queryads.php
+++ b/queryads.php
@@ -9,7 +9,7 @@
?>
diff --git a/scripts/pi-hole/js/db_queries.js b/scripts/pi-hole/js/db_queries.js
index 751081d4..916fc9e0 100644
--- a/scripts/pi-hole/js/db_queries.js
+++ b/scripts/pi-hole/js/db_queries.js
@@ -161,16 +161,16 @@ var reloadCallback = function()
statistics[3]++;
}
}
- $("h3#dns_queries").text(statistics[0]);
- $("h3#ads_blocked_exact").text(statistics[2]);
- $("h3#ads_wildcard_blocked").text(statistics[3]);
+ $("h3#dns_queries").text(statistics[0].toLocaleString());
+ $("h3#ads_blocked_exact").text(statistics[2].toLocaleString());
+ $("h3#ads_wildcard_blocked").text(statistics[3].toLocaleString());
var percent = 0.0;
if(statistics[2] + statistics[3] > 0)
{
percent = 100.0*(statistics[2] + statistics[3]) / statistics[0];
}
- $("h3#ads_percentage_today").text(parseFloat(percent).toFixed(1)+" %");
+ $("h3#ads_percentage_today").text(parseFloat(percent).toFixed(1).toLocaleString()+" %");
};
function refreshTableData() {
diff --git a/scripts/pi-hole/js/index.js b/scripts/pi-hole/js/index.js
index 9f8cb8c6..8c28f0bf 100644
--- a/scripts/pi-hole/js/index.js
+++ b/scripts/pi-hole/js/index.js
@@ -29,6 +29,94 @@ function objectToArray(p){
return [idx,arr];
}
+var lastTooltipTime = 0;
+
+var customTooltips = function(tooltip) {
+ // Tooltip Element
+ var tooltipEl = document.getElementById("chartjs-tooltip");
+ if (!tooltipEl) {
+ tooltipEl = document.createElement("div");
+ tooltipEl.id = "chartjs-tooltip";
+ document.body.appendChild(tooltipEl);
+ $(tooltipEl).html("
");
+ }
+ // Hide if no tooltip
+ if (tooltip.opacity === 0) {
+ tooltipEl.style.opacity = 0;
+ return;
+ }
+
+ // Limit rendering to once every 50ms. This gives the DOM time to react,
+ // and avoids "lag" caused by not giving the DOM time to reapply CSS.
+ var now = Date.now();
+ if(now - lastTooltipTime < 50) {
+ return;
+ }
+ lastTooltipTime = now;
+
+ // Set caret Position
+ tooltipEl.classList.remove("above", "below", "no-transform");
+ if (tooltip.yAlign) {
+ tooltipEl.classList.add(tooltip.yAlign);
+ } else {
+ tooltipEl.classList.add("above");
+ }
+ function getBody(bodyItem) {
+ return bodyItem.lines;
+ }
+ // Set Text
+ if (tooltip.body) {
+ var titleLines = tooltip.title || [];
+ var bodyLines = tooltip.body.map(getBody);
+ var innerHtml = "
";
+ titleLines.forEach(function(title) {
+ innerHtml += "" + title + " ";
+ });
+ innerHtml += " ";
+ var printed = 0;
+ bodyLines.forEach(function(body, i) {
+ var colors = tooltip.labelColors[i];
+ var style = "background:" + colors.backgroundColor;
+ style += "; border-color:" + colors.borderColor;
+ style += "; border-width: 2px";
+ var span = " ";
+ var num = body[0].split(": ");
+ if(num[1] > 0)
+ {
+ innerHtml += "" + span + body + " ";
+ printed++;
+ }
+ });
+ if(printed < 1)
+ {
+ innerHtml += "No activity recorded ";
+ }
+ innerHtml += "
";
+ $(tooltipEl).html(innerHtml);
+ }
+
+ // Display, position, and set styles for font
+ var position = this._chart.canvas.getBoundingClientRect();
+ var width = tooltip.caretX;
+ // Prevent compression of the tooltip at the right edge of the screen
+ if($(document).width() - tooltip.caretX < 400)
+ {
+ width = $(document).width()-400;
+ }
+ // Prevent tooltip disapearing behind the sidebar
+ if(tooltip.caretX < 100)
+ {
+ width = 100;
+ }
+ tooltipEl.style.opacity = 1;
+ tooltipEl.style.left = position.left + width + "px";
+ tooltipEl.style.top = position.top + tooltip.caretY + window.scrollY + "px";
+ tooltipEl.style.fontFamily = tooltip._bodyFontFamily;
+ tooltipEl.style.fontSize = tooltip.bodyFontSize + "px";
+ tooltipEl.style.fontStyle = tooltip._bodyFontStyle;
+ tooltipEl.style.padding = tooltip.yPadding + "px " + tooltip.xPadding + "px";
+};
+
// Functions to update data in page
var failures = 0;
@@ -178,6 +266,23 @@ function updateQueryTypesPie() {
queryTypePieChart.update();
// Don't use rotation animation for further updates
queryTypePieChart.options.animation.duration=0;
+ // Generate legend in separate div
+ $("#query-types-legend").html(queryTypePieChart.generateLegend());
+ $("#query-types-legend > ul > li").on("click",function(e){
+ $(this).toggleClass("strike");
+ var index = $(this).index();
+ var ci = e.view.queryTypePieChart;
+ var meta = ci.data.datasets[0]._meta;
+ for(let i in meta)
+ {
+ if ({}.hasOwnProperty.call(meta, i))
+ {
+ var curr = meta[i].data[index];
+ curr.hidden = !curr.hidden;
+ }
+ }
+ ci.update();
+ });
}).done(function() {
// Reload graph after minute
setTimeout(updateQueryTypesPie, 60000);
@@ -266,7 +371,6 @@ function updateForwardedOverTime() {
});
}
-
function updateClientsOverTime() {
$.getJSON("api.php?overTimeDataClients&getClientNames", function(data) {
@@ -277,6 +381,13 @@ function updateClientsOverTime() {
// convert received objects to arrays
data.over_time = objectToArray(data.over_time);
+
+ // Remove graph if there are no results (e.g. privacy mode enabled)
+ if(jQuery.isEmptyObject(data.over_time))
+ {
+ $("#clients").parent().remove();
+ return;
+ }
// remove last data point since it not representative
data.over_time[0].splice(-1,1);
var timestamps = data.over_time[0];
@@ -286,12 +397,16 @@ function updateClientsOverTime() {
for (key in data.clients)
{
if (!{}.hasOwnProperty.call(data.clients, key)) continue;
- if(key.indexOf("|") > -1)
+ var clientname;
+ if(data.clients[key].name.length > 0)
{
- var idx = key.indexOf("|");
- key = key.substr(0, idx);
+ clientname = data.clients[key].name;
}
- labels.push(key);
+ else
+ {
+ clientname = data.clients[key].ip;
+ }
+ labels.push(clientname);
}
// Get colors from AdminLTE
var colors = [];
@@ -349,7 +464,7 @@ function updateClientsOverTime() {
}
function updateForwardDestinationsPie() {
- $.getJSON("api.php?getForwardDestinations=unsorted", function(data) {
+ $.getJSON("api.php?getForwardDestinations", function(data) {
if("FTLnotrunning" in data)
{
@@ -370,9 +485,6 @@ function updateForwardDestinationsPie() {
values.push([key, value, colors.shift()]);
});
- // Sort data ASC accorwing to 2nd column, keep already assigned labels and colors
- values = values.sort(function(a,b) { return b[1] - a[1]; });
-
// Split data into individual arrays for the graphs
$.each(values, function(key , value) {
k.push(value[0]);
@@ -392,6 +504,23 @@ function updateForwardDestinationsPie() {
forwardDestinationPieChart.update();
// Don't use rotation animation for further updates
forwardDestinationPieChart.options.animation.duration=0;
+ // Generate legend in separate div
+ $("#forward-destinations-legend").html(forwardDestinationPieChart.generateLegend());
+ $("#forward-destinations-legend > ul > li").on("click",function(e){
+ $(this).toggleClass("strike");
+ var index = $(this).index();
+ var ci = e.view.forwardDestinationPieChart;
+ var meta = ci.data.datasets[0]._meta;
+ for(let i in meta)
+ {
+ if ({}.hasOwnProperty.call(meta, i))
+ {
+ var curr = meta[i].data[index];
+ curr.hidden = !curr.hidden;
+ }
+ }
+ ci.update();
+ });
}).done(function() {
// Reload graph after one minute
setTimeout(updateForwardDestinationsPie, 60000);
@@ -412,7 +541,7 @@ function escapeHtml(text) {
}
function updateTopClientsChart() {
- $.getJSON("api.php?summaryRaw&getQuerySources", function(data) {
+ $.getJSON("api.php?summaryRaw&getQuerySources&topClientsBlocked", function(data) {
if("FTLnotrunning" in data)
{
@@ -421,8 +550,8 @@ function updateTopClientsChart() {
// Clear tables before filling them with data
$("#client-frequency td").parent().remove();
- var clienttable = $("#client-frequency").find("tbody:last");
- var client, percentage, clientname, clientip;
+ var clienttable = $("#client-frequency").find("tbody:last");
+ var client, percentage, clientname, clientip, idx, url;
for (client in data.top_sources) {
if ({}.hasOwnProperty.call(data.top_sources, client)){
@@ -435,7 +564,7 @@ function updateTopClientsChart() {
client = escapeHtml(client);
if(client.indexOf("|") > -1)
{
- var idx = client.indexOf("|");
+ idx = client.indexOf("|");
clientname = client.substr(0, idx);
clientip = client.substr(idx+1, client.length-idx);
}
@@ -445,16 +574,61 @@ function updateTopClientsChart() {
clientip = client;
}
- var url = "
"+clientname+" ";
+ url = "
"+clientname+" ";
percentage = data.top_sources[client] / data.dns_queries_today * 100;
clienttable.append("
" + url +
" " + data.top_sources[client] + " ");
}
+ }
+ // Clear tables before filling them with data
+ $("#client-frequency-blocked td").parent().remove();
+ var clientblockedtable = $("#client-frequency-blocked").find("tbody:last");
+ for (client in data.top_sources_blocked) {
+
+ if ({}.hasOwnProperty.call(data.top_sources_blocked, client)){
+ // Sanitize client
+ if(escapeHtml(client) !== client)
+ {
+ // Make a copy with the escaped index if necessary
+ data.top_sources_blocked[escapeHtml(client)] = data.top_sources_blocked[client];
+ }
+ client = escapeHtml(client);
+ if(client.indexOf("|") > -1)
+ {
+ idx = client.indexOf("|");
+ clientname = client.substr(0, idx);
+ clientip = client.substr(idx+1, client.length-idx);
+ }
+ else
+ {
+ clientname = client;
+ clientip = client;
+ }
+
+ url = "
"+clientname+" ";
+ percentage = data.top_sources_blocked[client] / data.ads_blocked_today * 100;
+ clientblockedtable.append("
" + url +
+ " " + data.top_sources_blocked[client] + " ");
+ }
+ }
+
+ // Remove table if there are no results (e.g. privacy mode enabled)
+ if(jQuery.isEmptyObject(data.top_sources))
+ {
+ $("#client-frequency").parent().remove();
+ }
+
+ // Remove table if there are no results (e.g. privacy mode enabled)
+ if(jQuery.isEmptyObject(data.top_sources_blocked))
+ {
+ $("#client-frequency-blocked").parent().remove();
}
$("#client-frequency .overlay").hide();
+ $("#client-frequency-blocked .overlay").hide();
// Update top clients list data every ten seconds
setTimeout(updateTopClientsChart, 10000);
});
@@ -578,6 +752,11 @@ function updateSummaryData(runOnce) {
$("span#" + today).addClass("glow");
});
+ if(data.hasOwnProperty("dns_queries_all_types"))
+ {
+ $("#total_queries").prop("title", "only A + AAAA queries (" + data["dns_queries_all_types"] + " in total)");
+ }
+
window.setTimeout(function() {
["ads_blocked_today", "dns_queries_today", "domains_being_blocked", "ads_percentage_today", "unique_clients"].forEach(function(header, idx) {
var textData = (idx === 3 && data[header] !== "to") ? data[header] + "%" : data[header];
@@ -792,8 +971,12 @@ $(document).ready(function() {
},
options: {
tooltips: {
- enabled: true,
+ enabled: false,
mode: "x-axis",
+ custom: customTooltips,
+ itemSort: function(a, b) {
+ return b.yLabel - a.yLabel;
+ },
callbacks: {
title: function(tooltipItem, data) {
var label = tooltipItem[0].xLabel;
@@ -960,8 +1143,7 @@ $(document).ready(function() {
},
options: {
legend: {
- display: true,
- position: "right"
+ display: false
},
tooltips: {
enabled: true,
@@ -998,8 +1180,7 @@ $(document).ready(function() {
},
options: {
legend: {
- display: true,
- position: "right"
+ display: false
},
tooltips: {
enabled: true,
diff --git a/scripts/pi-hole/js/list.js b/scripts/pi-hole/js/list.js
index e438f1b9..1ac0e294 100644
--- a/scripts/pi-hole/js/list.js
+++ b/scripts/pi-hole/js/list.js
@@ -13,13 +13,14 @@ var listType = $("#list-type").html();
var fullName = listType === "white" ? "Whitelist" : "Blacklist";
function sub(index, entry, arg) {
- var domain = $("#"+index);
+ var domain = $("#list #"+index);
var locallistType = listType;
- domain.hide("highlight");
- if(arg === "wild")
+ if(arg === "regex")
{
- locallistType = "wild";
+ locallistType = "regex";
+ domain = $("#list-regex #"+index);
}
+ domain.hide("highlight");
$.ajax({
url: "scripts/pi-hole/php/sub.php",
method: "post",
@@ -42,7 +43,7 @@ function refresh(fade) {
var list = $("#list");
if(listType === "black")
{
- listw = $("#list-wildcard");
+ listw = $("#list-regex");
}
if(fade) {
list.fadeOut(100);
@@ -61,7 +62,7 @@ function refresh(fade) {
{
listw.html("");
}
- var data = JSON.parse(response).sort();
+ var data = JSON.parse(response);
if(data.length === 0) {
$("h3").hide();
@@ -74,37 +75,40 @@ function refresh(fade) {
list.html("
Your " + fullName + " is empty!
");
}
}
- else {
+ else
+ {
$("h3").show();
- data.forEach(function (entry, index) {
- if(entry.substr(0,1) === "*")
- {
- // Wildcard entry
- // remove leading *
- entry = entry.substr(1, entry.length - 1);
+ data[0] = data[0].sort();
+ data[0].forEach(function (entry, index) {
+ // Whitelist entry or Blacklist (exact entry) are in the zero-th
+ // array returned by get.php
+ list.append(
+ "
" + entry +
+ "" +
+ " ");
+ // Handle button
+ $("#list #"+index+"").on("click", "button", function() {
+ sub(index, entry, "exact");
+ });
+ });
+
+ // Add regex domains if present in returned list data
+ if(data.length === 2)
+ {
+ data[1] = data[1].sort();
+ data[1].forEach(function (entry, index) {
+ // Whitelist entry or Blacklist (exact entry) are in the zero-th
+ // array returned by get.php
listw.append(
"
" + entry +
"" +
" ");
// Handle button
- $("#list-wildcard #"+index+"").on("click", "button", function() {
- sub(index, entry, "wild");
+ $("#list-regex #"+index+"").on("click", "button", function() {
+ sub(index, entry, "regex");
});
- }
- else
- {
- // Normal entry
- list.append(
- "
" + entry +
- "" +
- " ");
- // Handle button
- $("#list #"+index+"").on("click", "button", function() {
- sub(index, entry, "exact");
- });
- }
-
- });
+ });
+ }
}
list.fadeIn(100);
if(listw)
@@ -123,12 +127,14 @@ window.onload = refresh(false);
function add(arg) {
var locallistType = listType;
var domain = $("#domain");
+ var wild = false;
if(domain.val().length === 0){
return;
}
- if(arg === "wild")
+ if(arg === "wild" || arg === "regex")
{
- locallistType = "wild";
+ locallistType = arg;
+ wild = true;
}
var alInfo = $("#alInfo");
@@ -143,7 +149,8 @@ function add(arg) {
method: "post",
data: {"domain":domain.val().trim(), "list":locallistType, "token":token},
success: function(response) {
- if (response.indexOf("] Pi-hole blocking is ") === -1) {
+ if (!wild && response.indexOf("] Pi-hole blocking is ") === -1 ||
+ wild && response.length > 1) {
alFailure.show();
err.html(response);
alFailure.delay(4000).fadeOut(2000, function() {
@@ -196,6 +203,10 @@ $("#btnAddWildcard").on("click", function() {
add("wild");
});
+$("#btnAddRegex").on("click", function() {
+ add("regex");
+});
+
$("#btnRefresh").on("click", function() {
refresh(true);
});
diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js
index 722f8543..79a9823b 100644
--- a/scripts/pi-hole/js/queries.js
+++ b/scripts/pi-hole/js/queries.js
@@ -82,7 +82,7 @@ function add(domain,list) {
}
});
});
-
+
// Reset Modal after it has faded out
alertModal.one("hidden.bs.modal", function() {
alProcessing.show();
@@ -136,41 +136,66 @@ $(document).ready(function() {
{
APIstring += "&domain="+GETDict["domain"];
}
+ // If we don't ask filtering and also not for all queries, just request the most recent 100 queries
else if(!("all" in GETDict))
{
- var timestamp = Math.floor(Date.now() / 1000);
- APIstring += "&from="+(timestamp - 600);
- APIstring += "&until="+(timestamp + 100);
+ APIstring += "=100";
}
tableApi = $("#all-queries").DataTable( {
"rowCallback": function( row, data, index ){
+ var blocked = false;
+
+ var dnssec_status = "";
+ if (data[5] === "1")
+ {
+ dnssec_status = "
SECURE ";
+ }
+ else if (data[5] === "2")
+ {
+ dnssec_status = "
INSECURE ";
+ }
+ else if (data[5] === "3")
+ {
+ dnssec_status = "
BOGUS ";
+ }
+ else if (data[5] === "4")
+ {
+ dnssec_status = "
ABANDONED ";
+ }
+ else if (data[5] === "5")
+ {
+ dnssec_status = "
? ";
+ }
if (data[4] === "1")
{
+ blocked = true;
$(row).css("color","red");
- $("td:eq(4)", row).html( "Pi-holed" );
+ $("td:eq(4)", row).html( "Pi-holed"+dnssec_status );
$("td:eq(6)", row).html( "
Whitelist" );
}
else if (data[4] === "2")
{
$(row).css("color","green");
- $("td:eq(4)", row).html( "OK
(forwarded)" );
+ $("td:eq(4)", row).html( "OK
(forwarded)"+dnssec_status );
$("td:eq(6)", row).html( "
Blacklist" );
}
else if (data[4] === "3")
{
$(row).css("color","green");
- $("td:eq(4)", row).html( "OK
(cached)" );
+ $("td:eq(4)", row).html( "OK
(cached)"+dnssec_status );
$("td:eq(6)", row).html( "
Blacklist" );
}
else if (data[4] === "4")
{
+ blocked = true;
$(row).css("color","red");
- $("td:eq(4)", row).html( "Pi-holed
(wildcard)" );
+ $("td:eq(4)", row).html( "Pi-holed
(wildcard)");
$("td:eq(6)", row).html( "" );
}
else if (data[4] === "5")
{
+ blocked = true;
$(row).css("color","red");
$("td:eq(4)", row).html( "Pi-holed
(blacklist)" );
$("td:eq(6)", row).html( "
Whitelist" );
@@ -180,36 +205,47 @@ $(document).ready(function() {
$("td:eq(4)", row).html( "Unknown" );
$("td:eq(6)", row).html( "" );
}
- if (data[5] === "1")
+
+ // Check for existance of sixth column and display only if not Pi-holed
+ if(data.length > 6 && !blocked)
{
- $("td:eq(5)", row).css("color","green");
- $("td:eq(5)", row).html( "SECURE" );
- }
- else if (data[5] === "2")
- {
- $("td:eq(5)", row).css("color","orange");
- $("td:eq(5)", row).html( "INSECURE" );
- }
- else if (data[5] === "3")
- {
- $("td:eq(5)", row).css("color","red");
- $("td:eq(5)", row).html( "BOGUS" );
- }
- else if (data[5] === "4")
- {
- $("td:eq(5)", row).css("color","red");
- $("td:eq(5)", row).html( "ABANDONED" );
- }
- else if (data[5] === "5")
- {
- $("td:eq(5)", row).css("color","red");
- $("td:eq(5)", row).html( "?" );
+ $("td:eq(5)", row).css("color","black");
+ if (data[6] === "0")
+ {
+ $("td:eq(5)", row).html("N/A");
+ }
+ else if (data[6] === "1")
+ {
+ $("td:eq(5)", row).html("NODATA");
+ }
+ else if (data[6] === "2")
+ {
+ $("td:eq(5)", row).html("NXDOMAIN");
+ }
+ else if (data[6] === "3")
+ {
+ $("td:eq(5)", row).html("CNAME");
+ }
+ else if (data[6] === "4")
+ {
+ $("td:eq(5)", row).html("IP");
+ }
+ else
+ {
+ $("td:eq(5)", row).html("? ("+data[6]+")");
+ }
}
else
{
$("td:eq(5)", row).css("color","black");
- $("td:eq(5)", row).html( "-" );
+ $("td:eq(5)", row).html("-");
}
+ if(data.length > 7 && data[7] > 0)
+ {
+ var content = $("td:eq(5)", row).html();
+ $("td:eq(5)", row).html(content + " (" + (0.1*data[7]).toFixed(1)+"ms)");
+ }
+
},
dom: "<'row'<'col-sm-12'f>>" +
"<'row'<'col-sm-4'l><'col-sm-8'p>>" +
@@ -221,14 +257,32 @@ $(document).ready(function() {
"order" : [[0, "desc"]],
"columns": [
{ "width" : "15%", "render": function (data, type, full, meta) { if(type === "display"){return moment.unix(data).format("Y-MM-DD [
]HH:mm:ss z");}else{return data;} }},
- { "width" : "10%" },
- { "width" : "37%", "render": $.fn.dataTable.render.text() },
+ { "width" : "4%" },
+ { "width" : "36%", "render": $.fn.dataTable.render.text() },
{ "width" : "8%", "render": $.fn.dataTable.render.text() },
- { "width" : "10%" },
- { "width" : "5%" },
- { "width" : "10%" }
+ { "width" : "14%", "orderData": 4 },
+ { "width" : "8%", "orderData": 6 },
+ { "width" : "10%", "orderData": 4 }
],
"lengthMenu": [[10, 25, 50, 100, -1], [10, 25, 50, 100, "All"]],
+ "stateSave": true,
+ stateSaveCallback: function(settings, data) {
+ // Store current state in client's local storage area
+ localStorage.setItem("query_log_table", JSON.stringify(data));
+ },
+ stateLoadCallback: function(settings) {
+ // Receive previous state from client's local storage area
+ var data = localStorage.getItem("query_log_table");
+ // Return if not available
+ if(data === null){ return null; }
+ data = JSON.parse(data);
+ // Always start on the first page to show most recent queries
+ data["start"] = 0;
+ // Always start with empty search field
+ data["search"]["search"] = "";
+ // Apply loaded state to table
+ return data;
+ },
"columnDefs": [ {
"targets": -1,
"data": null,
diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js
index 257ab7d4..6660c63a 100644
--- a/scripts/pi-hole/js/settings.js
+++ b/scripts/pi-hole/js/settings.js
@@ -68,7 +68,7 @@ $(".confirm-restartdns").confirm({
});
$(".confirm-flushlogs").confirm({
- text: "By default, the log is flushed at the end of the day via cron, but a very large log file can slow down the Web interface, so flushing it can be useful. Note that your statistics will be reset and you lose the statistics up to this point. Are you sure you want to flush your logs?",
+ text: "Are you sure you want to flush your logs?",
title: "Confirmation required",
confirm(button) {
$("#flushlogsform").submit();
@@ -85,7 +85,7 @@ $(".confirm-flushlogs").confirm({
});
$(".confirm-disablelogging").confirm({
- text: "Note that disabling query logging will render graphs on the web user interface useless. Are you sure you want to disable logging and flush your Pi-hole logs?",
+ text: "Are you sure you want to disable logging and flush your Pi-hole logs?",
title: "Confirmation required",
confirm(button) {
$("#disablelogsform").submit();
@@ -102,7 +102,7 @@ $(".confirm-disablelogging").confirm({
});
$(".confirm-disablelogging-noflush").confirm({
- text: "Note that disabling query logging will render graphs on the web user interface useless after this point. Are you sure you want to disable logging?",
+ text: "Are you sure you want to disable logging?",
title: "Confirmation required",
confirm(button) {
$("#disablelogsform-noflush").submit();
@@ -209,16 +209,11 @@ $(".nav-tabs a").on("shown.bs.tab", function (e) {
window.scrollTo(0, 0);
});
-// Auto dismissal for info and error notifications
+// Auto dismissal for info notifications
$(document).ready(function(){
var alInfo = $("#alInfo");
- var alError = $("#alError");
if(alInfo.length)
{
alInfo.delay(3000).fadeOut(2000, function() { alInfo.hide(); });
}
- if(alError.length)
- {
- alError.delay(3000).fadeOut(2000, function() { alError.hide(); });
- }
});
diff --git a/scripts/pi-hole/js/taillog-FTL.js b/scripts/pi-hole/js/taillog-FTL.js
index cbe709b5..2645badf 100644
--- a/scripts/pi-hole/js/taillog-FTL.js
+++ b/scripts/pi-hole/js/taillog-FTL.js
@@ -14,14 +14,15 @@ function reloadData(){
clearTimeout(timer);
$.getJSON("scripts/pi-hole/php/tailLog.php?FTL&offset="+offset, function (data)
{
- offset = data["offset"];
pre.append(data["lines"]);
+
+ if(scrolling && offset !== data["offset"]) {
+ pre.scrollTop(pre[0].scrollHeight);
+ }
+
+ offset = data["offset"];
});
- if(scrolling)
- {
- window.scrollTo(0,document.body.scrollHeight);
- }
timer = setTimeout(reloadData, interval);
}
diff --git a/scripts/pi-hole/js/taillog.js b/scripts/pi-hole/js/taillog.js
index 7e3b2dc3..153bd18d 100644
--- a/scripts/pi-hole/js/taillog.js
+++ b/scripts/pi-hole/js/taillog.js
@@ -14,14 +14,15 @@ function reloadData(){
clearTimeout(timer);
$.getJSON("scripts/pi-hole/php/tailLog.php?offset="+offset, function (data)
{
- offset = data["offset"];
pre.append(data["lines"]);
+
+ if(scrolling && offset !== data["offset"]) {
+ pre.scrollTop(pre[0].scrollHeight);
+ }
+
+ offset = data["offset"];
});
- if(scrolling)
- {
- window.scrollTo(0,document.body.scrollHeight);
- }
timer = setTimeout(reloadData, interval);
}
diff --git a/scripts/pi-hole/php/FTL.php b/scripts/pi-hole/php/FTL.php
index 0c07ce5d..caddd758 100644
--- a/scripts/pi-hole/php/FTL.php
+++ b/scripts/pi-hole/php/FTL.php
@@ -6,30 +6,8 @@
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
-function testFTL($address)
+function connectFTL($address, $port=4711)
{
- if($address === "127.0.0.1")
- {
- $ret = shell_exec("pidof pihole-FTL");
- return intval($ret);
- }
- // We cannot relly test for a distant FTL instance
- // in the same way, so for any other IP address
- // we simply return true here and rely on the API
- // socket connection itself to fail if there is nothing
- // on that address
- return true;
-}
-
-function connectFTL($address, $port=4711, $quiet=true)
-{
- $timeout = 3;
-
- if(!$quiet)
- {
- echo "Attempting to connect to '$address' on port '$port'...\n";
- }
-
if($address == "127.0.0.1")
{
// Read port
@@ -38,104 +16,42 @@ function connectFTL($address, $port=4711, $quiet=true)
$port = intval($portfile);
}
- // Create a TCP/IP socket
- $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)
- or die("socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n");
-
- socket_set_nonblock($socket) or die("Unable to set nonblock on socket\n");
-
- $time = time();
- while (!@socket_connect($socket, $address, $port))
- {
- $err = socket_last_error($socket);
- if ($err == 115 || $err == 114)
- {
- if ((time() - $time) >= $timeout)
- {
- socket_close($socket);
- die("Connection timed out.\n");
- }
- // Wait for 1 millisecond
- usleep(1000);
- continue;
- }
- die(socket_strerror($err) . "\n");
- }
-
- socket_set_block($socket) or die("Unable to set block on socket\n");
-
- // Set timeout to 3 seconds
- socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ['sec'=>$timeout, 'usec'=>0]);
- socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, ['sec'=>$timeout, 'usec'=>0]);
-
- if(!$quiet)
- {
- echo "Success!\n\n";
- }
+ // Open Internet socket connection
+ $socket = @fsockopen($address, $port, $errno, $errstr, 1.0);
return $socket;
}
-function sendRequestFTL($requestin, $quiet=true)
+function sendRequestFTL($requestin)
{
global $socket;
$request = ">".$requestin;
- if(!$quiet)
- {
- echo "Sending request (".$request.")...\n";
- }
-
- socket_write($socket, $request, strlen($request)) or die("Could not send data to server\n");
- if(!$quiet)
- {
- echo "OK.\n";
- }
+ fwrite($socket, $request) or die("Could not send data to server\n");
}
-function getResponseFTL($quiet=true)
+function getResponseFTL()
{
global $socket;
- if(!$quiet)
- {
- echo "Reading response:\n";
- }
$response = [];
while(true)
{
- $out = socket_read($socket, 2048, PHP_NORMAL_READ);
- if(!$quiet)
- {
- echo $out;
- }
+ $out = fgets($socket);
if(strrpos($out,"---EOM---") !== false)
- {
break;
- }
+
$out = rtrim($out);
if(strlen($out) > 0)
- {
$response[] = $out;
- }
}
return $response;
}
-function disconnectFTL($quiet=true)
+function disconnectFTL()
{
global $socket;
- if(!$quiet)
- {
- echo "Closing socket...";
- }
-
- socket_close($socket);
-
- if(!$quiet)
- {
- echo "OK.\n\n";
- }
+ fclose($socket);
}
?>
diff --git a/scripts/pi-hole/php/add.php b/scripts/pi-hole/php/add.php
index a19aae7a..dc6e87c2 100644
--- a/scripts/pi-hole/php/add.php
+++ b/scripts/pi-hole/php/add.php
@@ -34,13 +34,14 @@ switch($type) {
}
break;
case "wild":
- if(!isset($_POST["auditlog"]))
- echo exec("sudo pihole -wild -q ${_POST['domain']}");
- else
- {
- echo exec("sudo pihole -wild -q -n ${_POST['domain']}");
- echo exec("sudo pihole -a audit ${_POST['domain']}");
- }
+ // Escape "." so it won't be interpreted as the wildcard character
+ $domain = str_replace(".","\.",$_POST['domain']);
+ // Add regex filter for legacy wildcard behavior
+ add_regex("(^|\.)".$domain."$");
+ break;
+ case "regex":
+ add_regex($_POST['domain']);
+ break;
case "audit":
echo exec("sudo pihole -a audit ${_POST['domain']}");
break;
diff --git a/scripts/pi-hole/php/api_token.php b/scripts/pi-hole/php/api_token.php
index be83895f..ae6ce9d5 100644
--- a/scripts/pi-hole/php/api_token.php
+++ b/scripts/pi-hole/php/api_token.php
@@ -11,6 +11,9 @@ if($auth)
require_once("../../vendor/qrcode.php");
$qr = QRCode::getMinimumQRCode($pwhash, QR_ERROR_CORRECT_LEVEL_Q);
$qr->printHTML("10px");
+ print("Raw API Token: ");
+ print($pwhash);
+
}
else
{
diff --git a/scripts/pi-hole/php/auth.php b/scripts/pi-hole/php/auth.php
index 0b32c732..027220c9 100644
--- a/scripts/pi-hole/php/auth.php
+++ b/scripts/pi-hole/php/auth.php
@@ -11,6 +11,7 @@ $ERRORLOG = getenv('PHP_ERROR_LOG');
if (empty($ERRORLOG)) {
$ERRORLOG = '/var/log/lighttpd/error.log';
}
+$regexfile = "/etc/pihole/regex.list";
function pi_log($message) {
error_log(date('Y-m-d H:i:s') . ': ' . $message . "\n", 3, $GLOBALS['ERRORLOG']);
@@ -133,6 +134,14 @@ function list_verify($type) {
{
log_and_die("Not allowed!");
}
- check_domain();
+
+ // Don't check if the added item is a
+ // valid domain for regex expressions
+ // Regex filters are validated by FTL
+ // on import and skipped if invalid
+ if($_POST['list'] !== "regex")
+ {
+ check_domain();
+ }
}
?>
diff --git a/scripts/pi-hole/php/data.php b/scripts/pi-hole/php/data.php
deleted file mode 100644
index f7226698..00000000
--- a/scripts/pi-hole/php/data.php
+++ /dev/null
@@ -1,822 +0,0 @@
- 0 ? ($ads_blocked_today / $dns_queries_today * 100) : 0;
-
- return array(
- 'domains_being_blocked' => $domains_being_blocked,
- 'dns_queries_today' => $dns_queries_today,
- 'ads_blocked_today' => $ads_blocked_today,
- 'ads_percentage_today' => $ads_percentage_today,
- );
- }
-
- function getOverTimeData() {
- global $log;
-
- // Get log lines
- $dns_queries = getDnsQueries($log);
-
- // Get list of ad domains
- $gravity_domains = getGravity();
-
- // Bin log entries separated into Domains and Ads in 1 hour intervals
- list($domains_over_time, $ads_over_time) = overTime($dns_queries, $gravity_domains);
-
- // Align arrays
- alignTimeArrays($ads_over_time, $domains_over_time);
-
- // Provide a minimal valid array if there have are no blocked
- // queries at all. Otherwise the output of the API is inconsistent.
- if(count($ads_over_time) == 0)
- {
- $ads_over_time = [1 => 0];
- }
-
- return Array(
- 'domains_over_time' => $domains_over_time,
- 'ads_over_time' => $ads_over_time,
- );
- }
-
- function getOverTimeData10mins() {
- global $log;
-
- // Get log lines
- $dns_queries = getDnsQueries($log);
-
- // Get list of ad domains
- $gravity_domains = getGravity();
-
- // Bin log entries separated into Domains and Ads in 10 minute intervals
- list($domains_over_time, $ads_over_time) = overTime10mins($dns_queries, $gravity_domains);
-
- // Align arrays (in case there have been hours without ad queries)
- alignTimeArrays($ads_over_time, $domains_over_time);
-
- // Provide a minimal valid array if there have are no blocked
- // queries at all. Otherwise the output of the API is inconsistent.
- if(count($ads_over_time) == 0)
- {
- $ads_over_time = [1 => 0];
- }
-
- return Array(
- 'domains_over_time' => $domains_over_time,
- 'ads_over_time' => $ads_over_time,
- );
- }
-
- // Test if variable exists and is positive
- function ispositive(&$arg)
- {
- if(isset($arg))
- {
- if($arg > 0)
- {
- return true;
- }
- return false;
- }
- return false;
- }
-
- function getTopItems($argument) {
- global $log,$setupVars,$privacyMode;
-
- // Process log file
- $dns_domains = getDnsQueryDomains($log);
- // Get list of ad domains
- $gravity_domains = getGravity();
-
- // Exclude domains the user doesn't want to see
- if(isset($setupVars["API_EXCLUDE_DOMAINS"]))
- {
- excludeFromList($dns_domains, "API_EXCLUDE_DOMAINS");
- }
-
- // Sort array in descending order
- arsort($dns_domains);
-
- // Prepare arrays and counters for Top Items
- $topDomains = []; $domaincounter = 0;
- $topAds = []; $adcounter = 0;
-
- // Default number of Top Items to show is 10
- $qty = 10;
-
- // If argument is numeric, the user may want to
- // see a different number of entries
- if(is_numeric($argument))
- {
- $qty = intval($argument);
- }
-
- // Process sorted domain names
- foreach ($dns_domains as $key => $value) {
- if(ispositive($gravity_domains[$key]) && $adcounter < $qty)
- {
- // New entry for Top Ads
- $topAds[$key] = $value;
- $adcounter++;
- }
- else if($domaincounter < $qty && !$privacyMode)
- {
- // New entry for Top Domains
- $topDomains[$key] = $value;
- $domaincounter++;
- }
- elseif($domaincounter >= $qty && $adcounter >= $qty)
- {
- // Already collected enough entries for both lists
- // Exit loop early
- break;
- }
- }
-
- return Array(
- 'top_queries' => $topDomains,
- 'top_ads' => $topAds,
- );
- }
-
- function getRecentItems($qty) {
- global $log;
- $dns_queries = getDnsQueries($log);
- return Array(
- 'recent_queries' => getRecent($dns_queries, $qty)
- );
- }
-
- function getIpvType() {
- global $log;
- $dns_queries = getDnsQueries($log);
- $queryTypes = array();
-
- foreach($dns_queries as $query) {
- $info = trim(explode(": ", $query)[1]);
- $queryType = explode(" ", $info)[0];
- if (isset($queryTypes[$queryType])) {
- $queryTypes[$queryType]++;
- }
- else {
- $queryTypes[$queryType] = 1;
- }
- }
-
- return $queryTypes;
- }
-
- function resolveIPs(&$array) {
- $hostarray = [];
- foreach ($array as $key => $value)
- {
- $hostname = gethostbyaddr($key);
- // If we found a hostname for the IP, replace it
- if($hostname)
- {
- // Generate HOST entry
- $hostarray["$hostname|$key"] = $value;
- }
- else
- {
- // Generate IP entry
- $hostarray[$key] = $value;
- }
- }
- $array = $hostarray;
-
- // Sort new array
- arsort($array);
- }
-
- function getForwardDestinations() {
- global $log, $setupVars;
- $forwards = getForwards($log);
- $destinations = array();
- foreach ($forwards as $forward) {
- $exploded = explode(" ", trim($forward));
- $dest = $exploded[count($exploded) - 1];
- if (isset($destinations[$dest])) {
- $destinations[$dest]++;
- }
- else {
- $destinations[$dest] = 1;
- }
- }
-
- if(istrue($setupVars["API_GET_UPSTREAM_DNS_HOSTNAME"]))
- {
- resolveIPs($destinations);
- }
-
- return $destinations;
-
- }
-
- // Check for existance of variable
- // and test it only if it exists
- function istrue(&$argument) {
- $ret = false;
- if(isset($argument))
- {
- if($argument)
- {
- $ret = true;
- }
- }
- return $ret;
- }
-
- function getQuerySources() {
- global $log, $setupVars;
- $dns_queries = getDnsQueries($log);
- $sources = array();
- foreach($dns_queries as $query) {
- $exploded = explode(" ", $query);
- $ip = trim($exploded[count($exploded)-1]);
- if (isset($sources[$ip])) {
- $sources[$ip]++;
- }
- else {
- $sources[$ip] = 1;
- }
- }
-
- global $setupVars;
- if(isset($setupVars["API_EXCLUDE_CLIENTS"]))
- {
- excludeFromList($sources, "API_EXCLUDE_CLIENTS");
- }
-
- arsort($sources);
- $sources = array_slice($sources, 0, 10);
-
- if(istrue($setupVars["API_GET_CLIENT_HOSTNAME"]))
- {
- resolveIPs($sources);
- }
-
- return Array(
- 'top_sources' => $sources
- );
- }
-
- $showBlocked = false;
- $showPermitted = false;
-
- function setShowBlockedPermitted()
- {
- global $showBlocked, $showPermitted, $setupVars;
- if(isset($setupVars["API_QUERY_LOG_SHOW"]))
- {
- if($setupVars["API_QUERY_LOG_SHOW"] === "all")
- {
- $showBlocked = true;
- $showPermitted = true;
- }
- elseif($setupVars["API_QUERY_LOG_SHOW"] === "permittedonly")
- {
- $showBlocked = false;
- $showPermitted = true;
- }
- elseif($setupVars["API_QUERY_LOG_SHOW"] === "blockedonly")
- {
- $showBlocked = true;
- $showPermitted = false;
- }
- elseif($setupVars["API_QUERY_LOG_SHOW"] === "nothing")
- {
- $showBlocked = false;
- $showPermitted = false;
- }
- else
- {
- // Invalid settings, show everything
- $showBlocked = true;
- $showPermitted = true;
- }
- }
- else
- {
- $showBlocked = true;
- $showPermitted = true;
- }
- }
-
- function getAllQueries($orderBy) {
- global $log,$showBlocked,$showPermitted,$privacyMode,$setupVars;
- $allQueries = array("data" => array());
- $dns_queries = getDnsQueries($log);
-
- $hostnames=array();
-
- // Create empty array for gravity
- $gravity_domains = getGravity();
- $wildcard_domains = getWildcardListContent();
-
- if(isset($_GET["from"]))
- {
- $from = new DateTime($_GET["from"]);
- }
- if(isset($_GET["until"]))
- {
- $until = new DateTime($_GET["until"]);
- }
-
- setShowBlockedPermitted();
-
- // Privacy mode?
- if($privacyMode)
- {
- $showPermitted = false;
- }
-
- if(!$showBlocked && !$showPermitted)
- {
- // Nothing to do for us here
- return [];
- }
-
- foreach ($dns_queries as $query) {
- $time = new DateTime(substr($query, 0, 16));
-
- // Check if we want to restrict the time where we want to show queries
- if(isset($from))
- {
- if($time <= $from)
- {
- continue;
- }
- }
- if(isset($until))
- {
- if($time >= $until)
- {
- continue;
- }
- }
-
- // print_r([$time->getTimestamp(),$_GET["from"],$_GET["until"]]);
-
- $exploded = explode(" ", trim($query));
- $domain = $exploded[count($exploded)-3];
-
- $status = "";
-
- if(isset($gravity_domains[$domain]))
- {
- if($gravity_domains[$domain] > 0)
- {
- // Exact matching gravity domain
- $status = "Pi-holed (exact)";
- }
- else
- {
- // Explicitly whitelisted
- $status = "OK (whitelisted)";
- }
- }
- else
- {
- // Test for wildcard blocking
- foreach ($wildcard_domains as $entry) {
- if(strpos($domain, $entry) !== false)
- {
- $status = "Pi-holed (wildcard)";
- }
- }
- if(!strlen($status))
- {
- $status = "OK";
- }
- }
- if((substr($status,0,2) === "Pi" && $showBlocked) || (substr($status,0,2) === "OK" && $showPermitted))
- {
- $type = substr($exploded[count($exploded)-4], 6, -1);
-
- if(istrue($setupVars["API_GET_CLIENT_HOSTNAME"])) {
- $ip = $exploded[count($exploded) - 1];
-
- if (isset($hostnames[$ip])) {
- $client = $hostnames[$ip];
- } else {
- $hostnames[$ip] = gethostbyaddr($ip);
- $client = $hostnames[$ip];
- }
- }
- else
- {
- $client = $exploded[count($exploded)-1];
- }
-
- if($orderBy == "orderByClientDomainTime"){
- $allQueries['data'][hasHostName($client)][$domain][$time->format('Y-m-d T H:i:s')] = $status;
- }elseif ($orderBy == "orderByClientTimeDomain"){
- $allQueries['data'][hasHostName($client)][$time->format('Y-m-d T H:i:s')][$domain] = $status;
- }elseif ($orderBy == "orderByTimeClientDomain"){
- $allQueries['data'][$time->format('Y-m-d T H:i:s')][hasHostName($client)][$domain] = $status;
- }elseif ($orderBy == "orderByTimeDomainClient"){
- $allQueries['data'][$time->format('Y-m-d T H:i:s')][$domain][hasHostName($client)] = $status;
- }elseif ($orderBy == "orderByDomainClientTime"){
- $allQueries['data'][$domain][hasHostName($client)][$time->format('Y-m-d T H:i:s')] = $status;
- }elseif ($orderBy == "orderByDomainTimeClient"){
- $allQueries['data'][$domain][$time->format('Y-m-d T H:i:s')][hasHostName($client)] = $status;
- }else{
- array_push($allQueries['data'], array(
- $time->format('Y-m-d T H:i:s'),
- $type,
- $domain,
- hasHostName($client),
- $status,
- ""
- ));
- }
- }
- }
-
- return $allQueries;
- }
-
- function tailPiholeLog($param) {
- // Not using SplFileObject here, since direct
- // usage of f-streams will be much faster for
- // files as large as the pihole.log
- global $logListName;
- $file = fopen($logListName,"r");
- $offset = intval($param);
- if($offset > 0)
- {
- // Seeks on the file pointer where we want to continue reading is known
- fseek($file, $offset);
- $lines = [];
- while (!feof($file)) {
- array_push($lines,fgets($file));
- }
- return ["offset" => ftell($file), "lines" => $lines];
- }
- else
- {
- // Locate the current position of the file read/write pointer
- fseek($file, -1, SEEK_END);
- // Add one to skip the very last "\n" in the log file
- return ["offset" => ftell($file)+1];
- }
- fclose($file);
- }
-
- /******** Private Members ********/
- function gravityCount() {
- global $gravityListName,$blackListFile;
- $preEventHorizon = exec("grep -c ^ $gravityListName");
- $blacklist = exec("grep -c ^ $blackListFile");
- return ($preEventHorizon + $blacklist);
- }
-
- function getDnsQueries(\SplFileObject $log) {
- $log->rewind();
- $lines = [];
- foreach ($log as $line) {
- if(strpos($line, ": query[A") !== false) {
- $lines[] = $line;
- }
- }
- return $lines;
- }
-
- function getDnsQueryDomains(\SplFileObject $log) {
- $log->rewind();
- $domains = [];
- foreach ($log as $line) {
- if(strpos($line, ": query[A") !== false) {
- $exploded = explode(" ", $line);
- $domain = trim($exploded[count($exploded) - 3]);
- if (isset($domains[$domain])) {
- $domains[$domain]++;
- }
- else {
- $domains[$domain] = 1;
- }
- }
- }
- return $domains;
- }
-
- function countDnsQueries() {
- global $logListName;
- return intval(exec("grep -c \": query\\[A\" $logListName"));
- }
-
- function getDnsQueriesAll(\SplFileObject $log) {
- $log->rewind();
- $lines = [];
- foreach ($log as $line) {
- if(strpos($line, ": query[A") || strpos($line, "gravity.list") || strpos($line, ": forwarded") !== false) {
- $lines[] = $line;
- }
- }
- return $lines;
- }
-
- function getDomains($file, &$array, $action){
- $file->rewind();
- foreach ($file as $line) {
- // Strip newline (and possibly carriage return) from end of key
- $key = rtrim($line);
- // if $action = true -> we want that domain to be ADDED to the list
- // doesn't harm to do this if it has already been set before
- // (e.g. once in gravity list, once in blacklist)
- if($action && strlen($key) > 0)
- {
- // $action is true (we want to add) *and* key is not empty
- $array[$key] = 1;
- }
- elseif(!$action && isset($array[$key]))
- {
- // $action is false (we want to remove) *and* key is set
- $array[$key] = -1;
- }
- }
- }
-
- function getWildcardListContent() {
- $rawList = file_get_contents(checkfile("/etc/dnsmasq.d/03-pihole-wildcard.conf"));
- $wclist = explode("\n", $rawList);
- $list = [];
-
- foreach ($wclist as $entry) {
- $expl = explode("/", $entry);
- if(count($expl) == 3)
- {
- array_push($list,$expl[1]);
- }
- }
-
- return array_unique($list);
-
- }
-
- function getGravity() {
- global $gravity,$whitelist,$blacklist;
- $domains = [];
-
- // ADD (true) preEventHorizon domains
- getDomains($gravity, $domains, true);
-
- // ADD (true) blacklist domains
- getDomains($blacklist, $domains, true);
-
- // REMOVE (false) whitelist domains
- getDomains($whitelist, $domains, false);
-
- return $domains;
- }
-
- function getBlockedQueries(\SplFileObject $log) {
- $log->rewind();
- $lines = [];
- foreach ($log as $line) {
- $exploded = explode(" ", str_replace(" "," ",$line));
- if(count($exploded) == 8 || count($exploded) == 10) {
- // Structure of data is currently like:
- // Array
- // (
- // [0] => Dec
- // [1] => 19
- // [2] => 11:21:51
- // [3] => dnsmasq[2584]:
- // [4] => /etc/pihole/gravity.list
- // [5] => doubleclick.com
- // [6] => is
- // [7] => ip.of.pi.hole
- // )
- // with extra logging enabled
- // Array
- // (
- // [0] => Dec
- // [1] => 19
- // [2] => 11:21:51
- // [3] => dnsmasq[2584]:
- // [4] => 1 (identifier)
- // [5] => 1.2.3.4/12345
- // [6] => /etc/pihole/gravity.list
- // [7] => doubleclick.com
- // [8] => is
- // [9] => ip.of.pi.hole
- // )
- $list = $exploded[count($exploded)-4];
- $is = $exploded[count($exploded)-2];
- // Consider only gravity.list as DNS source (not e.g. hostname.list)
- if(substr($list, strlen($list) - 12, 12) === "gravity.list" && $is === "is") {
- $lines[] = $line;
- };
- }
- }
- return $lines;
- }
-
- function countBlockedQueries() {
- global $logListName;
- // Blocked due to gravity entries (ad lists + blacklist)
- $gravityblocked = intval(exec("grep -c -e \"gravity\.list.*is\" $logListName"));
-
- // Blocked due to wildcard entries
- $wildcard_domains = getWildcardListContent();
- $wildcardblocked = 0;
- foreach ($wildcard_domains as $domain) {
- $wildcardblocked += intval(exec("grep -c -e \"config.*$domain is\" $logListName"));
- }
-
- return $gravityblocked +$wildcardblocked;
- }
-
- function getForwards(\SplFileObject $log) {
- $log->rewind();
- $lines = [];
- foreach ($log as $line) {
- if(strpos($line, ": forwarded") !== false) {
- $lines[] = $line;
- }
- }
- return $lines;
- }
-
- function excludeFromList(&$array,$key)
- {
- global $setupVars;
- $domains = explode(",",$setupVars[$key]);
- foreach ($domains as $domain) {
- if(isset($array[$domain]))
- {
- unset($array[$domain]);
- }
- }
- return $array;
- }
-
- function overTime($entries, $gravity_domains) {
- $byTimeDomains = [];
- $byTimeAds = [];
- foreach ($entries as $entry) {
- $time = date_create(substr($entry, 0, 16));
- $hour = $time->format('G');
-
- $exploded = explode(" ", $entry);
- $domain = trim($exploded[count($exploded) - 3]);
-
- if(ispositive($gravity_domains[$domain]))
- {
- if (isset($byTimeAds[$time])) {
- $byTimeAds[$time]++;
- }
- else {
- $byTimeAds[$time] = 1;
- }
- }
-
- if (isset($byTimeDomains[$time])) {
- $byTimeDomains[$time]++;
- }
- else {
- $byTimeDomains[$time] = 1;
- }
- }
- return [$byTimeDomains,$byTimeAds];
- }
-
- function overTime10mins($entries, $gravity_domains=[]) {
- $byTimeDomains = [];
- $byTimeAds = [];
- foreach ($entries as $entry) {
- $time = date_create(substr($entry, 0, 16));
- $hour = $time->format('G');
- $minute = $time->format('i');
-
- // 00:00 - 00:09 -> 0
- // 00:10 - 00:19 -> 1
- // ...
- // 12:00 - 12:10 -> 72
- // ...
- // 15:30 - 15:39 -> 93
- // etc.
- $time = ($minute-$minute%10)/10 + 6*$hour;
-
- $exploded = explode(" ", $entry);
- $domain = trim($exploded[count($exploded) - 3]);
-
- if(ispositive($gravity_domains[$domain]))
- {
- if (isset($byTimeAds[$time])) {
- $byTimeAds[$time]++;
- }
- else {
- $byTimeAds[$time] = 1;
- }
- }
-
- if (isset($byTimeDomains[$time])) {
- $byTimeDomains[$time]++;
- }
- else {
- $byTimeDomains[$time] = 1;
- }
- }
- return [$byTimeDomains,$byTimeAds];
- }
-
- function alignTimeArrays(&$times1, &$times2) {
- if(count($times1) == 0 || count($times2) < 2) {
- return;
- }
-
- $max = max(array_merge(array_keys($times1), array_keys($times2)));
- $min = min(array_merge(array_keys($times1), array_keys($times2)));
-
- for ($i = $min; $i <= $max; $i++) {
- if (!isset($times2[$i])) {
- $times2[$i] = 0;
- }
- if (!isset($times1[$i])) {
- $times1[$i] = 0;
- }
- }
-
- ksort($times1);
- ksort($times2);
- }
-
- function getRecent($queries, $qty){
- $recent = array();
- foreach (array_slice($queries, -$qty) as $query) {
- $queryArray = array();
- $exploded = explode(" ", $query);
- $time = date_create(substr($query, 0, 16));
- $queryArray['time'] = $time->format('h:i:s a');
- $queryArray['domain'] = trim($exploded[count($exploded) - 3]);
- $queryArray['ip'] = trim($exploded[count($exploded)-1]);
- array_push($recent, $queryArray);
-
- }
- return array_reverse($recent);
- }
-
- function hasHostName($var){
- global $hosts;
- foreach ($hosts as $host){
- $x = preg_split('/\s+/', $host);
- if ( $var == $x[0] ){
- $var = $x[1] . "($var)";
- }
- }
- return $var;
- }
-?>
diff --git a/scripts/pi-hole/php/func.php b/scripts/pi-hole/php/func.php
index bc353218..fb9cda82 100644
--- a/scripts/pi-hole/php/func.php
+++ b/scripts/pi-hole/php/func.php
@@ -45,4 +45,21 @@ if(!function_exists('hash_equals')) {
}
}
+function add_regex($regex, $mode=FILE_APPEND, $append="\n")
+{
+ global $regexfile;
+ if(file_put_contents($regexfile, $append.$regex, $mode) === FALSE)
+ {
+ $err = error_get_last()["message"];
+ echo "Unable to add regex \"".htmlspecialchars($regex)."\" to ${regexfile}
Error message: $err";
+ }
+ else
+ {
+ // Send SIGHUP to pihole-FTL using a frontend command
+ // to force reloading of the regex domains
+ // This will also wipe the resolver's cache
+ echo exec("sudo pihole restartdns reload");
+ }
+}
+
?>
diff --git a/scripts/pi-hole/php/get.php b/scripts/pi-hole/php/get.php
index a0aea17e..63e69ec9 100644
--- a/scripts/pi-hole/php/get.php
+++ b/scripts/pi-hole/php/get.php
@@ -12,51 +12,42 @@ if(!isset($_GET['list']))
$listtype = $_GET['list'];
+$basedir = "/etc/pihole/";
+
require "func.php";
switch ($listtype) {
- case "white":
- $list = getListContent("white");
- break;
+ case "white":
+ $list = array(getListContent("whitelist.txt"));
+ break;
- case "black":
- $list = array_merge(getListContent("black"),getWildcardListContent());
- break;
+ case "black":
+ $exact = getListContent("blacklist.txt");
+ $regex = getListContent("regex.list");
+ $list = array($exact, $regex);
+ break;
- default:
- die("Invalid list parameter");
- break;
+ default:
+ die("Invalid list parameter");
+ break;
}
-function getListContent($type) {
- $rawList = file_get_contents(checkfile("/etc/pihole/".$type."list.txt"));
+function getListContent($listname) {
+ global $basedir;
+ $rawList = file_get_contents(checkfile($basedir.$listname));
$list = explode("\n", $rawList);
- // Get rid of empty lines
+ // Get rid of empty lines and comments
for($i = sizeof($list)-1; $i >= 0; $i--) {
- if($list[$i] == "")
+ if(strlen($list[$i]) < 1 || $list[$i][0] === '#')
unset($list[$i]);
}
- return $list;
+ // Re-index list after possible unset() activity
+ $newlist = array_values($list);
-}
-
-function getWildcardListContent() {
- $rawList = file_get_contents(checkfile("/etc/dnsmasq.d/03-pihole-wildcard.conf"));
- $wclist = explode("\n", $rawList);
- $list = [];
-
- foreach ($wclist as $entry) {
- $expl = explode("/", $entry);
- if(count($expl) == 3)
- {
- array_push($list,"*${expl[1]}");
- }
- }
-
- return array_unique($list);
+ return $newlist;
}
diff --git a/scripts/pi-hole/php/gravity.php b/scripts/pi-hole/php/gravity.php
new file mode 100644
index 00000000..cb3c3404
--- /dev/null
+++ b/scripts/pi-hole/php/gravity.php
@@ -0,0 +1,51 @@
+ | April 23rd, 2018:
+ Checks when the gravity list was last updated, if it exists at all.
+ Returns the info in human-readable format for use on the dashboard,
+ or raw for use by the API.
+ */
+ $gravitylist = "/etc/pihole/gravity.list";
+ if (file_exists($gravitylist)){
+ $date_file_created_unix = filemtime($gravitylist);
+ $date_file_created = date_create("@".$date_file_created_unix);
+ $date_now = date_create("now");
+ $gravitydiff = date_diff($date_file_created,$date_now);
+ if($raw){
+ $output = array(
+ "file_exists"=> true,
+ "absolute" => $date_file_created_unix,
+ "relative" => array(
+ "days" => $gravitydiff->format("%a"),
+ "hours" => $gravitydiff->format("%H"),
+ "minutes" => $gravitydiff->format("%I"),
+ )
+ );
+ }else{
+ if($gravitydiff->d > 1){
+ $output = $gravitydiff->format("Blocking list updated %a days, %H:%I ago");
+ }elseif($gravitydiff->d == 1){
+ $output = $gravitydiff->format("Blocking list updated one day, %H:%I ago");
+ }else{
+ $output = $gravitydiff->format("Blocking list updated %H:%I ago");
+ }
+ }
+ }else{
+ if($raw){
+ $output = array("file_exists"=>false);
+ }else{
+ $output = "Blocking list not found";
+ }
+ }
+ return $output;
+}
+
+?>
\ No newline at end of file
diff --git a/scripts/pi-hole/php/header.php b/scripts/pi-hole/php/header.php
index 709a8093..49727926 100644
--- a/scripts/pi-hole/php/header.php
+++ b/scripts/pi-hole/php/header.php
@@ -167,6 +167,7 @@
Pi-hole Admin Console
+
@@ -188,10 +189,6 @@
-
class="active">
- Whitelist
+ Whitelist
@@ -461,7 +458,7 @@ if($auth) {
-
Disable
+
Disable