Merge pull request #795 from pi-hole/release/v4.0

Release v4.0
This commit is contained in:
Dan Schaper
2018-08-05 10:44:28 -07:00
committed by GitHub
37 changed files with 1525 additions and 1708 deletions
+174 -20
View File
@@ -1,32 +1,186 @@
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)
<p align="center">
<a href="https://pi-hole.net"><img src="https://pi-hole.github.io/graphics/Vortex/Vortex_with_text.png" width="150" height="255" alt="Pi-hole"></a><br/></br>
<b>Network-wide ad blocking via your own Linux hardware</b><br/>
</br>
<a href="https://pi-hole.net"><img src="https://pi-hole.github.io/graphics/Screenshots/pihole-dashboard.png" alt="Pi-hole Web interface"></a><br/>
</p>
[![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")
Pi-hole[®](https://pi-hole.net/trademark-rules-and-brand-guidelines/)'s Web interface (based off of [AdminLTE](https://almsaeedstudio.com)) provides a central location to manage your Pi-hole and review the statistics generated by FTLDNS[™](https://pi-hole.net/trademark-rules-and-brand-guidelines/).
Using **[AdminLTE](https://almsaeedstudio.com)**, this project will create a Web interface for the ad-blocking Pi-hole: **a black hole for Internet advertisements**.
- **Easy-to-interpret**: simple graphs and beautiful colors make Pi-hole's stats easy to understand
- **Responsive**: looks great on desktop, tablets, and mobile devices
- **Useful**: control and configure your Pi-hole with our settings
- **Insightful**: use the query log, audit log, or long-term stats to gain insight into your networks activity
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.
---
<a class="badge-align" href="https://www.codacy.com/app/Pi-hole/AdminLTE?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=pi-hole/AdminLTE&amp;utm_campaign=Badge_Grade"><img src="https://api.codacy.com/project/badge/Grade/10540ea45a3b4d5f992c05a2c9714609"/></a>
<img src="https://pi-hole.github.io/graphics/Badges/browserstack-badge.png" height="80"><br>
![Pi-hole Web interface](http://i.imgur.com/EgGZXbT.png)
# Installation
The Web interface is enabled by default when you install Pi-hole.
## Post-installation: access the Web interface and gain insight into your network's activity
There are several ways to [access the dashboard](https://discourse.pi-hole.net/t/how-do-i-access-pi-holes-dashboard-admin-interface/3168):
1. `http://<IP_ADDPRESS_OF_YOUR_PI_HOLE>/admin/`
2. `http:/pi.hole/admin/` (when using Pi-hole as your DNS server)
3. `http://pi.hole/` (when using Pi-hole as your DNS server)
Once logged in (forgot your password?), you can view your network stats to see things like:
- the domains being queried on your network
- the time the queries were initiated
- the amount of domains that were blocked
- the upstream server queries were sent to
- the type of queries (`A`, `AAAA`, `CNAME`, `SRV`, `TXT`, etc.)
---
## Pi-hole is free, but powered by your support
There are many reoccurring costs involved with maintaining free, open source, and privacy respecting software; expenses which [our volunteer developers](https://github.com/orgs/pi-hole/people) pitch in to cover out-of-pocket. This is just one example of how strongly we feel about our software, as well as the importance of keeping it maintained.
Make no mistake: **your support is absolutely vital to help keep us innovating!**
### Donations
Sending a donation using our links below is **extremely helpful** in offsetting a portion of our monthly expenses:
- <img src="https://pi-hole.github.io/graphics/Badges/paypal-badge-black.svg" width="24" height="24" alt="PP"/> <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3J2L3Z4DHW9UY">Donate via PayPal</a><br/>
- <img src="https://pi-hole.github.io/graphics/Badges/bitcoin-badge-black.svg" width="24" height="24" alt="BTC"/> [Bitcoin](https://commerce.coinbase.com/checkout/fb7facaf-bebd-46be-bb77-b358f4546763): <code>1GKnevUnVaQM2pQieMyeHkpr8DXfkpfAtL</code></br>
- <img src="https://pi-hole.github.io/graphics/Badges/bitcoin-badge-black.svg" width="24" height="24" alt="BTC"/> [Bitcoin Cash](https://commerce.coinbase.com/checkout/fb7facaf-bebd-46be-bb77-b358f4546763): <code>qqh25hlmqaj99xraw00e47xmf8sysnyxhyww2d7dnh</code></br>
- <img src="https://pi-hole.github.io/graphics/Badges/ethereum-badge-black.svg" width="24" height="24" alt="BTC"/> [Ethereum](https://commerce.coinbase.com/checkout/fb7facaf-bebd-46be-bb77-b358f4546763): <code>0xF00aF43d2431BAD585056492b310e48eC40D87e8</code>
### Alternative support
If you'd rather not [donate](https://pi-hole.net/donate/) (_which is okay!_), there are other ways you can help support us:
- [Patreon](https://patreon.com/pihole) _Become a patron for rewards_
- [Digital Ocean](http://www.digitalocean.com/?refcode=344d234950e1) _affiliate link_
- [UNIXstickers.com](http://unixstickers.refr.cc/jacobs) _save $5 when you spend $9 using our affiliate link_
- [Pi-hole Swag Store](https://pi-hole.net/shop/) _affiliate link_
- [Amazon](http://www.amazon.com/exec/obidos/redirect-home/pihole09-20) _affiliate link_
- [DNS Made Easy](https://cp.dnsmadeeasy.com/u/133706) _affiliate link_
- [Vultr](http://www.vultr.com/?ref=7190426) _affiliate link_
- Spreading the word about our software, and how you have benefited from it
### Contributing via GitHub
We welcome _everyone_ to contribute to issue reports, suggest new features, and create pull requests.
If you have something to add - anything from a typo through to a whole new feature, we're happy to check it out! Just make sure to fill out our template when submitting your request; the questions that it asks will help the volunteers quickly understand what you're aiming to achieve.
### Presentations about Pi-hole
Word-of-mouth continues to help our project grow immensely, and so we are helping make this easier for people.
If you are going to be presenting Pi-hole at a conference, meetup or even a school project, [get in touch with us](https://pi-hole.net/2017/05/17/giving-a-presentation-on-pi-hole-contact-us-first-for-some-goodies-and-support/) so we can hook you up with free swag to hand out to your audience!
-----
## Getting in touch with us
While we are primarily reachable on our <a href="https://discourse.pi-hole.net/">Discourse User Forum</a>, we can also be found on a variety of social media outlets. **Please be sure to check the FAQ's** before starting a new discussion, as we do not have the spare time to reply to every request for assistance.
<ul>
<li><b><a href="https://discourse.pi-hole.net/c/faqs">Frequently Asked Questions</a></b></li>
<li><b><a href="https://github.com/pi-hole/pi-hole/wiki">Pi-hole Wiki</a></b></li>
<li><b><a href="https://discourse.pi-hole.net/c/feature-requests?order=votes">Feature Requests</a></b></li>
<li><a href="https://discourse.pi-hole.net/">Discourse User Forum</a></li>
<li><a href="https://www.reddit.com/r/pihole/">Reddit</a></li>
<li><a href="https://twitter.com/The_Pi_Hole">Twitter</a></li>
<li><a href="https://www.facebook.com/ThePiHole/">Facebook</a></li>
<li><a href="https://gitter.im/pi-hole/pi-hole">Gitter</a> (Real-time chat)</li>
<li><a href="https://www.youtube.com/channel/UCT5kq9w0wSjogzJb81C9U0w">YouTube</a></li>
</ul>
# Features
## Mobile friendly interface
<p align="center">
<img src="https://pi-hole.github.io/graphics/Screenshots/mobile-friendly.png" height="300" alt="Mobile friendly"></a>
</p>
## Password protection
<p align="center">
<img src="https://pi-hole.github.io/graphics/Screenshots/password-protection.png" height="400" alt="Password protection"></a>
</p>
## Detailed graphs and doughnut charts
<p align="center">
<img src="https://pi-hole.github.io/graphics/Screenshots/piecharts.png" alt="Pie charts"></a>
</p>
## Top lists of domains and clients
<p align="center">
<img src="https://pi-hole.github.io/graphics/Screenshots/topdomains-clients.png" alt="Top domains/top clients"></a>
</p>
## A filterable and sortable query log
<p align="center">
<img src="https://pi-hole.github.io/graphics/Screenshots/query-log-sorted.png" alt="Query log"></a>
</p>
## An audit log
<p align="center">
<img src="https://pi-hole.github.io/graphics/Screenshots/audit-log.png" alt="Pi-hole Web interface"></a>
</p>
## Long Term Statistics to view data over user defined time ranges
<p align="center">
<img src="https://pi-hole.github.io/graphics/Screenshots/long-term-stats.png" alt="Long-term stats"></a>
</p>
## A built-in debugger
<p align="center">
<img src="https://pi-hole.github.io/graphics/Screenshots/debug.png" alt="Debugger"></a>
</p>
## Black and white lists
<p align="center">
<img src="https://pi-hole.github.io/graphics/Screenshots/blacklist.png" alt="Blacklist"></a>
</br></br>
<img src="https://pi-hole.github.io/graphics/Screenshots/whitelist.png" alt="Whitelist"></a>
</p>
## The ability to easily manage and configure Pi-hole features
<p align="center">
<img src="https://pi-hole.github.io/graphics/Screenshots/dashboard-settings.gif" alt="Settings"></a>
</p>
## ... and all the main features of the Command Line Interface!
<p align="center">
<img src="https://pi-hole.github.io/graphics/Screenshots/pihole-t.png" alt="Tail the log"></a>
</br></br>
<img src="https://pi-hole.github.io/graphics/Screenshots/pihole-q.png" alt="Query ad lists"></a>
</p>
## 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:
Full usage available [here](https://discourse.pi-hole.net/t/pi-hole-api/1863). can be accessed at `/admin/api.php`. With either no parameters or `/admin/api.php?summary` it returns the following JSON:
```JSON
{
"domains_being_blocked": "136,708",
"dns_queries_today": "18,108",
"ads_blocked_today": "14,648",
"ads_percentage_today": "80.9"
{
"domains_being_blocked":243038,
"dns_queries_today":2385,
"ads_blocked_today":414,
"ads_percentage_today":17.35849,
"unique_domains":429,
"queries_forwarded":1537,
"queries_cached":434,
"clients_ever_seen":5,
"unique_clients":5,
"status":"enabled"
}
```
There are many more parameters, such as `summaryRaw`, `overTimeData10mins`, `topItems`, ` topClients` or `getQuerySources`, `getQueryTypes`, `getForwardDestinations`, and finally `getAllQueries`.
Together with a token it is also possible to enable and disable (also with a set timeout) blocking via the API
[There are many more parameters](https://discourse.pi-hole.net/t/pi-hole-api/1863), such as:
The API returns more information (in a slighly different format if `FTL` is running) - it supports a fall-back to the "old" PHP API if `FTL` is not running. Test the type and/or version of the API by using the parameter `type` and `version`.
- `type & version`
- `summaryRaw`
- `summary`
- `overTimeData10mins`
- `topItems`
- `getQuerySources`
- `getForwardDestinations`
- `getQueryTypes`
- `getAllQueries`
- `enable`
- `disable`
- `recentBlocked`
<hr>
<img src="https://assets.pi-hole.net/static/BStackLogo.png" height="80"><br>
We use BrowserStack for multi-platform multi-browser testing.
Together with a token it is also possible to [enable and disable (also with a set timeout) blocking via the API](https://discourse.pi-hole.net/t/is-there-an-api-command-to-disable-ad-blocking/7693).
The API returns more information (in a slightly different format if `FTL` is running) - it supports a fall-back to the "old" PHP API if `FTL` is not running. Test the type and/or version of the API by using the parameter `type` and `version`.
+25 -15
View File
@@ -73,23 +73,33 @@ elseif (isset($_GET['disable']) && $auth)
}
$data = array_merge($data, array("status" => "disabled"));
}
elseif (isset($_GET['versions']))
{
// Determine if updates are available for Pi-hole
// using the same script that we use for the footer
// on the dashboard (update notifications are
// suppressed if on development branches)
require "scripts/pi-hole/php/update_checker.php";
$updates = array("core_update" => $core_update,
"web_update" => $web_update,
"FTL_update" => $FTL_update);
$current = array("core_current" => $core_current,
"web_current" => $web_current,
"FTL_current" => $FTL_current);
$latest = array("core_latest" => $core_latest,
"web_latest" => $web_latest,
"FTL_latest" => $FTL_latest);
$branches = array("core_branch" => $core_branch,
"web_branch" => $web_branch,
"FTL_branch" => $FTL_branch);
$data = array_merge($data, $updates);
$data = array_merge($data, $current);
$data = array_merge($data, $latest);
$data = array_merge($data, $branches);
}
// Other API functions
if(!testFTL($FTL_IP) && !isset($_GET["PHP"]))
{
$data = array_merge($data, array("FTLnotrunning" => true));
}
else
{
if(!isset($_GET["PHP"]))
{
require("api_FTL.php");
}
else
{
require("api_PHP.php");
}
}
require("api_FTL.php");
if(isset($_GET["jsonForceObject"]))
{
+306 -290
View File
@@ -15,339 +15,355 @@ if(!isset($api))
// $FTL_IP is defined in api.php
$socket = connectFTL($FTL_IP);
if (isset($_GET['type'])) {
$data["type"] = "FTL";
}
if (isset($_GET['version'])) {
$data["version"] = 3;
}
if (isset($_GET['summary']) || isset($_GET['summaryRaw']) || !count($_GET))
if(!is_resource($socket))
{
sendRequestFTL("stats");
$return = getResponseFTL();
$data = array_merge($data, array("FTLnotrunning" => true));
}
else
{
if (isset($_GET['type'])) {
$data["type"] = "FTL";
}
$stats = [];
foreach($return as $line)
if (isset($_GET['version'])) {
$data["version"] = 3;
}
if (isset($_GET['summary']) || isset($_GET['summaryRaw']) || !count($_GET))
{
$tmp = explode(" ",$line);
require_once("scripts/pi-hole/php/gravity.php");
sendRequestFTL("stats");
$return = getResponseFTL();
if(($tmp[0] === "domains_being_blocked" && !is_numeric($tmp[1])) || $tmp[0] === "status")
$stats = [];
foreach($return as $line)
{
$stats[$tmp[0]] = $tmp[1];
continue;
}
$tmp = explode(" ",$line);
if(isset($_GET['summary']))
{
if($tmp[0] !== "ads_percentage_today")
if(($tmp[0] === "domains_being_blocked" && !is_numeric($tmp[1])) || $tmp[0] === "status")
{
$stats[$tmp[0]] = number_format($tmp[1]);
$stats[$tmp[0]] = $tmp[1];
continue;
}
if(isset($_GET['summary']))
{
if($tmp[0] !== "ads_percentage_today")
{
$stats[$tmp[0]] = number_format($tmp[1]);
}
else
{
$stats[$tmp[0]] = number_format($tmp[1], 1, '.', '');
}
}
else
{
$stats[$tmp[0]] = number_format($tmp[1], 1, '.', '');
$stats[$tmp[0]] = floatval($tmp[1]);
}
}
else
$stats['gravity_last_updated'] = gravity_last_update(true);
$data = array_merge($data,$stats);
}
if (isset($_GET['overTimeData10mins']))
{
sendRequestFTL("overTime");
$return = getResponseFTL();
$domains_over_time = array();
$ads_over_time = array();
foreach($return as $line)
{
$stats[$tmp[0]] = floatval($tmp[1]);
$tmp = explode(" ",$line);
$domains_over_time[intval($tmp[0])] = intval($tmp[1]);
$ads_over_time[intval($tmp[0])] = intval($tmp[2]);
}
}
$data = array_merge($data,$stats);
}
if (isset($_GET['overTimeData10mins']))
{
sendRequestFTL("overTime");
$return = getResponseFTL();
$domains_over_time = array();
$ads_over_time = array();
foreach($return as $line)
{
$tmp = explode(" ",$line);
$domains_over_time[intval($tmp[0])] = intval($tmp[1]);
$ads_over_time[intval($tmp[0])] = intval($tmp[2]);
}
$result = array('domains_over_time' => $domains_over_time,
'ads_over_time' => $ads_over_time);
$data = array_merge($data, $result);
}
if (isset($_GET['topItems']) && $auth)
{
if($_GET['topItems'] === "audit")
{
sendRequestFTL("top-domains for audit");
}
else if(is_numeric($_GET['topItems']))
{
sendRequestFTL("top-domains (".$_GET['topItems'].")");
}
else
{
sendRequestFTL("top-domains");
$result = array('domains_over_time' => $domains_over_time,
'ads_over_time' => $ads_over_time);
$data = array_merge($data, $result);
}
$return = getResponseFTL();
$top_queries = array();
foreach($return as $line)
if (isset($_GET['topItems']) && $auth)
{
$tmp = explode(" ",$line);
$top_queries[$tmp[2]] = intval($tmp[1]);
}
if($_GET['topItems'] === "audit")
{
sendRequestFTL("top-ads for audit");
}
else if(is_numeric($_GET['topItems']))
{
sendRequestFTL("top-ads (".$_GET['topItems'].")");
}
else
{
sendRequestFTL("top-ads");
}
$return = getResponseFTL();
$top_ads = array();
foreach($return as $line)
{
$tmp = explode(" ",$line);
if(count($tmp) === 4)
$top_ads[$tmp[2]." (".$tmp[3].")"] = intval($tmp[1]);
else
$top_ads[$tmp[2]] = intval($tmp[1]);
}
$result = array('top_queries' => $top_queries,
'top_ads' => $top_ads);
$data = array_merge($data, $result);
}
if ((isset($_GET['topClients']) || isset($_GET['getQuerySources'])) && $auth)
{
if(isset($_GET['topClients']))
{
$number = $_GET['topClients'];
}
elseif(isset($_GET['getQuerySources']))
{
$number = $_GET['getQuerySources'];
}
if(is_numeric($number))
{
sendRequestFTL("top-clients (".$number.")");
}
else
{
sendRequestFTL("top-clients");
}
$return = getResponseFTL();
$top_clients = array();
foreach($return as $line)
{
$tmp = explode(" ",$line);
if(count($tmp) == 4)
if($_GET['topItems'] === "audit")
{
$top_clients[$tmp[3]."|".$tmp[2]] = intval($tmp[1]);
sendRequestFTL("top-domains for audit");
}
else if(is_numeric($_GET['topItems']))
{
sendRequestFTL("top-domains (".$_GET['topItems'].")");
}
else
{
$top_clients[$tmp[2]] = intval($tmp[1]);
sendRequestFTL("top-domains");
}
}
$result = array('top_sources' => $top_clients);
$data = array_merge($data, $result);
}
if (isset($_GET['getForwardDestinations']) && $auth)
{
if($_GET['getForwardDestinations'] === "unsorted")
{
sendRequestFTL("forward-dest unsorted");
}
else
{
sendRequestFTL("forward-dest");
}
$return = getResponseFTL();
$forward_dest = array();
foreach($return as $line)
{
$tmp = explode(" ",$line);
if(count($tmp) == 4)
$return = getResponseFTL();
$top_queries = array();
foreach($return as $line)
{
$forward_dest[$tmp[3]."|".$tmp[2]] = floatval($tmp[1]);
$tmp = explode(" ",$line);
$top_queries[$tmp[2]] = intval($tmp[1]);
}
if($_GET['topItems'] === "audit")
{
sendRequestFTL("top-ads for audit");
}
else if(is_numeric($_GET['topItems']))
{
sendRequestFTL("top-ads (".$_GET['topItems'].")");
}
else
{
$forward_dest[$tmp[2]] = floatval($tmp[1]);
sendRequestFTL("top-ads");
}
}
$result = array('forward_destinations' => $forward_dest);
$data = array_merge($data, $result);
}
if (isset($_GET['getQueryTypes']) && $auth)
{
sendRequestFTL("querytypes");
$return = getResponseFTL();
$querytypes = array();
foreach($return as $ret)
{
$tmp = explode(": ",$ret);
$querytypes[$tmp[0]] = floatval($tmp[1]);
}
$result = array('querytypes' => $querytypes);
$data = array_merge($data, $result);
}
if (isset($_GET['getAllQueries']) && $auth)
{
if(isset($_GET['from']) && isset($_GET['until']))
{
// Get limited time interval
sendRequestFTL("getallqueries-time ".$_GET['from']." ".$_GET['until']);
}
else if(isset($_GET['domain']))
{
// Get specific domain only
sendRequestFTL("getallqueries-domain ".$_GET['domain']);
}
else if(isset($_GET['client']))
{
// Get specific client only
sendRequestFTL("getallqueries-client ".$_GET['client']);
}
else
{
// Get all queries
sendRequestFTL("getallqueries");
}
$return = getResponseFTL();
$allQueries = array();
foreach($return as $line)
{
$tmp = explode(" ",$line);
array_push($allQueries,$tmp);
}
$result = array('data' => $allQueries);
$data = array_merge($data, $result);
}
if(isset($_GET["recentBlocked"]))
{
sendRequestFTL("recentBlocked");
die(getResponseFTL()[0]);
unset($data);
}
if (isset($_GET['overTimeDataForwards']) && $auth)
{
sendRequestFTL("ForwardedoverTime");
$return = getResponseFTL();
$over_time = array();
foreach($return as $line)
{
$tmp = explode(" ",$line);
for ($i=0; $i < count($tmp)-1; $i++) {
$over_time[intval($tmp[0])][$i] = floatval($tmp[$i+1]);
}
}
$result = array('over_time' => $over_time);
$data = array_merge($data, $result);
}
if (isset($_GET['getForwardDestinationNames']) && $auth)
{
sendRequestFTL("forward-names");
$return = getResponseFTL();
$forward_dest = array();
foreach($return as $line)
{
$tmp = explode(" ",$line);
if(count($tmp) == 4)
$return = getResponseFTL();
$top_ads = array();
foreach($return as $line)
{
$forward_dest[$tmp[3]."|".$tmp[2]] = floatval($tmp[1]);
$tmp = explode(" ",$line);
if(count($tmp) > 3)
$top_ads[$tmp[2]." (".$tmp[3].")"] = intval($tmp[1]);
else
$top_ads[$tmp[2]] = intval($tmp[1]);
}
$result = array('top_queries' => $top_queries,
'top_ads' => $top_ads);
$data = array_merge($data, $result);
}
if ((isset($_GET['topClients']) || isset($_GET['getQuerySources'])) && $auth)
{
if(isset($_GET['topClients']))
{
$number = $_GET['topClients'];
}
elseif(isset($_GET['getQuerySources']))
{
$number = $_GET['getQuerySources'];
}
if(is_numeric($number))
{
sendRequestFTL("top-clients (".$number.")");
}
else
{
$forward_dest[$tmp[2]] = floatval($tmp[1]);
sendRequestFTL("top-clients");
}
}
$result = array('forward_destinations' => $forward_dest);
$data = array_merge($data, $result);
}
if (isset($_GET['overTimeDataQueryTypes']) && $auth)
{
sendRequestFTL("QueryTypesoverTime");
$return = getResponseFTL();
$over_time = array();
foreach($return as $line)
{
$tmp = explode(" ",$line);
for ($i=0; $i < count($tmp)-1; $i++) {
$over_time[intval($tmp[0])][$i] = floatval($tmp[$i+1]);
}
}
$result = array('over_time' => $over_time);
$data = array_merge($data, $result);
}
if (isset($_GET['getClientNames']) && $auth)
{
sendRequestFTL("client-names");
$return = getResponseFTL();
$forward_dest = array();
foreach($return as $line)
{
$tmp = explode(" ",$line);
if(count($tmp) == 4)
$return = getResponseFTL();
$top_clients = array();
foreach($return as $line)
{
$forward_dest[$tmp[3]."|".$tmp[2]] = floatval($tmp[1]);
$tmp = explode(" ",$line);
if(count($tmp) > 3 && strlen($tmp[3]) > 0)
$top_clients[$tmp[3]."|".$tmp[2]] = intval($tmp[1]);
else
$top_clients[$tmp[2]] = intval($tmp[1]);
}
$result = array('top_sources' => $top_clients);
$data = array_merge($data, $result);
}
if (isset($_GET['topClientsBlocked']) && $auth)
{
if(isset($_GET['topClientsBlocked']))
{
$number = $_GET['topClientsBlocked'];
}
if(is_numeric($number))
{
sendRequestFTL("top-clients blocked (".$number.")");
}
else
{
$forward_dest[$tmp[2]] = floatval($tmp[1]);
sendRequestFTL("top-clients blocked");
}
$return = getResponseFTL();
$top_clients = array();
foreach($return as $line)
{
$tmp = explode(" ",$line);
if(count($tmp) > 3 && strlen($tmp[3]) > 0)
$top_clients[$tmp[3]."|".$tmp[2]] = intval($tmp[1]);
else
$top_clients[$tmp[2]] = intval($tmp[1]);
}
$result = array('top_sources_blocked' => $top_clients);
$data = array_merge($data, $result);
}
$result = array('clients' => $forward_dest);
$data = array_merge($data, $result);
}
if (isset($_GET['overTimeDataClients']) && $auth)
{
sendRequestFTL("ClientsoverTime");
$return = getResponseFTL();
$over_time = array();
foreach($return as $line)
if (isset($_GET['getForwardDestinations']) && $auth)
{
$tmp = explode(" ",$line);
for ($i=0; $i < count($tmp)-1; $i++) {
$over_time[intval($tmp[0])][$i] = floatval($tmp[$i+1]);
if($_GET['getForwardDestinations'] === "unsorted")
{
sendRequestFTL("forward-dest unsorted");
}
else
{
sendRequestFTL("forward-dest");
}
$return = getResponseFTL();
$forward_dest = array();
foreach($return as $line)
{
$tmp = explode(" ",$line);
if(count($tmp) > 3 && strlen($tmp[3]) > 0)
$forward_dest[$tmp[3]."|".$tmp[2]] = floatval($tmp[1]);
else
$forward_dest[$tmp[2]] = floatval($tmp[1]);
}
}
$result = array('over_time' => $over_time);
$data = array_merge($data, $result);
}
disconnectFTL();
$result = array('forward_destinations' => $forward_dest);
$data = array_merge($data, $result);
}
if (isset($_GET['getQueryTypes']) && $auth)
{
sendRequestFTL("querytypes");
$return = getResponseFTL();
$querytypes = array();
foreach($return as $ret)
{
$tmp = explode(": ",$ret);
$querytypes[$tmp[0]] = floatval($tmp[1]);
}
$result = array('querytypes' => $querytypes);
$data = array_merge($data, $result);
}
if (isset($_GET['getAllQueries']) && $auth)
{
if(isset($_GET['from']) && isset($_GET['until']))
{
// Get limited time interval
sendRequestFTL("getallqueries-time ".$_GET['from']." ".$_GET['until']);
}
else if(isset($_GET['domain']))
{
// Get specific domain only
sendRequestFTL("getallqueries-domain ".$_GET['domain']);
}
else if(isset($_GET['client']))
{
// Get specific client only
sendRequestFTL("getallqueries-client ".$_GET['client']);
}
else if(is_numeric($_GET['getAllQueries']))
{
sendRequestFTL("getallqueries (".$_GET['getAllQueries'].")");
}
else
{
// Get all queries
sendRequestFTL("getallqueries");
}
$return = getResponseFTL();
$allQueries = array();
foreach($return as $line)
{
$tmp = explode(" ",$line);
array_push($allQueries,$tmp);
}
$result = array('data' => $allQueries);
$data = array_merge($data, $result);
}
if(isset($_GET["recentBlocked"]))
{
sendRequestFTL("recentBlocked");
die(getResponseFTL()[0]);
unset($data);
}
if (isset($_GET['getForwardDestinationNames']) && $auth)
{
sendRequestFTL("forward-names");
$return = getResponseFTL();
$forward_dest = array();
foreach($return as $line)
{
$tmp = explode(" ",$line);
if(count($tmp) > 3)
{
$forward_dest[$tmp[3]."|".$tmp[2]] = floatval($tmp[1]);
}
else
{
$forward_dest[$tmp[2]] = floatval($tmp[1]);
}
}
$result = array('forward_destinations' => $forward_dest);
$data = array_merge($data, $result);
}
if (isset($_GET['overTimeDataQueryTypes']) && $auth)
{
sendRequestFTL("QueryTypesoverTime");
$return = getResponseFTL();
$over_time = array();
foreach($return as $line)
{
$tmp = explode(" ",$line);
for ($i=0; $i < count($tmp)-1; $i++) {
$over_time[intval($tmp[0])][$i] = floatval($tmp[$i+1]);
}
}
$result = array('over_time' => $over_time);
$data = array_merge($data, $result);
}
if (isset($_GET['getClientNames']) && $auth)
{
sendRequestFTL("client-names");
$return = getResponseFTL();
$client_names = array();
foreach($return as $line)
{
$tmp = explode(" ", $line);
$client_names[] = array(
"name" => $tmp[0],
"ip" => $tmp[1]
);
}
$result = array('clients' => $client_names);
$data = array_merge($data, $result);
}
if (isset($_GET['overTimeDataClients']) && $auth)
{
sendRequestFTL("ClientsoverTime");
$return = getResponseFTL();
$over_time = array();
foreach($return as $line)
{
$tmp = explode(" ",$line);
for ($i=0; $i < count($tmp)-1; $i++) {
$over_time[intval($tmp[0])][$i] = floatval($tmp[$i+1]);
}
}
$result = array('over_time' => $over_time);
$data = array_merge($data, $result);
}
disconnectFTL();
}
?>
-93
View File
@@ -1,93 +0,0 @@
<?php
/* Pi-hole: A black hole for Internet advertisements
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
if(!isset($api))
{
die("Direct call to api_PHP.php is not allowed!");
}
include('scripts/pi-hole/php/data.php');
// Non-Auth
if (isset($_GET['type'])) {
$data["type"] = "PHP";
}
if (isset($_GET['version'])) {
$data["version"] = 2;
}
if (isset($_GET['summaryRaw'])) {
$data = array_merge($data, getSummaryData());
}
if (isset($_GET['summary']) || !count($_GET)) {
$sum = getSummaryData();
$sum['ads_blocked_today'] = number_format( $sum['ads_blocked_today']);
$sum['dns_queries_today'] = number_format( $sum['dns_queries_today']);
$sum['ads_percentage_today'] = number_format( $sum['ads_percentage_today'], 1, '.', '');
$sum['domains_being_blocked'] = number_format( $sum['domains_being_blocked']);
$data = array_merge($data, $sum);
}
if (isset($_GET['overTimeData'])) {
$data = array_merge($data, getOverTimeData());
}
if (isset($_GET['overTimeData10mins'])) {
$data = array_merge($data, getOverTimeData10mins());
}
// Auth Required
if (isset($_GET['topItems']) && $auth) {
$data = array_merge($data, getTopItems($_GET['topItems']));
}
if (isset($_GET['recentItems']) && $auth) {
if (is_numeric($_GET['recentItems'])) {
$data = array_merge($data, getRecentItems($_GET['recentItems']));
}
}
if (isset($_GET['getQueryTypes']) && $auth) {
$data = array_merge($data, getIpvType());
}
if (isset($_GET['getForwardDestinations']) && $auth) {
$data = array_merge($data, getForwardDestinations());
}
if (isset($_GET['getQuerySources']) && $auth) {
$data = array_merge($data, getQuerySources());
}
if (isset($_GET['getAllQueries']) && $auth) {
$data = array_merge($data, getAllQueries($_GET['getAllQueries']));
}
if (isset($_GET['getGravityDomains'])) {
$data = array_merge($data, getGravity());
}
function filterArray(&$inArray) {
$outArray = array();
foreach ($inArray as $key=>$value) {
if (is_array($value)) {
$outArray[htmlspecialchars($key)] = filterArray($value);
} else {
$outArray[htmlspecialchars($key)] = !is_numeric($value) ? htmlspecialchars($value) : $value;
}
}
return $outArray;
}
$data = filterArray($data);
?>
+74 -21
View File
@@ -16,6 +16,37 @@ check_cors();
ini_set("max_execution_time","600");
$data = array();
$clients = array();
function resolveHostname($clientip, $printIP)
{
global $clients;
$ipaddr = strtolower($clientip);
if(array_key_exists($clientip, $clients))
{
// Entry already exists
$clientname = $clients[$ipaddr];
if($printIP)
return $clientname."|".$clientip;
return $clientname;
}
else if(filter_var($clientip, FILTER_VALIDATE_IP))
{
// Get host name of client and convert to lower case
$clientname = strtolower(gethostbyaddr($ipaddr));
}
else
{
// This is already a host name
$clientname = $ipaddr;
}
// Buffer result
$clients[$ipaddr] = $clientname;
if($printIP)
return $clientname."|".$clientip;
return $clientname;
}
// Get posible non-standard location of FTL's database
$FTLsettings = parse_ini_file("/etc/pihole/pihole-FTL.conf");
@@ -77,7 +108,8 @@ if (isset($_GET['getAllQueries']) && $auth)
if(!is_bool($results))
while ($row = $results->fetchArray())
{
$allQueries[] = [$row[0],$row[1] == 1 ? "IPv4" : "IPv6",$row[2],$row[3],$row[4]];
$c = resolveHostname($row[3],false);
$allQueries[] = [$row[0],$row[1] == 1 ? "IPv4" : "IPv6",$row[2],$c,$row[4]];
}
}
$result = array('data' => $allQueries);
@@ -105,32 +137,33 @@ if (isset($_GET['topClients']) && $auth)
$stmt->bindValue(":until", intval($_GET['until']), SQLITE3_INTEGER);
$results = $stmt->execute();
$clients = array();
$clientnums = array();
if(!is_bool($results))
while ($row = $results->fetchArray())
{
// Convert client to lower case
$c = strtolower($row[0]);
if(array_key_exists($c, $clients))
$c = resolveHostname($row[0],false);
if(array_key_exists($c, $clientnums))
{
// Entry already exists, add to it (might appear multiple times due to mixed capitalization in the database)
$clients[$c] += intval($row[1]);
$clientnums[$c] += intval($row[1]);
}
else
{
// Entry does not yet exist
$clients[$c] = intval($row[1]);
$clientnums[$c] = intval($row[1]);
}
}
// Sort by number of hits
arsort($clients);
arsort($clientnums);
// Extract only the first ten entries
$clients = array_slice($clients, 0, 10);
$clientnums = array_slice($clientnums, 0, 10);
$result = array('top_sources' => $clients);
$result = array('top_sources' => $clientnums);
$data = array_merge($data, $result);
}
@@ -292,13 +325,38 @@ if (isset($_GET['getGraphData']) && $auth)
$stmt->bindValue(":interval", $interval, SQLITE3_INTEGER);
$results = $stmt->execute();
$domains = array();
// Parse the DB result into graph data, filling in missing sections with zero
function parseDBData($results, $interval) {
$data = array();
$min = null;
$max = null;
if(!is_bool($results))
while ($row = $results->fetchArray())
{
$domains[$row[0]] = intval($row[1]);
if(!is_bool($results)) {
// Read in the data
while($row = $results->fetchArray()) {
// Get min and max timestamps
if($min === null || $min > $row[0])
$min = $row[0];
if($max === null || $max < $row[0])
$max = $row[0];
// Get the non-zero graph data
$data[$row[0]] = intval($row[1]);
}
// Fill the missing intervals with zero
for($i = $min; $i < $max; $i += $interval) {
if(!array_key_exists($i, $data))
$data[$i] = 0;
}
}
return $data;
}
$domains = parseDBData($results, $interval);
$result = array('domains_over_time' => $domains);
$data = array_merge($data, $result);
@@ -309,13 +367,8 @@ if (isset($_GET['getGraphData']) && $auth)
$stmt->bindValue(":interval", $interval, SQLITE3_INTEGER);
$results = $stmt->execute();
$addomains = array();
$addomains = parseDBData($results, $interval);
if(!is_bool($results))
while ($row = $results->fetchArray())
{
$addomains[$row[0]] = intval($row[1]);
}
$result = array('ads_over_time' => $addomains);
$data = array_merge($data, $result);
}
+2 -2
View File
@@ -9,7 +9,7 @@
?>
<!-- Title -->
<div class="page-header">
<h1>Update list of ad-serving domains / blacklist / whitelist</h1>
<h1>Update Gravity (list of blocked domains)</h1>
</div>
<!-- Alerts -->
@@ -22,7 +22,7 @@
Success!
</div>
<button class="btn btn-lg btn-primary btn-block" id="gravityBtn">Update Lists</button>
<button class="btn btn-lg btn-primary btn-block" id="gravityBtn">Update</button>
<pre id="output" style="width: 100%; height: 100%;" hidden="true"></pre>
<?php
+18 -24
View File
@@ -70,7 +70,7 @@
<div class="col-md-12">
<h2>White- / Blacklist</h2>
<p>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.</p>
<p>Wildcard blacklisting is supported (entering <samp>something.de</samp> will block this domain including all subdomains like <samp>a.bb.c.999.something.de</samp>). Note that wildcard whitelisting is <em>not</em> supported.</p>
<p>Regex blacklisting is supported (entering <samp>^example</samp> will block any domain starting with <samp>example</samp>, see also our <a href="https://docs.pi-hole.net/ftldns/regex/" target="_blank">Regex documentation</a>). You can still whitelist specific domains even if they fall under a regex pattern.</p>
<p>You can white-/blacklist multiple entries at a time if you separate the domains by spaces.</p>
</div>
</div>
@@ -82,14 +82,14 @@
</div>
<div class="row">
<div class="col-md-12">
<h2>Tools &rarr; Update Lists</h2>
<p>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).</p>
<h2>Tools &rarr; Update Gravity</h2>
<p>Will download any updates from the third-party blocklists that we source. By default, this command runs once a week via cron (Sunday).</p>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h2>Tools &rarr; Query adlists</h2>
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 <samp>apple.com</samp> and <samp>microsoft.com</samp> to their block list.</p>
<h2>Tools &rarr; Query Lists</h2>
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 blocklists, 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 <samp>apple.com</samp> and <samp>microsoft.com</samp> to their block list.</p>
</div>
</div>
<div class="row">
@@ -102,26 +102,20 @@
<div class="col-md-12">
<h2>Settings</h2>
Change settings for the Pi-hole
<h4>Networking</h4>
Displays information about the interfaces of the Pi-hole. No changes possible.
<h4>Pi-hole DHCP Server</h4>
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.
<h4>Upstream DNS Servers</h4>
<h4>System</h4>
Displays network and other system information of Pi-hole. At the bottom there is a "Danger Zone" with actions such as disable query logging and reboot.
<h4>Blocklists</h4>
View and edit the blocklists sourced for blocked domains.
<h4>DNS</h4>
Customize used upstream DNS servers + advanced settings for DNS servers. Note that any number of DNS servers may be enabled at a time.
<h4>Query Logging</h4>
Enabled/disable query logging on your Pi-hole + provide option to flush the log
<h4>API</h4>
Change settings which apply to the API as well as the web UI
<ul>
<li>Show permitted domain entries: Toogle permitted queries in Query Log + Top Domains on Main Page</li>
<li>Show blockes domain entries: Toogle blocked queries in Query Log + Top Ads on Main Page</li>
<li>Privacy mode: Replace IPs in query log with "hidden"</li>
</ul>
<h4>Web User Interface</h4>
Other settings which affect the webUI but not the API of Pi-hole
<h4>System Administration</h4>
Apply system-wide actions like restarting of the server
</div>
<h4>DHCP</h4>
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.
<h4>API / Web Interface</h4>
Change settings which apply to the API as well as the web interface
<h4>Privacy</h4>
Set the privacy level for queries. Note that decreasing the privacy level will not disclose previously hidden query data.
<h4>Teleporter</h4>
Import and export Pi-hole settings.
</div>
<div class="row">
<div class="col-md-12">
+50 -30
View File
@@ -7,12 +7,13 @@
* Please see LICENSE file for your rights under this license. */
$indexpage = true;
require "scripts/pi-hole/php/header.php";
require_once("scripts/pi-hole/php/gravity.php");
?>
<!-- Small boxes (Stat box) -->
<div class="row">
<div class="col-lg-3 col-xs-12">
<!-- small box -->
<div class="small-box bg-green">
<div class="small-box bg-green" id="total_queries" title="only A + AAAA queries">
<div class="inner">
<p>Total queries (<span id="unique_clients">-</span> clients)</p>
<h3 class="statistic"><span id="dns_queries_today">---</span></h3>
@@ -48,27 +49,10 @@
</div>
</div>
</div>
<?php
$gravitylist = "/etc/pihole/gravity.list";
if (file_exists($gravitylist))
{
$gravitydiff = date_diff(date_create("@".filemtime($gravitylist)),date_create("now"));
if($gravitydiff->d > 1)
$gravitydate = $gravitydiff->format("Blocking list updated %a days, %H:%I ago");
elseif($gravitydiff->d == 1)
$gravitydate = $gravitydiff->format("Blocking list updated one day, %H:%I ago");
else
$gravitydate = $gravitydiff->format("Blocking list updated %H:%I ago");
}
else
{
$gravitydate = "Blocking list not found";
}
?>
<!-- ./col -->
<div class="col-lg-3 col-xs-12">
<!-- small box -->
<div class="small-box bg-red" title="<?php echo $gravitydate; ?>">
<div class="small-box bg-red" title="<?php echo gravity_last_update(); ?>">
<div class="inner">
<p>Domains on Blocklist</p>
<h3 class="statistic"><span id="domains_being_blocked">---</span></h3>
@@ -114,7 +98,7 @@ else
</div>
<div class="box-body">
<div class="chart">
<canvas id="clientsChart" width="800" height="140"></canvas>
<canvas id="clientsChart" width="800" height="140" class="extratooltipcanvas"></canvas>
</div>
</div>
<div class="overlay">
@@ -129,11 +113,14 @@ else
<div class="col-md-12 col-lg-6">
<div class="box" id="query-types-pie">
<div class="box-header with-border">
<h3 class="box-title">Query Types (integrated)</h3>
<h3 class="box-title">Query Types</h3>
</div>
<div class="box-body">
<div class="chart">
<canvas id="queryTypePieChart" width="400" height="150"></canvas>
<div class="float-left" style="width:50%">
<canvas id="queryTypePieChart" width="120" height="120"></canvas>
</div>
<div class="float-left" style="width:50%">
<div id="query-types-legend" class="chart-legend"></div>
</div>
</div>
<div class="overlay">
@@ -145,11 +132,14 @@ else
<div class="col-md-12 col-lg-6">
<div class="box" id="forward-destinations-pie">
<div class="box-header with-border">
<h3 class="box-title">Forward Destinations (integrated)</h3>
<h3 class="box-title">Queries answered by</h3>
</div>
<div class="box-body">
<div class="chart">
<canvas id="forwardDestinationPieChart" width="400" height="150"></canvas>
<div class="float-left" style="width:50%">
<canvas id="forwardDestinationPieChart" width="120" height="120"></canvas>
</div>
<div class="float-left" style="width:50%">
<div id="forward-destinations-legend" class="chart-legend"></div>
</div>
</div>
<div class="overlay">
@@ -223,18 +213,18 @@ else
<?php
if($boxedlayout)
{
$tablelayout = "col-md-6";
$tablelayout = "col-md-6";
}
else
{
$tablelayout = "col-md-6 col-lg-4";
$tablelayout = "col-md-6 col-lg-6";
}
?>
<div class="row">
<div class="<?php echo $tablelayout; ?>">
<div class="box" id="domain-frequency">
<div class="box-header with-border">
<h3 class="box-title">Top Domains</h3>
<h3 class="box-title">Top Permitted Domains</h3>
</div>
<!-- /.box-header -->
<div class="box-body">
@@ -284,11 +274,41 @@ else
</div>
<!-- /.box -->
</div>
</div>
<div class="row">
<!-- /.col -->
<div class="<?php echo $tablelayout; ?>">
<div class="box" id="client-frequency">
<div class="box-header with-border">
<h3 class="box-title">Top Clients</h3>
<h3 class="box-title">Top Clients (total)</h3>
</div>
<!-- /.box-header -->
<div class="box-body">
<div class="table-responsive">
<table class="table table-bordered">
<tbody>
<tr>
<th>Client</th>
<th>Requests</th>
<th>Frequency</th>
</tr>
</tbody>
</table>
</div>
</div>
<div class="overlay">
<i class="fa fa-refresh fa-spin"></i>
</div>
<!-- /.box-body -->
</div>
<!-- /.box -->
</div>
<!-- /.col -->
<!-- /.col -->
<div class="<?php echo $tablelayout; ?>">
<div class="box" id="client-frequency-blocked">
<div class="box-header with-border">
<h3 class="box-title">Top Clients (blocked only)</h3>
</div>
<!-- /.box-header -->
<div class="box-body">
+4 -8
View File
@@ -1,4 +1,4 @@
<?php /*
<?php /*
* Pi-hole: A black hole for Internet advertisements
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
@@ -30,7 +30,6 @@ function getFullName() {
<div class="page-header">
<h1><?php getFullName(); ?></h1>
</div>
<?php if($list == "white"){ ?><p>Note that the ad list domains are automatically added to the whitelist so that a list can never get blocked by another list.</p><?php } ?>
<!-- Domain Input -->
<div class="form-group input-group">
@@ -39,16 +38,13 @@ function getFullName() {
<?php if($list === "black") { ?>
<button id="btnAdd" class="btn btn-default" type="button">Add (exact)</button>
<button id="btnAddWildcard" class="btn btn-default" type="button">Add (wildcard)</button>
<button id="btnAddRegex" class="btn btn-default" type="button">Add (regex)</button>
<?php }else{ ?>
<button id="btnAdd" class="btn btn-default" type="button">Add</button>
<?php } ?>
<button id="btnRefresh" class="btn btn-default" type="button"><i class="fa fa-refresh"></i></button>
</span>
</div>
<?php if($list === "white") { ?>
<p>Note: Whitelisting a subdomain of a wildcard blocked domain is not possible.</p>
<p>Some of the domains shown below are domains of the adlists sources, which are automatically added in order to prevent adlists being able to blacklist each other. See <a href="https://github.com/pi-hole/pi-hole/blob/master/adlists.default" target="_blank">here</a> for the default set of adlists.</p>
<?php } ?>
<!-- Alerts -->
<div id="alInfo" class="alert alert-info alert-dismissible fade in" role="alert" hidden="true">
@@ -71,8 +67,8 @@ function getFullName() {
<?php } ?>
<ul class="list-group" id="list"></ul>
<?php if($list === "black") { ?>
<h3>Wildcard blocking</h3>
<ul class="list-group" id="list-wildcard"></ul>
<h3><a href="https://docs.pi-hole.net/ftldns/regex/overview/" target="_blank" title="Click for Pi-hole Regex documentation">Regex</a> &amp; Wildcard blocking</h3>
<ul class="list-group" id="list-regex"></ul>
<?php } ?>
<?php
+17 -13
View File
@@ -19,47 +19,49 @@ if(isset($setupVars["API_QUERY_LOG_SHOW"]))
{
if($setupVars["API_QUERY_LOG_SHOW"] === "all")
{
$showing = "showing all queries";
$showing = "showing";
}
elseif($setupVars["API_QUERY_LOG_SHOW"] === "permittedonly")
{
$showing = "showing permitted queries only";
$showing = "showing permitted";
}
elseif($setupVars["API_QUERY_LOG_SHOW"] === "blockedonly")
{
$showing = "showing blocked queries only";
$showing = "showing blocked";
}
elseif($setupVars["API_QUERY_LOG_SHOW"] === "nothing")
{
$showing = "showing no queries at all";
$showing = "showing no queries (due to setting)";
}
}
else
{
// If filter variable is not set, we
// automatically show all queries
$showing = "showing all queries";
$showing = "showing";
}
$showall = false;
if(isset($_GET["all"]))
{
$showing .= " within the Pi-hole log";
$showing .= " all queries within the Pi-hole log";
}
else if(isset($_GET["client"]))
{
$showing .= " for client ".htmlentities($_GET["client"]);
$showing .= " queries for client ".htmlentities($_GET["client"]);
}
else if(isset($_GET["domain"]))
{
$showing .= " for domain ".htmlentities($_GET["domain"]);
$showing .= " queries for domain ".htmlentities($_GET["domain"]);
}
else if(isset($_GET["from"]) && isset($_GET["until"]))
else if(isset($_GET["from"]) || isset($_GET["until"]))
{
$showing .= " within limited time interval";
$showing .= " queries within specified time interval";
}
else
{
$showing .= " within recent 10 minutes, <a href=\"?all\">show all</a>";
$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 .= ", <a href=\"?all\">show all</a>";
}
?>
<!-- Send PHP info to JS -->
@@ -135,7 +139,7 @@ if(strlen($showing) > 0)
<th>Domain</th>
<th>Client</th>
<th>Status</th>
<th>DNSSEC</th>
<th>Reply</th>
<th>Action</th>
</tr>
</thead>
@@ -146,7 +150,7 @@ if(strlen($showing) > 0)
<th>Domain</th>
<th>Client</th>
<th>Status</th>
<th>DNSSEC</th>
<th>Reply</th>
<th>Action</th>
</tr>
</tfoot>
+1 -1
View File
@@ -9,7 +9,7 @@
?>
<!-- Title -->
<div class="page-header">
<h1>Find Ad Domain In Lists</h1>
<h1>Find Blocked Domain In Lists</h1>
</div>
<!-- Domain Input -->
<div class="form-group input-group">
+4 -4
View File
@@ -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() {
+200 -19
View File
@@ -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("<table></table>");
}
// 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 = "<table><thead>";
titleLines.forEach(function(title) {
innerHtml += "<tr><th>" + title + "</th></tr>";
});
innerHtml += "</thead><tbody>";
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 = "<span class=\"chartjs-tooltip-key\" style=\"" + style + "\"></span>";
var num = body[0].split(": ");
if(num[1] > 0)
{
innerHtml += "<tr><td>" + span + body + "</td></tr>";
printed++;
}
});
if(printed < 1)
{
innerHtml += "<tr><td>No activity recorded</td></tr>";
}
innerHtml += "</tbody></table>";
$(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 = "<a href=\"queries.php?client="+clientip+"\" title=\""+clientip+"\">"+clientname+"</a>";
url = "<a href=\"queries.php?client="+clientip+"\" title=\""+clientip+"\">"+clientname+"</a>";
percentage = data.top_sources[client] / data.dns_queries_today * 100;
clienttable.append("<tr> <td>" + url +
"</td> <td>" + data.top_sources[client] + "</td> <td> <div class=\"progress progress-sm\" title=\""+percentage.toFixed(1)+"% of " + data.dns_queries_today + "\"> <div class=\"progress-bar progress-bar-blue\" style=\"width: " +
percentage + "%\"></div> </div> </td> </tr> ");
}
}
// 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 = "<a href=\"queries.php?client="+clientip+"\" title=\""+clientip+"\">"+clientname+"</a>";
percentage = data.top_sources_blocked[client] / data.ads_blocked_today * 100;
clientblockedtable.append("<tr> <td>" + url +
"</td> <td>" + data.top_sources_blocked[client] + "</td> <td> <div class=\"progress progress-sm\" title=\""+percentage.toFixed(1)+"% of " + data.dns_queries_today + "\"> <div class=\"progress-bar progress-bar-blue\" style=\"width: " +
percentage + "%\"></div> </div> </td> </tr> ");
}
}
// 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,
+44 -33
View File
@@ -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("<div class=\"alert alert-info\" role=\"alert\">Your " + fullName + " is empty!</div>");
}
}
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(
"<li id=\"" + index + "\" class=\"list-group-item clearfix\">" + entry +
"<button class=\"btn btn-danger btn-xs pull-right\" type=\"button\">" +
"<span class=\"glyphicon glyphicon-trash\"></span></button></li>");
// 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(
"<li id=\"" + index + "\" class=\"list-group-item clearfix\">" + entry +
"<button class=\"btn btn-danger btn-xs pull-right\" type=\"button\">" +
"<span class=\"glyphicon glyphicon-trash\"></span></button></li>");
// 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(
"<li id=\"" + index + "\" class=\"list-group-item clearfix\">" + entry +
"<button class=\"btn btn-danger btn-xs pull-right\" type=\"button\">" +
"<span class=\"glyphicon glyphicon-trash\"></span></button></li>");
// 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);
});
+91 -37
View File
@@ -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 = "<br><span style=\"color:green\">SECURE</span>";
}
else if (data[5] === "2")
{
dnssec_status = "<br><span style=\"color:orange\">INSECURE</span>";
}
else if (data[5] === "3")
{
dnssec_status = "<br><span style=\"color:red\">BOGUS</span>";
}
else if (data[5] === "4")
{
dnssec_status = "<br><span style=\"color:red\">ABANDONED</span>";
}
else if (data[5] === "5")
{
dnssec_status = "<br><span style=\"color:red\">?</span>";
}
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( "<button style=\"color:green; white-space: nowrap;\"><i class=\"fa fa-pencil-square-o\"></i> Whitelist</button>" );
}
else if (data[4] === "2")
{
$(row).css("color","green");
$("td:eq(4)", row).html( "OK <br class='hidden-lg'>(forwarded)" );
$("td:eq(4)", row).html( "OK <br class='hidden-lg'>(forwarded)"+dnssec_status );
$("td:eq(6)", row).html( "<button style=\"color:red; white-space: nowrap;\"><i class=\"fa fa-ban\"></i> Blacklist</button>" );
}
else if (data[4] === "3")
{
$(row).css("color","green");
$("td:eq(4)", row).html( "OK <br class='hidden-lg'>(cached)" );
$("td:eq(4)", row).html( "OK <br class='hidden-lg'>(cached)"+dnssec_status );
$("td:eq(6)", row).html( "<button style=\"color:red; white-space: nowrap;\"><i class=\"fa fa-ban\"></i> Blacklist</button>" );
}
else if (data[4] === "4")
{
blocked = true;
$(row).css("color","red");
$("td:eq(4)", row).html( "Pi-holed <br class='hidden-lg'>(wildcard)" );
$("td:eq(4)", row).html( "Pi-holed <br class='hidden-lg'>(wildcard)");
$("td:eq(6)", row).html( "" );
}
else if (data[4] === "5")
{
blocked = true;
$(row).css("color","red");
$("td:eq(4)", row).html( "Pi-holed <br class='hidden-lg'>(blacklist)" );
$("td:eq(6)", row).html( "<button style=\"color:green; white-space: nowrap;\"><i class=\"fa fa-pencil-square-o\"></i> Whitelist</button>" );
@@ -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 [<br class='hidden-lg'>]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,
+4 -9
View File
@@ -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(); });
}
});
+6 -5
View File
@@ -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);
}
+6 -5
View File
@@ -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);
}
+10 -94
View File
@@ -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);
}
?>
+8 -7
View File
@@ -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;
+3
View File
@@ -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
{
+10 -1
View File
@@ -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();
}
}
?>
-822
View File
@@ -1,822 +0,0 @@
<?php
/* Pi-hole: A black hole for Internet advertisements
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
$log = array();
$setupVars = parse_ini_file("/etc/pihole/setupVars.conf");
$hosts = file_exists("/etc/hosts") ? file("/etc/hosts") : array();
// Check if pihole.log exists and is readable
$logListName = checkfile("/var/log/pihole.log");
$log = new \SplFileObject($logListName);
// Check if preEventHorizon exists and is readable
$gravityListName = checkfile("/etc/pihole/list.preEventHorizon");
$gravity = new \SplFileObject($gravityListName);
// whitelist.txt is optional and might not be there
$whiteListFile = checkfile("/etc/pihole/whitelist.txt");
$whitelist = new \SplFileObject($whiteListFile);
// blacklist.txt is optional and might not be there
$blackListFile = checkfile("/etc/pihole/blacklist.txt");
$blacklist = new \SplFileObject($blackListFile);
if(isset($setupVars["API_PRIVACY_MODE"]))
{
$privacyMode = $setupVars["API_PRIVACY_MODE"];
}
else
{
$privacyMode = false;
}
// Check if time zone is set
// https://github.com/pi-hole/AdminLTE/pull/394
if (!date_default_timezone_get("date.timezone")) {
date_default_timezone_set("UTC");
}
/******* Public Members ********/
function getSummaryData() {
$domains_being_blocked = gravityCount();
$dns_queries_today = countDnsQueries();
$ads_blocked_today = countBlockedQueries();
$ads_percentage_today = $dns_queries_today > 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;
}
?>
+17
View File
@@ -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}<br>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");
}
}
?>
+21 -30
View File
@@ -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;
}
+51
View File
@@ -0,0 +1,51 @@
<?php
/* Pi-hole: A black hole for Internet advertisements
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
function gravity_last_update($raw = false){
/*
@walter-exit <walter@exclusive-it.nl> | 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;
}
?>
+7 -10
View File
@@ -167,6 +167,7 @@
<title>Pi-hole Admin Console</title>
<!-- 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">
<!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<link rel="shortcut icon" href="img/favicon.png" type="image/x-icon" />
@@ -188,10 +189,6 @@
<link href="style/vendor/skin-blue.min.css" rel="stylesheet" type="text/css" />
<link href="style/pi-hole.css" rel="stylesheet" type="text/css" />
<link rel="icon" type="image/png" sizes="160x160" href="img/logo.svg" />
<style type="text/css">
.glow { text-shadow: 0px 0px 5px #fff; }
.small-box span { transition-duration: 500ms }
</style>
<!--[if lt IE 9]>
<script src="scripts/vendor/html5shiv.min.js"></script>
@@ -445,7 +442,7 @@ if($auth) {
<!-- Whitelist -->
<li<?php if($scriptname === "whitelist"){ ?> class="active"<?php } ?>>
<a href="list.php?l=white">
<i class="fa fa-pencil-square-o"></i> <span>Whitelist</span>
<i class="fa fa-check-circle-o "></i> <span>Whitelist</span>
</a>
</li>
<!-- Blacklist -->
@@ -461,7 +458,7 @@ if($auth) {
<span class="pull-right-container">
<i class="fa fa-angle-down pull-right" style="padding-right: 5px;"></i>
</span>
<i class="fa fa-stop"></i> <span>Disable</span>&nbsp;&nbsp;&nbsp;<span id="flip-status-disable"></span>
<i class="fa fa-stop"></i> <span>Disable&nbsp;&nbsp;&nbsp;<span id="flip-status-disable"></span></span>
</a>
<ul class="treeview-menu">
<li>
@@ -493,7 +490,7 @@ if($auth) {
<!-- <a href="#" id="flip-status"><i class="fa fa-stop"></i> <span>Disable</span></a> -->
</li>
<li id="pihole-enable" class="treeview"<?php if ($pistatus == "1") { ?> hidden="true"<?php } ?>>
<a href="#"><i class="fa fa-play"></i> <span id="enableLabel">Enable</span>&nbsp;&nbsp;&nbsp;<span id="flip-status-enable"></span></a>
<a href="#"><i class="fa fa-play"></i> <span id="enableLabel">Enable&nbsp;&nbsp;&nbsp;<span id="flip-status-enable"></span></span></a>
</li>
<!-- Tools -->
<li class="treeview <?php if(in_array($scriptname, array("gravity.php", "queryads.php", "auditlog.php", "taillog.php", "taillog-FTL.php", "debug.php"))){ ?>active<?php } ?>">
@@ -507,13 +504,13 @@ if($auth) {
<!-- Run gravity.sh -->
<li<?php if($scriptname === "gravity.php"){ ?> class="active"<?php } ?>>
<a href="gravity.php">
<i class="fa fa-arrow-circle-down"></i> <span>Update Lists</span>
<i class="fa fa-arrow-circle-down"></i> <span>Update Gravity</span>
</a>
</li>
<!-- Query adlists -->
<!-- Query Lists -->
<li<?php if($scriptname === "queryads.php"){ ?> class="active"<?php } ?>>
<a href="queryads.php">
<i class="fa fa-search"></i> <span>Query adlists</span>
<i class="fa fa-search"></i> <span>Query Lists</span>
</a>
</li>
<!-- Audit log -->
+3
View File
@@ -35,6 +35,9 @@
</ul>
</div>
<div class="col-xs-4">
<div class="form-group">
<div class="checkbox pull-right"><label><input type="checkbox" id="logincookie" name="persistentlogin">Remember me for 7 days</label></div>
</div>
<button type="submit" href="#" class="btn btn-primary pull-right"><i class="glyphicon glyphicon-log-in"></i>&nbsp;&nbsp;&nbsp;Log in</button>
</div>
</div>
+26 -1
View File
@@ -24,9 +24,13 @@
}
// If the user wants to log out, we free all session variables currently registered
// and delete any persistent cookie.
if(isset($_GET["logout"]))
{
session_unset();
setcookie('persistentlogin', '');
header('Location: index.php');
exit();
}
$wrongpassword = false;
@@ -35,8 +39,24 @@
// Test if password is set
if(strlen($pwhash) > 0)
{
// Check for and authorize from persistent cookie
if (isset($_COOKIE["persistentlogin"]))
{
if ($pwhash === $_COOKIE["persistentlogin"])
{
$auth = true;
// Refresh cookie with new expiry
setcookie('persistentlogin', $pwhash, time()+60*60*24*7);
}
else
{
// Invalid cookie
$auth = false;
setcookie('persistentlogin', '');
}
}
// Compare doubly hashes password input with saved hash
if(isset($_POST["pw"]))
else if(isset($_POST["pw"]))
{
$postinput = hash('sha256',hash('sha256',$_POST["pw"]));
if(hash_equals($pwhash, $postinput))
@@ -45,6 +65,11 @@
// Login successful, redirect the user to the homepage to discard the POST request
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $_SERVER['QUERY_STRING'] === 'login') {
// Set persistent cookie if selected
if (isset($_POST['persistentlogin']))
{
setcookie('persistentlogin', $pwhash, time()+60*60*24*7);
}
header('Location: index.php');
exit();
}
+1 -1
View File
@@ -58,7 +58,7 @@ else
$exact = "";
}
$proc = popen("sudo pihole -q ".$url." ".$exact, 'r');
$proc = popen("sudo pihole -q -adlist ".$url." ".$exact, 'r');
while (!feof($proc)) {
echoEvent(fread($proc, 4096));
}
+69 -7
View File
@@ -123,7 +123,8 @@ function isinserverlist($addr) {
"Norton" => ["v4_1" => "199.85.126.10", "v4_2" => "199.85.127.10"],
"Comodo" => ["v4_1" => "8.26.56.26", "v4_2" => "8.20.247.20"],
"DNS.WATCH" => ["v4_1" => "84.200.69.80", "v4_2" => "84.200.70.40", "v6_1" => "2001:1608:10:25:0:0:1c04:b12f", "v6_2" => "2001:1608:10:25:0:0:9249:d69b"],
"Quad9" => ["v4_1" => "9.9.9.9", "v4_2" => "149.112.112.112", "v6_1" => "2620:fe::fe"]
"Quad9" => ["v4_1" => "9.9.9.9", "v4_2" => "149.112.112.112", "v6_1" => "2620:fe::fe"],
"Cloudflare" => ["v4_1" => "1.1.1.1", "v4_2" => "1.0.0.1", "v6_1" => "2606:4700:4700::1111", "v6_2" => "2606:4700:4700::1001"]
];
$adlist = [];
@@ -186,20 +187,34 @@ function readAdlists()
{
if(array_key_exists("custom".$i,$_POST))
{
$IP = $_POST["custom".$i."val"];
if(validIP($IP))
$exploded = explode("#", $_POST["custom".$i."val"], 2);
$IP = $exploded[0];
if(count($exploded) > 1)
{
array_push($DNSservers,$IP);
$port = $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);
}
}
}
$DNSservercount = count($DNSservers);
// Check if at least one DNS server has been added
if(count($DNSservers) < 1)
if($DNSservercount < 1)
{
$error .= "No DNS server has been selected.<br>";
}
@@ -234,6 +249,28 @@ function readAdlists()
$extra .= "no-dnssec";
}
// Check if Conditional Forwarding is requested
if(isset($_POST["conditionalForwarding"]))
{
// Validate conditional forwarding IP
if (!validIP($_POST["conditionalForwardingIP"]))
{
$error .= "Conditional forwarding IP (".htmlspecialchars($_POST["conditionalForwardingIP"]).") is invalid!<br>";
}
// Validate conditional forwarding domain name
if(!validDomain($_POST["conditionalForwardingDomain"]))
{
$error .= "Conditional forwarding domain name (".htmlspecialchars($_POST["conditionalForwardingDomain"]).") is invalid!<br>";
}
if(!$error)
{
$addressArray = explode(".", $_POST["conditionalForwardingIP"]);
$reverseAddress = $addressArray[2].".".$addressArray[1].".".$addressArray[0].".in-addr.arpa";
$extra .= " conditional_forwarding ".$_POST["conditionalForwardingIP"]." ".$_POST["conditionalForwardingDomain"]." $reverseAddress";
}
}
// Check if DNSinterface is set
if(isset($_POST["DNSinterface"]))
{
@@ -261,9 +298,9 @@ function readAdlists()
if(!strlen($error))
{
$IPs = implode (",", $DNSservers);
$return = exec("sudo pihole -a setdns ".$IPs." ".$extra);
$return = exec("sudo pihole -a setdns \"".$IPs."\" ".$extra);
$success .= htmlspecialchars($return)."<br>";
$success .= "The DNS settings have been updated (using ".count($DNSservers)." DNS servers)";
$success .= "The DNS settings have been updated (using ".$DNSservercount." DNS servers)";
}
else
{
@@ -415,6 +452,19 @@ function readAdlists()
{
exec('sudo pihole -a -c');
}
$adminemail = trim($_POST["adminemail"]);
if(strlen($adminemail) == 0 || !isset($adminemail))
{
$adminemail = 'noadminemail';
}
elseif(!filter_var($adminemail, FILTER_VALIDATE_EMAIL))
{
$error .= "Administrator email address (".htmlspecialchars($adminemail).") is invalid!<br>";
}
else
{
exec('sudo pihole -a -e '.$adminemail);
}
if(isset($_POST["boxedlayout"]))
{
exec('sudo pihole -a layout boxed');
@@ -612,7 +662,19 @@ function readAdlists()
// Reread available adlists
$adlist = readAdlists();
break;
case "privacyLevel":
$level = intval($_POST["privacylevel"]);
if($level >= 0 && $level <= 3)
{
exec("sudo pihole -a privacylevel ".$level);
$success .= "The privacy level has been updated";
}
else
{
$error .= "Invalid privacy level (".$level.")!";
}
break;
default:
+24 -2
View File
@@ -21,8 +21,30 @@ switch($type) {
case "black":
exec("sudo pihole -b -q -d ${_POST['domain']}");
break;
case "wild":
exec("sudo pihole -wild -q -d ${_POST['domain']}");
case "regex":
if(($list = file_get_contents($regexfile)) === FALSE)
{
$err = error_get_last()["message"];
echo "Unable to read ${regexfile}<br>Error message: $err";
}
// Remove the regex and any empty lines from the list
$list = explode("\n", $list);
$list = array_diff($list, array($_POST['domain'], ""));
$list = implode("\n", $list);
if(file_put_contents($regexfile, $list) === FALSE)
{
$err = error_get_last()["message"];
echo "Unable to remove regex \"".htmlspecialchars($_POST['domain'])."\" from ${regexfile}<br>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");
}
break;
}
+33 -37
View File
@@ -14,14 +14,14 @@ if (php_sapi_name() !== "cli") {
check_csrf(isset($_POST["token"]) ? $_POST["token"] : "");
}
function archive_add_file($path,$name)
function archive_add_file($path,$name,$subdir="")
{
global $archive;
if(file_exists($path.$name))
$archive[$name] = file_get_contents($path.$name);
$archive[$subdir.$name] = file_get_contents($path.$name);
}
function archive_add_directory($path)
function archive_add_directory($path,$subdir="")
{
if($dir = opendir($path))
{
@@ -29,7 +29,7 @@ function archive_add_directory($path)
{
if($entry !== "." && $entry !== "..")
{
archive_add_file($path,$entry);
archive_add_file($path,$entry,$subdir);
}
}
closedir($dir);
@@ -43,7 +43,7 @@ function limit_length(&$item, $key)
$item = substr($item, 0, 253);
}
function process_file($contents)
function process_file($contents,$check=True)
{
$domains = array_filter(explode("\n",$contents));
@@ -51,8 +51,12 @@ function process_file($contents)
// function to every member of the array of domains
array_walk($domains, "limit_length");
// Check validity of domains (after possible clipping)
check_domains($domains);
// Check validity of domains (don't do it for regex filters)
if($check)
{
check_domains($domains);
}
return $domains;
}
@@ -66,27 +70,6 @@ function check_domains($domains)
}
}
function getWildcardListContent() {
if(file_exists("/etc/dnsmasq.d/03-pihole-wildcard.conf"))
{
$rawList = file_get_contents("/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 implode("\n",array_unique($list));
}
return "";
}
if(isset($_POST["action"]))
{
if($_FILES["zip_file"]["name"] && $_POST["action"] == "in")
@@ -125,28 +108,41 @@ if(isset($_POST["action"]))
if(isset($_POST["blacklist"]) && $file->getFilename() === "blacklist.txt")
{
$blacklist = process_file(file_get_contents($file));
echo "Processing blacklist.txt<br>\n";
echo "Processing blacklist.txt (".count($blacklist)." entries)<br>\n";
exec("sudo pihole -b -nr --nuke");
exec("sudo pihole -b -q -nr ".implode(" ", $blacklist));
$importedsomething = true;
}
if(isset($_POST["whitelist"]) && $file->getFilename() === "whitelist.txt")
{
$whitelist = process_file(file_get_contents($file));
echo "Processing whitelist.txt<br>\n";
echo "Processing whitelist.txt (".count($whitelist)." entries)<br>\n";
exec("sudo pihole -w -nr --nuke");
exec("sudo pihole -w -q -nr ".implode(" ", $whitelist));
$importedsomething = true;
}
if(isset($_POST["wildlist"]) && $file->getFilename() === "wildcardblocking.txt")
if(isset($_POST["regexlist"]) && $file->getFilename() === "regex.list")
{
$wildlist = process_file(file_get_contents($file));
echo "Processing wildcardblocking.txt<br>\n";
exec("sudo pihole -wild -nr --nuke");
exec("sudo pihole -wild -q -nr ".implode(" ", $wildlist));
$regexraw = file_get_contents($file);
$regexlist = process_file($regexraw,false);
echo "Processing regex.list (".count($regexlist)." entries)<br>\n";
// NULL = overwrite (or create) the regex filter file
add_regex($regexraw, NULL,"");
$importedsomething = true;
}
// Also try to import legacy wildcard list if found
if(isset($_POST["regexlist"]) && $file->getFilename() === "wildcardblocking.txt")
{
$wildlist = process_file(file_get_contents($file));
echo "Processing wildcardblocking.txt (".count($wildlist)." entries)<br>\n";
exec("sudo pihole --wild -nr --nuke");
exec("sudo pihole --wild -q -nr ".implode(" ", $wildlist));
$importedsomething = true;
}
if($importedsomething)
{
exec("sudo pihole restartdns");
@@ -177,9 +173,9 @@ else
archive_add_file("/etc/pihole/","adlists.list");
archive_add_file("/etc/pihole/","setupVars.conf");
archive_add_file("/etc/pihole/","auditlog.list");
archive_add_directory("/etc/dnsmasq.d/");
archive_add_file("/etc/pihole/","regex.list");
archive_add_directory("/etc/dnsmasq.d/","dnsmasq.d/");
$archive["wildcardblocking.txt"] = getWildcardListContent();
$archive->compress(Phar::GZ); // Creates a gziped copy
unlink($archive_file_name); // Unlink original tar file as it is not needed anymore
$archive_file_name .= ".gz"; // Append ".gz" extension to ".tar"
+153 -64
View File
@@ -9,34 +9,29 @@ require "scripts/pi-hole/php/header.php";
require "scripts/pi-hole/php/savesettings.php";
// Reread ini file as things might have been changed
$setupVars = parse_ini_file("/etc/pihole/setupVars.conf");
$piholeFTLConfFile = "/etc/pihole/pihole-FTL.conf";
if(is_readable($piholeFTLConfFile))
{
$piholeFTLConf = parse_ini_file($piholeFTLConfFile);
}
else
{
$piholeFTLConf = array();
}
// Handling of PHP internal errors
$last_error = error_get_last();
if($last_error["type"] === E_WARNING || $last_error["type"] === E_ERROR)
{
$error .= "There was a problem applying your settings.<br>Debugging information:<br>PHP error (".htmlspecialchars($last_error["type"])."): ".htmlspecialchars($last_error["message"])." in ".htmlspecialchars($last_error["file"]).":".htmlspecialchars($last_error["line"]);
}
?>
<style type="text/css">
.tooltip-inner {
max-width: none;
white-space: nowrap;
}
@-webkit-keyframes Pulse {
from {
color: #630030;
-webkit-text-shadow: 0 0 9px #333;
}
50% {
color: #e33100;
-webkit-text-shadow: 0 0 18px #e33100;
}
to {
color: #630030;
-webkit-text-shadow: 0 0 9px #333;
}
}
p.lookatme {
-webkit-animation-name: Pulse;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: infinite;
}
.tooltip-inner {
max-width: none;
white-space: nowrap;
}
</style>
<?php // Check if ad lists should be updated after saving ...
@@ -119,13 +114,13 @@ $i = 1;
while (isset($setupVars["PIHOLE_DNS_" . $i])) {
if (isinserverlist($setupVars["PIHOLE_DNS_" . $i])) {
array_push($DNSactive, $setupVars["PIHOLE_DNS_" . $i]);
} elseif (strpos($setupVars["PIHOLE_DNS_" . $i], ".")) {
} elseif (strpos($setupVars["PIHOLE_DNS_" . $i], ".") !== false) {
if (!isset($custom1)) {
$custom1 = $setupVars["PIHOLE_DNS_" . $i];
} else {
$custom2 = $setupVars["PIHOLE_DNS_" . $i];
}
} elseif (strpos($setupVars["PIHOLE_DNS_" . $i], ":")) {
} elseif (strpos($setupVars["PIHOLE_DNS_" . $i], ":") !== false) {
if (!isset($custom3)) {
$custom3 = $setupVars["PIHOLE_DNS_" . $i];
} else {
@@ -176,6 +171,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"];
} else {
$conditionalForwarding = false;
}
?>
<?php
@@ -223,7 +225,7 @@ if (isset($setupVars["API_PRIVACY_MODE"])) {
?>
<?php
if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists", "dns", "piholedhcp", "api", "teleporter"))) {
if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists", "dns", "piholedhcp", "api", "privacy", "teleporter"))) {
$tab = $_GET['tab'];
} else {
$tab = "sysadmin";
@@ -234,10 +236,11 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists"
<div class="nav-tabs-custom">
<ul class="nav nav-tabs">
<li<?php if($tab === "sysadmin"){ ?> class="active"<?php } ?>><a data-toggle="tab" href="#sysadmin">System</a></li>
<li<?php if($tab === "blocklists"){ ?> class="active"<?php } ?>><a data-toggle="tab" href="#blocklists">Block Lists</a></li>
<li<?php if($tab === "blocklists"){ ?> class="active"<?php } ?>><a data-toggle="tab" href="#blocklists">Blocklists</a></li>
<li<?php if($tab === "dns"){ ?> class="active"<?php } ?>><a data-toggle="tab" href="#dns">DNS</a></li>
<li<?php if($tab === "piholedhcp"){ ?> class="active"<?php } ?>><a data-toggle="tab" href="#piholedhcp">DHCP</a></li>
<li<?php if($tab === "api"){ ?> class="active"<?php } ?>><a data-toggle="tab" href="#api">API / Web interface</a></li>
<li<?php if($tab === "privacy"){ ?> class="active"<?php } ?>><a data-toggle="tab" href="#privacy">Privacy</a></li>
<li<?php if($tab === "teleporter"){ ?> class="active"<?php } ?>><a data-toggle="tab" href="#teleporter">Teleporter</a></li>
</ul>
<div class="tab-content">
@@ -248,7 +251,7 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists"
<div class="col-md-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Lists used to generate Pi-hole's Gravity</h3>
<h3 class="box-title">Blocklists used to generate Pi-hole's Gravity: <?php echo count($adlist); ?></h3>
</div>
<div class="box-body">
<div class="table-responsive">
@@ -281,7 +284,7 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists"
</table>
</div>
<div class="form-group">
<textarea name="newuserlists" class="form-control" rows="1" placeholder="Enter one URL per line to add new ad lists"></textarea>
<textarea name="newuserlists" class="form-control" rows="1" placeholder="Enter one URL per line to add new blocklists"></textarea>
</div>
<input type="hidden" name="field" value="adlists">
<input type="hidden" name="token" value="<?php echo $token ?>">
@@ -555,7 +558,7 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists"
<tr data-placement="auto" data-container="body" data-toggle="tooltip"
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"><?php echo $lease["IP"]; ?></td>
<td id="IP" data-order="<?php echo bin2hex(inet_pton($lease["IP"])); ?>"><?php echo $lease["IP"]; ?></td>
<td id="HOST"><?php echo $lease["host"]; ?></td>
<td>
<button class="btn btn-warning btn-xs" type="button" id="button" data-static="alert">
@@ -584,7 +587,7 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists"
<?php foreach ($dhcp_static_leases as $lease) { ?>
<tr>
<td><?php echo $lease["hwaddr"]; ?></td>
<td><?php echo $lease["IP"]; ?></td>
<td data-order="<?php echo bin2hex(inet_pton($lease["IP"])); ?>"><?php echo $lease["IP"]; ?></td>
<td><?php echo $lease["host"]; ?></td>
<td><?php if (strlen($lease["hwaddr"]) > 0) { ?>
<button class="btn btn-danger btn-xs" type="submit" name="removestatic"
@@ -694,7 +697,6 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists"
<?php if (isset($custom1)){ ?>checked<?php } ?>>
</div>
<input type="text" name="custom1val" class="form-control"
data-inputmask="'alias': 'ip'" data-mask
<?php if (isset($custom1)){ ?>value="<?php echo $custom1; ?>"<?php } ?>>
</div>
<label>Custom 2 (IPv4)</label>
@@ -704,7 +706,6 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists"
<?php if (isset($custom2)){ ?>checked<?php } ?>>
</div>
<input type="text" name="custom2val" class="form-control"
data-inputmask="'alias': 'ip'" data-mask
<?php if (isset($custom2)){ ?>value="<?php echo $custom2; ?>"<?php } ?>>
</div>
<label>Custom 3 (IPv6)</label>
@@ -714,7 +715,6 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists"
<?php if (isset($custom3)){ ?>checked<?php } ?>>
</div>
<input type="text" name="custom3val" class="form-control"
data-inputmask="'alias': 'ipv6'" data-mask
<?php if (isset($custom3)){ ?>value="<?php echo $custom3; ?>"<?php } ?>>
</div>
<label>Custom 4 (IPv6)</label>
@@ -724,7 +724,6 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists"
<?php if (isset($custom4)){ ?>checked<?php } ?>>
</div>
<input type="text" name="custom4val" class="form-control"
data-inputmask="'alias': 'ipv6'" data-mask
<?php if (isset($custom4)){ ?>value="<?php echo $custom4; ?>"<?php } ?>>
</div>
</div>
@@ -812,6 +811,42 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists"
DNSSEC. Note that the size of your log might increase significantly
when enabling DNSSEC. A DNSSEC resolver test can be found
<a href="http://dnssec.vs.uni-due.de/" target="_blank">here</a>.</p>
<label>Conditional Forwarding</label>
<p>If not configured as your DHCP server, Pi-hole won't 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 home router, but only for devices on your
home network. To configure this we will need to know the IP
address of your router and the name of your local network.</p>
<p>Note: The local domain name must match the domain name specified
in your router, likely found within the DHCP settings.</p>
<div class="form-group">
<div class="checkbox">
<label><input type="checkbox" name="conditionalForwarding" value="conditionalForwarding"
<?php if(isset($conditionalForwarding) && ($conditionalForwarding == true)){ ?>checked<?php }
?>>Use Conditional Forwarding</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" data-inputmask="'alias': 'ip'" data-mask
<?php if(isset($conditionalForwardingIP)){ ?>value="<?php echo $conditionalForwardingIP; ?>"<?php } ?>>
</td>
<td><input type="text" name="conditionalForwardingDomain" class="form-control" data-mask
<?php if(isset($conditionalForwardingDomain)){ ?>value="<?php echo $conditionalForwardingDomain; ?>"<?php } ?>>
</td>
</div>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
@@ -831,7 +866,13 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists"
} else {
$temperatureunit = "C";
}
// Use $boxedlayout value determined in header.php
// Administrator email address
if (isset($setupVars["ADMIN_EMAIL"])) {
$adminemail = $setupVars["ADMIN_EMAIL"];
} else {
$adminemail = "";
}
?>
<div id="api" class="tab-pane fade<?php if($tab === "api"){ ?> in active<?php } ?>">
<div class="row">
@@ -869,39 +910,19 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists"
</div>
</div>
<div class="row">
<div class="col-lg-12">
<h4>Privacy settings (Statistics / Query Log)</h4>
<div class="col-md-12">
<h4>Query Log</h4>
</div>
</div>
<div class="row">
<div class="col-lg-6">
<div class="form-group">
<div class="checkbox">
<label><input type="checkbox" name="querylog-permitted"
<?php if ($queryLog === "permittedonly" || $queryLog === "all"){ ?>checked<?php }
?>>Show permitted domain entries</label>
</div>
<div class="checkbox"><label><input type="checkbox" name="querylog-permitted" <?php if($queryLog === "permittedonly" || $queryLog === "all"){ ?>checked<?php } ?>> Show permitted domain entries</label></div>
</div>
</div>
<div class="col-lg-6">
<div class="form-group">
<div class="checkbox">
<label><input type="checkbox" name="querylog-blocked"
<?php if ($queryLog === "blockedonly" || $queryLog === "all"){ ?>checked<?php }
?>>Show blocked domain entries</label>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<h4>Privacy mode</h4>
<div class="form-group">
<div class="checkbox">
<label><input type="checkbox" name="privacyMode"
<?php if ($privacyMode){ ?>checked<?php }
?>>Don't show origin of DNS requests in query log</label>
</div>
<div class="checkbox"><label><input type="checkbox" name="querylog-blocked" <?php if($queryLog === "blockedonly" || $queryLog === "all"){ ?>checked<?php } ?>> Show blocked domain entries</label></div>
</div>
</div>
</div>
@@ -950,6 +971,13 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists"
?>>Fahrenheit</label>
</div>
</div>
<h4>Administrator Email Address</h4>
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control" name="adminemail"
value="<?php echo htmlspecialchars($adminemail); ?>">
</div>
</div>
<input type="hidden" name="field" value="webUI">
<input type="hidden" name="token" value="<?php echo $token ?>">
</div>
@@ -963,6 +991,67 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists"
</div>
</div>
</div>
<!-- ######################################################### Privacy (may be expanded further later on) ######################################################### -->
<?php
// Get privacy level from piholeFTL config array
if (isset($piholeFTLConf["PRIVACYLEVEL"])) {
$privacylevel = intval($piholeFTLConf["PRIVACYLEVEL"]);
} else {
$privacylevel = 0;
}
?>
<div id="privacy" class="tab-pane fade<?php if($tab === "privacy"){ ?> in active<?php } ?>">
<div class="row">
<div class="col-md-12">
<form role="form" method="post">
<div class="box box-warning">
<div class="box-header with-border">
<h3 class="box-title">Privacy settings</h3>
</div>
<div class="box-body">
<div class="row">
<div class="col-md-12">
<h4>DNS resolver privacy level</h4>
<p>Specify if DNS queries should be anonymized, available options are:
<div class="form-group">
<div class="radio">
<label><input type="radio" name="privacylevel" value="0"
<?php if ($privacylevel === 0){ ?>checked<?php }
?>>Show everything and record everything<br>Gives maximum amount of statistics</label>
</div>
<div class="radio">
<label><input type="radio" name="privacylevel" value="1"
<?php if ($privacylevel === 1){ ?>checked<?php }
?>>Hide domains: Display and store all domains as "hidden"<br>This disables the Top Domains and Top Ads tables on the dashboard</label>
</div>
<div class="radio">
<label><input type="radio" name="privacylevel" value="2"
<?php if ($privacylevel === 2){ ?>checked<?php }
?>>Hide domains and clients: Display and store all domains as "hidden" and all clients as "0.0.0.0"<br>This disables all tables on the dashboard</label>
</div>
<div class="radio">
<label><input type="radio" name="privacylevel" value="3"
<?php if ($privacylevel === 3){ ?>checked<?php }
?>>Paranoia mode: This disables basically everything except the live anonymous stastics<br>No history is saved at all to the database, and nothing is shown in the query log. Also, there are no top item lists.</label>
</div>
</div>
<p>The privacy level may be changed at any time without having to restart the DNS resolver. However, note that queries with (partially) hidden details cannot be disclosed with a subsequent reduction of the privacy level.</p>
<?php if($privacylevel > 0 && $piHoleLogging){ ?>
<p class="lookatme">Warning: Pi-hole's query logging is activated. Although the dashboard will hide the requested details, all queries are still fully logged to the pihole.log file.</p>
<?php } ?>
</div>
</div>
</div>
<div class="box-footer clearfix">
<input type="hidden" name="field" value="privacyLevel">
<input type="hidden" name="token" value="<?php echo $token ?>">
<button type="submit" class="btn btn-primary pull-right">Apply</button>
</div>
</div>
</form>
</div>
</div>
</div>
<!-- ######################################################### Teleporter ######################################################### -->
<div id="teleporter" class="tab-pane fade<?php if($tab === "teleporter"){ ?> in active<?php } ?>">
<div class="row">
@@ -1007,9 +1096,9 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists"
Blacklist (exact)</label>
</div>
<div class="checkbox">
<label><input type="checkbox" name="wildlist" value="true"
<label><input type="checkbox" name="regexlist" value="true"
checked>
Blacklist (wildcard)</label>
Regex filters</label>
</div>
</div>
</div>
+61 -1
View File
@@ -15,6 +15,12 @@
-o-user-select: none;
user-select: none;
}
.small-box span {
transition-duration: 500ms;
}
.small-box span.glow {
text-shadow: 0px 0px 5px #fff;
}
.skin-blue .list-group-item:hover {
background: #ddd;
}
@@ -28,7 +34,7 @@
50% {color:#e33100;text-shadow:0 0 5px #e33100;}
to {color:#630030;text-shadow:0 0 2px transparent;}
}
a.lookatme {
.lookatme {
-webkit-animation-name: Pulse;
animation-name: Pulse;
-webkit-animation-duration: 2s;
@@ -115,3 +121,57 @@ a.lookatme {
-webkit-animation: fa-spin 1s infinite linear;
animation: fa-spin 1s infinite linear;
}
.extratooltipcanvas{
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
#chartjs-tooltip {
opacity: 1;
position: absolute;
background: rgba(0, 0, 0, .7);
color: white;
border-radius: 3px;
-webkit-transition: all .1s ease;
transition: all .1s ease;
pointer-events: none;
-webkit-transform: translate(-50%, 0);
transform: translate(-50%, 0);
}
.chartjs-tooltip-key {
display: inline-block;
width: 20px;
height: 10px;
margin-right: 10px;
}
.chart-legend {
height:250px;
overflow:auto;
}
.chart-legend ul {
list-style-type: none;
}
.chart-legend li {
cursor:pointer;
}
.chart-legend li span {
display: inline-block;
width: 20px;
height: 12px;
margin-right: 5px;
}
.strike {
text-decoration: line-through !important;
}
.float-left{
float:left;
}
+1 -1
View File
@@ -13,7 +13,7 @@
</div>
<div class="checkbox"><label><input type="checkbox" name="active" checked id="chk1"> Automatic scrolling on update</label></div>
<pre id="output" style="width: 100%; height: 100%;"></pre>
<pre id="output" style="width: 100%; height: 100%; max-height:650px; overflow-y:scroll;"></pre>
<div class="checkbox"><label><input type="checkbox" name="active" checked id="chk2"> Automatic scrolling on update</label></div>
<?php
+1 -1
View File
@@ -13,7 +13,7 @@
</div>
<div class="checkbox"><label><input type="checkbox" name="active" checked id="chk1"> Automatic scrolling on update</label></div>
<pre id="output" style="width: 100%; height: 100%;"></pre>
<pre id="output" style="width: 100%; height: 100%; max-height:650px; overflow-y:scroll;"></pre>
<div class="checkbox"><label><input type="checkbox" name="active" checked id="chk2"> Automatic scrolling on update</label></div>
<?php