diff --git a/README.md b/README.md index 6e225856b4..12769874c3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ Pi-hole Admin Dashboard ============ +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/938b4d9e61b7487da77cf63ba05c683d)](https://www.codacy.com/app/Pi-hole/AdminLTE?utm_source=github.com&utm_medium=referral&utm_content=pi-hole/AdminLTE&utm_campaign=badger) [![Join the chat at https://gitter.im/pi-hole/AdminLTE](https://badges.gitter.im/pi-hole/AdminLTE.svg)](https://gitter.im/pi-hole/AdminLTE?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif "AdminLTE Presentation")](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3J2L3Z4DHW9UY "Donate") @@ -8,8 +9,7 @@ Using **[AdminLTE](https://almsaeedstudio.com)**, this project will create a Web From this interface, you will be able to see stats on how well your Pi-hole is performing. You will also be able to update the lists used to block ads. -![Pi-hole Web interface](http://i.imgur.com/5lLAUGo.png) -![Fully responsive](http://i.imgur.com/fHuWR6E.png) +![Pi-hole Web interface](http://i.imgur.com/EgGZXbT.png) ## API A read-only API can be accessed at `/admin/api.php`. With either no parameters or `api.php?summary` it returns the following JSON: @@ -22,6 +22,9 @@ A read-only API can be accessed at `/admin/api.php`. With either no parameters o } ``` -There are many more parameters, such as `summaryRaw`, `overTimeData`, `topItems`, `recentItems`, `getQueryTypes`, `getForwardDestinations`, `getQuerySources`, and finally `getAllQueries`. +There are many more parameters, such as `summaryRaw`, `overTimeData10mins`, ` topClients` or `getQuerySources`, `getQueryTypes`, `getForwardDestinations`, and `getAllQueries`. +Together with a token it is also possible to enable and disable (also with a set timeout) blocking via the API. -`getAllQueries` can optionally be set with one of these values to return JSON hash ordered as value implies: `orderByClientDomainTime`, `orderByClientTimeDomain`, `orderByTimeClientDomain`, `orderByTimeDomainClient`, `orderByDomainClientTime` or `orderByDomainTimeClient`. +
+
+We use BrowserStack for multi-platform multi-browser testing. diff --git a/api.php b/api.php index 65fa5576c6..589ae0bd54 100644 --- a/api.php +++ b/api.php @@ -89,20 +89,20 @@ $data = array_merge($data, getGravity()); } - if (isset($_GET['tailLog'])) { + if (isset($_GET['tailLog']) && $auth) { $data = array_merge($data, tailPiholeLog($_GET['tailLog'])); } function filterArray(&$inArray) { - $outArray = array(); - foreach ($inArray as $key=>$value) { - if (is_array($value)) { - $outArray[htmlspecialchars($key)] = filterArray($value); + $outArray = array(); + foreach ($inArray as $key=>$value) { + if (is_array($value)) { + $outArray[htmlspecialchars($key)] = filterArray($value); } else { - $outArray[htmlspecialchars($key)] = htmlspecialchars($value); + $outArray[htmlspecialchars($key)] = !is_numeric($value) ? htmlspecialchars($value) : $value; } - } - return $outArray; + } + return $outArray; } $data = filterArray($data); diff --git a/help.php b/help.php index 6af44722b2..6f19ad6043 100644 --- a/help.php +++ b/help.php @@ -51,7 +51,7 @@
  • and others
  • -
  • Query Types: Shows to which upstream DNS the permitted requests have been forwarded to.
  • +
  • Forward Destinations: Shows to which upstream DNS the permitted requests have been forwarded to.
  • Top Domains: Ranking of requested sites by number of DNS lookups.
  • Top Advertisers: Ranking of requested advertisements by number of DNS lookups.
  • Top Clients: Ranking of how many DNS requests each client has made on the local network.
  • @@ -70,25 +70,33 @@

    White- / Blacklist

    -

    Add or remove domains (or subdomains) from the white-/blacklist. If a domain is added to e.g. the whitelist, any possible entry of the same domain will be automatically removed from the blacklist and vice versa. Adding wildcards using the web UI is currently not supported.

    +

    Add or remove domains (or subdomains) from the white-/blacklist. If a domain is added to e.g. the whitelist, any possible entry of the same domain will be automatically removed from the blacklist and vice versa.

    +

    Wildcard blacklisting is supported (entering something.de will block this domain including all subdomains like a.bb.c.999.something.de). Note that wildcard whitelisting is not supported.

    +

    You can white-/blacklist multiple entries at a time if you separate the domains by spaces.

    -

    Update Lists

    -

    Will download any updates from the third-party ad-serving domain lists that we source. By default, this command runs once a week via cron.

    +

    Disable / Enable

    + Disables/enables Pi-Hole blocking completely. You may have to wait a few minutes for the changes to reach all of your devices. The change will be reflected by a changed status (top left)
    -

    Query adlists

    - This function is useful to find out what list a domain appears on. Since we don't control what the third-parties put on the block lists, you may find that a domain you normally visit stops working. If this is the case, you could run this command to scan for strings in the list of blocked domains and it will return the list the domain is found on. This proved useful a while back when the Mahakala list was adding apple.com and microsoft.com to their block list.

    +

    Tools → Update Lists

    +

    Will download any updates from the third-party ad-serving domain lists that we source. By default, this command runs once a week via cron (Sunday at 01:59).

    -

    Disable / Enable

    - Disables/enables Pi-Hole blocking completely. You may have to wait a few minutes for the changes to reach all of your devices. The change will be reflected by a changed status (top left) +

    Tools → Query adlists

    + This function is useful to find out what list a domain appears on. Since we don't control what the third-parties put on the block lists, you may find that a domain you normally visit stops working. If this is the case, you could run this command to scan for strings in the list of blocked domains and it will return the list the domain is found on. This proved useful a while back when the Mahakala list was adding apple.com and microsoft.com to their block list.

    +
    +
    +
    +
    +

    Tools → Tail pihole.log

    + Live tailing of the raw Pi-hole log.

    @@ -96,13 +104,13 @@

    Settings

    Change settings for the Pi-Hole

    Networking

    - Displays information about the interfaces of the Pi-Hole. No changes possible + Displays information about the interfaces of the Pi-Hole. No changes possible.

    Pi-Hole DHCP Server

    - Using this setting you can enable/disable the DHCP server of the Pi-Hole. Note that you should disable any other DHCP server on your network to avoid IP addresses being used more than once. You have to give the range of IPs that DHCP will serve and the IP of the local router (gateway). If the DHCP server is active, the current leases are shown on the settings page. + Using this setting you can enable/disable the DHCP server of the Pi-Hole. Note that you should disable any other DHCP server on your network to avoid IP addresses being used more than once. You have to give the range of IPs that DHCP will serve and the IP of the local router (gateway). If the DHCP server is active, the current leases are shown on the settings page. IPv4 DHCP will always be activated, IPv6 (stateless + statefull) can be enabled.

    Upstream DNS Servers

    - Customize used upstream DNS servers + advanced settings + Customize used upstream DNS servers + advanced settings for DNS servers. Note that any number of DNS servers may be enabled at a time.

    Query Logging

    - Enabled/disable query logging on your Pi-hole + Enabled/disable query logging on your Pi-hole + provide option to flush the log

    API

    Change settings which apply to the API as well as the web UI
    Note that Top Clients have to be given as IP addresses diff --git a/list.php b/list.php index dc8097c16c..3f720e78af 100644 --- a/list.php +++ b/list.php @@ -29,10 +29,18 @@ function getFullName() {
    + + + + + + }
    + +

    Note that whitelisting domains which are blocked using the wildcard method won't work.

    + +

    Exact blocking

    + + +

    Wildcard blocking

    + +
    diff --git a/scripts/pi-hole/js/footer.js b/scripts/pi-hole/js/footer.js index ce64015515..6dd4ce65e6 100644 --- a/scripts/pi-hole/js/footer.js +++ b/scripts/pi-hole/js/footer.js @@ -10,9 +10,9 @@ $("body").on("click", function(event) { function piholeChanged(action) { - const status = $("#status"); - const ena = $("#pihole-enable"); - const dis = $("#pihole-disable"); + var status = $("#status"); + var ena = $("#pihole-enable"); + var dis = $("#pihole-disable"); switch(action) { case "enabled": @@ -33,7 +33,7 @@ function piholeChanged(action) function piholeChange(action, duration) { - const token = encodeURIComponent($("#token").html()); + var token = encodeURIComponent($("#token").html()); var btnStatus; switch(action) { @@ -62,25 +62,25 @@ function piholeChange(action, duration) } // Handle Enable/Disable -$("#pihole-enable").on("click", (e) => { +$("#pihole-enable").on("click", function(e){ e.preventDefault(); piholeChange("enable",""); }); -$("#pihole-disable-permanently").on("click", (e) => { +$("#pihole-disable-permanently").on("click", function(e){ e.preventDefault(); piholeChange("disable","0"); }); -$("#pihole-disable-10s").on("click", (e) => { +$("#pihole-disable-10s").on("click", function(e){ e.preventDefault(); piholeChange("disable","10"); setTimeout(function(){piholeChanged("enabled");},10000); }); -$("#pihole-disable-30s").on("click", (e) => { +$("#pihole-disable-30s").on("click", function(e){ e.preventDefault(); piholeChange("disable","30"); setTimeout(function(){piholeChanged("enabled");},30000); }); -$("#pihole-disable-5m").on("click", (e) => { +$("#pihole-disable-5m").on("click", function(e){ e.preventDefault(); piholeChange("disable","300"); setTimeout(function(){piholeChanged("enabled");},300000); @@ -204,3 +204,27 @@ $(document).keypress(function(e) { $("#loginform").submit(); } }); + +function testCookies() +{ + if (navigator.cookieEnabled) + { + return true; + } + + // set and read cookie + document.cookie = "cookietest=1"; + var ret = document.cookie.indexOf("cookietest=") !== -1; + + // delete cookie + document.cookie = "cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT"; + + return ret; +} + +$(function() { + if(!testCookies() && $("#cookieInfo").length) + { + $("#cookieInfo").show(); + } +}); diff --git a/scripts/pi-hole/js/gravity.js b/scripts/pi-hole/js/gravity.js index a208cae127..02c91e3c7d 100644 --- a/scripts/pi-hole/js/gravity.js +++ b/scripts/pi-hole/js/gravity.js @@ -2,6 +2,13 @@ function eventsource() { var alInfo = $("#alInfo"); var alSuccess = $("#alSuccess"); var ta = $("#output"); + + // IE does not support EventSource - exit early + if (typeof EventSource !== "function") { + ta.show(); + ta.html("Updating lists of ad-serving domains is not supported with this browser!"); + return; + } var source = new EventSource("scripts/pi-hole/php/gravity.sh.php"); ta.html(""); @@ -27,7 +34,7 @@ function eventsource() { }, false); } -$("#gravityBtn").on("click", () => { +$("#gravityBtn").on("click", function(){ $("#gravityBtn").attr("disabled", true); eventsource(); }); diff --git a/scripts/pi-hole/js/index.js b/scripts/pi-hole/js/index.js index 5030af14bb..89b1d2ba06 100644 --- a/scripts/pi-hole/js/index.js +++ b/scripts/pi-hole/js/index.js @@ -132,7 +132,7 @@ function escapeHtml(text) { function updateTopClientsChart() { $.getJSON("api.php?summaryRaw&getQuerySources", function(data) { var clienttable = $("#client-frequency").find("tbody:last"); - var domain, percentage, domainname; + var domain, percentage, domainname, domainip; for (domain in data.top_sources) { if ({}.hasOwnProperty.call(data.top_sources, domain)){ @@ -140,14 +140,17 @@ function updateTopClientsChart() { domain = escapeHtml(domain); if(domain.indexOf("|") > -1) { - domainname = domain.substr(0, domain.indexOf("|")); + var idx = domain.indexOf("|"); + domainname = domain.substr(0, idx); + domainip = domain.substr(idx+1, domain.length-idx); } else { domainname = domain; + domainip = domain; } - var url = ""+domainname+""; + var url = ""+domainname+""; percentage = data.top_sources[domain] / data.dns_queries_today * 100; clienttable.append(" " + url + " " + data.top_sources[domain] + "
    -1) { - key = key.substr(0, key.indexOf("|")); + var idx = key.indexOf("|"); + key = key.substr(0, idx)+" ("+key.substr(idx+1, key.length-idx)+")"; } forwardDestinationChart.data.labels.push(key); }); diff --git a/scripts/pi-hole/js/list.js b/scripts/pi-hole/js/list.js index de2692ad0f..d0dc387d24 100644 --- a/scripts/pi-hole/js/list.js +++ b/scripts/pi-hole/js/list.js @@ -6,13 +6,18 @@ var token = $("#token").html(); var listType = $("#list-type").html(); var fullName = listType === "white" ? "Whitelist" : "Blacklist"; -function sub(index, entry) { +function sub(index, entry, arg) { var domain = $("#"+index); + var locallistType = listType; domain.hide("highlight"); + if(arg === "wild") + { + locallistType = "wild"; + } $.ajax({ url: "scripts/pi-hole/php/sub.php", method: "post", - data: {"domain":entry, "list":listType, "token":token}, + data: {"domain":entry, "list":locallistType, "token":token}, success: function(response) { if(response.length !== 0){ return; @@ -21,14 +26,24 @@ function sub(index, entry) { }, error: function(jqXHR, exception) { alert("Failed to remove the domain!"); + domain.show({queue:true}); } }); } function refresh(fade) { + var listw; var list = $("#list"); + if(listType === "black") + { + listw = $("#list-wildcard"); + } if(fade) { list.fadeOut(100); + if(listw) + { + listw.fadeOut(100); + } } $.ajax({ url: "scripts/pi-hole/php/get.php", @@ -36,26 +51,60 @@ function refresh(fade) { data: {"list":listType}, success: function(response) { list.html(""); + if(listw) + { + listw.html(""); + } var data = JSON.parse(response); if(data.length === 0) { - list.html("
    Your " + fullName + " is empty!
    "); + $("h3").hide(); + if(listw) + { + listw.html("
    Your " + fullName + " is empty!
    "); + } + else + { + list.html("
    Your " + fullName + " is empty!
    "); + } } else { + $("h3").show(); data.forEach(function (entry, index) { - list.append( + if(entry.substr(0,1) === "*") + { + // Wildcard entry + // remove leading * + entry = entry.substr(1, entry.length - 1); + listw.append( "
  • " + entry + "
  • " - ); + ""); + // Handle button + $("#list-wildcard #"+index+"").on("click", "button", function() { + sub(index, entry, "wild"); + }); + } + else + { + // Normal entry + list.append( + "
  • " + entry + + "
  • "); + // Handle button + $("#list #"+index+"").on("click", "button", function() { + sub(index, entry, "exact"); + }); + } - // Handle button - $("#list #"+index+"").on("click", "button", function() { - sub(index, entry); - }); }); } - list.fadeIn("fast"); + list.fadeIn(100); + if(listw) + { + listw.fadeIn(100); + } }, error: function(jqXHR, exception) { $("#alFailure").show(); @@ -65,30 +114,37 @@ function refresh(fade) { window.onload = refresh(false); -function add() { +function add(arg) { + var locallistType = listType; var domain = $("#domain"); if(domain.val().length === 0){ return; } + if(arg === "wild") + { + locallistType = "wild"; + } var alInfo = $("#alInfo"); var alSuccess = $("#alSuccess"); var alFailure = $("#alFailure"); + var err = $("#err"); alInfo.show(); alSuccess.hide(); alFailure.hide(); $.ajax({ url: "scripts/pi-hole/php/add.php", method: "post", - data: {"domain":domain.val(), "list":listType, "token":token}, + data: {"domain":domain.val(), "list":locallistType, "token":token}, success: function(response) { if (response.indexOf("not a valid argument") >= 0 || response.indexOf("is not a valid domain") >= 0) { alFailure.show(); - alFailure.delay(1000).fadeOut(2000, function() { + err.html(response); + alFailure.delay(4000).fadeOut(2000, function() { alFailure.hide(); }); - alInfo.delay(1000).fadeOut(2000, function() { + alInfo.delay(4000).fadeOut(2000, function() { alInfo.hide(); }); } else { @@ -105,6 +161,7 @@ function add() { }, error: function(jqXHR, exception) { alFailure.show(); + err.html(""); alFailure.delay(1000).fadeOut(2000, function() { alFailure.hide(); }); @@ -121,14 +178,19 @@ function add() { $(document).keypress(function(e) { if(e.which === 13 && $("#domain").is(":focus")) { // Enter was pressed, and the input has focus - add(); + add("exact"); } }); // Handle buttons $("#btnAdd").on("click", function() { - add(); + add("exact"); }); + +$("#btnAddWildcard").on("click", function() { + add("wild"); +}); + $("#btnRefresh").on("click", function() { refresh(true); }); diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index 1c01fcf14c..569122492e 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -22,6 +22,7 @@ function add(domain,list) { alDomain.html(domain); var alSuccess = $("#alSuccess"); var alFailure = $("#alFailure"); + var err = $("#err"); if(list === "white") { @@ -43,7 +44,8 @@ function add(domain,list) { if (response.indexOf("not a valid argument") >= 0 || response.indexOf("is not a valid domain") >= 0) { alFailure.show(); - alFailure.delay(1000).fadeOut(2000, function() { alFailure.hide(); }); + err.html(response); + alFailure.delay(4000).fadeOut(2000, function() { alFailure.hide(); }); } else { @@ -58,6 +60,7 @@ function add(domain,list) { }, error: function(jqXHR, exception) { alFailure.show(); + err.html(""); alFailure.delay(1000).fadeOut(2000, function() { alFailure.hide(); }); @@ -69,8 +72,20 @@ function add(domain,list) { } }); } +function handleAjaxError( xhr, textStatus, error ) { + if ( textStatus === "timeout" ) { + alert( "The server took too long to send the data." ); + } + else { + alert( "An error occured while loading the data. Presumably your log is too large to be processed." ); + } + $("#all-queries_processing").hide(); + tableApi.clear(); + tableApi.draw(); +} $(document).ready(function() { + var status; // Do we want to filter queries? var GETDict = {}; @@ -90,13 +105,18 @@ $(document).ready(function() { tableApi = $("#all-queries").DataTable( { "rowCallback": function( row, data, index ){ - if (data[4] === "Pi-holed") { + status = data[4]; + if (status === "Pi-holed (exact)") { + $(row).css("color","red"); + $("td:eq(5)", row).html( "" ); + } + else if (status === "Pi-holed (wildcard)") { $(row).css("color","red"); - $("td:eq(5)", row).html( "" ); + $("td:eq(5)", row).html( "" ); } else{ $(row).css("color","green"); - $("td:eq(5)", row).html( "" ); + $("td:eq(5)", row).html( "" ); } }, @@ -104,8 +124,9 @@ $(document).ready(function() { "<'row'<'col-sm-4'l><'col-sm-8'p>>" + "<'row'<'col-sm-12'tr>>" + "<'row'<'col-sm-5'i><'col-sm-7'p>>", - "ajax": APIstring, + "ajax": {"url": APIstring, "error": handleAjaxError }, "autoWidth" : false, + "processing": true, "order" : [[0, "desc"]], "columns": [ { "width" : "20%", "type": "date" }, @@ -124,7 +145,8 @@ $(document).ready(function() { }); $("#all-queries tbody").on( "click", "button", function () { var data = tableApi.row( $(this).parents("tr") ).data(); - if (data[4] === "Pi-holed") + status = data[4]; + if (status.substr(0,2) === "Pi") { add(data[2],"white"); } diff --git a/scripts/pi-hole/js/queryads.js b/scripts/pi-hole/js/queryads.js index 11d7dd0302..6bf5e4696b 100644 --- a/scripts/pi-hole/js/queryads.js +++ b/scripts/pi-hole/js/queryads.js @@ -1,4 +1,54 @@ var exact = ""; + +function quietfilter(ta,data) +{ + var lines = data.split("\n"); + for(var i = 0;i diff --git a/scripts/pi-hole/php/auth.php b/scripts/pi-hole/php/auth.php index 690daed3e1..9d83428fda 100644 --- a/scripts/pi-hole/php/auth.php +++ b/scripts/pi-hole/php/auth.php @@ -17,10 +17,12 @@ function log_and_die($message) { function check_cors() { $setupVars = parse_ini_file("/etc/pihole/setupVars.conf"); $ipv4 = isset($setupVars["IPV4_ADDRESS"]) ? explode("/", $setupVars["IPV4_ADDRESS"])[0] : $_SERVER['SERVER_ADDR']; + $ipv6 = isset($setupVars["IPV6_ADDRESS"]) ? explode("/", $setupVars["IPV6_ADDRESS"])[0] : $_SERVER['SERVER_ADDR']; // Check CORS $AUTHORIZED_HOSTNAMES = array( $ipv4, + $ipv6, $_SERVER["SERVER_NAME"], "pi.hole", "localhost" @@ -40,10 +42,15 @@ function check_cors() { // https://pi.hole // pi.hole:8080 // However, we don't use parse_url(...) if there is no colon, since it will fail for e.g. "pi.hole" - if(strpos($server_host, ":")) + + // Don't use parse_url for IPv6 addresses, since it does not support them + // see PHP bug report: https://bugs.php.net/bug.php?id=72811 + if(strpos($server_host, ":") && !strpos($server_host, "[") && !strpos($server_host, "]")) { $server_host = parse_url($_SERVER['HTTP_HOST'], PHP_URL_HOST); } + // Remove "[" ... "]" + $server_host = str_replace(array("[","]"), array("",""), $server_host); if(isset($_SERVER['HTTP_HOST']) && !in_array($server_host, $AUTHORIZED_HOSTNAMES)) { log_and_die("Failed Host Check: " . $server_host .' vs '. join(', ', $AUTHORIZED_HOSTNAMES)); @@ -53,10 +60,12 @@ function check_cors() { $server_origin = $_SERVER['HTTP_ORIGIN']; // Detect colon in $_SERVER['HTTP_ORIGIN'] (see comment above) - if(strpos($server_origin, ":")) + if(strpos($server_origin, ":") && !strpos($server_origin, "[") && !strpos($server_origin, "]")) { $server_origin = parse_url($_SERVER['HTTP_ORIGIN'], PHP_URL_HOST); } + // Remove "[" ... "]" + $server_origin = str_replace(array("[","]"), array("",""), $server_origin); if(!in_array($server_origin, $AUTHORIZED_HOSTNAMES)) { log_and_die("Failed CORS: " . $server_origin .' vs '. join(', ', $AUTHORIZED_HOSTNAMES)); @@ -103,9 +112,13 @@ function hash_equals($known_string, $user_string) { function check_domain() { if(isset($_POST['domain'])){ - $validDomain = is_valid_domain_name($_POST['domain']); - if(!$validDomain){ - log_and_die($_POST['domain']. ' is not a valid domain'); + $domains = explode(" ",$_POST['domain']); + foreach($domains as $domain) + { + $validDomain = is_valid_domain_name($domain); + if(!$validDomain){ + log_and_die(htmlspecialchars($domain. ' is not a valid domain')); + } } } } @@ -126,11 +139,11 @@ function list_verify($type) { require("password.php"); if(strlen($pwhash) == 0) { - log_and_die("No password set - ".$type."listing with password not supported"); + log_and_die("No password set - ".htmlspecialchars($type)."listing with password not supported"); } elseif($wrongpassword) { - log_and_die("Wrong password - ".$type."listing of ${_POST['domain']} not permitted"); + log_and_die("Wrong password - ".htmlspecialchars($type)."listing of ${_POST['domain']} not permitted"); } } else diff --git a/scripts/pi-hole/php/data.php b/scripts/pi-hole/php/data.php index 73176ca08c..8c6a8b7e92 100644 --- a/scripts/pi-hole/php/data.php +++ b/scripts/pi-hole/php/data.php @@ -333,6 +333,7 @@ function getAllQueries($orderBy) { // Create empty array for gravity $gravity_domains = getGravity(); + $wildcard_domains = getWildcardListContent(); if(isset($_GET["from"])) { @@ -380,10 +381,37 @@ function getAllQueries($orderBy) { $exploded = explode(" ", trim($query)); $domain = $exploded[count($exploded)-3]; - $tmp = $exploded[count($exploded)-4]; - $status = isset($gravity_domains[$domain]) ? "Pi-holed" : "OK"; - if(($status === "Pi-holed" && $showBlocked) || ($status === "OK" && $showPermitted)) + $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); $client = $exploded[count($exploded)-1]; @@ -481,7 +509,7 @@ function getDnsQueryDomains(\SplFileObject $log) { function countDnsQueries() { global $logListName; - return exec("grep -c \": query\\[A\" $logListName"); + return intval(exec("grep -c \": query\\[A\" $logListName")); } function getDnsQueriesAll(\SplFileObject $log) { @@ -506,16 +534,33 @@ function getDomains($file, &$array, $action){ if($action && strlen($key) > 0) { // $action is true (we want to add) *and* key is not empty - $array[$key] = true; + $array[$key] = 1; } elseif(!$action && isset($array[$key])) { // $action is false (we want to remove) *and* key is set - unset($array[$key]); + $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 = []; @@ -577,7 +622,17 @@ function getBlockedQueries(\SplFileObject $log) { function countBlockedQueries() { global $logListName; - return exec("grep \"gravity.list\" $logListName | grep -c \" is \""); + // 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) { @@ -623,14 +678,12 @@ function overTime($entries, $gravity_domains) { $byTimeAds[$time] = 1; } } - else - { - if (isset($byTimeDomains[$time])) { - $byTimeDomains[$time]++; - } - else { - $byTimeDomains[$time] = 1; - } + + if (isset($byTimeDomains[$time])) { + $byTimeDomains[$time]++; + } + else { + $byTimeDomains[$time] = 1; } } return [$byTimeDomains,$byTimeAds]; @@ -665,14 +718,12 @@ function overTime10mins($entries, $gravity_domains=[]) { $byTimeAds[$time] = 1; } } - else - { - if (isset($byTimeDomains[$time])) { - $byTimeDomains[$time]++; - } - else { - $byTimeDomains[$time] = 1; - } + + if (isset($byTimeDomains[$time])) { + $byTimeDomains[$time]++; + } + else { + $byTimeDomains[$time] = 1; } } return [$byTimeDomains,$byTimeAds]; diff --git a/scripts/pi-hole/php/get.php b/scripts/pi-hole/php/get.php index fada75b946..043c8b8753 100644 --- a/scripts/pi-hole/php/get.php +++ b/scripts/pi-hole/php/get.php @@ -1,21 +1,55 @@ = 0; $i--) { + if($list[$i] == "") + unset($list[$i]); + } + + return $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); -// Get rid of empty lines -for($i = sizeof($list)-1; $i >= 0; $i--) { - if($list[$i] == "") - unset($list[$i]); } function filterArray(&$inArray) { diff --git a/scripts/pi-hole/php/header.php b/scripts/pi-hole/php/header.php index f5b795286f..91a56f7d28 100644 --- a/scripts/pi-hole/php/header.php +++ b/scripts/pi-hole/php/header.php @@ -83,15 +83,16 @@ $memory_usage = -1; } + if($auth) { + // For session timer + $maxlifetime = ini_get("session.gc_maxlifetime"); - // For session timer - $maxlifetime = ini_get("session.gc_maxlifetime"); - - // Generate CSRF token - if(empty($_SESSION['token'])) { - $_SESSION['token'] = base64_encode(openssl_random_pseudo_bytes(32)); + // Generate CSRF token + if(empty($_SESSION['token'])) { + $_SESSION['token'] = base64_encode(openssl_random_pseudo_bytes(32)); + } + $token = $_SESSION['token']; } - $token = $_SESSION['token']; if(isset($setupVars['WEBUIBOXEDLAYOUT'])) { @@ -149,6 +150,7 @@ +