diff --git a/.codespellignore b/.codespellignore new file mode 100644 index 0000000000..60e61ec68d --- /dev/null +++ b/.codespellignore @@ -0,0 +1,2 @@ +ede +EDE \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..a53bf58c6c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,28 @@ +# EditorConfig is awesome: https://editorconfig.org/ + +# top-most EditorConfig file +root = true + +# A newline ending every file +[*] +insert_final_newline = true +indent_style = space +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true + +[*.css] +indent_style = space +indent_size = 2 + +[scripts/**.js] +indent_style = space +indent_size = 2 + +[package.json] +indent_style = space +indent_size = 2 + +[*.yml] +indent_style = space +indent_size = 2 diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index f693cda2fd..0000000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,32 +0,0 @@ -**By submitting this pull request, I confirm the following:** `{please fill any appropriate checkboxes, e.g: [X]}` - -`{Please ensure that your pull request is for the 'devel' branch!}` - -- [ ] I have read and understood the [contributors guide](https://github.com/pi-hole/AdminLTE/blob/master/CONTRIBUTING.md), as well as this entire template. -- [ ] I have made only one major change in my proposed changes. -- [ ] I have commented my proposed changes within the code. -- [ ] I have tested my proposed changes. -- [ ] I am willing to help maintain this change if there are issues with it later. -- [ ] I give this submission freely and claim no ownership. -- [ ] It is compatible with the [EUPL 1.2 license](https://opensource.org/licenses/EUPL-1.1) -- [ ] I have squashed any insignificant commits. ([`git rebase`](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html)) -- [ ] I have Signed Off all commits. ([`git commit --signoff`](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---signoff)) - ---- - -**What does this PR aim to accomplish?:** - -`{A detailed description, screenshots (if necessary), as well as links to any relevant GitHub issues}` - -**How does this PR accomplish the above?:** - -`{A detailed description (such as a changelog) and screenshots (if necessary) of the implemented fix}` - -**What documentation changes (if any) are needed to support this PR?:** - -`{A detailed list of any necessary changes}` - -> - `{Please delete this quoted section when opening your pull request}` -> - You must follow the template instructions. Failure to do so will result in your issue being closed. -> - Please respect that Pi-hole is developed by volunteers, who can only reply in their spare time. -> - Detail helps us understand an issue quicker, but please ensure it's relevant. diff --git a/.github/dco.yml b/.github/dco.yml new file mode 100644 index 0000000000..37e411e1be --- /dev/null +++ b/.github/dco.yml @@ -0,0 +1,2 @@ +require: + members: false \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 90827660f1..d9341eea93 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -21,3 +21,15 @@ updates: versions: - 10.2.3 - 10.2.4 + reviewers: + - "pi-hole/web-maintainers" +- package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly + day: saturday + time: "10:00" + open-pull-requests-limit: 10 + target-branch: devel + reviewers: + - "pi-hole/web-maintainers" diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000000..2e8776e999 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,7 @@ +changelog: + exclude: + labels: + - internal + authors: + - dependabot + - github-actions diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml new file mode 100644 index 0000000000..4bcf9c3d28 --- /dev/null +++ b/.github/workflows/codespell.yml @@ -0,0 +1,20 @@ +name: Codespell + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + +jobs: + spell-check: + if: github.event.pull_request.draft == false + runs-on: ubuntu-latest + steps: + - + name: Checkout repository + uses: actions/checkout@v3 + - + name: Spell-Checking + uses: codespell-project/actions-codespell@master + with: + ignore_words_file: .codespellignore + skip: ./scripts/vendor,./style/vendor diff --git a/.github/workflows/editorconfig-checker b/.github/workflows/editorconfig-checker new file mode 100644 index 0000000000..37b20ba3f9 --- /dev/null +++ b/.github/workflows/editorconfig-checker @@ -0,0 +1,14 @@ +name: editorconfig-checker + +on: + pull_request: + push: + +jobs: + build: + name: editorconfig-checker + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + - uses: editorconfig-checker/action-editorconfig-checker@main + - run: editorconfig-checker diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index 77560afd39..fd0765a496 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -9,6 +9,14 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - name: Change PHP version + run: | + sudo update-alternatives --set php /usr/bin/php8.0 + sudo update-alternatives --set phar /usr/bin/phar8.0 + sudo update-alternatives --set phpdbg /usr/bin/phpdbg8.0 + sudo update-alternatives --set php-cgi /usr/bin/php-cgi8.0 + sudo update-alternatives --set phar.phar /usr/bin/phar.phar8.0 + php -version - name: Validate composer.json and composer.lock run: composer validate - name: Cache Composer packages diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000000..72c8548fab --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,26 @@ +name: Mark stale issues + +on: + schedule: + - cron: '0 8 * * *' + workflow_dispatch: + +jobs: + stale: + + runs-on: ubuntu-latest + permissions: + issues: write + + steps: + - uses: actions/stale@v5 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + days-before-stale: 30 + days-before-close: 5 + stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Please comment or update this issue or it will be closed in 5 days.' + stale-issue-label: 'stale' + exempt-issue-labels: 'internal, Fixed In Next Release, Bug' + exempt-all-issue-assignees: true + operations-per-run: 300 + close-issue-reason: 'not_planned' diff --git a/.github/workflows/sync-back-to-dev.yml b/.github/workflows/sync-back-to-dev.yml new file mode 100644 index 0000000000..fecf69629c --- /dev/null +++ b/.github/workflows/sync-back-to-dev.yml @@ -0,0 +1,27 @@ +name: Sync Back to Development + +on: + push: + branches: + - master + +jobs: + sync-branches: + runs-on: ubuntu-latest + name: Syncing branches + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Opening pull request + id: pull + uses: tretuna/sync-branches@1.4.0 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + FROM_BRANCH: 'master' + TO_BRANCH: 'devel' + - name: Label the pull request to ignore for release note generation + uses: actions-ecosystem/action-add-labels@v1 + with: + labels: internal + repo: ${{ github.repository }} + number: ${{ steps.pull.outputs.PULL_REQUEST_NUMBER }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2aea7823ed..c196aede9d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,24 +10,25 @@ on: - "**" env: - CI: true + FORCE_COLOR: 2 jobs: run: - name: Node ${{ matrix.node }} + name: Node runs-on: ubuntu-latest steps: - name: Clone repository uses: actions/checkout@v2 - - name: Set Node.js version - uses: actions/setup-node@v1 + - name: Set up Node.js + uses: actions/setup-node@v2 with: - node-version: "12.x" + node-version: "16.x" + cache: npm - name: Install npm dependencies run: npm ci - name: Run tests - run: npm test + run: npm run testpr diff --git a/.stickler.yml b/.stickler.yml new file mode 100644 index 0000000000..92beabd940 --- /dev/null +++ b/.stickler.yml @@ -0,0 +1,9 @@ +--- +linters: + yamllint: + config: ./.yamllint.conf + remarklint: +files: + ignore: + - 'scripts/vendor/*' + - 'style/vendor/*' diff --git a/.user.php.ini b/.user.php.ini deleted file mode 100644 index 7660f85a01..0000000000 --- a/.user.php.ini +++ /dev/null @@ -1,2 +0,0 @@ -memory_limit = 256M -max_execution_time = 300 diff --git a/.yamllint.conf b/.yamllint.conf new file mode 100644 index 0000000000..d1b0953bdf --- /dev/null +++ b/.yamllint.conf @@ -0,0 +1,3 @@ +rules: + line-length: disable + document-start: disable diff --git a/README.md b/README.md index dff6425e53..f0a37b6e9b 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,8 @@ The Web interface is enabled by default when you install Pi-hole. 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:///admin/` -2. `http:/pi.hole/admin/` (when using Pi-hole as your DNS server) +1. `http:///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: @@ -46,7 +46,7 @@ Once logged in (forgot your password?), you can view your network stats to see t ## 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. +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!** @@ -73,7 +73,7 @@ If you'd rather not [donate](https://pi-hole.net/donate/) (_which is okay!_), th 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. +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 diff --git a/api.php b/api.php index 34ffe65503..62ece96714 100644 --- a/api.php +++ b/api.php @@ -13,33 +13,10 @@ require_once("scripts/pi-hole/php/auth.php"); check_cors(); -$FTL_IP = "127.0.0.1"; - $data = array(); // Common API functions -if (isset($_GET['status'])) -{ - $pistatus = pihole_execute('status web'); - if(isset($pistatus[0])) - { - $pistatus = $pistatus[0]; - } - else - { - $pistatus = null; - } - if ($pistatus === "1") - { - $data = array_merge($data, array("status" => "enabled")); - } - else - { - $data = array_merge($data, array("status" => "disabled")); - } -} -elseif (isset($_GET['enable']) && $auth) -{ +if (isset($_GET['enable']) && $auth) { if(isset($_GET["auth"])) { if($_GET["auth"] !== $pwhash) @@ -160,17 +137,70 @@ return; } +elseif(isset($_GET['customdns']) && $auth) +{ + if (isset($_GET["auth"])) { + if ($_GET["auth"] !== $pwhash) { + die("Not authorized!"); + } + } else { + // Skip token validation if explicit auth string is given + check_csrf($_GET['token']); + } + + switch ($_GET["action"]) { + case 'get': + $data = echoCustomDNSEntries(); + break; + + case 'add': + $data = addCustomDNSEntry(); + break; + + case 'delete': + $data = deleteCustomDNSEntry(); + break; + + default: + die("Wrong action"); + } +} +elseif(isset($_GET['customcname']) && $auth) +{ + if (isset($_GET["auth"])) { + if ($_GET["auth"] !== $pwhash) { + die("Not authorized!"); + } + } else { + // Skip token validation if explicit auth string is given + check_csrf($_GET['token']); + } + + switch ($_GET["action"]) { + case 'get': + $data = echoCustomCNAMEEntries(); + break; + + case 'add': + $data = addCustomCNAMEEntry(); + break; + + case 'delete': + $data = deleteCustomCNAMEEntry(); + break; + + default: + die("Wrong action"); + } +} // Other API functions require("api_FTL.php"); header('Content-type: application/json'); -if(isset($_GET["jsonForceObject"])) -{ - echo json_encode($data, JSON_FORCE_OBJECT); -} -else -{ - echo json_encode($data); +if(isset($_GET["jsonForceObject"])) { + echo json_encode($data, JSON_FORCE_OBJECT); +} else { + echo json_encode($data); } ?> diff --git a/api_FTL.php b/api_FTL.php index ed6741dde5..cb0a8574b1 100644 --- a/api_FTL.php +++ b/api_FTL.php @@ -6,423 +6,436 @@ * 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_FTL.php is not allowed!"); +} + +if (isset($_GET['type'])) { + $data["type"] = "FTL"; +} + +if (isset($_GET['version'])) { + $data["version"] = 3; +} + +if (isset($_GET['status'])) { + $return = callFTLAPI("stats"); + if (array_key_exists("FTLnotrunning", $return)) { + $data = array("FTLnotrunning" => true); + } else { + if (in_array("status enabled", $return)) { + $data = array_merge($data, array("status" => "enabled")); + } else { + $data = array_merge($data, array("status" => "disabled")); + } + } +} + +if (isset($_GET['summary']) || isset($_GET['summaryRaw']) || !count($_GET)) { + require_once("scripts/pi-hole/php/gravity.php"); + + $return = callFTLAPI("stats"); + if (array_key_exists("FTLnotrunning", $return)) { + $data = array("FTLnotrunning" => true); + } else { + $stats = []; + foreach ($return as $line) { + $tmp = explode(" ",$line); + + if ($tmp[0] === "domains_being_blocked" && !is_numeric($tmp[1]) || $tmp[0] === "status") { + // Expect string response + $stats[$tmp[0]] = $tmp[1]; + } elseif (isset($_GET['summary'])) { + // "summary" expects a formmated string response + if ($tmp[0] !== "ads_percentage_today") { + $stats[$tmp[0]] = number_format($tmp[1]); + } else { + $stats[$tmp[0]] = number_format($tmp[1], 1, '.', ''); + } + } else { + // Expect float response + $stats[$tmp[0]] = floatval($tmp[1]); + } + + } + $stats['gravity_last_updated'] = gravity_last_update(true); + $data = array_merge($data,$stats); + } +} -if(!isset($api)) -{ - die("Direct call to api_FTL.php is not allowed!"); +if (isset($_GET["getMaxlogage"]) && $auth) { + $return = callFTLAPI("maxlogage"); + if (array_key_exists("FTLnotrunning", $return)) { + $data = array("FTLnotrunning" => true); + } else { + // Convert seconds to hours and rounds to one decimal place. + $ret = round(intval($return[0]) / 3600, 1); + // Return 24h if value is 0, empty, null or non numeric. + $ret = $ret ?: 24; + + $data = array_merge($data, array("maxlogage" => $ret)); + } } -// $FTL_IP is defined in api.php -$socket = connectFTL($FTL_IP); +if (isset($_GET['overTimeData10mins'])) { + $return = callFTLAPI("overTime"); + if (array_key_exists("FTLnotrunning", $return)) { + $data = array("FTLnotrunning" => true); + } else { + $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") { + $return = callFTLAPI("top-domains for audit"); + } elseif (is_numeric($_GET['topItems'])) { + $return = callFTLAPI("top-domains (".$_GET['topItems'].")"); + } else { + $return = callFTLAPI("top-domains"); + } + + if (array_key_exists("FTLnotrunning", $return)) { + $data = array("FTLnotrunning" => true); + } else { + $top_queries = array(); + foreach ($return as $line) { + $tmp = explode(" ",$line); + if (count($tmp) == 2) { + $tmp[2]=""; + } + $domain = utf8_encode($tmp[2]); + $top_queries[$domain] = intval($tmp[1]); + } + } + + if ($_GET['topItems'] === "audit") { + $return = callFTLAPI("top-ads for audit"); + } elseif (is_numeric($_GET['topItems'])) { + $return = callFTLAPI("top-ads (".$_GET['topItems'].")"); + } else { + $return = callFTLAPI("top-ads"); + } + + if (array_key_exists("FTLnotrunning", $return)) { + $data = array("FTLnotrunning" => true); + } else { + $top_ads = array(); + foreach ($return as $line) { + $tmp = explode(" ",$line); + $domain = utf8_encode($tmp[2]); + if (count($tmp) > 3) { + $top_ads[$domain." (".$tmp[3].")"] = intval($tmp[1]); + } else { + $top_ads[$domain] = 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)) { + $return = callFTLAPI("top-clients (".$number.")"); + } else { + $return = callFTLAPI("top-clients"); + } + if (array_key_exists("FTLnotrunning", $return)) { + $data = array("FTLnotrunning" => true); + } else { + $top_clients = array(); + foreach ($return as $line) { + $tmp = explode(" ",$line); + $clientip = utf8_encode($tmp[2]); + if (count($tmp) > 3 && strlen($tmp[3]) > 0) { + $clientname = utf8_encode($tmp[3]); + $top_clients[$clientname."|".$clientip] = intval($tmp[1]); + } else { + $top_clients[$clientip] = 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)) { + $return = callFTLAPI("top-clients blocked (".$number.")"); + } else { + $return = callFTLAPI("top-clients blocked"); + } + if (array_key_exists("FTLnotrunning", $return)) { + $data = array("FTLnotrunning" => true); + } else { + $top_clients = array(); + foreach ($return as $line) { + $tmp = explode(" ",$line); + $clientip = utf8_encode($tmp[2]); + if (count($tmp) > 3 && strlen($tmp[3]) > 0) { + $clientname = utf8_encode($tmp[3]); + $top_clients[$clientname."|".$clientip] = intval($tmp[1]); + } else { + $top_clients[$clientip] = intval($tmp[1]); + } + } + + $result = array('top_sources_blocked' => $top_clients); + $data = array_merge($data, $result); + } +} + +if (isset($_GET['getForwardDestinations']) && $auth) { + if ($_GET['getForwardDestinations'] === "unsorted") { + $return = callFTLAPI("forward-dest unsorted"); + } else { + $return = callFTLAPI("forward-dest"); + } + if (array_key_exists("FTLnotrunning", $return)) { + $data = array("FTLnotrunning" => true); + } else { + $forward_dest = array(); + foreach ($return as $line) { + $tmp = explode(" ",$line); + $forwardip = utf8_encode($tmp[2]); + if (count($tmp) > 3 && strlen($tmp[3]) > 0) { + $forwardname = utf8_encode($tmp[3]); + $forward_dest[$forwardname."|".$forwardip] = floatval($tmp[1]); + } else { + $forward_dest[$forwardip] = floatval($tmp[1]); + } + } + + $result = array('forward_destinations' => $forward_dest); + $data = array_merge($data, $result); + } +} -if(!is_resource($socket)) -{ - $data = array_merge($data, array("FTLnotrunning" => true)); +if (isset($_GET['getQueryTypes']) && $auth) { + $return = callFTLAPI("querytypes"); + if (array_key_exists("FTLnotrunning", $return)) { + $data = array("FTLnotrunning" => true); + } else { + $querytypes = array(); + foreach ($return as $ret) { + $tmp = explode(": ",$ret); + // Reply cannot contain non-ASCII characters + $querytypes[$tmp[0]] = floatval($tmp[1]); + } + + $result = array('querytypes' => $querytypes); + $data = array_merge($data, $result); + } } -else -{ - if (isset($_GET['type'])) { - $data["type"] = "FTL"; - } - - if (isset($_GET['version'])) { - $data["version"] = 3; - } - - if (isset($_GET['summary']) || isset($_GET['summaryRaw']) || !count($_GET)) - { - require_once("scripts/pi-hole/php/gravity.php"); - sendRequestFTL("stats"); - $return = getResponseFTL(); - - $stats = []; - foreach($return as $line) - { - $tmp = explode(" ",$line); - - if(($tmp[0] === "domains_being_blocked" && !is_numeric($tmp[1])) || $tmp[0] === "status") - { - $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]] = floatval($tmp[1]); - } - } - $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) - { - $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"); - } - - $return = getResponseFTL(); - $top_queries = array(); - foreach($return as $line) - { - $tmp = explode(" ",$line); - $domain = utf8_encode($tmp[2]); - $top_queries[$domain] = 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); - $domain = utf8_encode($tmp[2]); - if(count($tmp) > 3) - $top_ads[$domain." (".$tmp[3].")"] = intval($tmp[1]); - else - $top_ads[$domain] = 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); - $clientip = utf8_encode($tmp[2]); - if(count($tmp) > 3 && strlen($tmp[3]) > 0) - { - $clientname = utf8_encode($tmp[3]); - $top_clients[$clientname."|".$clientip] = intval($tmp[1]); - } - else - $top_clients[$clientip] = 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 - { - sendRequestFTL("top-clients blocked"); - } - - $return = getResponseFTL(); - $top_clients = array(); - foreach($return as $line) - { - $tmp = explode(" ",$line); - $clientip = utf8_encode($tmp[2]); - if(count($tmp) > 3 && strlen($tmp[3]) > 0) - { - $clientname = utf8_encode($tmp[3]); - $top_clients[$clientname."|".$clientip] = intval($tmp[1]); - } - else - $top_clients[$clientip] = intval($tmp[1]); - } - - $result = array('top_sources_blocked' => $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); - $forwardip = utf8_encode($tmp[2]); - if(count($tmp) > 3 && strlen($tmp[3]) > 0) - { - $forwardname = utf8_encode($tmp[3]); - $forward_dest[$forwardname."|".$forwardip] = floatval($tmp[1]); - } - else - $forward_dest[$forwardip] = floatval($tmp[1]); - } - - $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); - // Reply cannot contain non-ASCII characters - $querytypes[$tmp[0]] = floatval($tmp[1]); - } - - $result = array('querytypes' => $querytypes); - $data = array_merge($data, $result); - } - - if (isset($_GET['getCacheInfo']) && $auth) - { - sendRequestFTL("cacheinfo"); - $return = getResponseFTL(); - $cacheinfo = array(); - foreach($return as $ret) - { - $tmp = explode(": ",$ret); - // Reply cannot contain non-ASCII characters - $cacheinfo[$tmp[0]] = floatval($tmp[1]); - } - - $result = array('cacheinfo' => $cacheinfo); - $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']) && (isset($_GET['type']) && $_GET['type'] === "blocked")) - { - // Get specific client only - sendRequestFTL("getallqueries-client-blocked ".$_GET['client']); - } - else if(isset($_GET['client'])) - { - // Get specific client only - sendRequestFTL("getallqueries-client ".$_GET['client']); - } - else if(isset($_GET['querytype'])) - { - // Get specific query type only - sendRequestFTL("getallqueries-qtype ".$_GET['querytype']); - } - else if(isset($_GET['forwarddest'])) - { - // Get specific forward destination only - sendRequestFTL("getallqueries-forward ".$_GET['forwarddest']); - } - 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 = str_getcsv($line," "); - // UTF-8 encode domain - $tmp[2] = utf8_encode(str_replace("~"," ",$tmp[2])); - // UTF-8 encode client host name - $tmp[3] = utf8_encode($tmp[3]); - array_push($allQueries,$tmp); - } - - $result = array('data' => $allQueries); - $data = array_merge($data, $result); - } - - if(isset($_GET["recentBlocked"]) && $auth) - { - sendRequestFTL("recentBlocked"); - die(utf8_encode(getResponseFTL()[0])); - unset($data); - } - - if (isset($_GET['getForwardDestinationNames']) && $auth) - { - sendRequestFTL("forward-names"); - $return = getResponseFTL(); - $forward_dest = array(); - foreach($return as $line) - { - $tmp = explode(" ",$line); - $forwardip = utf8_encode($tmp[2]); - if(count($tmp) > 3) - { - $forwardname = utf8_encode($tmp[3]); - $forward_dest[$forwardname."|".$forwardip] = floatval($tmp[1]); - } - else - { - $forward_dest[$forwardip] = 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" => utf8_encode($tmp[0]), - "ip" => utf8_encode($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); - } - - if (isset($_GET['delete_lease']) && $auth) - { - sendRequestFTL("delete-lease ".$_GET['delete_lease']); - $return = getResponseFTL(); - $data["delete_lease"] = $return[0]; - } - - disconnectFTL(); + +if (isset($_GET['getCacheInfo']) && $auth) { + $return = callFTLAPI("cacheinfo"); + if (array_key_exists("FTLnotrunning", $return)) { + $data = array("FTLnotrunning" => true); + } else { + $cacheinfo = array(); + foreach ($return as $ret) { + $tmp = explode(": ",$ret); + // Reply cannot contain non-ASCII characters + $cacheinfo[$tmp[0]] = floatval($tmp[1]); + } + + $result = array('cacheinfo' => $cacheinfo); + $data = array_merge($data, $result); + } +} + +if (isset($_GET['getAllQueries']) && $auth) { + if (isset($_GET['from']) && isset($_GET['until'])) { + // Get limited time interval + $return = callFTLAPI("getallqueries-time ".$_GET['from']." ".$_GET['until']); + } elseif (isset($_GET['domain'])) { + // Get specific domain only + $return = callFTLAPI("getallqueries-domain ".$_GET['domain']); + } elseif (isset($_GET['client']) && (isset($_GET['type']) && $_GET['type'] === "blocked")) { + // Get specific client only + $return = callFTLAPI("getallqueries-client-blocked ".$_GET['client']); + } elseif (isset($_GET['client'])) { + // Get specific client only + $return = callFTLAPI("getallqueries-client ".$_GET['client']); + } elseif (isset($_GET['querytype'])) { + // Get specific query type only + $return = callFTLAPI("getallqueries-qtype ".$_GET['querytype']); + } elseif (isset($_GET['forwarddest'])) { + // Get specific forward destination only + $return = callFTLAPI("getallqueries-forward ".$_GET['forwarddest']); + } elseif (is_numeric($_GET['getAllQueries'])) { + $return = callFTLAPI("getallqueries (".$_GET['getAllQueries'].")"); + } else { + // Get all queries + $return = callFTLAPI("getallqueries"); + } + + if (array_key_exists("FTLnotrunning", $return)) { + $data = array("FTLnotrunning" => true); + } else { + // Set the header + header('Content-type: application/json'); + + // Start the JSON string + echo '{"data":['; + $first = true; + + foreach($return as $line) { + + // Insert a comma before the next record (except on the first one) + if (!$first) { + echo ","; + } else { + $first = false; + } + + $row = str_getcsv($line," "); + // UTF-8 encode domain + $domain = utf8_encode(str_replace("~"," ",$row[2])); + // UTF-8 encode client host name + $client = utf8_encode($row[3]); + + // Insert into array and output it in JSON format + // array: time type domain client status dnssecStatus reply response_time CNAMEDomain regexID upstream destination EDE + echo json_encode([$row[0], $row[1], $domain, $client, $row[4], $row[5], $row[6], $row[7], $row[8], $row[9], $row[10], $row[11]]); + } + // Finish the JSON string + echo ']}'; + // exit at the end + exit(); + } +} + +if (isset($_GET["recentBlocked"]) && $auth) { + die(utf8_encode(callFTLAPI("recentBlocked")[0])); + unset($data); +} + +if (isset($_GET['getForwardDestinationNames']) && $auth) { + $return = callFTLAPI("forward-names"); + + if (array_key_exists("FTLnotrunning", $return)) { + $data = array("FTLnotrunning" => true); + } else { + $forward_dest = array(); + foreach ($return as $line) { + $tmp = explode(" ",$line); + $forwardip = utf8_encode($tmp[2]); + if (count($tmp) > 3) { + $forwardname = utf8_encode($tmp[3]); + $forward_dest[$forwardname."|".$forwardip] = floatval($tmp[1]); + } else { + $forward_dest[$forwardip] = floatval($tmp[1]); + } + } + + $result = array('forward_destinations' => $forward_dest); + $data = array_merge($data, $result); + } +} + +if (isset($_GET['overTimeDataQueryTypes']) && $auth) { + $return = callFTLAPI("QueryTypesoverTime"); + if (array_key_exists("FTLnotrunning", $return)) { + $data = array("FTLnotrunning" => true); + } else { + $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) { + $return = callFTLAPI("client-names"); + if (array_key_exists("FTLnotrunning", $return)) { + $data = array("FTLnotrunning" => true); + } else { + $client_names = array(); + foreach ($return as $line) { + $tmp = explode(" ", $line); + $client_names[] = array( + "name" => utf8_encode($tmp[0]), + "ip" => utf8_encode($tmp[1]) + ); + } + + $result = array('clients' => $client_names); + $data = array_merge($data, $result); + } +} + +if (isset($_GET['overTimeDataClients']) && $auth) { + $return = callFTLAPI("ClientsoverTime"); + + if (array_key_exists("FTLnotrunning", $return)) { + $data = array("FTLnotrunning" => true); + } else { + $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['delete_lease']) && $auth) { + $return = callFTLAPI("delete-lease ".$_GET['delete_lease']); + if (array_key_exists("FTLnotrunning", $return)) { + $data = array("FTLnotrunning" => true); + } else { + $data["delete_lease"] = $return[0]; + } +} + +if (isset($_GET['dns-port']) && $auth) { + $return = callFTLAPI("dns-port"); + if (array_key_exists("FTLnotrunning", $return)) { + $data = array("FTLnotrunning" => true); + } else { + $data["dns-port"] = $return[0]; + } } ?> diff --git a/api_db.php b/api_db.php index 427fc3cc51..15f580471c 100644 --- a/api_db.php +++ b/api_db.php @@ -18,37 +18,6 @@ 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(validIP($clientip)) - { - // 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; -} // Needs package php5-sqlite, e.g. // sudo apt-get install php5-sqlite @@ -95,7 +64,20 @@ function resolveHostname($clientip, $printIP) { $from = intval($_GET["from"]); $until = intval($_GET["until"]); - $dbquery = "SELECT timestamp, type, domain, client, status, forward FROM queries WHERE timestamp >= :from AND timestamp <= :until "; + + // Use table "query_storage" + // - replace domain ID with domain + // - replace client ID with client name + // - replace forward ID with forward destination + $dbquery = "SELECT timestamp, type,"; + $dbquery .= " CASE typeof(domain) WHEN 'integer' THEN (SELECT domain FROM domain_by_id d WHERE d.id = q.domain) ELSE domain END domain,"; + $dbquery .= " CASE typeof(client) WHEN 'integer' THEN ("; + $dbquery .= " SELECT CASE TRIM(name) WHEN '' THEN c.ip ELSE c.name END name FROM client_by_id c WHERE c.id = q.client"; + $dbquery .= " ) ELSE client END client,"; + $dbquery .= " CASE typeof(forward) WHEN 'integer' THEN (SELECT forward FROM forward_by_id f WHERE f.id = q.forward) ELSE forward END forward,"; + $dbquery .= " status, reply_type, reply_time, dnssec"; + $dbquery .= " FROM query_storage q"; + $dbquery .= " WHERE timestamp >= :from AND timestamp <= :until "; if(isset($_GET["types"])) { $types = $_GET["types"]; @@ -118,20 +100,44 @@ function resolveHostname($clientip, $printIP) $stmt->bindValue(":from", intval($from), SQLITE3_INTEGER); $stmt->bindValue(":until", intval($until), SQLITE3_INTEGER); $results = $stmt->execute(); - if(!is_bool($results)) - while ($row = $results->fetchArray()) - { - // Try to resolve host name of this client - $c = resolveHostname($row[3],false); - // Convert query type ID to name - $query_type = getQueryTypeStr($row[1]); - - // Insert into array - // array: time type domain client status upstream destination - $allQueries[] = [$row[0], $query_type, utf8_encode(str_replace("~"," ",$row[2])), utf8_encode($c), $row[4], utf8_encode($row[5])]; + // Start the JSON string + echo '{"data":['; + + if (!is_bool($results)) { + $first = true; + while ($row = $results->fetchArray(SQLITE3_ASSOC)) { + // Insert a comma before the next record (except on the first one) + if (!$first) { + echo ","; + } else { + $first = false; + } + + // Format, encode, transform each field (if necessary). + $time = $row["timestamp"]; + $query_type = getQueryTypeStr($row["type"]); // Convert query type ID to name + $domain = utf8_encode(str_replace("~"," ",$row["domain"])); + $client = $row["client"]; + $status = $row["status"]; + $destination = utf8_encode($row["forward"]); + $reply_type = $row["reply_type"]; + $reply_time = $row["reply_time"]; + $dnssec = $row["dnssec"]; + $client_id = $row["client_id"]; + + // Insert into array and output it in JSON format + echo json_encode([$time, $query_type, $domain, $client, $status, $destination, $reply_type, $reply_time, $dnssec]); } + } + + // Finish the JSON string + echo ']}'; + + // exit at the end + exit(); } + // only used if getAllQueries==empty $result = array('data' => $allQueries); $data = array_merge($data, $result); } @@ -152,7 +158,11 @@ function resolveHostname($clientip, $printIP) { $limit = "WHERE timestamp <= :until"; } - $stmt = $db->prepare('SELECT client,count(client) FROM queries '.$limit.' GROUP by client order by count(client) desc limit 20'); + $dbquery = "SELECT CASE typeof(client) WHEN 'integer' THEN ("; + $dbquery .= " SELECT CASE TRIM(name) WHEN '' THEN c.ip ELSE c.name END name FROM client_by_id c WHERE c.id = q.client)"; + $dbquery .= " ELSE client END client, count(client) FROM query_storage q ".$limit." GROUP BY client ORDER BY count(client) DESC LIMIT 20"; + + $stmt = $db->prepare($dbquery); $stmt->bindValue(":from", intval($_GET['from']), SQLITE3_INTEGER); $stmt->bindValue(":until", intval($_GET['until']), SQLITE3_INTEGER); $results = $stmt->execute(); @@ -162,18 +172,17 @@ function resolveHostname($clientip, $printIP) if(!is_bool($results)) while ($row = $results->fetchArray()) { - // Try to resolve host name and convert to UTF-8 - $c = utf8_encode(resolveHostname($row[0],false)); + // $row[0] is the client IP - if(array_key_exists($c, $clientnums)) + if(array_key_exists($row[0], $clientnums)) { // Entry already exists, add to it (might appear multiple times due to mixed capitalization in the database) - $clientnums[$c] += intval($row[1]); + $clientnums[$row[0]] += intval($row[1]); } else { // Entry does not yet exist - $clientnums[$c] = intval($row[1]); + $clientnums[$row[0]] = intval($row[1]); } } @@ -335,7 +344,7 @@ function resolveHostname($clientip, $printIP) if(isset($_GET["interval"])) { $q = intval($_GET["interval"]); - if($q > 10) + if($q >= 10) $interval = $q; } @@ -398,9 +407,12 @@ function parseDBData($results, $interval, $from, $until) { $data = array_merge($data, $result); } -if (isset($_GET['status'])) +if (isset($_GET['status']) && $auth) { - $results = $db->query('SELECT COUNT(*) FROM message;'); + $extra = ";"; + if(isset($_GET["ignore"]) && $_GET["ignore"] === 'DNSMASQ_WARN') + $extra = "WHERE type != 'DNSMASQ_WARN';"; + $results = $db->query('SELECT COUNT(*) FROM message '.$extra); if(!is_bool($results)) $result = array('message_count' => $results->fetchArray()[0]); @@ -412,8 +424,12 @@ function parseDBData($results, $interval, $from, $until) { if(isset($_GET["messages"]) && $auth) { + $extra = ";"; + if(isset($_GET["ignore"]) && $_GET["ignore"] === 'DNSMASQ_WARN') + $extra = "WHERE type != 'DNSMASQ_WARN';"; + $messages = array(); - $results = $db->query('SELECT * FROM message'); + $results = $db->query('SELECT * FROM message '.$extra); while($results !== false && $res = $results->fetchArray(SQLITE3_ASSOC)) { diff --git a/auditlog.php b/auditlog.php index 895dc58123..fd1219ed20 100644 --- a/auditlog.php +++ b/auditlog.php @@ -23,12 +23,14 @@
- + - - - + + + + +
DomainHitsActionsDomainHitsActions
@@ -51,12 +53,14 @@
- + - - - + + + + +
DomainHitsActionsDomainHitsActions
@@ -72,7 +76,6 @@
- @@ -50,23 +51,6 @@
- - - - -
@@ -94,7 +78,6 @@
- + + + + + -
- + - - - + + + + +
DomainHitsFrequencyDomainHitsFrequency
@@ -92,12 +94,14 @@
- + - - - + + + + +
DomainHitsFrequencyDomainHitsFrequency
@@ -119,12 +123,14 @@
- + - - - + + + + +
ClientRequestsFrequencyClientRequestsFrequency
@@ -140,7 +146,6 @@
-
+
+
+   New options selected. Please reload the data or choose another time range. + +
+
@@ -32,37 +38,38 @@
+
+
+ +
+
+
+

+
+
+
+
+

+
+
+
+
+

+
+
+
+
+

+

+
+
+
+
-
-
- -
-
-
-

-
-
-
-
-

-
-
-
-

-
-
-
-

-

-
-
-
-
- @@ -73,7 +80,7 @@
-

---

+

---

Queries Blocked

@@ -86,7 +93,7 @@
-

---

+

---

Queries Blocked (Wildcards)

@@ -112,7 +119,7 @@
-

---

+

---

Queries Blocked

@@ -173,6 +180,7 @@ Domain Client Status + Reply Action @@ -183,6 +191,7 @@ Domain Client Status + Reply Action @@ -196,7 +205,6 @@ -
- +

Once you click this button a debug log will be generated and can automatically be uploaded if we detect a working internet connection.

diff --git a/dns_records.php b/dns_records.php index e20c2058f1..dcf9cf25e1 100644 --- a/dns_records.php +++ b/dns_records.php @@ -1,12 +1,13 @@ - @@ -39,29 +40,21 @@
- - - - -
@@ -89,7 +82,6 @@
- diff --git a/groups-adlists.php b/groups-adlists.php index 1fb0417921..b905d520d1 100644 --- a/groups-adlists.php +++ b/groups-adlists.php @@ -63,11 +63,12 @@ ID + Address Status Comment Group assignment - Action +   @@ -81,7 +82,6 @@ - 192.168.2.0/24), their MAC addresses (like 12:34:56:78:9A:BC), by their hostnames (like localhost), or by the interface they are connected to (prefaced with a colon, like :eth0).

-

Note that client recognition by IP addresses (incl. subnet ranges) are prefered over MAC address, host name or interface recognition as +

Note that client recognition by IP addresses (incl. subnet ranges) are preferred over MAC address, host name or interface recognition as the two latter will only be available after some time. Furthermore, MAC address recognition only works for devices at most one networking hop away from your Pi-hole.

@@ -70,10 +70,11 @@ ID + Client Comment Group assignment - Action +   @@ -88,7 +89,6 @@ -
- + +
@@ -88,6 +89,16 @@
+
+

Note:
+ The domain or regex filter will be automatically assigned to the Default Group.
+ Other groups can optionally be assigned + + within Group Management > Domains. + + in the list below (using Group assignment). +

+