Compare commits

..

3 Commits

Author SHA1 Message Date
DL6ER
eaaa0c1f7f Also use killproc.
Signed-off-by: DL6ER <dl6er@dl6er.de>
2019-08-14 23:13:26 +02:00
DL6ER
cea9205136 Use start_daemon to start pihole-FTL.
Signed-off-by: DL6ER <dl6er@dl6er.de>
2019-08-14 23:00:33 +02:00
DL6ER
0cc1e88608 Remove is_running from init.d script.
Signed-off-by: DL6ER <dl6er@dl6er.de>
2019-08-14 22:59:25 +02:00
100 changed files with 3442 additions and 5279 deletions

View File

@@ -1,4 +1,4 @@
# EditorConfig is awesome: https://editorconfig.org/
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true

37
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,37 @@
**In raising this issue, I confirm the following:** `{please fill the checkboxes, e.g: [X]}`
- [] I have read and understood the [contributors guide](https://github.com/pi-hole/pi-hole/blob/master/CONTRIBUTING.md).
- [] The issue I am reporting can be *replicated*.
- [] The issue I am reporting isn't a duplicate (see [FAQs](https://github.com/pi-hole/pi-hole/wiki/FAQs), [closed issues](https://github.com/pi-hole/pi-hole/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), and [open issues](https://github.com/pi-hole/pi-hole/issues)).
**How familiar are you with the the source code relevant to this issue?:**
`{Replace this with a number from 1 to 10. 1 being not familiar, and 10 being very familiar}`
---
**Expected behaviour:**
`{A detailed description of what you expect to see}`
**Actual behaviour:**
`{A detailed description and/or screenshots of what you do see}`
**Steps to reproduce:**
`{Detailed steps of how we can reproduce this}`
**Debug token provided by [uploading `pihole -d` log](https://discourse.pi-hole.net/t/the-pihole-command-with-examples/738#debug):**
`{Alphanumeric token}`
**Troubleshooting undertaken, and/or other relevant information:**
`{Steps of what you have done to fix this}`
> * `{Please delete this quoted section when opening your issue}`
> * You must follow the template instructions. Failure to do so will result in your issue being closed.
> * Please [submit any feature requests here](https://discourse.pi-hole.net/c/feature-requests), so it is votable and trackable by the community.
> * Please respect that Pi-hole is developed by volunteers, who can only reply in their spare time.
> * Detail helps us understand and resolve an issue quicker, but please ensure it's relevant.
> * _This template was created based on the work of [`udemy-dl`](https://github.com/nishad/udemy-dl/blob/master/LICENSE)._

31
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,31 @@
**By submitting this pull request, I confirm the following:**
*please fill any appropriate checkboxes, e.g: [X]*
- [ ] I have read and understood the [contributors guide](https://github.com/pi-hole/pi-hole/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, and have included unit tests where possible.
- [ ] 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))
Please make sure you [Sign Off](https://github.com/pi-hole/pi-hole/wiki/How-to-signoff-your-commits.) all commits. Pi-hole enforces the [DCO](https://github.com/pi-hole/pi-hole/wiki/Contributing-to-the-project).
---
**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*
---
* You must follow the template instructions. Failure to do so will result in your pull request being closed.
* Please respect that Pi-hole is developed by volunteers, who can only reply in their spare time.

View File

@@ -1,10 +0,0 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weekly
day: saturday
time: "10:00"
open-pull-requests-limit: 10
target-branch: developement

View File

@@ -1,40 +0,0 @@
name: "CodeQL"
on:
push:
branches:
- master
- development
pull_request:
branches:
- master
- development
schedule:
- cron: '32 11 * * 6'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
-
name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
-
name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: 'python'
-
name: Autobuild
uses: github/codeql-action/autobuild@v1
-
name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@@ -1,48 +0,0 @@
name: Test Supported Distributions
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
jobs:
smoke-test:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
-
name: Checkout repository
uses: actions/checkout@v2
-
name: Run Smoke Tests
run: |
# Ensure scripts in repository are executable
IFS=$'\n';
for f in $(find . -name '*.sh'); do if [[ ! -x $f ]]; then echo "$f is not executable" && FAIL=1; fi ;done
unset IFS;
# If FAIL is 1 then we fail.
[[ $FAIL == 1 ]] && exit 1 || echo "Smoke Tests Passed"
distro-test:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
needs: smoke-test
strategy:
matrix:
distro: [debian_9, debian_10, debian_11, ubuntu_16, ubuntu_18, ubuntu_20, ubuntu_21, centos_7, centos_8, fedora_32, fedora_33]
env:
DISTRO: ${{matrix.distro}}
steps:
-
name: Checkout repository
uses: actions/checkout@v2
-
name: Set up Python 3.7
uses: actions/setup-python@v2
with:
python-version: 3.7
-
name: Install dependencies
run: pip install -r test/requirements.txt
-
name: Test with tox
run: tox -c test/tox.${DISTRO}.ini

67
.gitignore vendored
View File

@@ -7,5 +7,70 @@ __pycache__
.tox
.eggs
*.egg-info
.idea/
# Created by https://www.gitignore.io/api/jetbrains+iml
### JetBrains+iml ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# All idea files, with execptions
.idea
!.idea/codeStyles/*
!.idea/codeStyleSettings.xml
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# CMake
cmake-build-debug/
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Ruby plugin and RubyMine
/.rakeTasks
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### JetBrains+iml Patch ###
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
*.iml
.idea/misc.xml
*.ipr
# End of https://www.gitignore.io/api/jetbrains+iml

25
.idea/codeStyleSettings.xml generated Normal file
View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectCodeStyleSettingsManager">
<option name="PER_PROJECT_SETTINGS">
<value>
<option name="OTHER_INDENT_OPTIONS">
<value>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="8" />
<option name="TAB_SIZE" value="2" />
<option name="USE_TAB_CHARACTER" value="false" />
<option name="SMART_TABS" value="false" />
<option name="LABEL_INDENT_SIZE" value="0" />
<option name="LABEL_INDENT_ABSOLUTE" value="false" />
<option name="USE_RELATIVE_INDENTS" value="false" />
</value>
</option>
<MarkdownNavigatorCodeStyleSettings>
<option name="RIGHT_MARGIN" value="72" />
</MarkdownNavigatorCodeStyleSettings>
</value>
</option>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</component>
</project>

7
.idea/codeStyles/Project.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<MarkdownNavigatorCodeStyleSettings>
<option name="RIGHT_MARGIN" value="72" />
</MarkdownNavigatorCodeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View File

@@ -2,5 +2,5 @@ linters:
shellcheck:
shell: bash
phpcs:
csslint:
flake8:
max-line-length: 120

12
.travis.yml Normal file
View File

@@ -0,0 +1,12 @@
sudo: required
services:
- docker
language: python
python:
- "2.7"
install:
- pip install -r requirements.txt
script:
# tox.ini handles setup, ordering of docker build first, and then run tests
- tox

View File

@@ -4,8 +4,8 @@ Please read and understand the contribution guide before creating an issue or pu
## Etiquette
- Our goal for Pi-hole is **stability before features**. This means we focus on squashing critical bugs before adding new features. Often, we can do both in tandem, but bugs will take priority over a new feature.
- Pi-hole is open source and [powered by donations](https://pi-hole.net/donate/), and as such, we give our **free time** to build, maintain, and **provide user support** for this project. It would be extremely unfair for us to suffer abuse or anger for our hard work, so please take a moment to consider that.
- Our goal for Pi-hole is **stability before features**. This means we focus on squashing critical bugs before adding new features. Often, we can do both in tandem, but bugs will take priority over a new feature.
- Pi-hole is open source and [powered by donations](https://pi-hole.net/donate/), and as such, we give our **free time** to build, maintain, and **provide user support** for this project. It would be extremely unfair for us to suffer abuse or anger for our hard work, so please take a moment to consider that.
- Please be considerate towards the developers and other users when raising issues or presenting pull requests.
- Respect our decision(s), and do not be upset or abusive if your submission is not used.
@@ -26,87 +26,13 @@ When requesting or submitting new features, first consider whether it might be u
- Check the codebase to ensure that your feature doesn't already exist.
- Check the pull requests to ensure that another person hasn't already submitted the feature or fix.
- Read and understand the [DCO guidelines](https://docs.pi-hole.net/guides/github/contributing/) for the project.
- Read and understand the [DCO guidelines](https://github.com/pi-hole/pi-hole/wiki/Contributing-to-the-project) for the project.
## Technical Requirements
- Submit Pull Requests to the **development branch only**.
- Before Submitting your Pull Request, merge `development` with your new branch and fix any conflicts. (Make sure you don't break anything in development!)
- Please use the [Google Style Guide for Shell](https://google.github.io/styleguide/shell.xml) for your code submission styles.
- Please use the [Google Style Guide for Shell](https://google.github.io/styleguide/shell.xml) for your code submission styles.
- Commit Unix line endings.
- Please use the Pi-hole brand: **Pi-hole** (Take a special look at the capitalized 'P' and a low 'h' with a hyphen)
- (Optional fun) keep to the theme of Star Trek/black holes/gravity.
## Forking and Cloning from GitHub to GitHub
1. Fork <https://github.com/pi-hole/pi-hole/> to a repo under a namespace you control, or have permission to use, for example: `https://github.com/<your_namespace>/<your_repo_name>/`. You can do this from the github.com website.
2. Clone `https://github.com/<your_namespace>/<your_repo_name>/` with the tool of you choice.
3. To keep your fork in sync with our repo, add an upstream remote for pi-hole/pi-hole to your repo.
```bash
git remote add upstream https://github.com/pi-hole/pi-hole.git
```
4. Checkout the `development` branch from your fork `https://github.com/<your_namespace>/<your_repo_name>/`.
5. Create a topic/branch, based on the `development` branch code. *Bonus fun to keep to the theme of Star Trek/black holes/gravity.*
6. Make your changes and commit to your topic branch in your repo.
7. Rebase your commits and squash any insignificant commits. See the notes below for an example.
8. Merge `development` your branch and fix any conflicts.
9. Open a Pull Request to merge your topic branch into our repo's `development` branch.
- Keep in mind the technical requirements from above.
## Forking and Cloning from GitHub to other code hosting sites
- Forking is a GitHub concept and cannot be done from GitHub to other git-based code hosting sites. However, those sites may be able to mirror a GitHub repo.
1. To contribute from another code hosting site, you must first complete the steps above to fork our repo to a GitHub namespace you have permission to use, for example: `https://github.com/<your_namespace>/<your_repo_name>/`.
2. Create a repo in your code hosting site, for example: `https://gitlab.com/<your_namespace>/<your_repo_name>/`
3. Follow the instructions from your code hosting site to create a mirror between `https://github.com/<your_namespace>/<your_repo_name>/` and `https://gitlab.com/<your_namespace>/<your_repo_name>/`.
4. When you are ready to create a Pull Request (PR), follow the steps `(starting at step #6)` from [Forking and Cloning from GitHub to GitHub](#forking-and-cloning-from-github-to-github) and create the PR from `https://github.com/<your_namespace>/<your_repo_name>/`.
## Notes for squashing commits with rebase
- To rebase your commits and squash previous commits, you can use:
```bash
git rebase -i your_topic_branch~(number of commits to combine)
```
- For more details visit [gitready.com](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html)
1. The following would combine the last four commits in the branch `mytopic`.
```bash
git rebase -i mytopic~4
```
2. An editor window opens with the most recent commits indicated: (edit the commands to the left of the commit ID)
```gitattributes
pick 9dff55b2 existing commit comments
squash ebb1a730 existing commit comments
squash 07cc5b50 existing commit comments
reword 9dff55b2 existing commit comments
```
3. Save and close the editor. The next editor window opens: (edit the new commit message). *If you select reword for a commit, an additional editor window will open for you to edit the comment.*
```bash
new commit comments
Signed-off-by: yourname <your email address>
```
4. Save and close the editor for the rebase process to execute. The terminal output should say something like the following:
```bash
Successfully rebased and updated refs/heads/mytopic.
```
5. Once you have a successful rebase, and before you sync your local clone, you have to force push origin to update your repo:
```bash
git push -f origin
```
6. Continue on from step #7 from [Forking and Cloning from GitHub to GitHub](#forking-and-cloning-from-github-to-github)

231
README.md
View File

@@ -1,164 +1,213 @@
<!-- markdownlint-configure-file { "MD004": { "style": "consistent" } } -->
<!-- markdownlint-disable MD033 -->
#
<p align="center">
<a href="https://pi-hole.net/">
<img src="https://pi-hole.github.io/graphics/Vortex/Vortex_with_Wordmark.svg" width="150" height="260" alt="Pi-hole">
</a>
<br>
<strong>Network-wide ad blocking via your own Linux hardware</strong>
<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/>
<b>Network-wide ad blocking via your own Linux hardware</b><br/>
</p>
<!-- markdownlint-enable MD033 -->
The Pi-hole® is a [DNS sinkhole](https://en.wikipedia.org/wiki/DNS_Sinkhole) that protects your devices from unwanted content without installing any client-side software.
The Pi-hole[®](https://pi-hole.net/trademark-rules-and-brand-guidelines/) is a [DNS sinkhole](https://en.wikipedia.org/wiki/DNS_Sinkhole) that protects your devices from unwanted content, without installing any client-side software.
- **Easy-to-install**: our versatile installer walks you through the process and takes less than ten minutes
- **Easy-to-install**: our versatile installer walks you through the process, and [takes less than ten minutes](https://www.youtube.com/watch?v=vKWjx1AQYgs)
- **Resolute**: content is blocked in _non-browser locations_, such as ad-laden mobile apps and smart TVs
- **Responsive**: seamlessly speeds up the feel of everyday browsing by caching DNS queries
- **Lightweight**: runs smoothly with [minimal hardware and software requirements](https://docs.pi-hole.net/main/prerequisites/)
- **Lightweight**: runs smoothly with [minimal hardware and software requirements](https://discourse.pi-hole.net/t/hardware-software-requirements/273)
- **Robust**: a command line interface that is quality assured for interoperability
- **Insightful**: a beautiful responsive Web Interface dashboard to view and control your Pi-hole
- **Versatile**: can optionally function as a [DHCP server](https://discourse.pi-hole.net/t/how-do-i-use-pi-holes-built-in-dhcp-server-and-why-would-i-want-to/3026), ensuring *all* your devices are protected automatically
- **Scalable**: [capable of handling hundreds of millions of queries](https://pi-hole.net/2017/05/24/how-much-traffic-can-pi-hole-handle/) when installed on server-grade hardware
- **Modern**: blocks ads over both IPv4 and IPv6
- **Free**: open source software that helps ensure _you_ are the sole person in control of your privacy
- **Free**: open source software which helps ensure _you_ are the sole person in control of your privacy
-----
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/c558a0f8d7124c99b02b84f0f5564238)](https://www.codacy.com/app/Pi-hole/pi-hole?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=pi-hole/pi-hole&amp;utm_campaign=Badge_Grade)
[![Build Status](https://travis-ci.org/pi-hole/pi-hole.svg?branch=development)](https://travis-ci.org/pi-hole/pi-hole)
[![BountySource](https://www.bountysource.com/badge/tracker?tracker_id=3011939)](https://www.bountysource.com/trackers/3011939-pi-hole-pi-hole?utm_source=3011939&utm_medium=shield&utm_campaign=TRACKER_BADGE)
## One-Step Automated Install
Those who want to get started quickly and conveniently may install Pi-hole using the following command:
### `curl -sSL https://install.pi-hole.net | bash`
#### `curl -sSL https://install.pi-hole.net | bash`
## Alternative Install Methods
Piping to `bash` is [controversial](https://pi-hole.net/2016/07/25/curling-and-piping-to-bash), as it prevents you from [reading code that is about to run](https://github.com/pi-hole/pi-hole/blob/master/automated%20install/basic-install.sh) on your system. Therefore, we provide these alternative installation methods which allow code review before installation:
[Piping to `bash` is controversial](https://pi-hole.net/2016/07/25/curling-and-piping-to-bash), as it prevents you from [reading code that is about to run](https://github.com/pi-hole/pi-hole/blob/master/automated%20install/basic-install.sh) on your system. Therefore, we provide these alternative installation methods which allow code review before installation:
### Method 1: Clone our repository and run
```bash
```
git clone --depth 1 https://github.com/pi-hole/pi-hole.git Pi-hole
cd "Pi-hole/automated install/"
sudo bash basic-install.sh
```
### Method 2: Manually download the installer and run
```bash
```
wget -O basic-install.sh https://install.pi-hole.net
sudo bash basic-install.sh
```
### Method 3: Using Docker to deploy Pi-hole
Please refer to the [Pi-hole docker repo](https://github.com/pi-hole/docker-pi-hole) to use the Official Docker Images.
## [Post-install: Make your network take advantage of Pi-hole](https://docs.pi-hole.net/main/post-install/)
## Post-install: Make your network take advantage of Pi-hole
Once the installer has been run, you will need to [configure your router to have **DHCP clients use Pi-hole as their DNS server**](https://discourse.pi-hole.net/t/how-do-i-configure-my-devices-to-use-pi-hole-as-their-dns-server/245) which ensures that all devices connecting to your network will have content blocked without any further intervention.
If your router does not support setting the DNS server, you can [use Pi-hole's built-in DHCP server](https://discourse.pi-hole.net/t/how-do-i-use-pi-holes-built-in-dhcp-server-and-why-would-i-want-to/3026); be sure to disable DHCP on your router first (if it has that feature available).
If your router does not support setting the DNS server, you can [use Pi-hole's built-in DHCP server](https://discourse.pi-hole.net/t/how-do-i-use-pi-holes-built-in-dhcp-server-and-why-would-i-want-to/3026); just be sure to disable DHCP on your router first (if it has that feature available).
As a last resort, you can manually set each device to use Pi-hole as their DNS server.
As a last resort, you can always manually set each device to use Pi-hole as their DNS server.
-----
## 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 and the importance of keeping it maintained.
## 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](https://pi-hole.net/donate)
### Donations
Sending a donation using our links below is **extremely helpful** in offsetting a portion of our monthly expenses:
Donating using our Sponsor Button 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, Bitcoin Cash, Ethereum, Litecoin](https://commerce.coinbase.com/checkout/dd304d04-f324-4a77-931b-0db61c77a41b)
### Alternative support
If you'd rather not donate (_which is okay!_), there are other ways you can help support us:
- [GitHub Sponsors](https://github.com/sponsors/pi-hole/)
- [Patreon](https://patreon.com/pihole)
- [Hetzner Cloud](https://hetzner.cloud/?ref=7aceisRX3AzA) _affiliate link_
- [Digital Ocean](https://www.digitalocean.com/?refcode=344d234950e1) _affiliate link_
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_
- [Stickermule](https://www.stickermule.com/unlock?ref_id=9127301701&utm_medium=link&utm_source=invite) _earn a $10 credit after your first purchase_
- [Amazon US](http://www.amazon.com/exec/obidos/redirect-home/pihole09-20) _affiliate link_
- Spreading the word about our software and how you have benefited from it
- [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 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.
You'll find that the [install script](https://github.com/pi-hole/pi-hole/blob/master/automated%20install/basic-install.sh) and the [debug script](https://github.com/pi-hole/pi-hole/blob/master/advanced/Scripts/piholeDebug.sh) have an abundance of comments, which will help you better understand how Pi-hole works. They're also a valuable resource to those who want to learn how to write scripts or code a program! We encourage anyone who likes to tinker to read through it and submit a pull request for us to review.
### 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.
While we are primarily reachable on our [Discourse User Forum](https://discourse.pi-hole.net/), we can also be found on various social media outlets.
**Please be sure to check the FAQs** before starting a new discussion, as we do not have the spare time to reply to every request for assistance.
- [Frequently Asked Questions](https://discourse.pi-hole.net/c/faqs)
- [Feature Requests](https://discourse.pi-hole.net/c/feature-requests?order=votes)
- [Reddit](https://www.reddit.com/r/pihole/)
- [Twitter](https://twitter.com/The_Pi_hole)
<ul>
<li><a href="https://discourse.pi-hole.net/c/faqs">Frequently Asked Questions</a></li>
<li><a href="https://github.com/pi-hole/pi-hole/wiki">Pi-hole Wiki</a></li>
<li><a href="https://discourse.pi-hole.net/c/feature-requests?order=votes">Feature Requests</a></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://gitter.im/pi-hole/pi-hole">Gitter</a> (Real-time chat)</li>
<li><a href="https://twitter.com/The_Pi_Hole">Twitter</a></li>
<li><a href="https://www.youtube.com/channel/UCT5kq9w0wSjogzJb81C9U0w">YouTube</a></li>
<li><a href="https://www.facebook.com/ThePiHole/">Facebook</a></li>
</ul>
-----
## Breakdown of Features
### [Faster-than-light Engine](https://github.com/pi-hole/ftl)
[FTLDNS](https://github.com/pi-hole/ftl) is a lightweight, purpose-built daemon used to provide statistics needed for the Web Interface, and its API can be easily integrated into your own projects. As the name implies, FTLDNS does this all *very quickly*!
Some of the statistics you can integrate include:
- Total number of domains being blocked
- Total number of DNS queries today
- Total number of ads blocked today
- Percentage of ads blocked
- Unique domains
- Queries forwarded (to your chosen upstream DNS server)
- Queries cached
- Unique clients
Access the API via [`telnet`](https://github.com/pi-hole/FTL), the Web (`admin/api.php`) and Command Line (`pihole -c -j`). You can find out [more details over here](https://discourse.pi-hole.net/t/pi-hole-api/1863).
### The Command Line Interface
The `pihole` command has all the functionality necessary to be able to fully administer the Pi-hole, without the need of the Web Interface. It's fast, user-friendly, and auditable by anyone with an understanding of `bash`.
The [pihole](https://docs.pi-hole.net/core/pihole-command/) command has all the functionality necessary to fully administer the Pi-hole, without the need of the Web Interface. It's fast, user-friendly, and auditable by anyone with an understanding of `bash`.
<a href="https://pi-hole.github.io/graphics/Screenshots/blacklist-cli.gif"><img src="https://pi-hole.github.io/graphics/Screenshots/blacklist-cli.gif" alt="Pi-hole Blacklist Demo"/></a>
Some notable features include:
* [Whitelisting, Blacklisting and Wildcards](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#whitelisting-blacklisting-and-wildcards)
* [Debugging utility](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#debugger)
* [Viewing the live log file](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#tail)
* [Real-time Statistics via `ssh`](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#chronometer) or [your TFT LCD screen](http://www.amazon.com/exec/obidos/ASIN/B00ID39LM4/pihole09-20)
* [Updating Ad Lists](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#gravity)
* [Querying Ad Lists for blocked domains](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#query)
* [Enabling and Disabling Pi-hole](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#enable--disable)
* ... and *many* more!
- [Whitelisting, Blacklisting, and Regex](https://docs.pi-hole.net/core/pihole-command/#whitelisting-blacklisting-and-regex)
- [Debugging utility](https://docs.pi-hole.net/core/pihole-command/#debugger)
- [Viewing the live log file](https://docs.pi-hole.net/core/pihole-command/#tail)
- [Updating Ad Lists](https://docs.pi-hole.net/core/pihole-command/#gravity)
- [Querying Ad Lists for blocked domains](https://docs.pi-hole.net/core/pihole-command/#query)
- [Enabling and Disabling Pi-hole](https://docs.pi-hole.net/core/pihole-command/#enable-disable)
- ... and *many* more!
You can read our [Core Feature Breakdown](https://docs.pi-hole.net/core/pihole-command/#pi-hole-core) for more information.
You can read our [Core Feature Breakdown](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown), as well as read up on [example usage](https://discourse.pi-hole.net/t/the-pihole-command-with-examples/738) for more information.
### The Web Interface Dashboard
This [optional dashboard](https://github.com/pi-hole/AdminLTE) allows you to view stats, change settings, and configure your Pi-hole. It's the power of the Command Line Interface, with none of the learning curve!
Some notable features include:
<img src="https://pi-hole.github.io/graphics/Screenshots/pihole-dashboard.png" alt="Pi-hole Dashboard"/></a>
- Mobile-friendly interface
- Password protection
- Detailed graphs and doughnut charts
- Top lists of domains and clients
- A filterable and sortable query log
- Long Term Statistics to view data over user-defined time ranges
- The ability to easily manage and configure Pi-hole features
- ... and all the main features of the Command Line Interface!
Some notable features include:
* Mobile friendly interface
* Password protection
* Detailed graphs and doughnut charts
* Top lists of domains and clients
* A filterable and sortable query log
* Long Term Statistics to view data over user-defined time ranges
* The ability to easily manage and configure Pi-hole features
* ... and all the main features of the Command Line Interface!
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://pi.hole/admin/` (when using Pi-hole as your DNS server)
2. `http://<IP_ADDPRESS_OF_YOUR_PI_HOLE>/admin/`
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)
## Faster-than-light Engine
FTLDNS is a lightweight, purpose-built daemon used to provide statistics needed for the Web Interface, and its API can be easily integrated into your own projects. As the name implies, FTLDNS does this all *very quickly*!
Some of the statistics you can integrate include:
* Total number of domains being blocked
* Total number of DNS queries today
* Total number of ads blocked today
* Percentage of ads blocked
* Unique domains
* Queries forwarded (to your chosen upstream DNS server)
* Queries cached
* Unique clients
The API can be accessed via [`telnet`](https://github.com/pi-hole/FTL), the Web (`admin/api.php`) and Command Line (`pihole -c -j`). You can out find [more details over here](https://discourse.pi-hole.net/t/pi-hole-api/1863).
-----
## The Origin Of Pi-hole
Pi-hole being an **advertising-aware DNS/Web server**, makes use of the following technologies:
* [`dnsmasq`](http://www.thekelleys.org.uk/dnsmasq/doc.html) - a lightweight DNS and DHCP server
* [`curl`](https://curl.haxx.se) - A command line tool for transferring data with URL syntax
* [`lighttpd`](https://www.lighttpd.net) - web server designed and optimized for high performance
* [`php`](https://secure.php.net) - a popular general-purpose web scripting language
* [AdminLTE Dashboard](https://github.com/almasaeed2010/AdminLTE) - premium admin control panel based on Bootstrap 3.x
While quite outdated at this point, [this original blog post about Pi-hole](https://jacobsalmela.com/2015/06/16/block-millions-ads-network-wide-with-a-raspberry-pi-hole-2-0/) goes into **great detail** about how Pi-hole was originally set up and how it works. Syntactically, it's no longer accurate, but the same basic principles and logic still apply to Pi-hole's current state.
-----
## Coverage
- [Lifehacker: Turn A Raspberry Pi Into An Ad Blocker With A Single Command](https://www.lifehacker.com.au/2015/02/turn-a-raspberry-pi-into-an-ad-blocker-with-a-single-command/) (Feburary, 2015)
- [MakeUseOf: Adblock Everywhere: The Raspberry Pi-Hole Way](http://www.makeuseof.com/tag/adblock-everywhere-raspberry-pi-hole-way/) (March, 2015)
- [Catchpoint: Ad-Blocking on Apple iOS9: Valuing the End User Experience](http://blog.catchpoint.com/2015/09/14/ad-blocking-apple/) (September, 2015)
- [Security Now Netcast: Pi-hole](https://www.youtube.com/watch?v=p7-osq_y8i8&t=100m26s) (October, 2015)
- [TekThing: Raspberry Pi-Hole Makes Ads Disappear!](https://youtu.be/8Co59HU2gY0?t=2m) (December, 2015)
- [Foolish Tech Show](https://youtu.be/bYyena0I9yc?t=2m4s) (December, 2015)
- [Block Ads on All Home Devices for $53.18](https://medium.com/@robleathern/block-ads-on-all-home-devices-for-53-18-a5f1ec139693#.gj1xpgr5d) (December, 2015)
- [Pi-Hole for Ubuntu 14.04](http://www.boyter.org/2015/12/pi-hole-ubuntu-14-04/) (December, 2015)
- [MacObserver Podcast 585](https://www.macobserver.com/tmo/podcast/macgeekgab-585) (December, 2015)
- [The Defrag Show: Endoscope USB Camera, The Final [HoloLens] Vote, Adblock Pi and more](https://channel9.msdn.com/Shows/The-Defrag-Show/Defrag-Endoscope-USB-Camera-The-Final-HoloLens-Vote-Adblock-Pi-and-more?WT.mc_id=dlvr_twitter_ch9#time=20m39s) (January, 2016)
- [Adafruit: Pi-hole is a black hole for internet ads](https://blog.adafruit.com/2016/03/04/pi-hole-is-a-black-hole-for-internet-ads-piday-raspberrypi-raspberry_pi/) (March, 2016)
- [Digital Trends: 5 Fun, Easy Projects You Can Try With a $35 Raspberry Pi](https://youtu.be/QwrKlyC2kdM?t=1m42s) (March, 2016)
- [Adafruit: Raspberry Pi Quick Look at Pi Hole ad blocking server with Tony D](https://www.youtube.com/watch?v=eg4u2j1HYlI) (June, 2016)
- [Devacron: OrangePi Zero as an Ad-Block server with Pi-Hole](http://www.devacron.com/orangepi-zero-as-an-ad-block-server-with-pi-hole/) (December, 2016)
- [Linux Pro: The Hole Truth](http://www.linuxpromagazine.com/Issues/2017/200/The-sysadmin-s-daily-grind-Pi-hole) (July, 2017)
- [Adafruit: installing Pi-hole on a Pi Zero W](https://learn.adafruit.com/pi-hole-ad-blocker-with-pi-zero-w/install-pi-hole) (August, 2017)
- [CryptoAUSTRALIA: How We Tried 5 Privacy Focused Raspberry Pi Projects](https://blog.cryptoaustralia.org.au/2017/10/05/5-privacy-focused-raspberry-pi-projects/) (October, 2017)
- [CryptoAUSTRALIA: Pi-hole Workshop](https://blog.cryptoaustralia.org.au/2017/11/02/pi-hole-network-wide-ad-blocker/) (November, 2017)
- [Know How 355: Killing ads with a Raspberry Pi-Hole!](https://www.twit.tv/shows/know-how/episodes/355) (November, 2017)
- [Hobohouse: Block Advertising on your Network with Pi-hole and Raspberry Pi](https://hobo.house/2018/02/27/block-advertising-with-pi-hole-and-raspberry-pi/) (March, 2018)
- [Scott Helme: Securing DNS across all of my devices with Pi-Hole + DNS-over-HTTPS + 1.1.1.1](https://scotthelme.co.uk/securing-dns-across-all-of-my-devices-with-pihole-dns-over-https-1-1-1-1/) (April, 2018)
- [Scott Helme: Catching and dealing with naughty devices on my home network](https://scotthelme.co.uk/catching-naughty-devices-on-my-home-network/) (April, 2018)
- [Bloomberg Business Week: Brotherhood of the Ad blockers](https://www.bloomberg.com/news/features/2018-05-10/inside-the-brotherhood-of-pi-hole-ad-blockers) (May, 2018)
- [Software Engineering Daily: Interview with the creator of Pi-hole](https://softwareengineeringdaily.com/2018/05/29/pi-hole-ad-blocker-hardware-with-jacob-salmela/) (May, 2018)
- [Raspberry Pi: Block ads at home using Pi-hole and a Raspberry Pi](https://www.raspberrypi.org/blog/pi-hole-raspberry-pi/) (July, 2018)
- [Troy Hunt: Mmm... Pi-hole...](https://www.troyhunt.com/mmm-pi-hole/) (September, 2018)
- [PEBKAK Podcast: Interview With Jacob Salmela](https://www.jerseystudios.net/2018/10/11/150-pi-hole/) (October, 2018)
-----
## Pi-hole Projects
- [The Big Blocklist Collection](https://wally3k.github.io)
- [Pie in the Sky-Hole](https://dlaa.me/blog/post/skyhole)
- [Copernicus: Windows Tray Application](https://github.com/goldbattle/copernicus)
- [Magic Mirror with DNS Filtering](https://zonksec.com/blog/magic-mirror-dns-filtering/#dnssoftware)
- [Windows DNS Swapper](https://github.com/roots84/DNS-Swapper)

View File

@@ -18,8 +18,9 @@
# WITHIN /etc/dnsmasq.d/yourname.conf #
###############################################################################
addn-hosts=/etc/pihole/gravity.list
addn-hosts=/etc/pihole/black.list
addn-hosts=/etc/pihole/local.list
addn-hosts=/etc/pihole/custom.list
domain-needed
@@ -34,9 +35,16 @@ server=@DNS2@
interface=@INT@
cache-size=@CACHE_SIZE@
cache-size=10000
log-queries
log-facility=/var/log/pihole.log
local-ttl=2
log-async
# If a DHCP client claims that its name is "wpad", ignore that.
# This fixes a security hole. see CERT Vulnerability VU#598349
dhcp-name-match=set:wpad-ignore,wpad
dhcp-ignore-names=tag:wpad-ignore

View File

@@ -1,41 +0,0 @@
# Pi-hole: A black hole for Internet advertisements
# (c) 2021 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware.
#
# RFC 6761 config file for Pi-hole
#
# This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license.
###############################################################################
# FILE AUTOMATICALLY POPULATED BY PI-HOLE INSTALL/UPDATE PROCEDURE. #
# ANY CHANGES MADE TO THIS FILE AFTER INSTALL WILL BE LOST ON THE NEXT UPDATE #
# #
# CHANGES SHOULD BE MADE IN A SEPARATE CONFIG FILE #
# WITHIN /etc/dnsmasq.d/yourname.conf #
###############################################################################
# RFC 6761: Caching DNS servers SHOULD recognize
# test, localhost, invalid
# names as special and SHOULD NOT attempt to look up NS records for them, or
# otherwise query authoritative DNS servers in an attempt to resolve these
# names.
server=/test/
server=/localhost/
server=/invalid/
# The same RFC requests something similar for
# 16.172.in-addr.arpa. 22.172.in-addr.arpa. 27.172.in-addr.arpa.
# 17.172.in-addr.arpa. 30.172.in-addr.arpa. 28.172.in-addr.arpa.
# 18.172.in-addr.arpa. 23.172.in-addr.arpa. 29.172.in-addr.arpa.
# 19.172.in-addr.arpa. 24.172.in-addr.arpa. 31.172.in-addr.arpa.
# 20.172.in-addr.arpa. 25.172.in-addr.arpa. 168.192.in-addr.arpa.
# Pi-hole implements this via the dnsmasq option "bogus-priv" (see
# 01-pihole.conf) because this also covers IPv6.
# OpenWRT furthermore blocks bind, local, onion domains
# see https://git.openwrt.org/?p=openwrt/openwrt.git;a=blob_plain;f=package/network/services/dnsmasq/files/rfc6761.conf;hb=HEAD
# and https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml
# We do not include the ".local" rule ourselves, see https://github.com/pi-hole/pi-hole/pull/4282#discussion_r689112972
server=/bind/
server=/onion/

View File

@@ -1,7 +1,7 @@
# Determine if terminal is capable of showing colors
# Determine if terminal is capable of showing colours
if [[ -t 1 ]] && [[ $(tput colors) -ge 8 ]]; then
# Bold and underline may not show up on all clients
# If something MUST be emphasized, use both
# If something MUST be emphasised, use both
COL_BOLD=''
COL_ULINE=''

View File

@@ -13,21 +13,19 @@ LC_NUMERIC=C
# Retrieve stats from FTL engine
pihole-FTL() {
local ftl_port LINE
ftl_port=$(cat /run/pihole-FTL.port 2> /dev/null)
ftl_port=$(cat /var/run/pihole-FTL.port 2> /dev/null)
if [[ -n "$ftl_port" ]]; then
# Open connection to FTL
exec 3<>"/dev/tcp/127.0.0.1/$ftl_port"
# Test if connection is open
if { "true" >&3; } 2> /dev/null; then
# Send command to FTL and ask to quit when finished
echo -e ">$1 >quit" >&3
# Send command to FTL
echo -e ">$1" >&3
# Read input until we received an empty string and the connection is
# closed
# Read input
read -r -t 1 LINE <&3
until [[ -z "${LINE}" ]] && [[ ! -t 3 ]]; do
until [[ ! $? ]] || [[ "$LINE" == *"EOM"* ]]; do
echo "$LINE" >&1
read -r -t 1 LINE <&3
done
@@ -74,7 +72,7 @@ printFunc() {
# Remove excess characters from main text
if [[ "$text_main_len" -gt "$text_main_max_len" ]]; then
# Trim text without colors
# Trim text without colours
text_main_trim="${text_main_nocol:0:$text_main_max_len}"
# Replace with trimmed text
text_main="${text_main/$text_main_nocol/$text_main_trim}"
@@ -90,7 +88,7 @@ printFunc() {
[[ "$spc_num" -le 0 ]] && spc_num="0"
spc=$(printf "%${spc_num}s")
#spc="${spc// /.}" # Debug: Visualize spaces
#spc="${spc// /.}" # Debug: Visualise spaces
printf "%s%s$spc" "$title" "$text_main"
@@ -133,7 +131,7 @@ get_init_stats() {
printf "%s%02d:%02d:%02d\\n" "$days" "$hrs" "$mins" "$secs"
}
# Set Color Codes
# Set Colour Codes
coltable="/opt/pihole/COL_TABLE"
if [[ -f "${coltable}" ]]; then
source ${coltable}
@@ -155,7 +153,7 @@ get_init_stats() {
sys_throttle_raw=$(vgt=$(sudo vcgencmd get_throttled); echo "${vgt##*x}")
# Active Throttle Notice: https://bit.ly/2gnunOo
# Active Throttle Notice: http://bit.ly/2gnunOo
if [[ "$sys_throttle_raw" != "0" ]]; then
case "$sys_throttle_raw" in
*0001) thr_type="${COL_YELLOW}Under Voltage";;
@@ -230,21 +228,15 @@ get_sys_stats() {
mapfile -t ph_ver_raw < <(pihole -v -c 2> /dev/null | sed -n 's/^.* v/v/p')
if [[ -n "${ph_ver_raw[0]}" ]]; then
ph_core_ver="${ph_ver_raw[0]}"
if [[ ${#ph_ver_raw[@]} -eq 2 ]]; then
# AdminLTE not installed
ph_lte_ver="(not installed)"
ph_ftl_ver="${ph_ver_raw[1]}"
else
ph_lte_ver="${ph_ver_raw[1]}"
ph_ftl_ver="${ph_ver_raw[2]}"
fi
ph_lte_ver="${ph_ver_raw[1]}"
ph_ftl_ver="${ph_ver_raw[2]}"
else
ph_core_ver="-1"
fi
sys_name=$(hostname)
[[ -n "$TEMPERATUREUNIT" ]] && temp_unit="${TEMPERATUREUNIT^^}" || temp_unit="C"
[[ -n "$TEMPERATUREUNIT" ]] && temp_unit="$TEMPERATUREUNIT" || temp_unit="c"
# Get storage stats for partition mounted on /
read -r -a disk_raw <<< "$(df -B1 / 2> /dev/null | awk 'END{ print $3,$2,$5 }')"
@@ -277,7 +269,7 @@ get_sys_stats() {
scr_lines="${scr_size[0]}"
scr_cols="${scr_size[1]}"
# Determine Chronometer size behavior
# Determine Chronometer size behaviour
if [[ "$scr_cols" -ge 58 ]]; then
chrono_width="large"
elif [[ "$scr_cols" -gt 40 ]]; then
@@ -316,7 +308,7 @@ get_sys_stats() {
[[ "${cpu_freq}" == *".0"* ]] && cpu_freq="${cpu_freq/.0/}"
fi
# Determine color for temperature
# Determine colour for temperature
if [[ -n "$temp_file" ]]; then
if [[ "$temp_unit" == "C" ]]; then
cpu_temp=$(printf "%.0fc\\n" "$(calcFunc "$(< $temp_file) / 1000")")
@@ -498,6 +490,10 @@ chronoFunc() {
printFunc " RAM usage: " "$ram_perc%" "$ram_info"
printFunc " HDD usage: " "$disk_perc" "$disk_info"
if [[ "$scr_lines" -gt 17 ]] && [[ "$chrono_width" != "small" ]]; then
printFunc " LAN addr: " "${IPV4_ADDRESS/\/*/}" "$lan_info"
fi
if [[ "$DHCP_ACTIVE" == "true" ]]; then
printFunc "DHCP usage: " "$ph_dhcp_percent%" "$dhcp_info"
fi
@@ -555,7 +551,7 @@ Calculates stats and displays to an LCD
Options:
-j, --json Output stats as JSON formatted string
-r, --refresh Set update frequency (in seconds)
-e, --exit Output stats and exit without refreshing
-e, --exit Output stats and exit witout refreshing
-h, --help Display this help text"
fi

View File

@@ -1,125 +0,0 @@
#!/usr/bin/env bash
# shellcheck disable=SC1090
# Pi-hole: A black hole for Internet advertisements
# (c) 2019 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware.
#
# Updates gravity.db database
#
# This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license.
readonly scriptPath="/etc/.pihole/advanced/Scripts/database_migration/gravity"
upgrade_gravityDB(){
local database piholeDir auditFile version
database="${1}"
piholeDir="${2}"
auditFile="${piholeDir}/auditlog.list"
# Get database version
version="$(sqlite3 "${database}" "SELECT \"value\" FROM \"info\" WHERE \"property\" = 'version';")"
if [[ "$version" == "1" ]]; then
# This migration script upgrades the gravity.db file by
# adding the domain_audit table
echo -e " ${INFO} Upgrading gravity database from version 1 to 2"
sqlite3 "${database}" < "${scriptPath}/1_to_2.sql"
version=2
# Store audit domains in database table
if [ -e "${auditFile}" ]; then
echo -e " ${INFO} Migrating content of ${auditFile} into new database"
# database_table_from_file is defined in gravity.sh
database_table_from_file "domain_audit" "${auditFile}"
fi
fi
if [[ "$version" == "2" ]]; then
# This migration script upgrades the gravity.db file by
# renaming the regex table to regex_blacklist, and
# creating a new regex_whitelist table + corresponding linking table and views
echo -e " ${INFO} Upgrading gravity database from version 2 to 3"
sqlite3 "${database}" < "${scriptPath}/2_to_3.sql"
version=3
fi
if [[ "$version" == "3" ]]; then
# This migration script unifies the formally separated domain
# lists into a single table with a UNIQUE domain constraint
echo -e " ${INFO} Upgrading gravity database from version 3 to 4"
sqlite3 "${database}" < "${scriptPath}/3_to_4.sql"
version=4
fi
if [[ "$version" == "4" ]]; then
# This migration script upgrades the gravity and list views
# implementing necessary changes for per-client blocking
echo -e " ${INFO} Upgrading gravity database from version 4 to 5"
sqlite3 "${database}" < "${scriptPath}/4_to_5.sql"
version=5
fi
if [[ "$version" == "5" ]]; then
# This migration script upgrades the adlist view
# to return an ID used in gravity.sh
echo -e " ${INFO} Upgrading gravity database from version 5 to 6"
sqlite3 "${database}" < "${scriptPath}/5_to_6.sql"
version=6
fi
if [[ "$version" == "6" ]]; then
# This migration script adds a special group with ID 0
# which is automatically associated to all clients not
# having their own group assignments
echo -e " ${INFO} Upgrading gravity database from version 6 to 7"
sqlite3 "${database}" < "${scriptPath}/6_to_7.sql"
version=7
fi
if [[ "$version" == "7" ]]; then
# This migration script recreated the group table
# to ensure uniqueness on the group name
# We also add date_added and date_modified columns
echo -e " ${INFO} Upgrading gravity database from version 7 to 8"
sqlite3 "${database}" < "${scriptPath}/7_to_8.sql"
version=8
fi
if [[ "$version" == "8" ]]; then
# This migration fixes some issues that were introduced
# in the previous migration script.
echo -e " ${INFO} Upgrading gravity database from version 8 to 9"
sqlite3 "${database}" < "${scriptPath}/8_to_9.sql"
version=9
fi
if [[ "$version" == "9" ]]; then
# This migration drops unused tables and creates triggers to remove
# obsolete groups assignments when the linked items are deleted
echo -e " ${INFO} Upgrading gravity database from version 9 to 10"
sqlite3 "${database}" < "${scriptPath}/9_to_10.sql"
version=10
fi
if [[ "$version" == "10" ]]; then
# This adds timestamp and an optional comment field to the client table
# These fields are only temporary and will be replaces by the columns
# defined in gravity.db.sql during gravity swapping. We add them here
# to keep the copying process generic (needs the same columns in both the
# source and the destination databases).
echo -e " ${INFO} Upgrading gravity database from version 10 to 11"
sqlite3 "${database}" < "${scriptPath}/10_to_11.sql"
version=11
fi
if [[ "$version" == "11" ]]; then
# Rename group 0 from "Unassociated" to "Default"
echo -e " ${INFO} Upgrading gravity database from version 11 to 12"
sqlite3 "${database}" < "${scriptPath}/11_to_12.sql"
version=12
fi
if [[ "$version" == "12" ]]; then
# Add column date_updated to adlist table
echo -e " ${INFO} Upgrading gravity database from version 12 to 13"
sqlite3 "${database}" < "${scriptPath}/12_to_13.sql"
version=13
fi
if [[ "$version" == "13" ]]; then
# Add columns number and status to adlist table
echo -e " ${INFO} Upgrading gravity database from version 13 to 14"
sqlite3 "${database}" < "${scriptPath}/13_to_14.sql"
version=14
fi
}

View File

@@ -1,16 +0,0 @@
.timeout 30000
BEGIN TRANSACTION;
ALTER TABLE client ADD COLUMN date_added INTEGER;
ALTER TABLE client ADD COLUMN date_modified INTEGER;
ALTER TABLE client ADD COLUMN comment TEXT;
CREATE TRIGGER tr_client_update AFTER UPDATE ON client
BEGIN
UPDATE client SET date_modified = (cast(strftime('%s', 'now') as int)) WHERE id = NEW.id;
END;
UPDATE info SET value = 11 WHERE property = 'version';
COMMIT;

View File

@@ -1,19 +0,0 @@
.timeout 30000
PRAGMA FOREIGN_KEYS=OFF;
BEGIN TRANSACTION;
UPDATE "group" SET name = 'Default' WHERE id = 0;
UPDATE "group" SET description = 'The default group' WHERE id = 0;
DROP TRIGGER IF EXISTS tr_group_zero;
CREATE TRIGGER tr_group_zero AFTER DELETE ON "group"
BEGIN
INSERT OR IGNORE INTO "group" (id,enabled,name,description) VALUES (0,1,'Default','The default group');
END;
UPDATE info SET value = 12 WHERE property = 'version';
COMMIT;

View File

@@ -1,18 +0,0 @@
.timeout 30000
PRAGMA FOREIGN_KEYS=OFF;
BEGIN TRANSACTION;
ALTER TABLE adlist ADD COLUMN date_updated INTEGER;
DROP TRIGGER tr_adlist_update;
CREATE TRIGGER tr_adlist_update AFTER UPDATE OF address,enabled,comment ON adlist
BEGIN
UPDATE adlist SET date_modified = (cast(strftime('%s', 'now') as int)) WHERE id = NEW.id;
END;
UPDATE info SET value = 13 WHERE property = 'version';
COMMIT;

View File

@@ -1,13 +0,0 @@
.timeout 30000
PRAGMA FOREIGN_KEYS=OFF;
BEGIN TRANSACTION;
ALTER TABLE adlist ADD COLUMN number INTEGER NOT NULL DEFAULT 0;
ALTER TABLE adlist ADD COLUMN invalid_domains INTEGER NOT NULL DEFAULT 0;
ALTER TABLE adlist ADD COLUMN status INTEGER NOT NULL DEFAULT 0;
UPDATE info SET value = 14 WHERE property = 'version';
COMMIT;

View File

@@ -1,14 +0,0 @@
.timeout 30000
BEGIN TRANSACTION;
CREATE TABLE domain_audit
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
domain TEXT UNIQUE NOT NULL,
date_added INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int))
);
UPDATE info SET value = 2 WHERE property = 'version';
COMMIT;

View File

@@ -1,65 +0,0 @@
.timeout 30000
PRAGMA FOREIGN_KEYS=OFF;
BEGIN TRANSACTION;
ALTER TABLE regex RENAME TO regex_blacklist;
CREATE TABLE regex_blacklist_by_group
(
regex_blacklist_id INTEGER NOT NULL REFERENCES regex_blacklist (id),
group_id INTEGER NOT NULL REFERENCES "group" (id),
PRIMARY KEY (regex_blacklist_id, group_id)
);
INSERT INTO regex_blacklist_by_group SELECT * FROM regex_by_group;
DROP TABLE regex_by_group;
DROP VIEW vw_regex;
DROP TRIGGER tr_regex_update;
CREATE VIEW vw_regex_blacklist AS SELECT DISTINCT domain
FROM regex_blacklist
LEFT JOIN regex_blacklist_by_group ON regex_blacklist_by_group.regex_blacklist_id = regex_blacklist.id
LEFT JOIN "group" ON "group".id = regex_blacklist_by_group.group_id
WHERE regex_blacklist.enabled = 1 AND (regex_blacklist_by_group.group_id IS NULL OR "group".enabled = 1)
ORDER BY regex_blacklist.id;
CREATE TRIGGER tr_regex_blacklist_update AFTER UPDATE ON regex_blacklist
BEGIN
UPDATE regex_blacklist SET date_modified = (cast(strftime('%s', 'now') as int)) WHERE domain = NEW.domain;
END;
CREATE TABLE regex_whitelist
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
domain TEXT UNIQUE NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT 1,
date_added INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
date_modified INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
comment TEXT
);
CREATE TABLE regex_whitelist_by_group
(
regex_whitelist_id INTEGER NOT NULL REFERENCES regex_whitelist (id),
group_id INTEGER NOT NULL REFERENCES "group" (id),
PRIMARY KEY (regex_whitelist_id, group_id)
);
CREATE VIEW vw_regex_whitelist AS SELECT DISTINCT domain
FROM regex_whitelist
LEFT JOIN regex_whitelist_by_group ON regex_whitelist_by_group.regex_whitelist_id = regex_whitelist.id
LEFT JOIN "group" ON "group".id = regex_whitelist_by_group.group_id
WHERE regex_whitelist.enabled = 1 AND (regex_whitelist_by_group.group_id IS NULL OR "group".enabled = 1)
ORDER BY regex_whitelist.id;
CREATE TRIGGER tr_regex_whitelist_update AFTER UPDATE ON regex_whitelist
BEGIN
UPDATE regex_whitelist SET date_modified = (cast(strftime('%s', 'now') as int)) WHERE domain = NEW.domain;
END;
UPDATE info SET value = 3 WHERE property = 'version';
COMMIT;

View File

@@ -1,96 +0,0 @@
.timeout 30000
PRAGMA FOREIGN_KEYS=OFF;
BEGIN TRANSACTION;
CREATE TABLE domainlist
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
type INTEGER NOT NULL DEFAULT 0,
domain TEXT UNIQUE NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT 1,
date_added INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
date_modified INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
comment TEXT
);
ALTER TABLE whitelist ADD COLUMN type INTEGER;
UPDATE whitelist SET type = 0;
INSERT INTO domainlist (type,domain,enabled,date_added,date_modified,comment)
SELECT type,domain,enabled,date_added,date_modified,comment FROM whitelist;
ALTER TABLE blacklist ADD COLUMN type INTEGER;
UPDATE blacklist SET type = 1;
INSERT INTO domainlist (type,domain,enabled,date_added,date_modified,comment)
SELECT type,domain,enabled,date_added,date_modified,comment FROM blacklist;
ALTER TABLE regex_whitelist ADD COLUMN type INTEGER;
UPDATE regex_whitelist SET type = 2;
INSERT INTO domainlist (type,domain,enabled,date_added,date_modified,comment)
SELECT type,domain,enabled,date_added,date_modified,comment FROM regex_whitelist;
ALTER TABLE regex_blacklist ADD COLUMN type INTEGER;
UPDATE regex_blacklist SET type = 3;
INSERT INTO domainlist (type,domain,enabled,date_added,date_modified,comment)
SELECT type,domain,enabled,date_added,date_modified,comment FROM regex_blacklist;
DROP TABLE whitelist_by_group;
DROP TABLE blacklist_by_group;
DROP TABLE regex_whitelist_by_group;
DROP TABLE regex_blacklist_by_group;
CREATE TABLE domainlist_by_group
(
domainlist_id INTEGER NOT NULL REFERENCES domainlist (id),
group_id INTEGER NOT NULL REFERENCES "group" (id),
PRIMARY KEY (domainlist_id, group_id)
);
DROP TRIGGER tr_whitelist_update;
DROP TRIGGER tr_blacklist_update;
DROP TRIGGER tr_regex_whitelist_update;
DROP TRIGGER tr_regex_blacklist_update;
CREATE TRIGGER tr_domainlist_update AFTER UPDATE ON domainlist
BEGIN
UPDATE domainlist SET date_modified = (cast(strftime('%s', 'now') as int)) WHERE domain = NEW.domain;
END;
DROP VIEW vw_whitelist;
CREATE VIEW vw_whitelist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
FROM domainlist
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
WHERE domainlist.enabled = 1 AND (domainlist_by_group.group_id IS NULL OR "group".enabled = 1)
AND domainlist.type = 0
ORDER BY domainlist.id;
DROP VIEW vw_blacklist;
CREATE VIEW vw_blacklist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
FROM domainlist
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
WHERE domainlist.enabled = 1 AND (domainlist_by_group.group_id IS NULL OR "group".enabled = 1)
AND domainlist.type = 1
ORDER BY domainlist.id;
DROP VIEW vw_regex_whitelist;
CREATE VIEW vw_regex_whitelist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
FROM domainlist
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
WHERE domainlist.enabled = 1 AND (domainlist_by_group.group_id IS NULL OR "group".enabled = 1)
AND domainlist.type = 2
ORDER BY domainlist.id;
DROP VIEW vw_regex_blacklist;
CREATE VIEW vw_regex_blacklist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
FROM domainlist
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
WHERE domainlist.enabled = 1 AND (domainlist_by_group.group_id IS NULL OR "group".enabled = 1)
AND domainlist.type = 3
ORDER BY domainlist.id;
UPDATE info SET value = 4 WHERE property = 'version';
COMMIT;

View File

@@ -1,38 +0,0 @@
.timeout 30000
PRAGMA FOREIGN_KEYS=OFF;
BEGIN TRANSACTION;
DROP TABLE gravity;
CREATE TABLE gravity
(
domain TEXT NOT NULL,
adlist_id INTEGER NOT NULL REFERENCES adlist (id),
PRIMARY KEY(domain, adlist_id)
);
DROP VIEW vw_gravity;
CREATE VIEW vw_gravity AS SELECT domain, adlist_by_group.group_id AS group_id
FROM gravity
LEFT JOIN adlist_by_group ON adlist_by_group.adlist_id = gravity.adlist_id
LEFT JOIN adlist ON adlist.id = gravity.adlist_id
LEFT JOIN "group" ON "group".id = adlist_by_group.group_id
WHERE adlist.enabled = 1 AND (adlist_by_group.group_id IS NULL OR "group".enabled = 1);
CREATE TABLE client
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
ip TEXT NOL NULL UNIQUE
);
CREATE TABLE client_by_group
(
client_id INTEGER NOT NULL REFERENCES client (id),
group_id INTEGER NOT NULL REFERENCES "group" (id),
PRIMARY KEY (client_id, group_id)
);
UPDATE info SET value = 5 WHERE property = 'version';
COMMIT;

View File

@@ -1,18 +0,0 @@
.timeout 30000
PRAGMA FOREIGN_KEYS=OFF;
BEGIN TRANSACTION;
DROP VIEW vw_adlist;
CREATE VIEW vw_adlist AS SELECT DISTINCT address, adlist.id AS id
FROM adlist
LEFT JOIN adlist_by_group ON adlist_by_group.adlist_id = adlist.id
LEFT JOIN "group" ON "group".id = adlist_by_group.group_id
WHERE adlist.enabled = 1 AND (adlist_by_group.group_id IS NULL OR "group".enabled = 1)
ORDER BY adlist.id;
UPDATE info SET value = 6 WHERE property = 'version';
COMMIT;

View File

@@ -1,35 +0,0 @@
.timeout 30000
PRAGMA FOREIGN_KEYS=OFF;
BEGIN TRANSACTION;
INSERT OR REPLACE INTO "group" (id,enabled,name) VALUES (0,1,'Unassociated');
INSERT INTO domainlist_by_group (domainlist_id, group_id) SELECT id, 0 FROM domainlist;
INSERT INTO client_by_group (client_id, group_id) SELECT id, 0 FROM client;
INSERT INTO adlist_by_group (adlist_id, group_id) SELECT id, 0 FROM adlist;
CREATE TRIGGER tr_domainlist_add AFTER INSERT ON domainlist
BEGIN
INSERT INTO domainlist_by_group (domainlist_id, group_id) VALUES (NEW.id, 0);
END;
CREATE TRIGGER tr_client_add AFTER INSERT ON client
BEGIN
INSERT INTO client_by_group (client_id, group_id) VALUES (NEW.id, 0);
END;
CREATE TRIGGER tr_adlist_add AFTER INSERT ON adlist
BEGIN
INSERT INTO adlist_by_group (adlist_id, group_id) VALUES (NEW.id, 0);
END;
CREATE TRIGGER tr_group_zero AFTER DELETE ON "group"
BEGIN
INSERT OR REPLACE INTO "group" (id,enabled,name) VALUES (0,1,'Unassociated');
END;
UPDATE info SET value = 7 WHERE property = 'version';
COMMIT;

View File

@@ -1,35 +0,0 @@
.timeout 30000
PRAGMA FOREIGN_KEYS=OFF;
BEGIN TRANSACTION;
ALTER TABLE "group" RENAME TO "group__";
CREATE TABLE "group"
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
enabled BOOLEAN NOT NULL DEFAULT 1,
name TEXT UNIQUE NOT NULL,
date_added INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
date_modified INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
description TEXT
);
CREATE TRIGGER tr_group_update AFTER UPDATE ON "group"
BEGIN
UPDATE "group" SET date_modified = (cast(strftime('%s', 'now') as int)) WHERE id = NEW.id;
END;
INSERT OR IGNORE INTO "group" (id,enabled,name,description) SELECT id,enabled,name,description FROM "group__";
DROP TABLE "group__";
CREATE TRIGGER tr_group_zero AFTER DELETE ON "group"
BEGIN
INSERT OR IGNORE INTO "group" (id,enabled,name) VALUES (0,1,'Unassociated');
END;
UPDATE info SET value = 8 WHERE property = 'version';
COMMIT;

View File

@@ -1,27 +0,0 @@
.timeout 30000
PRAGMA FOREIGN_KEYS=OFF;
BEGIN TRANSACTION;
DROP TRIGGER IF EXISTS tr_group_update;
DROP TRIGGER IF EXISTS tr_group_zero;
PRAGMA legacy_alter_table=ON;
ALTER TABLE "group" RENAME TO "group__";
PRAGMA legacy_alter_table=OFF;
ALTER TABLE "group__" RENAME TO "group";
CREATE TRIGGER tr_group_update AFTER UPDATE ON "group"
BEGIN
UPDATE "group" SET date_modified = (cast(strftime('%s', 'now') as int)) WHERE id = NEW.id;
END;
CREATE TRIGGER tr_group_zero AFTER DELETE ON "group"
BEGIN
INSERT OR IGNORE INTO "group" (id,enabled,name) VALUES (0,1,'Unassociated');
END;
UPDATE info SET value = 9 WHERE property = 'version';
COMMIT;

View File

@@ -1,29 +0,0 @@
.timeout 30000
PRAGMA FOREIGN_KEYS=OFF;
BEGIN TRANSACTION;
DROP TABLE IF EXISTS whitelist;
DROP TABLE IF EXISTS blacklist;
DROP TABLE IF EXISTS regex_whitelist;
DROP TABLE IF EXISTS regex_blacklist;
CREATE TRIGGER tr_domainlist_delete AFTER DELETE ON domainlist
BEGIN
DELETE FROM domainlist_by_group WHERE domainlist_id = OLD.id;
END;
CREATE TRIGGER tr_adlist_delete AFTER DELETE ON adlist
BEGIN
DELETE FROM adlist_by_group WHERE adlist_id = OLD.id;
END;
CREATE TRIGGER tr_client_delete AFTER DELETE ON client
BEGIN
DELETE FROM client_by_group WHERE client_id = OLD.id;
END;
UPDATE info SET value = 10 WHERE property = 'version';
COMMIT;

View File

@@ -1,6 +1,4 @@
#!/usr/bin/env bash
# shellcheck disable=SC1090
# 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.
@@ -11,98 +9,71 @@
# Please see LICENSE file for your rights under this license.
# Globals
piholeDir="/etc/pihole"
GRAVITYDB="${piholeDir}/gravity.db"
# Source pihole-FTL from install script
pihole_FTL="${piholeDir}/pihole-FTL.conf"
if [[ -f "${pihole_FTL}" ]]; then
source "${pihole_FTL}"
fi
basename=pihole
piholeDir=/etc/"${basename}"
whitelist="${piholeDir}"/whitelist.txt
blacklist="${piholeDir}"/blacklist.txt
# Set this only after sourcing pihole-FTL.conf as the gravity database path may
# have changed
gravityDBfile="${GRAVITYDB}"
noReloadRequested=false
readonly regexlist="/etc/pihole/regex.list"
reload=false
addmode=true
verbose=true
wildcard=false
web=false
domList=()
typeId=""
comment=""
declare -i domaincount
domaincount=0
reload=false
listMain=""
listAlt=""
colfile="/opt/pihole/COL_TABLE"
source ${colfile}
# IDs are hard-wired to domain interpretation in the gravity database scheme
# Clients (including FTL) will read them through the corresponding views
readonly whitelist="0"
readonly blacklist="1"
readonly regex_whitelist="2"
readonly regex_blacklist="3"
GetListnameFromTypeId() {
if [[ "$1" == "${whitelist}" ]]; then
echo "whitelist"
elif [[ "$1" == "${blacklist}" ]]; then
echo "blacklist"
elif [[ "$1" == "${regex_whitelist}" ]]; then
echo "regex whitelist"
elif [[ "$1" == "${regex_blacklist}" ]]; then
echo "regex blacklist"
fi
}
GetListParamFromTypeId() {
if [[ "${typeId}" == "${whitelist}" ]]; then
echo "w"
elif [[ "${typeId}" == "${blacklist}" ]]; then
echo "b"
elif [[ "${typeId}" == "${regex_whitelist}" && "${wildcard}" == true ]]; then
echo "-white-wild"
elif [[ "${typeId}" == "${regex_whitelist}" ]]; then
echo "-white-regex"
elif [[ "${typeId}" == "${regex_blacklist}" && "${wildcard}" == true ]]; then
echo "-wild"
elif [[ "${typeId}" == "${regex_blacklist}" ]]; then
echo "-regex"
fi
}
helpFunc() {
local listname param
listname="$(GetListnameFromTypeId "${typeId}")"
param="$(GetListParamFromTypeId)"
if [[ "${listMain}" == "${whitelist}" ]]; then
param="w"
type="white"
elif [[ "${listMain}" == "${regexlist}" && "${wildcard}" == true ]]; then
param="-wild"
type="wildcard black"
elif [[ "${listMain}" == "${regexlist}" ]]; then
param="-regex"
type="regex black"
else
param="b"
type="black"
fi
echo "Usage: pihole -${param} [options] <domain> <domain2 ...>
Example: 'pihole -${param} site.com', or 'pihole -${param} site1.com site2.com'
${listname^} one or more domains
${type^}list one or more domains
Options:
-d, --delmode Remove domain(s) from the ${listname}
-nr, --noreload Update ${listname} without reloading the DNS server
-d, --delmode Remove domain(s) from the ${type}list
-nr, --noreload Update ${type}list without refreshing dnsmasq
-q, --quiet Make output less verbose
-h, --help Show this help dialog
-l, --list Display all your ${listname}listed domains
-l, --list Display all your ${type}listed domains
--nuke Removes all entries in a list"
exit 0
}
ValidateDomain() {
EscapeRegexp() {
# This way we may safely insert an arbitrary
# string in our regular expressions
# This sed is intentionally executed in three steps to ease maintainability
# The first sed removes any amount of leading dots
echo $* | sed 's/^\.*//' | sed "s/[]\.|$(){}?+*^]/\\\\&/g" | sed "s/\\//\\\\\//g"
}
HandleOther() {
# Convert to lowercase
domain="${1,,}"
# Check validity of domain (don't check for regex entries)
if [[ "${#domain}" -le 253 ]]; then
if [[ ( "${typeId}" == "${regex_blacklist}" || "${typeId}" == "${regex_whitelist}" ) && "${wildcard}" == false ]]; then
if [[ "${listMain}" == "${regexlist}" && "${wildcard}" == false ]]; then
validDomain="${domain}"
else
validDomain=$(grep -P "^((-|_)*[a-z\\d]((-|_)*[a-z\\d])*(-|_)*)(\\.(-|_)*([a-z\\d]((-|_)*[a-z\\d])*))*$" <<< "${domain}") # Valid chars check
@@ -111,190 +82,194 @@ ValidateDomain() {
fi
if [[ -n "${validDomain}" ]]; then
domList=("${domList[@]}" "${validDomain}")
domList=("${domList[@]}" ${validDomain})
else
echo -e " ${CROSS} ${domain} is not a valid argument or domain name!"
fi
domaincount=$((domaincount+1))
}
ProcessDomainList() {
for dom in "${domList[@]}"; do
# Format domain into regex filter if requested
if [[ "${wildcard}" == true ]]; then
dom="(\\.|^)${dom//\./\\.}$"
fi
PoplistFile() {
# Check whitelist file exists, and if not, create it
if [[ ! -f "${whitelist}" ]]; then
touch "${whitelist}"
fi
# Logic: If addmode then add to desired list and remove from the other;
# if delmode then remove from desired list but do not add to the other
# Check blacklist file exists, and if not, create it
if [[ ! -f "${blacklist}" ]]; then
touch "${blacklist}"
fi
for dom in "${domList[@]}"; do
# Logic: If addmode then add to desired list and remove from the other; if delmode then remove from desired list but do not add to the other
if ${addmode}; then
AddDomain "${dom}"
AddDomain "${dom}" "${listMain}"
RemoveDomain "${dom}" "${listAlt}"
else
RemoveDomain "${dom}"
RemoveDomain "${dom}" "${listMain}"
fi
done
}
AddDomain() {
local domain num requestedListname existingTypeId existingListname
domain="$1"
list="$2"
domain=$(EscapeRegexp "$1")
# Is the domain in the list we want to add it to?
num="$(sqlite3 "${gravityDBfile}" "SELECT COUNT(*) FROM domainlist WHERE domain = '${domain}';")"
requestedListname="$(GetListnameFromTypeId "${typeId}")"
[[ "${list}" == "${whitelist}" ]] && listname="whitelist"
[[ "${list}" == "${blacklist}" ]] && listname="blacklist"
if [[ "${num}" -ne 0 ]]; then
existingTypeId="$(sqlite3 "${gravityDBfile}" "SELECT type FROM domainlist WHERE domain = '${domain}';")"
if [[ "${existingTypeId}" == "${typeId}" ]]; then
if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} ${1} already exists in ${requestedListname}, no need to add!"
if [[ "${list}" == "${whitelist}" || "${list}" == "${blacklist}" ]]; then
[[ "${list}" == "${whitelist}" && -z "${type}" ]] && type="--whitelist-only"
[[ "${list}" == "${blacklist}" && -z "${type}" ]] && type="--blacklist-only"
bool=true
# Is the domain in the list we want to add it to?
grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false
if [[ "${bool}" == false ]]; then
# Domain not found in the whitelist file, add it!
if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} Adding ${1} to ${listname}..."
fi
reload=true
# Add it to the list we want to add it to
echo "$1" >> "${list}"
else
if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} ${1} already exists in ${listname}, no need to add!"
fi
fi
else
existingListname="$(GetListnameFromTypeId "${existingTypeId}")"
sqlite3 "${gravityDBfile}" "UPDATE domainlist SET type = ${typeId} WHERE domain='${domain}';"
if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} ${1} already exists in ${existingListname}, it has been moved to ${requestedListname}!"
fi
fi
return
fi
elif [[ "${list}" == "${regexlist}" ]]; then
[[ -z "${type}" ]] && type="--wildcard-only"
bool=true
domain="${1}"
# Domain not found in the table, add it!
if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} Adding ${domain} to the ${requestedListname}..."
fi
reload=true
# Insert only the domain here. The enabled and date_added fields will be filled
# with their default values (enabled = true, date_added = current timestamp)
if [[ -z "${comment}" ]]; then
sqlite3 "${gravityDBfile}" "INSERT INTO domainlist (domain,type) VALUES ('${domain}',${typeId});"
else
# also add comment when variable has been set through the "--comment" option
sqlite3 "${gravityDBfile}" "INSERT INTO domainlist (domain,type,comment) VALUES ('${domain}',${typeId},'${comment}');"
[[ "${wildcard}" == true ]] && domain="(^|\\.)${domain//\./\\.}$"
# Is the domain in the list?
# Search only for exactly matching lines
grep -Fx "${domain}" "${regexlist}" > /dev/null 2>&1 || bool=false
if [[ "${bool}" == false ]]; then
if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} Adding ${domain} to regex list..."
fi
reload="restart"
echo "$domain" >> "${regexlist}"
else
if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} ${domain} already exists in regex list, no need to add!"
fi
fi
fi
}
RemoveDomain() {
local domain num requestedListname
domain="$1"
list="$2"
domain=$(EscapeRegexp "$1")
# Is the domain in the list we want to remove it from?
num="$(sqlite3 "${gravityDBfile}" "SELECT COUNT(*) FROM domainlist WHERE domain = '${domain}' AND type = ${typeId};")"
[[ "${list}" == "${whitelist}" ]] && listname="whitelist"
[[ "${list}" == "${blacklist}" ]] && listname="blacklist"
requestedListname="$(GetListnameFromTypeId "${typeId}")"
if [[ "${list}" == "${whitelist}" || "${list}" == "${blacklist}" ]]; then
bool=true
[[ "${list}" == "${whitelist}" && -z "${type}" ]] && type="--whitelist-only"
[[ "${list}" == "${blacklist}" && -z "${type}" ]] && type="--blacklist-only"
# Is it in the list? Logic follows that if its whitelisted it should not be blacklisted and vice versa
grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false
if [[ "${bool}" == true ]]; then
# Remove it from the other one
echo -e " ${INFO} Removing $1 from ${listname}..."
# /I flag: search case-insensitive
sed -i "/${domain}/Id" "${list}"
reload=true
else
if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} ${1} does not exist in ${listname}, no need to remove!"
fi
fi
elif [[ "${list}" == "${regexlist}" ]]; then
[[ -z "${type}" ]] && type="--wildcard-only"
domain="${1}"
if [[ "${num}" -eq 0 ]]; then
if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} ${domain} does not exist in ${requestedListname}, no need to remove!"
fi
return
[[ "${wildcard}" == true ]] && domain="(^|\\.)${domain//\./\\.}$"
bool=true
# Is it in the list?
grep -Fx "${domain}" "${regexlist}" > /dev/null 2>&1 || bool=false
if [[ "${bool}" == true ]]; then
# Remove it from the other one
echo -e " ${INFO} Removing $domain from regex list..."
local lineNumber
lineNumber=$(grep -Fnx "$domain" "${list}" | cut -f1 -d:)
sed -i "${lineNumber}d" "${list}"
reload=true
else
if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} ${domain} does not exist in regex list, no need to remove!"
fi
fi
fi
}
# Domain found in the table, remove it!
if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} Removing ${domain} from the ${requestedListname}..."
fi
reload=true
# Remove it from the current list
sqlite3 "${gravityDBfile}" "DELETE FROM domainlist WHERE domain = '${domain}' AND type = ${typeId};"
# Update Gravity
Reload() {
echo ""
pihole -g --skip-download "${type:-}"
}
Displaylist() {
local count num_pipes domain enabled status nicedate requestedListname
requestedListname="$(GetListnameFromTypeId "${typeId}")"
data="$(sqlite3 "${gravityDBfile}" "SELECT domain,enabled,date_modified FROM domainlist WHERE type = ${typeId};" 2> /dev/null)"
if [[ -z $data ]]; then
echo -e "Not showing empty list"
else
echo -e "Displaying ${requestedListname}:"
if [[ -f ${listMain} ]]; then
if [[ "${listMain}" == "${whitelist}" ]]; then
string="gravity resistant domains"
else
string="domains caught in the sinkhole"
fi
verbose=false
echo -e "Displaying $string:\n"
count=1
while IFS= read -r line
do
# Count number of pipes seen in this line
# This is necessary because we can only detect the pipe separating the fields
# from the end backwards as the domain (which is the first field) may contain
# pipe symbols as they are perfectly valid regex filter control characters
num_pipes="$(grep -c "^" <<< "$(grep -o "|" <<< "${line}")")"
# Extract domain and enabled status based on the obtained number of pipe characters
domain="$(cut -d'|' -f"-$((num_pipes-1))" <<< "${line}")"
enabled="$(cut -d'|' -f"$((num_pipes))" <<< "${line}")"
datemod="$(cut -d'|' -f"$((num_pipes+1))" <<< "${line}")"
# Translate boolean status into human readable string
if [[ "${enabled}" -eq 1 ]]; then
status="enabled"
else
status="disabled"
fi
# Get nice representation of numerical date stored in database
nicedate=$(date --rfc-2822 -d "@${datemod}")
echo " ${count}: ${domain} (${status}, last modified ${nicedate})"
while IFS= read -r RD || [ -n "${RD}" ]; do
echo " ${count}: ${RD}"
count=$((count+1))
done <<< "${data}"
done < "${listMain}"
else
echo -e " ${COL_LIGHT_RED}${listMain} does not exist!${COL_NC}"
fi
exit 0;
}
NukeList() {
count=$(sqlite3 "${gravityDBfile}" "SELECT COUNT(1) FROM domainlist WHERE type = ${typeId};")
listname="$(GetListnameFromTypeId "${typeId}")"
if [ "$count" -gt 0 ];then
sqlite3 "${gravityDBfile}" "DELETE FROM domainlist WHERE type = ${typeId};"
echo " ${TICK} Removed ${count} domain(s) from the ${listname}"
else
echo " ${INFO} ${listname} already empty. Nothing to do!"
fi
exit 0;
}
GetComment() {
comment="$1"
if [[ "${comment}" =~ [^a-zA-Z0-9_\#:/\.,\ -] ]]; then
echo " ${CROSS} Found invalid characters in domain comment!"
exit
if [[ -f "${listMain}" ]]; then
# Back up original list
cp "${listMain}" "${listMain}.bck~"
# Empty out file
echo "" > "${listMain}"
fi
}
while (( "$#" )); do
case "${1}" in
"-w" | "whitelist" ) typeId=0;;
"-b" | "blacklist" ) typeId=1;;
"--white-regex" | "white-regex" ) typeId=2;;
"--white-wild" | "white-wild" ) typeId=2; wildcard=true;;
"--wild" | "wildcard" ) typeId=3; wildcard=true;;
"--regex" | "regex" ) typeId=3;;
"-nr"| "--noreload" ) noReloadRequested=true;;
for var in "$@"; do
case "${var}" in
"-w" | "whitelist" ) listMain="${whitelist}"; listAlt="${blacklist}";;
"-b" | "blacklist" ) listMain="${blacklist}"; listAlt="${whitelist}";;
"--wild" | "wildcard" ) listMain="${regexlist}"; wildcard=true;;
"--regex" | "regex" ) listMain="${regexlist}";;
"-nr"| "--noreload" ) reload=false;;
"-d" | "--delmode" ) addmode=false;;
"-q" | "--quiet" ) verbose=false;;
"-h" | "--help" ) helpFunc;;
"-l" | "--list" ) Displaylist;;
"--nuke" ) NukeList;;
"--web" ) web=true;;
"--comment" ) GetComment "${2}"; shift;;
* ) ValidateDomain "${1}";;
* ) HandleOther "${var}";;
esac
shift
done
shift
if [[ ${domaincount} == 0 ]]; then
if [[ $# = 0 ]]; then
helpFunc
fi
ProcessDomainList
PoplistFile
# Used on web interface
if $web; then
echo "DONE"
fi
if [[ ${reload} == true && ${noReloadRequested} == false ]]; then
pihole restartdns reload-lists
if [[ "${reload}" != false ]]; then
# Ensure that "restart" is used for Wildcard updates
Reload "${reload}"
fi

View File

@@ -1,23 +0,0 @@
#!/bin/bash
# Pi-hole: A black hole for Internet advertisements
# (c) 2020 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.
#
#
# The pihole disable command has the option to set a specified time before
# blocking is automatically re-enabled.
#
# Present script is responsible for the sleep & re-enable part of the job and
# is automatically terminated if it is still running when pihole is enabled by
# other means.
#
# This ensures that pihole ends up in the correct state after a sequence of
# commands suchs as: `pihole disable 30s; pihole enable; pihole disable`
readonly PI_HOLE_BIN_DIR="/usr/local/bin"
sleep "${1}"
"${PI_HOLE_BIN_DIR}"/pihole enable

View File

@@ -1,66 +0,0 @@
#!/usr/bin/env bash
# shellcheck disable=SC1090
# Pi-hole: A black hole for Internet advertisements
# (c) 2019 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware.
#
# ARP table interaction
#
# This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license.
coltable="/opt/pihole/COL_TABLE"
if [[ -f ${coltable} ]]; then
source ${coltable}
fi
# Determine database location
# Obtain DBFILE=... setting from pihole-FTL.db
# Constructed to return nothing when
# a) the setting is not present in the config file, or
# b) the setting is commented out (e.g. "#DBFILE=...")
FTLconf="/etc/pihole/pihole-FTL.conf"
if [ -e "$FTLconf" ]; then
DBFILE="$(sed -n -e 's/^\s*DBFILE\s*=\s*//p' ${FTLconf})"
fi
# Test for empty string. Use standard path in this case.
if [ -z "$DBFILE" ]; then
DBFILE="/etc/pihole/pihole-FTL.db"
fi
flushARP(){
local output
if [[ "${args[1]}" != "quiet" ]]; then
echo -ne " ${INFO} Flushing network table ..."
fi
# Truncate network_addresses table in pihole-FTL.db
# This needs to be done before we can truncate the network table due to
# foreign key constraints
if ! output=$(sqlite3 "${DBFILE}" "DELETE FROM network_addresses" 2>&1); then
echo -e "${OVER} ${CROSS} Failed to truncate network_addresses table"
echo " Database location: ${DBFILE}"
echo " Output: ${output}"
return 1
fi
# Truncate network table in pihole-FTL.db
if ! output=$(sqlite3 "${DBFILE}" "DELETE FROM network" 2>&1); then
echo -e "${OVER} ${CROSS} Failed to truncate network table"
echo " Database location: ${DBFILE}"
echo " Output: ${output}"
return 1
fi
if [[ "${args[1]}" != "quiet" ]]; then
echo -e "${OVER} ${TICK} Flushed network table"
fi
}
args=("$@")
case "${args[0]}" in
"arpflush" ) flushARP;;
esac

21
advanced/Scripts/piholeCheckout.sh Executable file → Normal file
View File

@@ -3,7 +3,7 @@
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware.
#
# Switch Pi-hole subsystems to a different GitHub branch.
# Switch Pi-hole subsystems to a different Github branch.
#
# This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license.
@@ -36,7 +36,7 @@ warning1() {
return 0
;;
*)
echo -e "\\n ${INFO} Branch change has been canceled"
echo -e "\\n ${INFO} Branch change has been cancelled"
return 1
;;
esac
@@ -46,12 +46,6 @@ checkout() {
local corebranches
local webbranches
# Check if FTL is installed - do this early on as FTL is a hard dependency for Pi-hole
local funcOutput
funcOutput=$(get_binary_name) #Store output of get_binary_name here
local binary
binary="pihole-FTL${funcOutput##*pihole-FTL}" #binary name will be the last line of the output of get_binary_name (it always begins with pihole-FTL)
# Avoid globbing
set -f
@@ -84,7 +78,7 @@ checkout() {
echo -e " ${INFO} Shortcut \"dev\" detected - checking out development / devel branches..."
echo ""
echo -e " ${INFO} Pi-hole Core"
fetch_checkout_pull_branch "${PI_HOLE_FILES_DIR}" "development" || { echo " ${CROSS} Unable to pull Core development branch"; exit 1; }
fetch_checkout_pull_branch "${PI_HOLE_FILES_DIR}" "development" || { echo " ${CROSS} Unable to pull Core developement branch"; exit 1; }
if [[ "${INSTALL_WEB_INTERFACE}" == "true" ]]; then
echo ""
echo -e " ${INFO} Web interface"
@@ -92,10 +86,10 @@ checkout() {
fi
#echo -e " ${TICK} Pi-hole Core"
get_binary_name
local path
path="development/${binary}"
echo "development" > /etc/pihole/ftlbranch
chmod 644 /etc/pihole/ftlbranch
elif [[ "${1}" == "master" ]] ; then
# Shortcut to check out master branches
echo -e " ${INFO} Shortcut \"master\" detected - checking out master branches..."
@@ -106,10 +100,10 @@ checkout() {
fetch_checkout_pull_branch "${webInterfaceDir}" "master" || { echo " ${CROSS} Unable to pull Web master branch"; exit 1; }
fi
#echo -e " ${TICK} Web Interface"
get_binary_name
local path
path="master/${binary}"
echo "master" > /etc/pihole/ftlbranch
chmod 644 /etc/pihole/ftlbranch
elif [[ "${1}" == "core" ]] ; then
str="Fetching branches from ${piholeGitUrl}"
echo -ne " ${INFO} $str"
@@ -165,16 +159,13 @@ checkout() {
fi
checkout_pull_branch "${webInterfaceDir}" "${2}"
elif [[ "${1}" == "ftl" ]] ; then
get_binary_name
local path
local oldbranch
path="${2}/${binary}"
oldbranch="$(pihole-FTL -b)"
if check_download_exists "$path"; then
echo " ${TICK} Branch ${2} exists"
echo "${2}" > /etc/pihole/ftlbranch
chmod 644 /etc/pihole/ftlbranch
echo -e " ${INFO} Switching to branch: \"${2}\" from \"${oldbranch}\""
FTLinstall "${binary}"
restart_service pihole-FTL
enable_service pihole-FTL

File diff suppressed because it is too large Load Diff

View File

@@ -11,11 +11,6 @@
colfile="/opt/pihole/COL_TABLE"
source ${colfile}
# In case we're running at the same time as a system logrotate, use a
# separate logrotate state file to prevent stepping on each other's
# toes.
STATEFILE="/var/lib/logrotate/pihole"
# Determine database location
# Obtain DBFILE=... setting from pihole-FTL.db
# Constructed to return nothing when
@@ -37,29 +32,27 @@ if [[ "$@" == *"once"* ]]; then
# Nightly logrotation
if command -v /usr/sbin/logrotate >/dev/null; then
# Logrotate once
/usr/sbin/logrotate --force --state "${STATEFILE}" /etc/pihole/logrotate
/usr/sbin/logrotate --force /etc/pihole/logrotate
else
# Copy pihole.log over to pihole.log.1
# and empty out pihole.log
# Note that moving the file is not an option, as
# dnsmasq would happily continue writing into the
# moved file (it will have the same file handler)
cp -p /var/log/pihole.log /var/log/pihole.log.1
cp /var/log/pihole.log /var/log/pihole.log.1
echo " " > /var/log/pihole.log
chmod 644 /var/log/pihole.log
fi
else
# Manual flushing
if command -v /usr/sbin/logrotate >/dev/null; then
# Logrotate twice to move all data out of sight of FTL
/usr/sbin/logrotate --force --state "${STATEFILE}" /etc/pihole/logrotate; sleep 3
/usr/sbin/logrotate --force --state "${STATEFILE}" /etc/pihole/logrotate
/usr/sbin/logrotate --force /etc/pihole/logrotate; sleep 3
/usr/sbin/logrotate --force /etc/pihole/logrotate
else
# Flush both pihole.log and pihole.log.1 (if existing)
echo " " > /var/log/pihole.log
if [ -f /var/log/pihole.log.1 ]; then
echo " " > /var/log/pihole.log.1
chmod 644 /var/log/pihole.log.1
fi
fi
# Delete most recent 24 hours from FTL's database, leave even older data intact (don't wipe out all history)

247
advanced/Scripts/query.sh Executable file → Normal file
View File

@@ -1,6 +1,5 @@
#!/usr/bin/env bash
# shellcheck disable=SC1090
# Pi-hole: A black hole for Internet advertisements
# (c) 2018 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware.
@@ -12,49 +11,52 @@
# Globals
piholeDir="/etc/pihole"
GRAVITYDB="${piholeDir}/gravity.db"
adListsList="$piholeDir/adlists.list"
wildcardlist="/etc/dnsmasq.d/03-pihole-wildcard.conf"
options="$*"
adlist=""
all=""
exact=""
blockpage=""
matchType="match"
# Source pihole-FTL from install script
pihole_FTL="${piholeDir}/pihole-FTL.conf"
if [[ -f "${pihole_FTL}" ]]; then
source "${pihole_FTL}"
fi
# Set this only after sourcing pihole-FTL.conf as the gravity database path may
# have changed
gravityDBfile="${GRAVITYDB}"
colfile="/opt/pihole/COL_TABLE"
source "${colfile}"
# Print each subdomain
# e.g: foo.bar.baz.com = "foo.bar.baz.com bar.baz.com baz.com com"
processWildcards() {
IFS="." read -r -a array <<< "${1}"
for (( i=${#array[@]}-1; i>=0; i-- )); do
ar=""
for (( j=${#array[@]}-1; j>${#array[@]}-i-2; j-- )); do
if [[ $j == $((${#array[@]}-1)) ]]; then
ar="${array[$j]}"
else
ar="${array[$j]}.${ar}"
fi
done
echo "${ar}"
done
}
# Scan an array of files for matching strings
scanList(){
# Escape full stops
local domain="${1}" esc_domain="${1//./\\.}" lists="${2}" type="${3:-}"
local domain="${1//./\\.}" lists="${2}" type="${3:-}"
# Prevent grep from printing file path
cd "$piholeDir" || exit 1
# Prevent grep -i matching slowly: https://bit.ly/2xFXtUX
# Prevent grep -i matching slowly: http://bit.ly/2xFXtUX
export LC_CTYPE=C
# /dev/null forces filename to be printed when only one list has been generated
# shellcheck disable=SC2086
case "${type}" in
"exact" ) grep -i -E -l "(^|(?<!#)\\s)${esc_domain}($|\\s|#)" ${lists} /dev/null 2>/dev/null;;
# Iterate through each regexp and check whether it matches the domainQuery
# If it does, print the matching regexp and continue looping
# Input 1 - regexps | Input 2 - domainQuery
"regex" )
for list in ${lists}; do
if [[ "${domain}" =~ ${list} ]]; then
printf "%b\n" "${list}";
fi
done;;
* ) grep -i "${esc_domain}" ${lists} /dev/null 2>/dev/null;;
"exact" ) grep -i -E -l "(^|(?<!#)\\s)${domain}($|\\s|#)" ${lists} /dev/null 2>/dev/null;;
"wc" ) grep -i -o -m 1 "/${domain}/" ${lists} 2>/dev/null;;
* ) grep -i "${domain}" ${lists} /dev/null 2>/dev/null;;
esac
}
@@ -64,16 +66,23 @@ Example: 'pihole -q -exact domain.com'
Query the adlists for a specified domain
Options:
-adlist Print the name of the block list URL
-exact Search the block lists for exact domain matches
-all Return all query matches within a block list
-h, --help Show this help dialog"
exit 0
fi
if [[ ! -e "$adListsList" ]]; then
echo -e "${COL_LIGHT_RED}The file $adListsList was not found${COL_NC}"
exit 1
fi
# Handle valid options
if [[ "${options}" == *"-bp"* ]]; then
exact="exact"; blockpage=true
else
[[ "${options}" == *"-adlist"* ]] && adlist=true
[[ "${options}" == *"-all"* ]] && all=true
if [[ "${options}" == *"-exact"* ]]; then
exact="exact"; matchType="exact ${matchType}"
@@ -98,115 +107,55 @@ if [[ -n "${str:-}" ]]; then
exit 1
fi
scanDatabaseTable() {
local domain table type querystr result extra
domain="$(printf "%q" "${1}")"
table="${2}"
type="${3:-}"
# As underscores are legitimate parts of domains, we escape them when using the LIKE operator.
# Underscores are SQLite wildcards matching exactly one character. We obviously want to suppress this
# behavior. The "ESCAPE '\'" clause specifies that an underscore preceded by an '\' should be matched
# as a literal underscore character. We pretreat the $domain variable accordingly to escape underscores.
if [[ "${table}" == "gravity" ]]; then
case "${exact}" in
"exact" ) querystr="SELECT gravity.domain,adlist.address,adlist.enabled FROM gravity LEFT JOIN adlist ON adlist.id = gravity.adlist_id WHERE domain = '${domain}'";;
* ) querystr="SELECT gravity.domain,adlist.address,adlist.enabled FROM gravity LEFT JOIN adlist ON adlist.id = gravity.adlist_id WHERE domain LIKE '%${domain//_/\\_}%' ESCAPE '\\'";;
esac
else
case "${exact}" in
"exact" ) querystr="SELECT domain,enabled FROM domainlist WHERE type = '${type}' AND domain = '${domain}'";;
* ) querystr="SELECT domain,enabled FROM domainlist WHERE type = '${type}' AND domain LIKE '%${domain//_/\\_}%' ESCAPE '\\'";;
esac
fi
# Send prepared query to gravity database
result="$(sqlite3 "${gravityDBfile}" "${querystr}")" 2> /dev/null
if [[ -z "${result}" ]]; then
# Return early when there are no matches in this table
return
fi
if [[ "${table}" == "gravity" ]]; then
echo "${result}"
return
fi
# Mark domain as having been white-/blacklist matched (global variable)
# Scan Whitelist and Blacklist
lists="whitelist.txt blacklist.txt"
mapfile -t results <<< "$(scanList "${domainQuery}" "${lists}" "${exact}")"
if [[ -n "${results[*]}" ]]; then
wbMatch=true
# Print table name
if [[ -z "${blockpage}" ]]; then
echo " ${matchType^} found in ${COL_BOLD}exact ${table}${COL_NC}"
fi
# Loop over results and print them
mapfile -t results <<< "${result}"
# Loop through each result in order to print unique file title once
for result in "${results[@]}"; do
fileName="${result%%.*}"
if [[ -n "${blockpage}" ]]; then
echo "π ${result}"
exit 0
fi
domain="${result/|*}"
if [[ "${result#*|}" == "0" ]]; then
extra=" (disabled)"
elif [[ -n "${exact}" ]]; then
echo " ${matchType^} found in ${COL_BOLD}${fileName^}${COL_NC}"
else
extra=""
fi
echo " ${domain}${extra}"
done
}
scanRegexDatabaseTable() {
local domain list
domain="${1}"
list="${2}"
type="${3:-}"
# Query all regex from the corresponding database tables
mapfile -t regexList < <(sqlite3 "${gravityDBfile}" "SELECT domain FROM domainlist WHERE type = ${type}" 2> /dev/null)
# If we have regexps to process
if [[ "${#regexList[@]}" -ne 0 ]]; then
# Split regexps over a new line
str_regexList=$(printf '%s\n' "${regexList[@]}")
# Check domain against regexps
mapfile -t regexMatches < <(scanList "${domain}" "${str_regexList}" "regex")
# If there were regex matches
if [[ "${#regexMatches[@]}" -ne 0 ]]; then
# Split matching regexps over a new line
str_regexMatches=$(printf '%s\n' "${regexMatches[@]}")
# Form a "matched" message
str_message="${matchType^} found in ${COL_BOLD}regex ${list}${COL_NC}"
# Form a "results" message
str_result="${COL_BOLD}${str_regexMatches}${COL_NC}"
# If we are displaying more than just the source of the block
if [[ -z "${blockpage}" ]]; then
# Set the wildcard match flag
wcMatch=true
# Echo the "matched" message, indented by one space
echo " ${str_message}"
# Echo the "results" message, each line indented by three spaces
# shellcheck disable=SC2001
echo "${str_result}" | sed 's/^/ /'
else
echo "π .wildcard"
exit 0
# Only print filename title once per file
if [[ ! "${fileName}" == "${fileName_prev:-}" ]]; then
echo " ${matchType^} found in ${COL_BOLD}${fileName^}${COL_NC}"
fileName_prev="${fileName}"
fi
echo " ${result#*:}"
fi
fi
}
done
fi
# Scan Whitelist and Blacklist
scanDatabaseTable "${domainQuery}" "whitelist" "0"
scanDatabaseTable "${domainQuery}" "blacklist" "1"
# Scan Wildcards
if [[ -e "${wildcardlist}" ]]; then
# Determine all subdomains, domain and TLDs
mapfile -t wildcards <<< "$(processWildcards "${domainQuery}")"
for match in "${wildcards[@]}"; do
# Search wildcard list for matches
mapfile -t results <<< "$(scanList "${match}" "${wildcardlist}" "wc")"
if [[ -n "${results[*]}" ]]; then
if [[ -z "${wcMatch:-}" ]] && [[ -z "${blockpage}" ]]; then
wcMatch=true
echo " ${matchType^} found in ${COL_BOLD}Wildcards${COL_NC}:"
fi
case "${blockpage}" in
true ) echo "π ${wildcardlist##*/}"; exit 0;;
* ) echo " *.${match}";;
esac
fi
done
fi
# Scan Regex table
scanRegexDatabaseTable "${domainQuery}" "whitelist" "2"
scanRegexDatabaseTable "${domainQuery}" "blacklist" "3"
# Get version sorted *.domains filenames (without dir path)
lists=("$(cd "$piholeDir" || exit 0; printf "%s\\n" -- *.domains | sort -V)")
# Query block lists
mapfile -t results <<< "$(scanDatabaseTable "${domainQuery}" "gravity")"
# Query blocklists for occurences of domain
mapfile -t results <<< "$(scanList "${domainQuery}" "${lists[*]}" "${exact}")"
# Handle notices
if [[ -z "${wbMatch:-}" ]] && [[ -z "${wcMatch:-}" ]] && [[ -z "${results[*]}" ]]; then
@@ -221,6 +170,29 @@ elif [[ -z "${all}" ]] && [[ "${#results[*]}" -ge 100 ]]; then
exit 0
fi
# Remove unwanted content from non-exact $results
if [[ -z "${exact}" ]]; then
# Delete lines starting with #
# Remove comments after domain
# Remove hosts format IP address
mapfile -t results <<< "$(IFS=$'\n'; sed \
-e "/:#/d" \
-e "s/[ \\t]#.*//g" \
-e "s/:.*[ \\t]/:/g" \
<<< "${results[*]}")"
# Exit if result was in a comment
[[ -z "${results[*]}" ]] && exit 0
fi
# Get adlist file content as array
if [[ -n "${adlist}" ]] || [[ -n "${blockpage}" ]]; then
for adlistUrl in $(< "${adListsList}"); do
if [[ "${adlistUrl:0:4}" =~ (http|www.) ]]; then
adlists+=("${adlistUrl}")
fi
done
fi
# Print "Exact matches for" title
if [[ -n "${exact}" ]] && [[ -z "${blockpage}" ]]; then
plural=""; [[ "${#results[*]}" -gt 1 ]] && plural="es"
@@ -228,25 +200,28 @@ if [[ -n "${exact}" ]] && [[ -z "${blockpage}" ]]; then
fi
for result in "${results[@]}"; do
match="${result/|*/}"
extra="${result#*|}"
adlistAddress="${extra/|*/}"
extra="${extra#*|}"
if [[ "${extra}" == "0" ]]; then
extra="(disabled)"
else
extra=""
fileName="${result/:*/}"
# Determine *.domains URL using filename's number
if [[ -n "${adlist}" ]] || [[ -n "${blockpage}" ]]; then
fileNum="${fileName/list./}"; fileNum="${fileNum%%.*}"
fileName="${adlists[$fileNum]}"
# Discrepency occurs when adlists has been modified, but Gravity has not been run
if [[ -z "${fileName}" ]]; then
fileName="${COL_LIGHT_RED}(no associated adlists URL found)${COL_NC}"
fi
fi
if [[ -n "${blockpage}" ]]; then
echo "0 ${adlistAddress}"
echo "${fileNum} ${fileName}"
elif [[ -n "${exact}" ]]; then
echo " - ${adlistAddress} ${extra}"
echo " ${fileName}"
else
if [[ ! "${adlistAddress}" == "${adlistAddress_prev:-}" ]]; then
if [[ ! "${fileName}" == "${fileName_prev:-}" ]]; then
count=""
echo " ${matchType^} found in ${COL_BOLD}${adlistAddress}${COL_NC}:"
adlistAddress_prev="${adlistAddress}"
echo " ${matchType^} found in ${COL_BOLD}${fileName}${COL_NC}:"
fileName_prev="${fileName}"
fi
: $((count++))
@@ -256,7 +231,7 @@ for result in "${results[@]}"; do
[[ "${count}" -gt "${max_count}" ]] && continue
echo " ${COL_GRAY}Over ${count} results found, skipping rest of file${COL_NC}"
else
echo " ${match} ${extra}"
echo " ${result#*:}"
fi
fi
done

View File

@@ -20,7 +20,7 @@ getInitSys() {
elif [ -f /etc/init.d/cron ] && [ ! -h /etc/init.d/cron ]; then
SYSTEMD=0
else
echo "Unrecognized init system"
echo "Unrecognised init system"
return 1
fi
}
@@ -70,5 +70,5 @@ setupcon
reboot
# Start showing the stats on the screen by running the command on another tty:
# https://unix.stackexchange.com/questions/170063/start-a-process-on-a-different-tty
# http://unix.stackexchange.com/questions/170063/start-a-process-on-a-different-tty
#setsid sh -c 'exec /usr/local/bin/chronometer.sh <> /dev/tty1 >&0 2>&1'

View File

@@ -31,6 +31,7 @@ source "/opt/pihole/COL_TABLE"
# make_repo() sourced from basic-install.sh
# update_repo() source from basic-install.sh
# getGitFiles() sourced from basic-install.sh
# get_binary_name() sourced from basic-install.sh
# FTLcheckUpdate() sourced from basic-install.sh
GitCheckUpdateAvail() {
@@ -95,10 +96,6 @@ main() {
# shellcheck disable=1090,2154
source "${setupVars}"
# Install packages used by this installation script (necessary if users have removed e.g. git from their systems)
package_manager_detect
install_dependent_packages "${INSTALLER_DEPS[@]}"
# This is unlikely
if ! is_repo "${PI_HOLE_FILES_DIR}" ; then
echo -e "\\n ${COL_LIGHT_RED}Error: Core Pi-hole repo is missing from system!"
@@ -132,12 +129,7 @@ main() {
fi
fi
local funcOutput
funcOutput=$(get_binary_name) #Store output of get_binary_name here
local binary
binary="pihole-FTL${funcOutput##*pihole-FTL}" #binary name will be the last line of the output of get_binary_name (it always begins with pihole-FTL)
if FTLcheckUpdate "${binary}" > /dev/null; then
if FTLcheckUpdate > /dev/null; then
FTL_update=true
echo -e " ${INFO} FTL:\\t\\t${COL_YELLOW}update available${COL_NC}"
else
@@ -202,14 +194,6 @@ main() {
${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --reconfigure --unattended || \
echo -e "${basicError}" && exit 1
fi
if [[ "${FTL_update}" == true || "${core_update}" == true || "${web_update}" == true ]]; then
# Force an update of the updatechecker
/opt/pihole/updatecheck.sh
/opt/pihole/updatecheck.sh x remote
echo -e " ${INFO} Local version file information updated."
fi
echo ""
exit 0
}

View File

@@ -51,7 +51,6 @@ if [[ "$2" == "remote" ]]; then
GITHUB_CORE_VERSION="$(json_extract tag_name "$(curl -s 'https://api.github.com/repos/pi-hole/pi-hole/releases/latest' 2> /dev/null)")"
echo -n "${GITHUB_CORE_VERSION}" > "${GITHUB_VERSION_FILE}"
chmod 644 "${GITHUB_VERSION_FILE}"
if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then
GITHUB_WEB_VERSION="$(json_extract tag_name "$(curl -s 'https://api.github.com/repos/pi-hole/AdminLTE/releases/latest' 2> /dev/null)")"
@@ -67,7 +66,6 @@ else
CORE_BRANCH="$(get_local_branch /etc/.pihole)"
echo -n "${CORE_BRANCH}" > "${LOCAL_BRANCH_FILE}"
chmod 644 "${LOCAL_BRANCH_FILE}"
if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then
WEB_BRANCH="$(get_local_branch /var/www/html/admin)"
@@ -81,7 +79,6 @@ else
CORE_VERSION="$(get_local_version /etc/.pihole)"
echo -n "${CORE_VERSION}" > "${LOCAL_VERSION_FILE}"
chmod 644 "${LOCAL_VERSION_FILE}"
if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then
WEB_VERSION="$(get_local_version /var/www/html/admin)"

View File

@@ -84,21 +84,6 @@ getRemoteVersion(){
# Get the version from the remote origin
local daemon="${1}"
local version
local cachedVersions
local arrCache
cachedVersions="/etc/pihole/GitHubVersions"
#If the above file exists, then we can read from that. Prevents overuse of GitHub API
if [[ -f "$cachedVersions" ]]; then
IFS=' ' read -r -a arrCache < "$cachedVersions"
case $daemon in
"pi-hole" ) echo "${arrCache[0]}";;
"AdminLTE" ) echo "${arrCache[1]}";;
"FTL" ) echo "${arrCache[2]}";;
esac
return 0
fi
version=$(curl --silent --fail "https://api.github.com/repos/pi-hole/${daemon}/releases/latest" | \
awk -F: '$1 ~/tag_name/ { print $2 }' | \
@@ -112,48 +97,22 @@ getRemoteVersion(){
return 0
}
getLocalBranch(){
# Get the checked out branch of the local directory
local directory="${1}"
local branch
# Local FTL btranch is stored in /etc/pihole/ftlbranch
if [[ "$1" == "FTL" ]]; then
branch="$(pihole-FTL branch)"
else
cd "${directory}" 2> /dev/null || { echo "${DEFAULT}"; return 1; }
branch=$(git rev-parse --abbrev-ref HEAD || echo "$DEFAULT")
fi
if [[ ! "${branch}" =~ ^v ]]; then
if [[ "${branch}" == "master" ]]; then
echo ""
elif [[ "${branch}" == "HEAD" ]]; then
echo "in detached HEAD state at "
else
echo "${branch} "
fi
else
# Branch started in "v"
echo "release "
fi
return 0
}
versionOutput() {
[[ "$1" == "pi-hole" ]] && GITDIR=$COREGITDIR
[[ "$1" == "AdminLTE" ]] && GITDIR=$WEBGITDIR
[[ "$1" == "FTL" ]] && GITDIR="FTL"
[[ "$2" == "-c" ]] || [[ "$2" == "--current" ]] || [[ -z "$2" ]] && current=$(getLocalVersion $GITDIR) && branch=$(getLocalBranch $GITDIR)
[[ "$2" == "-c" ]] || [[ "$2" == "--current" ]] || [[ -z "$2" ]] && current=$(getLocalVersion $GITDIR)
[[ "$2" == "-l" ]] || [[ "$2" == "--latest" ]] || [[ -z "$2" ]] && latest=$(getRemoteVersion "$1")
if [[ "$2" == "-h" ]] || [[ "$2" == "--hash" ]]; then
[[ "$3" == "-c" ]] || [[ "$3" == "--current" ]] || [[ -z "$3" ]] && curHash=$(getLocalHash "$GITDIR") && branch=$(getLocalBranch $GITDIR)
[[ "$3" == "-c" ]] || [[ "$3" == "--current" ]] || [[ -z "$3" ]] && curHash=$(getLocalHash "$GITDIR")
[[ "$3" == "-l" ]] || [[ "$3" == "--latest" ]] || [[ -z "$3" ]] && latHash=$(getRemoteHash "$1" "$(cd "$GITDIR" 2> /dev/null && git rev-parse --abbrev-ref HEAD)")
fi
if [[ -n "$current" ]] && [[ -n "$latest" ]]; then
output="${1^} version is $branch$current (Latest: $latest)"
output="${1^} version is $current (Latest: $latest)"
elif [[ -n "$current" ]] && [[ -z "$latest" ]]; then
output="Current ${1^} version is $branch$current"
output="Current ${1^} version is $current"
elif [[ -z "$current" ]] && [[ -n "$latest" ]]; then
output="Latest ${1^} version is $latest"
elif [[ "$curHash" == "N/A" ]] || [[ "$latHash" == "N/A" ]]; then
@@ -203,7 +162,7 @@ Repositories:
Options:
-c, --current Return the current version
-l, --latest Return the latest version
--hash Return the GitHub hash from your local repositories
--hash Return the Github hash from your local repositories
-h, --help Show this help dialog"
exit 0
}

View File

@@ -10,21 +10,12 @@
# This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license.
readonly setupVars="/etc/pihole/setupVars.conf"
readonly dnsmasqconfig="/etc/dnsmasq.d/01-pihole.conf"
readonly dhcpconfig="/etc/dnsmasq.d/02-pihole-dhcp.conf"
readonly FTLconf="/etc/pihole/pihole-FTL.conf"
# 03 -> wildcards
readonly dhcpstaticconfig="/etc/dnsmasq.d/04-pihole-static-dhcp.conf"
readonly dnscustomfile="/etc/pihole/custom.list"
readonly dnscustomcnamefile="/etc/dnsmasq.d/05-pihole-custom-cname.conf"
readonly gravityDBfile="/etc/pihole/gravity.db"
# Source install script for ${setupVars}, ${PI_HOLE_BIN_DIR} and valid_ip()
readonly PI_HOLE_FILES_DIR="/etc/.pihole"
# shellcheck disable=SC2034 # used in basic-install
PH_TEST="true"
source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh"
coltable="/opt/pihole/COL_TABLE"
if [[ -f ${coltable} ]]; then
@@ -41,11 +32,11 @@ Options:
-c, celsius Set Celsius as preferred temperature unit
-f, fahrenheit Set Fahrenheit as preferred temperature unit
-k, kelvin Set Kelvin as preferred temperature unit
-r, hostrecord Add a name to the DNS associated to an IPv4/IPv6 address
-e, email Set an administrative contact address for the Block Page
-h, --help Show this help dialog
-i, interface Specify dnsmasq's interface listening behavior
-l, privacylevel Set privacy level (0 = lowest, 3 = highest)
-t, teleporter Backup configuration as an archive"
-l, privacylevel Set privacy level (0 = lowest, 4 = highest)"
exit 0
}
@@ -54,7 +45,7 @@ add_setting() {
}
delete_setting() {
sed -i "/^${1}/d" "${setupVars}"
sed -i "/${1}/d" "${setupVars}"
}
change_setting() {
@@ -67,7 +58,7 @@ addFTLsetting() {
}
deleteFTLsetting() {
sed -i "/^${1}/d" "${FTLconf}"
sed -i "/${1}/d" "${FTLconf}"
}
changeFTLsetting() {
@@ -84,7 +75,7 @@ add_dnsmasq_setting() {
}
delete_dnsmasq_setting() {
sed -i "/^${1}/d" "${dnsmasqconfig}"
sed -i "/${1}/d" "${dnsmasqconfig}"
}
SetTemperatureUnit() {
@@ -94,9 +85,9 @@ SetTemperatureUnit() {
HashPassword() {
# Compute password hash twice to avoid rainbow table vulnerability
return=$(echo -n "${1}" | sha256sum | sed 's/\s.*$//')
return=$(echo -n "${return}" | sha256sum | sed 's/\s.*$//')
echo "${return}"
return=$(echo -n ${1} | sha256sum | sed 's/\s.*$//')
return=$(echo -n ${return} | sha256sum | sed 's/\s.*$//')
echo ${return}
}
SetWebPassword() {
@@ -150,29 +141,27 @@ ProcessDNSSettings() {
delete_dnsmasq_setting "server"
COUNTER=1
while true ; do
while [[ 1 ]]; do
var=PIHOLE_DNS_${COUNTER}
if [ -z "${!var}" ]; then
break;
fi
add_dnsmasq_setting "server" "${!var}"
(( COUNTER++ ))
let COUNTER=COUNTER+1
done
# The option LOCAL_DNS_PORT is deprecated
# We apply it once more, and then convert it into the current format
if [ -n "${LOCAL_DNS_PORT}" ]; then
if [ ! -z "${LOCAL_DNS_PORT}" ]; then
add_dnsmasq_setting "server" "127.0.0.1#${LOCAL_DNS_PORT}"
add_setting "PIHOLE_DNS_${COUNTER}" "127.0.0.1#${LOCAL_DNS_PORT}"
delete_setting "LOCAL_DNS_PORT"
fi
delete_dnsmasq_setting "domain-needed"
delete_dnsmasq_setting "expand-hosts"
if [[ "${DNS_FQDN_REQUIRED}" == true ]]; then
add_dnsmasq_setting "domain-needed"
add_dnsmasq_setting "expand-hosts"
fi
delete_dnsmasq_setting "bogus-priv"
@@ -186,13 +175,14 @@ ProcessDNSSettings() {
if [[ "${DNSSEC}" == true ]]; then
echo "dnssec
trust-anchor=.,19036,8,2,49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5
trust-anchor=.,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D
" >> "${dnsmasqconfig}"
fi
delete_dnsmasq_setting "host-record"
if [ -n "${HOSTRECORD}" ]; then
if [ ! -z "${HOSTRECORD}" ]; then
add_dnsmasq_setting "host-record" "${HOSTRECORD}"
fi
@@ -217,79 +207,9 @@ trust-anchor=.,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC68345710423
fi
if [[ "${CONDITIONAL_FORWARDING}" == true ]]; then
# Convert legacy "conditional forwarding" to rev-server configuration
# Remove any existing REV_SERVER settings
delete_setting "REV_SERVER"
delete_setting "REV_SERVER_DOMAIN"
delete_setting "REV_SERVER_TARGET"
delete_setting "REV_SERVER_CIDR"
REV_SERVER=true
add_setting "REV_SERVER" "true"
REV_SERVER_DOMAIN="${CONDITIONAL_FORWARDING_DOMAIN}"
add_setting "REV_SERVER_DOMAIN" "${REV_SERVER_DOMAIN}"
REV_SERVER_TARGET="${CONDITIONAL_FORWARDING_IP}"
add_setting "REV_SERVER_TARGET" "${REV_SERVER_TARGET}"
#Convert CONDITIONAL_FORWARDING_REVERSE if necessary e.g:
# 1.1.168.192.in-addr.arpa to 192.168.1.1/32
# 1.168.192.in-addr.arpa to 192.168.1.0/24
# 168.192.in-addr.arpa to 192.168.0.0/16
# 192.in-addr.arpa to 192.0.0.0/8
if [[ "${CONDITIONAL_FORWARDING_REVERSE}" == *"in-addr.arpa" ]];then
arrRev=("${CONDITIONAL_FORWARDING_REVERSE//./ }")
case ${#arrRev[@]} in
6 ) REV_SERVER_CIDR="${arrRev[3]}.${arrRev[2]}.${arrRev[1]}.${arrRev[0]}/32";;
5 ) REV_SERVER_CIDR="${arrRev[2]}.${arrRev[1]}.${arrRev[0]}.0/24";;
4 ) REV_SERVER_CIDR="${arrRev[1]}.${arrRev[0]}.0.0/16";;
3 ) REV_SERVER_CIDR="${arrRev[0]}.0.0.0/8";;
esac
else
# Set REV_SERVER_CIDR to whatever value it was set to
REV_SERVER_CIDR="${CONDITIONAL_FORWARDING_REVERSE}"
fi
# If REV_SERVER_CIDR is not converted by the above, then use the REV_SERVER_TARGET variable to derive it
if [ -z "${REV_SERVER_CIDR}" ]; then
# Convert existing input to /24 subnet (preserves legacy behavior)
# This sed converts "192.168.1.2" to "192.168.1.0/24"
# shellcheck disable=2001
REV_SERVER_CIDR="$(sed "s+\\.[0-9]*$+\\.0/24+" <<< "${REV_SERVER_TARGET}")"
fi
add_setting "REV_SERVER_CIDR" "${REV_SERVER_CIDR}"
# Remove obsolete settings from setupVars.conf
delete_setting "CONDITIONAL_FORWARDING"
delete_setting "CONDITIONAL_FORWARDING_REVERSE"
delete_setting "CONDITIONAL_FORWARDING_DOMAIN"
delete_setting "CONDITIONAL_FORWARDING_IP"
add_dnsmasq_setting "server=/${CONDITIONAL_FORWARDING_DOMAIN}/${CONDITIONAL_FORWARDING_IP}"
add_dnsmasq_setting "server=/${CONDITIONAL_FORWARDING_REVERSE}/${CONDITIONAL_FORWARDING_IP}"
fi
delete_dnsmasq_setting "rev-server"
if [[ "${REV_SERVER}" == true ]]; then
add_dnsmasq_setting "rev-server=${REV_SERVER_CIDR},${REV_SERVER_TARGET}"
if [ -n "${REV_SERVER_DOMAIN}" ]; then
# Forward local domain names to the CF target, too
add_dnsmasq_setting "server=/${REV_SERVER_DOMAIN}/${REV_SERVER_TARGET}"
fi
if [[ "${DNS_FQDN_REQUIRED}" != true ]]; then
# Forward unqualified names to the CF target only when the "never
# forward non-FQDN" option is unticked
add_dnsmasq_setting "server=//${REV_SERVER_TARGET}"
fi
fi
# We need to process DHCP settings here as well to account for possible
# changes in the non-FQDN forwarding. This cannot be done in 01-pihole.conf
# as we don't want to delete all local=/.../ lines so it's much safer to
# simply rewrite the entire corresponding config file (which is what the
# DHCP settings subroutie is doing)
ProcessDHCPSettings
}
SetDNSServers() {
@@ -298,16 +218,7 @@ SetDNSServers() {
IFS=',' read -r -a array <<< "${args[2]}"
for index in "${!array[@]}"
do
# Replace possible "\#" by "#". This fixes AdminLTE#1427
local ip
ip="${array[index]//\\#/#}"
if valid_ip "${ip}" || valid_ip6 "${ip}" ; then
add_setting "PIHOLE_DNS_$((index+1))" "${ip}"
else
echo -e " ${CROSS} Invalid IP has been passed"
exit 1
fi
add_setting "PIHOLE_DNS_$((index+1))" "${array[index]}"
done
if [[ "${args[3]}" == "domain-needed" ]]; then
@@ -328,13 +239,16 @@ SetDNSServers() {
change_setting "DNSSEC" "false"
fi
if [[ "${args[6]}" == "rev-server" ]]; then
change_setting "REV_SERVER" "true"
change_setting "REV_SERVER_CIDR" "${args[7]}"
change_setting "REV_SERVER_TARGET" "${args[8]}"
change_setting "REV_SERVER_DOMAIN" "${args[9]}"
if [[ "${args[6]}" == "conditional_forwarding" ]]; then
change_setting "CONDITIONAL_FORWARDING" "true"
change_setting "CONDITIONAL_FORWARDING_IP" "${args[7]}"
change_setting "CONDITIONAL_FORWARDING_DOMAIN" "${args[8]}"
change_setting "CONDITIONAL_FORWARDING_REVERSE" "${args[9]}"
else
change_setting "REV_SERVER" "false"
change_setting "CONDITIONAL_FORWARDING" "false"
delete_setting "CONDITIONAL_FORWARDING_IP"
delete_setting "CONDITIONAL_FORWARDING_DOMAIN"
delete_setting "CONDITIONAL_FORWARDING_REVERSE"
fi
ProcessDNSSettings
@@ -360,7 +274,7 @@ Reboot() {
}
RestartDNS() {
"${PI_HOLE_BIN_DIR}"/pihole restartdns
/usr/local/bin/pihole restartdns
}
SetQueryLogOptions() {
@@ -408,18 +322,9 @@ dhcp-option=option:router,${DHCP_ROUTER}
dhcp-leasefile=/etc/pihole/dhcp.leases
#quiet-dhcp
" > "${dhcpconfig}"
chmod 644 "${dhcpconfig}"
if [[ "${PIHOLE_DOMAIN}" != "none" ]]; then
echo "domain=${PIHOLE_DOMAIN}" >> "${dhcpconfig}"
# When there is a Pi-hole domain set and "Never forward non-FQDNs" is
# ticked, we add `local=/domain/` to tell FTL that this domain is purely
# local and FTL may answer queries from /etc/hosts or DHCP but should
# never forward queries on that domain to any upstream servers
if [[ "${DNS_FQDN_REQUIRED}" == true ]]; then
echo "local=/${PIHOLE_DOMAIN}/" >> "${dhcpconfig}"
fi
fi
# Sourced from setupVars
@@ -432,7 +337,7 @@ dhcp-leasefile=/etc/pihole/dhcp.leases
echo "#quiet-dhcp6
#enable-ra
dhcp-option=option6:dns-server,[::]
dhcp-range=::100,::1ff,constructor:${interface},ra-names,slaac,64,3600
dhcp-range=::100,::1ff,constructor:${interface},ra-names,slaac,${leasetime}
ra-param=*,0,0
" >> "${dhcpconfig}"
fi
@@ -458,14 +363,6 @@ EnableDHCP() {
delete_dnsmasq_setting "dhcp-"
delete_dnsmasq_setting "quiet-dhcp"
# If a DHCP client claims that its name is "wpad", ignore that.
# This fixes a security hole. see CERT Vulnerability VU#598349
# We also ignore "localhost" as Windows behaves strangely if a
# device claims this host name
add_dnsmasq_setting "dhcp-name-match=set:hostname-ignore,wpad
dhcp-name-match=set:hostname-ignore,localhost
dhcp-ignore-names=tag:hostname-ignore"
ProcessDHCPSettings
RestartDNS
@@ -487,47 +384,22 @@ SetWebUILayout() {
change_setting "WEBUIBOXEDLAYOUT" "${args[2]}"
}
SetWebUITheme() {
change_setting "WEBTHEME" "${args[2]}"
}
CheckUrl(){
local regex check_url
# Check for characters NOT allowed in URLs
regex="[^a-zA-Z0-9:/?&%=~._()-;]"
# this will remove first @ that is after schema and before domain
# \1 is optional schema, \2 is userinfo
check_url="$( sed -re 's#([^:/]*://)?([^/]+)@#\1\2#' <<< "$1" )"
if [[ "${check_url}" =~ ${regex} ]]; then
return 1
else
return 0
fi
}
CustomizeAdLists() {
local address
address="${args[3]}"
local comment
comment="${args[4]}"
list="/etc/pihole/adlists.list"
if CheckUrl "${address}"; then
if [[ "${args[2]}" == "enable" ]]; then
sqlite3 "${gravityDBfile}" "UPDATE adlist SET enabled = 1 WHERE address = '${address}'"
elif [[ "${args[2]}" == "disable" ]]; then
sqlite3 "${gravityDBfile}" "UPDATE adlist SET enabled = 0 WHERE address = '${address}'"
elif [[ "${args[2]}" == "add" ]]; then
sqlite3 "${gravityDBfile}" "INSERT OR IGNORE INTO adlist (address, comment) VALUES ('${address}', '${comment}')"
elif [[ "${args[2]}" == "del" ]]; then
sqlite3 "${gravityDBfile}" "DELETE FROM adlist WHERE address = '${address}'"
else
echo "Not permitted"
return 1
if [[ "${args[2]}" == "enable" ]]; then
sed -i "\\@${args[3]}@s/^#http/http/g" "${list}"
elif [[ "${args[2]}" == "disable" ]]; then
sed -i "\\@${args[3]}@s/^http/#http/g" "${list}"
elif [[ "${args[2]}" == "add" ]]; then
if [[ $(grep -c "^${args[3]}$" "${list}") -eq 0 ]] ; then
echo "${args[3]}" >> ${list}
fi
elif [[ "${args[2]}" == "del" ]]; then
var=$(echo "${args[3]}" | sed 's/\//\\\//g')
sed -i "/${var}/Id" "${list}"
else
echo "Invalid Url"
echo "Not permitted"
return 1
fi
}
@@ -570,13 +442,33 @@ AddDHCPStaticAddress() {
RemoveDHCPStaticAddress() {
mac="${args[2]}"
if [[ "$mac" =~ ^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$ ]]; then
sed -i "/dhcp-host=${mac}.*/d" "${dhcpstaticconfig}"
else
echo " ${CROSS} Invalid Mac Passed!"
exit 1
sed -i "/dhcp-host=${mac}.*/d" "${dhcpstaticconfig}"
}
SetHostRecord() {
if [[ "${1}" == "-h" ]] || [[ "${1}" == "--help" ]]; then
echo "Usage: pihole -a hostrecord <domain> [IPv4-address],[IPv6-address]
Example: 'pihole -a hostrecord home.domain.com 192.168.1.1,2001:db8:a0b:12f0::1'
Add a name to the DNS associated to an IPv4/IPv6 address
Options:
\"\" Empty: Remove host record
-h, --help Show this help dialog"
exit 0
fi
if [[ -n "${args[3]}" ]]; then
change_setting "HOSTRECORD" "${args[2]},${args[3]}"
echo -e " ${TICK} Setting host record for ${args[2]} to ${args[3]}"
else
change_setting "HOSTRECORD" ""
echo -e " ${TICK} Removing host record"
fi
ProcessDNSSettings
# Restart dnsmasq to load new configuration
RestartDNS
}
SetAdminEmail() {
@@ -592,16 +484,6 @@ Options:
fi
if [[ -n "${args[2]}" ]]; then
# Sanitize email address in case of security issues
# Regex from https://stackoverflow.com/a/2138832/4065967
local regex
regex="^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\$"
if [[ ! "${args[2]}" =~ ${regex} ]]; then
echo -e " ${CROSS} Invalid email address"
exit 0
fi
change_setting "ADMIN_EMAIL" "${args[2]}"
echo -e " ${TICK} Setting admin contact to ${args[2]}"
else
@@ -627,10 +509,10 @@ Interfaces:
fi
if [[ "${args[2]}" == "all" ]]; then
echo -e " ${INFO} Listening on all interfaces, permitting all origins. Please use a firewall!"
echo -e " ${INFO} Listening on all interfaces, permiting all origins. Please use a firewall!"
change_setting "DNSMASQ_LISTENING" "all"
elif [[ "${args[2]}" == "local" ]]; then
echo -e " ${INFO} Listening on all interfaces, permitting origins from one hop away (LAN)"
echo -e " ${INFO} Listening on all interfaces, permiting origins from one hop away (LAN)"
change_setting "DNSMASQ_LISTENING" "local"
else
echo -e " ${INFO} Listening only on interface ${PIHOLE_INTERFACE}"
@@ -647,127 +529,32 @@ Interfaces:
}
Teleporter() {
local datetimestamp
local host
datetimestamp=$(date "+%Y-%m-%d_%H-%M-%S")
host=$(hostname)
host="${host//./_}"
php /var/www/html/admin/scripts/pi-hole/php/teleporter.php > "pi-hole-${host:-noname}-teleporter_${datetimestamp}.tar.gz"
}
checkDomain()
{
local domain validDomain
# Convert to lowercase
domain="${1,,}"
validDomain=$(grep -P "^((-|_)*[a-z\\d]((-|_)*[a-z\\d])*(-|_)*)(\\.(-|_)*([a-z\\d]((-|_)*[a-z\\d])*))*$" <<< "${domain}") # Valid chars check
validDomain=$(grep -P "^[^\\.]{1,63}(\\.[^\\.]{1,63})*$" <<< "${validDomain}") # Length of each label
echo "${validDomain}"
local datetimestamp=$(date "+%Y-%m-%d_%H-%M-%S")
php /var/www/html/admin/scripts/pi-hole/php/teleporter.php > "pi-hole-teleporter_${datetimestamp}.tar.gz"
}
addAudit()
{
shift # skip "-a"
shift # skip "audit"
local domains validDomain
domains=""
for domain in "$@"
for var in "$@"
do
# Check domain to be added. Only continue if it is valid
validDomain="$(checkDomain "${domain}")"
if [[ -n "${validDomain}" ]]; then
# Put comma in between domains when there is
# more than one domains to be added
# SQL INSERT allows adding multiple rows at once using the format
## INSERT INTO table (domain) VALUES ('abc.de'),('fgh.ij'),('klm.no'),('pqr.st');
if [[ -n "${domains}" ]]; then
domains="${domains},"
fi
domains="${domains}('${domain}')"
fi
echo "${var}" >> /etc/pihole/auditlog.list
done
# Insert only the domain here. The date_added field will be
# filled with its default value (date_added = current timestamp)
sqlite3 "${gravityDBfile}" "INSERT INTO domain_audit (domain) VALUES ${domains};"
}
clearAudit()
{
sqlite3 "${gravityDBfile}" "DELETE FROM domain_audit;"
echo -n "" > /etc/pihole/auditlog.list
}
SetPrivacyLevel() {
# Set privacy level. Minimum is 0, maximum is 3
if [ "${args[2]}" -ge 0 ] && [ "${args[2]}" -le 3 ]; then
# Set privacy level. Minimum is 0, maximum is 4
if [ "${args[2]}" -ge 0 ] && [ "${args[2]}" -le 4 ]; then
changeFTLsetting "PRIVACYLEVEL" "${args[2]}"
pihole restartdns reload-lists
fi
}
AddCustomDNSAddress() {
echo -e " ${TICK} Adding custom DNS entry..."
ip="${args[2]}"
host="${args[3]}"
echo "${ip} ${host}" >> "${dnscustomfile}"
# Restart dnsmasq to load new custom DNS entries
RestartDNS
}
RemoveCustomDNSAddress() {
echo -e " ${TICK} Removing custom DNS entry..."
ip="${args[2]}"
host="${args[3]}"
if valid_ip "${ip}" || valid_ip6 "${ip}" ; then
sed -i "/^${ip} ${host}$/d" "${dnscustomfile}"
else
echo -e " ${CROSS} Invalid IP has been passed"
exit 1
fi
# Restart dnsmasq to update removed custom DNS entries
RestartDNS
}
AddCustomCNAMERecord() {
echo -e " ${TICK} Adding custom CNAME record..."
domain="${args[2]}"
target="${args[3]}"
echo "cname=${domain},${target}" >> "${dnscustomcnamefile}"
# Restart dnsmasq to load new custom CNAME records
RestartDNS
}
RemoveCustomCNAMERecord() {
echo -e " ${TICK} Removing custom CNAME record..."
domain="${args[2]}"
target="${args[3]}"
validDomain="$(checkDomain "${domain}")"
if [[ -n "${validDomain}" ]]; then
validTarget="$(checkDomain "${target}")"
if [[ -n "${validDomain}" ]]; then
sed -i "/cname=${validDomain},${validTarget}$/d" "${dnscustomcnamefile}"
else
echo " ${CROSS} Invalid Target Passed!"
exit 1
fi
else
echo " ${CROSS} Invalid Domain passed!"
exit 1
fi
# Restart dnsmasq to update removed custom CNAME records
RestartDNS
}
main() {
args=("$@")
@@ -786,12 +573,12 @@ main() {
"enabledhcp" ) EnableDHCP;;
"disabledhcp" ) DisableDHCP;;
"layout" ) SetWebUILayout;;
"theme" ) SetWebUITheme;;
"-h" | "--help" ) helpFunc;;
"privacymode" ) SetPrivacyMode;;
"resolve" ) ResolutionSettings;;
"addstaticdhcp" ) AddDHCPStaticAddress;;
"removestaticdhcp" ) RemoveDHCPStaticAddress;;
"-r" | "hostrecord" ) SetHostRecord "$3";;
"-e" | "email" ) SetAdminEmail "$3";;
"-i" | "interface" ) SetListeningMode "$@";;
"-t" | "teleporter" ) Teleporter;;
@@ -799,10 +586,6 @@ main() {
"audit" ) addAudit "$@";;
"clearaudit" ) clearAudit;;
"-l" | "privacylevel" ) SetPrivacyLevel;;
"addcustomdns" ) AddCustomDNSAddress;;
"removecustomdns" ) RemoveCustomDNSAddress;;
"addcustomcname" ) AddCustomCNAMERecord;;
"removecustomcname" ) RemoveCustomCNAMERecord;;
* ) helpFunc;;
esac

View File

@@ -0,0 +1,28 @@
#!/bin/bash
# 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.
#
# Provides an automated migration subroutine to convert Pi-hole v3.x wildcard domains to Pi-hole v4.x regex filters
#
# This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license.
# regexFile set in gravity.sh
wildcardFile="/etc/dnsmasq.d/03-pihole-wildcard.conf"
convert_wildcard_to_regex() {
if [ ! -f "${wildcardFile}" ]; then
return
fi
local addrlines domains uniquedomains
# Obtain wildcard domains from old file
addrlines="$(grep -oE "/.*/" ${wildcardFile})"
# Strip "/" from domain names and convert "." to regex-compatible "\."
domains="$(sed 's/\///g;s/\./\\./g' <<< "${addrlines}")"
# Remove repeated domains (may have been inserted two times due to A and AAAA blocking)
uniquedomains="$(uniq <<< "${domains}")"
# Automatically generate regex filters and remove old wildcards file
awk '{print "(^|\\.)"$0"$"}' <<< "${uniquedomains}" >> "${regexFile:?}" && rm "${wildcardFile}"
}

View File

@@ -1,193 +0,0 @@
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE "group"
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
enabled BOOLEAN NOT NULL DEFAULT 1,
name TEXT UNIQUE NOT NULL,
date_added INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
date_modified INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
description TEXT
);
INSERT INTO "group" (id,enabled,name,description) VALUES (0,1,'Default','The default group');
CREATE TABLE domainlist
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
type INTEGER NOT NULL DEFAULT 0,
domain TEXT NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT 1,
date_added INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
date_modified INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
comment TEXT,
UNIQUE(domain, type)
);
CREATE TABLE adlist
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
address TEXT UNIQUE NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT 1,
date_added INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
date_modified INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
comment TEXT,
date_updated INTEGER,
number INTEGER NOT NULL DEFAULT 0,
invalid_domains INTEGER NOT NULL DEFAULT 0,
status INTEGER NOT NULL DEFAULT 0
);
CREATE TABLE adlist_by_group
(
adlist_id INTEGER NOT NULL REFERENCES adlist (id),
group_id INTEGER NOT NULL REFERENCES "group" (id),
PRIMARY KEY (adlist_id, group_id)
);
CREATE TABLE gravity
(
domain TEXT NOT NULL,
adlist_id INTEGER NOT NULL REFERENCES adlist (id)
);
CREATE TABLE info
(
property TEXT PRIMARY KEY,
value TEXT NOT NULL
);
INSERT INTO "info" VALUES('version','14');
CREATE TABLE domain_audit
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
domain TEXT UNIQUE NOT NULL,
date_added INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int))
);
CREATE TABLE domainlist_by_group
(
domainlist_id INTEGER NOT NULL REFERENCES domainlist (id),
group_id INTEGER NOT NULL REFERENCES "group" (id),
PRIMARY KEY (domainlist_id, group_id)
);
CREATE TABLE client
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
ip TEXT NOT NULL UNIQUE,
date_added INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
date_modified INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
comment TEXT
);
CREATE TABLE client_by_group
(
client_id INTEGER NOT NULL REFERENCES client (id),
group_id INTEGER NOT NULL REFERENCES "group" (id),
PRIMARY KEY (client_id, group_id)
);
CREATE TRIGGER tr_adlist_update AFTER UPDATE OF address,enabled,comment ON adlist
BEGIN
UPDATE adlist SET date_modified = (cast(strftime('%s', 'now') as int)) WHERE id = NEW.id;
END;
CREATE TRIGGER tr_client_update AFTER UPDATE ON client
BEGIN
UPDATE client SET date_modified = (cast(strftime('%s', 'now') as int)) WHERE ip = NEW.ip;
END;
CREATE TRIGGER tr_domainlist_update AFTER UPDATE ON domainlist
BEGIN
UPDATE domainlist SET date_modified = (cast(strftime('%s', 'now') as int)) WHERE domain = NEW.domain;
END;
CREATE VIEW vw_whitelist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
FROM domainlist
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
WHERE domainlist.enabled = 1 AND (domainlist_by_group.group_id IS NULL OR "group".enabled = 1)
AND domainlist.type = 0
ORDER BY domainlist.id;
CREATE VIEW vw_blacklist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
FROM domainlist
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
WHERE domainlist.enabled = 1 AND (domainlist_by_group.group_id IS NULL OR "group".enabled = 1)
AND domainlist.type = 1
ORDER BY domainlist.id;
CREATE VIEW vw_regex_whitelist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
FROM domainlist
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
WHERE domainlist.enabled = 1 AND (domainlist_by_group.group_id IS NULL OR "group".enabled = 1)
AND domainlist.type = 2
ORDER BY domainlist.id;
CREATE VIEW vw_regex_blacklist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
FROM domainlist
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
WHERE domainlist.enabled = 1 AND (domainlist_by_group.group_id IS NULL OR "group".enabled = 1)
AND domainlist.type = 3
ORDER BY domainlist.id;
CREATE VIEW vw_gravity AS SELECT domain, adlist_by_group.group_id AS group_id
FROM gravity
LEFT JOIN adlist_by_group ON adlist_by_group.adlist_id = gravity.adlist_id
LEFT JOIN adlist ON adlist.id = gravity.adlist_id
LEFT JOIN "group" ON "group".id = adlist_by_group.group_id
WHERE adlist.enabled = 1 AND (adlist_by_group.group_id IS NULL OR "group".enabled = 1);
CREATE VIEW vw_adlist AS SELECT DISTINCT address, adlist.id AS id
FROM adlist
LEFT JOIN adlist_by_group ON adlist_by_group.adlist_id = adlist.id
LEFT JOIN "group" ON "group".id = adlist_by_group.group_id
WHERE adlist.enabled = 1 AND (adlist_by_group.group_id IS NULL OR "group".enabled = 1)
ORDER BY adlist.id;
CREATE TRIGGER tr_domainlist_add AFTER INSERT ON domainlist
BEGIN
INSERT INTO domainlist_by_group (domainlist_id, group_id) VALUES (NEW.id, 0);
END;
CREATE TRIGGER tr_client_add AFTER INSERT ON client
BEGIN
INSERT INTO client_by_group (client_id, group_id) VALUES (NEW.id, 0);
END;
CREATE TRIGGER tr_adlist_add AFTER INSERT ON adlist
BEGIN
INSERT INTO adlist_by_group (adlist_id, group_id) VALUES (NEW.id, 0);
END;
CREATE TRIGGER tr_group_update AFTER UPDATE ON "group"
BEGIN
UPDATE "group" SET date_modified = (cast(strftime('%s', 'now') as int)) WHERE id = NEW.id;
END;
CREATE TRIGGER tr_group_zero AFTER DELETE ON "group"
BEGIN
INSERT OR IGNORE INTO "group" (id,enabled,name) VALUES (0,1,'Default');
END;
CREATE TRIGGER tr_domainlist_delete AFTER DELETE ON domainlist
BEGIN
DELETE FROM domainlist_by_group WHERE domainlist_id = OLD.id;
END;
CREATE TRIGGER tr_adlist_delete AFTER DELETE ON adlist
BEGIN
DELETE FROM adlist_by_group WHERE adlist_id = OLD.id;
END;
CREATE TRIGGER tr_client_delete AFTER DELETE ON client
BEGIN
DELETE FROM client_by_group WHERE client_id = OLD.id;
END;
COMMIT;

View File

@@ -1,42 +0,0 @@
.timeout 30000
ATTACH DATABASE '/etc/pihole/gravity.db' AS OLD;
BEGIN TRANSACTION;
DROP TRIGGER tr_domainlist_add;
DROP TRIGGER tr_client_add;
DROP TRIGGER tr_adlist_add;
INSERT OR REPLACE INTO "group" SELECT * FROM OLD."group";
INSERT OR REPLACE INTO domain_audit SELECT * FROM OLD.domain_audit;
INSERT OR REPLACE INTO domainlist SELECT * FROM OLD.domainlist;
INSERT OR REPLACE INTO domainlist_by_group SELECT * FROM OLD.domainlist_by_group;
INSERT OR REPLACE INTO adlist SELECT * FROM OLD.adlist;
INSERT OR REPLACE INTO adlist_by_group SELECT * FROM OLD.adlist_by_group;
INSERT OR REPLACE INTO info SELECT * FROM OLD.info;
INSERT OR REPLACE INTO client SELECT * FROM OLD.client;
INSERT OR REPLACE INTO client_by_group SELECT * FROM OLD.client_by_group;
CREATE TRIGGER tr_domainlist_add AFTER INSERT ON domainlist
BEGIN
INSERT INTO domainlist_by_group (domainlist_id, group_id) VALUES (NEW.id, 0);
END;
CREATE TRIGGER tr_client_add AFTER INSERT ON client
BEGIN
INSERT INTO client_by_group (client_id, group_id) VALUES (NEW.id, 0);
END;
CREATE TRIGGER tr_adlist_add AFTER INSERT ON adlist
BEGIN
INSERT INTO adlist_by_group (adlist_id, group_id) VALUES (NEW.id, 0);
END;
COMMIT;

View File

@@ -1,37 +1,46 @@
#!/usr/bin/env sh
#!/bin/bash
### BEGIN INIT INFO
# Provides: pihole-FTL
# Required-Start: $remote_fs $syslog $network
# Required-Stop: $remote_fs $syslog $network
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: pihole-FTL daemon
# Description: Enable service provided by pihole-FTL daemon
### END INIT INFO
is_running() {
pgrep -xo "pihole-FTL" > /dev/null
}
FTLUSER=pihole
BINARY="/usr/bin/pihole-FTL"
PIDFILE=/var/run/pihole-FTL.pid
. /lib/lsb/init-functions
# Start the service
start() {
if is_running; then
if pidofproc -p "${PIDFILE}" > /dev/null 2>&1; then
echo "pihole-FTL is already running"
else
# Touch files to ensure they exist (create if non-existing, preserve if existing)
mkdir -pm 0755 /run/pihole
touch /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole-FTL.log /var/log/pihole.log /etc/pihole/dhcp.leases
touch /var/log/pihole-FTL.log /var/log/pihole.log
touch /run/pihole-FTL.pid /run/pihole-FTL.port
touch /etc/pihole/dhcp.leases
mkdir -p /var/run/pihole
mkdir -p /var/log/pihole
chown pihole:pihole /var/run/pihole /var/log/pihole
# Remove possible leftovers from previous pihole-FTL processes
rm -f /dev/shm/FTL-* 2> /dev/null
rm /var/run/pihole/FTL.sock 2> /dev/null
# Ensure that permissions are set so that pihole-FTL can edit all necessary files
chown pihole:pihole /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole-FTL.log /var/log/pihole.log /etc/pihole/dhcp.leases /run/pihole /etc/pihole
chmod 0644 /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole-FTL.log /var/log/pihole.log /etc/pihole/dhcp.leases /etc/pihole/macvendor.db
# Chown database files to the user FTL runs as. We ignore errors as the files may not (yet) exist
chown -f pihole:pihole /etc/pihole/pihole-FTL.db /etc/pihole/gravity.db /etc/pihole/macvendor.db
if setcap CAP_NET_BIND_SERVICE,CAP_NET_RAW,CAP_NET_ADMIN,CAP_SYS_NICE,CAP_IPC_LOCK,CAP_CHOWN+eip "/usr/bin/pihole-FTL"; then
su -s /bin/sh -c "/usr/bin/pihole-FTL" pihole
chown pihole:pihole /run/pihole-FTL.pid /run/pihole-FTL.port
chown pihole:pihole /etc/pihole /etc/pihole/dhcp.leases 2> /dev/null
chown pihole:pihole /var/log/pihole-FTL.log /var/log/pihole.log
chmod 0644 /var/log/pihole-FTL.log /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole.log
echo "nameserver 127.0.0.1" | /sbin/resolvconf -a lo.piholeFTL
if setcap CAP_NET_BIND_SERVICE,CAP_NET_RAW,CAP_NET_ADMIN+eip "$(which pihole-FTL)"; then
start_daemon -p "${PIDFILE}" /usr/bin/su -s /bin/sh -c "${BINARY} -f" "$FTLUSER" &
else
echo "Warning: Starting pihole-FTL as root because setting capabilities is not supported on this system"
/usr/bin/pihole-FTL
start_daemon -p "${PIDFILE}" "${BINARY}" -f &
fi
echo
fi
@@ -39,21 +48,22 @@ start() {
# Stop the service
stop() {
if is_running; then
pkill -xo "pihole-FTL"
for i in 1 2 3 4 5; do
if ! is_running; then
if pidofproc -p "${PIDFILE}" > /dev/null 2>&1; then
/sbin/resolvconf -d lo.piholeFTL
killproc -p "${PIDFILE}" "${BINARY}"
for i in {1..5}; do
if ! pidofproc -p "${PIDFILE}" > /dev/null 2>&1; then
break
fi
printf "."
echo -n "."
sleep 1
done
echo
if is_running; then
if pidofproc -p "${PIDFILE}" > /dev/null 2>&1; then
echo "Not stopped; may still be shutting down or shutdown may have failed, killing now"
pkill -xo -9 "pihole-FTL"
killproc -p "${PIDFILE}" "${BINARY}" 9
exit 1
else
echo "Stopped"
@@ -61,14 +71,12 @@ stop() {
else
echo "Not running"
fi
# Cleanup
rm -f /run/pihole/FTL.sock /dev/shm/FTL-*
echo
}
# Indicate the service status
status() {
if is_running; then
if pidofproc -p "${PIDFILE}" > /dev/null 2>&1; then
echo "[ ok ] pihole-FTL is running"
exit 0
else
@@ -91,7 +99,7 @@ case "$1" in
start
;;
*)
echo "Usage: $0 {start|stop|restart|reload|status}"
echo $"Usage: $0 {start|stop|restart|reload|status}"
exit 1
esac

View File

@@ -10,7 +10,7 @@
#
#
# This file is under source-control of the Pi-hole installation and update
# scripts, any changes made to this file will be overwritten when the software
# scripts, any changes made to this file will be overwritten when the softare
# is updated or re-installed. Please make any changes to the appropriate crontab
# or other cron file snippets.
@@ -18,19 +18,19 @@
# early morning. Download any updates from the adlists
# Squash output to log, then splat the log to stdout on error to allow for
# standard crontab job error handling.
59 1 * * 7 root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole updateGravity >/var/log/pihole_updateGravity.log || cat /var/log/pihole_updateGravity.log
59 1 * * 7 root PATH="$PATH:/usr/local/bin/" pihole updateGravity >/var/log/pihole_updateGravity.log || cat /var/log/pihole_updateGravity.log
# Pi-hole: Flush the log daily at 00:00
# The flush script will use logrotate if available
# parameter "once": logrotate only once (default is twice)
# parameter "quiet": don't print messages
00 00 * * * root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole flush once quiet
00 00 * * * root PATH="$PATH:/usr/local/bin/" pihole flush once quiet
@reboot root /usr/sbin/logrotate --state /var/lib/logrotate/pihole /etc/pihole/logrotate
@reboot root /usr/sbin/logrotate /etc/pihole/logrotate
# Pi-hole: Grab local version and branch every 10 minutes
*/10 * * * * root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole updatechecker local
*/10 * * * * root PATH="$PATH:/usr/local/bin/" pihole updatechecker local
# Pi-hole: Grab remote version every 24 hours
59 17 * * * root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole updatechecker remote
@reboot root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole updatechecker remote reboot
59 17 * * * root PATH="$PATH:/usr/local/bin/" pihole updatechecker remote
@reboot root PATH="$PATH:/usr/local/bin/" pihole updatechecker remote reboot

View File

@@ -7,7 +7,7 @@ _pihole() {
case "${prev}" in
"pihole")
opts="admin blacklist checkout chronometer debug disable enable flush help logging query reconfigure regex restartdns status tail uninstall updateGravity updatePihole version wildcard whitelist arpflush"
opts="admin blacklist checkout chronometer debug disable enable flush help logging query reconfigure regex restartdns status tail uninstall updateGravity updatePihole version wildcard whitelist"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
;;
"whitelist"|"blacklist"|"wildcard"|"regex")
@@ -15,7 +15,7 @@ _pihole() {
COMPREPLY=( $(compgen -W "${opts_lists}" -- ${cur}) )
;;
"admin")
opts_admin="celsius email fahrenheit interface kelvin password privacylevel"
opts_admin="celsius email fahrenheit hostrecord interface kelvin password privacylevel"
COMPREPLY=( $(compgen -W "${opts_admin}" -- ${cur}) )
;;
"checkout")
@@ -56,11 +56,11 @@ _pihole() {
;;
"privacylevel")
if ( [[ "$prev2" == "admin" ]] || [[ "$prev2" == "-a" ]] ); then
opts_privacy="0 1 2 3"
opts_privacy="0 1 2 3 4"
COMPREPLY=( $(compgen -W "${opts_privacy}" -- ${cur}) )
else
else
return 1
fi
fi
;;
"core"|"admin"|"ftl")
if [[ "$prev2" == "checkout" ]]; then

View File

@@ -6,46 +6,45 @@
* Please see LICENSE file for your rights under this license. */
/* Text Customisation Options ======> */
.title::before { content: "Website Blocked"; }
.altBtn::before { content: "Why am I here?"; }
.linkPH::before { content: "About Pi-hole"; }
.linkEmail::before { content: "Contact Admin"; }
.title:before { content: "Website Blocked"; }
.altBtn:before { content: "Why am I here?"; }
.linkPH:before { content: "About Pi-hole"; }
.linkEmail:before { content: "Contact Admin"; }
#bpOutput.add::before { content: "Info"; }
#bpOutput.add::after { content: "The domain is being whitelisted..."; }
#bpOutput.error::before, .unhandled::before { content: "Error"; }
#bpOutput.unhandled::after { content: "An unhandled exception occurred. This may happen when your browser is unable to load jQuery, or when the webserver is denying access to the Pi-hole API."; }
#bpOutput.success::before { content: "Success"; }
#bpOutput.success::after { content: "Website has been whitelisted! You may need to flush your DNS cache"; }
#bpOutput.add:before { content: "Info"; }
#bpOutput.add:after { content: "The domain is being whitelisted..."; }
#bpOutput.error:before, .unhandled:before { content: "Error"; }
#bpOutput.unhandled:after { content: "An unhandled exception occured. This may happen when your browser is unable to load jQuery, or when the webserver is denying access to the Pi-hole API."; }
#bpOutput.success:before { content: "Success"; }
#bpOutput.success:after { content: "Website has been whitelisted! You may need to flush your DNS cache"; }
.recentwl::before { content: "This site has been whitelisted. Please flush your DNS cache and/or restart your browser."; }
.unknown::before { content: "This website is not found in any of Pi-hole's blacklists. The reason you have arrived here is unknown."; }
.cname::before { content: "This site is an alias for "; } /* <a href="http://cname.com">cname.com</a> */
.cname::after { content: ", which may be blocked by Pi-hole."; }
.recentwl:before { content: "This site has been whitelisted. Please flush your DNS cache and/or restart your browser."; }
.unknown:before { content: "This website is not found in any of Pi-hole's blacklists. The reason you have arrived here is unknown."; }
.cname:before { content: "This site is an alias for "; } /* <a href="http://cname.com">cname.com</a> */
.cname:after { content: ", which may be blocked by Pi-hole."; }
.blacklist::before { content: "Manually Blacklisted"; }
.wildcard::before { content: "Manually Blacklisted by Wildcard"; }
.noblock::before { content: "Not found on any Blacklist"; }
.blacklist:before { content: "Manually Blacklisted"; }
.wildcard:before { content: "Manually Blacklisted by Wildcard"; }
.noblock:before { content: "Not found on any Blacklist"; }
#bpBlock::before { content: "Access to the following website has been denied:"; }
#bpFlag::before { content: "This is primarily due to being flagged as:"; }
#bpBlock:before { content: "Access to the following website has been denied:"; }
#bpFlag:before { content: "This is primarily due to being flagged as:"; }
#bpHelpTxt::before { content: "If you have an ongoing use for this website, please "; }
#bpHelpTxt a::before, #bpHelpTxt span::before { content: "ask the administrator"; }
#bpHelpTxt::after{ content: " of the Pi-hole on this network to have it whitelisted"; }
#bpHelpTxt:before { content: "If you have an ongoing use for this website, please "; }
#bpHelpTxt a:before, #bpHelpTxt span:before { content: "ask the administrator"; }
#bpHelpTxt:after{ content: " of the Pi-hole on this network to have it whitelisted"; }
#bpBack::before { content: "Back to safety"; }
#bpInfo::before { content: "Technical Info"; }
#bpFoundIn::before { content: "This site is found in "; }
#bpFoundIn span::after { content: " of "; }
#bpFoundIn::after { content: " lists:"; }
#bpWhitelist::before { content: "Whitelist"; }
#bpBack:before { content: "Back to safety"; }
#bpInfo:before { content: "Technical Info"; }
#bpFoundIn:before { content: "This site is found in "; }
#bpFoundIn span:after { content: " of "; }
#bpFoundIn:after { content: " lists:"; }
#bpWhitelist:before { content: "Whitelist"; }
footer span::before { content: "Page generated on "; }
footer span:before { content: "Page generated on "; }
/* Hide whitelisting form entirely */
/* #bpWLButtons { display: none; } */
/* Text Customisation Options <=============================== */
/* http://necolas.github.io/normalize.css ======> */
@@ -99,7 +98,7 @@ html { font-size: 62.5%; }
a { color: #3c8dbc; text-decoration: none; }
a:hover { color: #72afda; text-decoration: underline; }
b { color: rgb(68, 68, 68); }
b { color: rgb(68,68,68); }
p { margin: 0; }
label, .buttons a {
@@ -112,7 +111,7 @@ label, .buttons a {
label, .buttons *:not([disabled]) { cursor: pointer; }
/* Touch device dark tap highlight */
header h1 a, label, .buttons * { -webkit-tap-highlight-color: transparent; }
header h1 a, label, .buttons * { -webkit-tap-highlight-color: transparent; }
/* Webkit Focus Glow */
textarea, input, button { outline: none; }
@@ -121,20 +120,14 @@ textarea, input, button { outline: none; }
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
font-display: swap;
src: local("Source Sans Pro Regular"), local("SourceSansPro-Regular"),
url("/admin/style/vendor/SourceSansPro/source-sans-pro-v13-latin-regular.woff2") format("woff2"),
url("/admin/style/vendor/SourceSansPro/source-sans-pro-v13-latin-regular.woff") format("woff");
src: local("Source Sans Pro"), local("SourceSansPro-Regular"), url("/admin/style/vendor/SourceSansPro/SourceSansPro-Regular.ttf") format("truetype");
}
@font-face {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 700;
font-display: swap;
src: local("Source Sans Pro Bold"), local("SourceSansPro-Bold"),
url("/admin/style/vendor/SourceSansPro/source-sans-pro-v13-latin-700.woff2") format("woff2"),
url("/admin/style/vendor/SourceSansPro/source-sans-pro-v13-latin-700.woff") format("woff");
src: local("Source Sans Pro Bold"), local("SourceSansPro-Bold"), url("/admin/style/vendor/SourceSansPro/SourceSansPro-Bold.ttf") format("truetype");
}
body {
@@ -145,24 +138,14 @@ body {
}
/* User is greeted with a splash page when browsing to Pi-hole IP address */
#splashpage {
background: #222;
color: rgba(255, 255, 255, 0.7);
text-align: center;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
#splashpage { background: #222; color: rgba(255,255,255,0.7); text-align: center; }
#splashpage img { margin: 5px; width: 256px; }
#splashpage b { color: inherit; }
#bpWrapper {
margin: 0 auto;
max-width: 1250px;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.5);
box-shadow: 0 0 8px rgba(0,0,0,0.5);
}
header {
@@ -181,15 +164,15 @@ header h1, header h1 a, header .spc, header #bpAlt label {
}
h1 a {
background-color: rgba(0, 0, 0, 0.1);
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
background-color: rgba(0,0,0,0.1);
font-family: "Helvetica Neue", Helvetica, Arial ,sans-serif;
font-size: 2rem;
font-weight: 400;
font-weight: normal;
min-width: 230px;
text-align: center;
text-align: center;
}
h1 a:hover, header #bpAlt:hover { background-color: rgba(0, 0, 0, 0.12); color: inherit; text-decoration: none; }
h1 a:hover, header #bpAlt:hover { background-color: rgba(0,0,0,0.12); color: inherit; text-decoration: none; }
header .spc { width: 100%; }
@@ -197,34 +180,13 @@ header #bpAlt label {
background: url("/admin/img/logo.svg") no-repeat center left 15px;
background-size: 15px 23px;
padding: 0 15px;
text-indent: 30px;
text-indent: 30px;
}
[type="checkbox"][id$="Toggle"] { display: none; }
[type="checkbox"][id$="Toggle"]:checked ~ #bpAbout,
[type="checkbox"][id$="Toggle"]:checked ~ #bpMoreInfo {
display: block;
}
html, body {
height: 100%;
}
#pihole_card {
width: 400px;
height: auto;
max-width: 400px;
}
#pihole_card p, #pihole_card a {
font-size: 13pt;
text-align: center;
}
#pihole_logo_splash {
height: auto;
width: 100%;
}
[type=checkbox][id$="Toggle"] { display: none; }
[type=checkbox][id$="Toggle"]:checked ~ #bpAbout,
[type=checkbox][id$="Toggle"]:checked ~ #bpMoreInfo {
display: block; }
/* Click anywhere else on screen to hide #bpAbout */
#bpAboutToggle:checked {
@@ -235,28 +197,28 @@ html, body {
top: 0;
opacity: 0;
position: absolute;
width: 100%;
width: 100%;
}
#bpAbout {
background: #3c8dbc;
border-bottom-left-radius: 5px;
border: 1px solid #fff;
border: 1px solid #FFF;
border-right-width: 0;
box-shadow: -1px 1px 1px rgba(0, 0, 0, 0.12);
box-shadow: -1px 1px 1px rgba(0,0,0,0.12);
box-sizing: border-box;
display: none;
font-size: 1.7rem;
top: 50px;
position: absolute;
right: 0;
width: 280px;
width: 280px;
z-index: 1;
}
.aboutPH {
box-sizing: border-box;
color: rgba(255, 255, 255, 0.8);
color: rgba(255,255,255,0.8);
display: block;
padding: 10px;
width: 100%;
@@ -275,7 +237,7 @@ html, body {
.aboutPH p { margin: 10px 0; }
.aboutPH small { display: block; font-size: 1.2rem; }
.aboutLink {
.aboutLink {
background: #fff;
border-top: 1px solid #ddd;
display: table;
@@ -299,16 +261,16 @@ main {
#bpOutput {
background: #00c0ef;
border-radius: 3px;
border: 1px solid rgba(0, 0, 0, 0.1);
border: 1px solid rgba(0,0,0,0.1);
color: #fff;
font-size: 1.4rem;
margin-bottom: 10px;
margin-top: 5px;
padding: 15px;
padding: 15px;
}
#bpOutput::before {
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='14' viewBox='0 0 7 14'%3E%3Cpath fill='%23fff' d='M6 11a1.371 1.371 0 011 1v1a1.371 1.371 0 01-1 1H1a1.371 1.371 0 01-1-1v-1a1.371 1.371 0 011-1h1V8H1a1.371 1.371 0 01-1-1V6a1.371 1.371 0 011-1h3a1.371 1.371 0 011 1v5h1zM3.5 0A1.5 1.5 0 112 1.5 1.5 1.5 0 013.5 0z'/%3E%3C/svg%3E") no-repeat center left;
#bpOutput:before {
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='14' viewBox='0 0 7 14'%3E%3Cpath fill='%23fff' d='M6,11a1.371,1.371,0,0,1,1,1v1a1.371,1.371,0,0,1-1,1H1a1.371,1.371,0,0,1-1-1V12a1.371,1.371,0,0,1,1-1H2V8H1A1.371,1.371,0,0,1,0,7V6A1.371,1.371,0,0,1,1,5H4A1.371,1.371,0,0,1,5,6v5H6ZM3.5,0A1.5,1.5,0,1,1,2,1.5,1.5,1.5,0,0,1,3.5,0Z'/%3E%3C/svg%3E") no-repeat center left;
display: block;
font-size: 1.8rem;
text-indent: 15px;
@@ -319,8 +281,8 @@ main {
#bpOutput.error { background: #dd4b39; }
.blockMsg, .flagMsg {
font: 700 1.8rem Consolas, Courier, monospace;
padding: 5px 10px 10px;
font: bold 1.8rem Consolas, Courier, monospace;
padding: 5px 10px 10px 10px;
text-indent: 15px;
}
@@ -336,7 +298,7 @@ main {
-moz-appearance: none;
-webkit-appearance: none;
border-radius: 3px;
border: 1px solid rgba(0, 0, 0, 0.1);
border: 1px solid rgba(0,0,0,0.1);
box-sizing: content-box;
display: table-cell;
font-size: 1.65rem;
@@ -347,48 +309,46 @@ main {
text-align: center;
vertical-align: top;
white-space: nowrap;
width: auto;
width: auto;
}
.buttons a:hover { text-decoration: none; }
/* Button hover dark overlay */
.buttons *:not(input):not([disabled]):hover {
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1));
color: #fff;
background-image: linear-gradient(to bottom, rgba(0,0,0,0.1), rgba(0,0,0,0.1));
color: #FFF;
}
/* Button active shadow inset */
.buttons *:not([disabled]):not(input):active {
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
box-shadow: inset 0 3px 5px rgba(0,0,0,0.125);
}
/* Input border color */
/* Input border colour */
.buttons *:not([disabled]):hover, .buttons input:focus {
border-color: rgba(0, 0, 0, 0.25);
border-color: rgba(0,0,0,0.25);
}
#bpButtons * { width: 50%; color: #fff; }
#bpBack { background-color: #00a65a; }
#bpInfo { background-color: #3c8dbc; }
#bpButtons * { width: 50%; color: #FFF; }
#bpBack { background-color: #00a65a; }
#bpInfo { background-color: #3c8dbc; }
#bpWhitelist { background-color: #dd4b39; }
#blockpage .buttons [type="password"][disabled] { color: rgba(0, 0, 0, 1); }
#blockpage .buttons [disabled] { color: rgba(0, 0, 0, 0.55); background-color: #e3e3e3; }
#blockpage .buttons [type="password"]:-ms-input-placeholder { color: rgba(51, 51, 51, 0.8); }
#blockpage .buttons [type=password][disabled] { color: rgba(0,0,0,1); }
#blockpage .buttons [disabled] { color: rgba(0,0,0,0.55); background-color: #e3e3e3; }
#blockpage .buttons [type=password]:-ms-input-placeholder { color: rgba(51,51,51,0.8); }
input[type="password"] { font-size: 1.5rem; }
@-webkit-keyframes slidein { from { max-height: 0; opacity: 0; } to { max-height: 300px; opacity: 1; } }
input[type=password] { font-size: 1.5rem; }
@keyframes slidein { from { max-height: 0; opacity: 0; } to { max-height: 300px; opacity: 1; } }
#bpMoreToggle:checked ~ #bpMoreInfo { display: block; margin-top: 8px; -webkit-animation: slidein 0.05s linear; animation: slidein 0.05s linear; }
#bpMoreToggle:checked ~ #bpMoreInfo { display: block; margin-top: 8px; animation: slidein 0.05s linear; }
#bpMoreInfo { display: none; margin-top: 10px; }
#bpQueryOutput {
font-size: 1.2rem;
line-height: 1.65rem;
margin: 5px 0 0;
margin: 5px 0 0 0;
overflow: auto;
padding: 0 5px;
-webkit-overflow-scrolling: touch;
@@ -407,49 +367,17 @@ footer {
border-top: 1px solid #d2d6de;
color: #444;
font: 1.2rem Consolas, Courier, monospace;
padding: 8px;
padding: 8px;
}
/* Responsive Content */
@media only screen and (max-width: 500px) {
h1 a {
font-size: 1.8rem;
min-width: 170px;
}
footer span::before {
content: "Generated ";
}
footer span {
display: block;
}
h1 a { font-size: 1.8rem; min-width: 170px; }
footer span:before { content: "Generated "; }
footer span { display: block; }
}
@media only screen and (min-width: 1251px) {
#bpWrapper, footer {
border-radius: 0 0 5px 5px;
}
#bpAbout {
border-right-width: 1px;
}
}
@media only screen and (max-width: 400px) {
#pihole_card {
width: 100%;
height: auto;
}
#pihole_card p, #pihole_card a {
font-size: 100%;
}
}
@media only screen and (max-width: 256px) {
#pihole_logo_splash {
width: 90% !important;
height: auto;
}
#bpWrapper, footer { border-radius: 0 0 5px 5px; }
#bpAbout { border-right-width: 1px; }
}

View File

@@ -46,7 +46,7 @@
#resolv-file=
# By default, dnsmasq will send queries to any of the upstream
# servers it knows about and tries to favor servers to are known
# servers it knows about and tries to favour servers to are known
# to be up. Uncommenting this forces dnsmasq to try each query
# with each server strictly in the order they appear in
# /etc/resolv.conf
@@ -189,7 +189,7 @@
# add names to the DNS for the IPv6 address of SLAAC-configured dual-stack
# hosts. Use the DHCPv4 lease to derive the name, network segment and
# MAC address and assume that the host will also have an
# IPv6 address calculated using the SLAAC algorithm.
# IPv6 address calculated using the SLAAC alogrithm.
#dhcp-range=1234::, ra-names
# Do Router Advertisements, BUT NOT DHCP for this subnet.
@@ -210,7 +210,7 @@
#dhcp-range=1234::, ra-stateless, ra-names
# Do router advertisements for all subnets where we're doing DHCPv6
# Unless overridden by ra-stateless, ra-names, et al, the router
# Unless overriden by ra-stateless, ra-names, et al, the router
# advertisements will have the M and O bits set, so that the clients
# get addresses and configuration from DHCPv6, and the A bit reset, so the
# clients don't use SLAAC addresses.
@@ -281,7 +281,7 @@
# Give a fixed IPv6 address and name to client with
# DUID 00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2
# Note the MAC addresses CANNOT be used to identify DHCPv6 clients.
# Note also the they [] around the IPv6 address are obligatory.
# Note also the they [] around the IPv6 address are obilgatory.
#dhcp-host=id:00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2, fred, [1234::5]
# Ignore any clients which are not specified in dhcp-host lines
@@ -404,14 +404,14 @@
#dhcp-option=vendor:MSFT,2,1i
# Send the Encapsulated-vendor-class ID needed by some configurations of
# Etherboot to allow is to recognize the DHCP server.
# Etherboot to allow is to recognise the DHCP server.
#dhcp-option=vendor:Etherboot,60,"Etherboot"
# Send options to PXELinux. Note that we need to send the options even
# though they don't appear in the parameter request list, so we need
# to use dhcp-option-force here.
# See http://syslinux.zytor.com/pxe.php#special for details.
# Magic number - needed before anything else is recognized
# Magic number - needed before anything else is recognised
#dhcp-option-force=208,f1:00:74:7e
# Configuration file name
#dhcp-option-force=209,configs/common

View File

@@ -6,16 +6,25 @@
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
// Sanitize SERVER_NAME output
$serverName = htmlspecialchars($_SERVER["SERVER_NAME"]);
// Sanitise HTTP_HOST output
$serverName = htmlspecialchars($_SERVER["HTTP_HOST"]);
// Remove external ipv6 brackets if any
$serverName = preg_replace('/^\[(.*)\]$/', '${1}', $serverName);
if (!is_file("/etc/pihole/setupVars.conf"))
die("[ERROR] File not found: <code>/etc/pihole/setupVars.conf</code>");
// Get values from setupVars.conf
$setupVars = parse_ini_file("/etc/pihole/setupVars.conf");
$svPasswd = !empty($setupVars["WEBPASSWORD"]);
$svEmail = (!empty($setupVars["ADMIN_EMAIL"]) && filter_var($setupVars["ADMIN_EMAIL"], FILTER_VALIDATE_EMAIL)) ? $setupVars["ADMIN_EMAIL"] : "";
unset($setupVars);
// Set landing page location, found within /var/www/html/
$landPage = "../landing.php";
// Define array for hostnames to be accepted as self address for splash page
$authorizedHosts = [ "localhost" ];
$authorizedHosts = [];
if (!empty($_SERVER["FQDN"])) {
// If setenv.add-environment = ("fqdn" => "true") is configured in lighttpd,
// append $serverName to $authorizedHosts
@@ -25,48 +34,319 @@ if (!empty($_SERVER["FQDN"])) {
array_push($authorizedHosts, $_SERVER["VIRTUAL_HOST"]);
}
// Set which extension types render as Block Page (Including "" for index.ext)
$validExtTypes = array("asp", "htm", "html", "php", "rss", "xml", "");
// Get extension of current URL
$currentUrlExt = pathinfo($_SERVER["REQUEST_URI"], PATHINFO_EXTENSION);
// Set mobile friendly viewport
$viewPort = '<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>';
// Set response header
function setHeader($type = "x") {
header("X-Pi-hole: A black hole for Internet advertisements.");
if (isset($type) && $type === "js") header("Content-Type: application/javascript");
}
// Determine block page type
if ($serverName === "pi.hole"
|| (!empty($_SERVER["VIRTUAL_HOST"]) && $serverName === $_SERVER["VIRTUAL_HOST"])) {
if ($serverName === "pi.hole") {
// Redirect to Web Interface
exit(header("Location: /admin"));
} elseif (filter_var($serverName, FILTER_VALIDATE_IP) || in_array($serverName, $authorizedHosts)) {
// When directly browsing via IP or authorized hostname
// Render splash/landing page based off presence of $landPage file
// Unset variables so as to not be included in $landPage or $splashPage
unset($svPasswd, $svEmail, $authorizedHosts, $validExtTypes, $currentUrlExt);
// If $landPage file is present
if (is_file(getcwd()."/$landPage")) {
unset($serverName, $viewPort); // unset extra variables not to be included in $landpage
include $landPage;
exit();
}
// If $landPage file was not present, Set Splash Page output
$splashPage = <<<EOT
<!doctype html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<title>● $serverName</title>
<link rel='shortcut icon' href='admin/img/favicons/favicon.ico' type='image/x-icon'>
<style>
#splashpage { background: #222; color: rgba(255, 255, 255, 0.7); text-align: center; }
#splashpage img { margin: 5px; width: 256px; }
#splashpage b { color: inherit; }
</style>
</head>
<body id='splashpage'>
<img src='admin/img/logo.svg' alt='Pi-hole logo' width='256' height='377'>
<br>
<p>Pi-<strong>hole</strong>: Your black hole for Internet advertisements</p>
<a href='/admin'>Did you mean to go to the admin panel?</a>
</body>
</html>
EOT;
exit($splashPage);
// Set Splash Page output
$splashPage = "
<html><head>
$viewPort
<link rel='stylesheet' href='/pihole/blockingpage.css' type='text/css'/>
</head><body id='splashpage'><img src='/admin/img/logo.svg'/><br/>Pi-<b>hole</b>: Your black hole for Internet advertisements<br><a href='/admin'>Did you mean to go to the admin panel?</a></body></html>
";
// Set splash/landing page based off presence of $landPage
$renderPage = is_file(getcwd()."/$landPage") ? include $landPage : "$splashPage";
// Unset variables so as to not be included in $landPage
unset($serverName, $svPasswd, $svEmail, $authorizedHosts, $validExtTypes, $currentUrlExt, $viewPort);
// Render splash/landing page when directly browsing via IP or authorised hostname
exit($renderPage);
} elseif ($currentUrlExt === "js") {
// Serve Pi-hole Javascript for blocked domains requesting JS
exit(setHeader("js").'var x = "Pi-hole: A black hole for Internet advertisements."');
} elseif (strpos($_SERVER["REQUEST_URI"], "?") !== FALSE && isset($_SERVER["HTTP_REFERER"])) {
// Serve blank image upon receiving REQUEST_URI w/ query string & HTTP_REFERRER
// e.g: An iframe of a blocked domain
exit(setHeader().'<html>
<head><script>window.close();</script></head>
<body><img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs="></body>
</html>');
} elseif (!in_array($currentUrlExt, $validExtTypes) || substr_count($_SERVER["REQUEST_URI"], "?")) {
// Serve SVG upon receiving non $validExtTypes URL extension or query string
// e.g: Not an iframe of a blocked domain, such as when browsing to a file/query directly
// QoL addition: Allow the SVG to be clicked on in order to quickly show the full Block Page
$blockImg = '<a href="/"><svg xmlns="http://www.w3.org/2000/svg" width="110" height="16"><defs><style>a {text-decoration: none;} circle {stroke: rgba(152,2,2,0.5); fill: none; stroke-width: 2;} rect {fill: rgba(152,2,2,0.5);} text {opacity: 0.3; font: 11px Arial;}</style></defs><circle cx="8" cy="8" r="7"/><rect x="10.3" y="-6" width="2" height="12" transform="rotate(45)"/><text x="19.3" y="12">Blocked by Pi-hole</text></svg></a>';
exit(setHeader()."<html>
<head>$viewPort</head>
<body>$blockImg</body>
</html>");
}
exit(header("HTTP/1.1 404 Not Found"));
/* Start processing Block Page from here */
// Define admin email address text based off $svEmail presence
$bpAskAdmin = !empty($svEmail) ? '<a href="mailto:'.$svEmail.'?subject=Site Blocked: '.$serverName.'"></a>' : "<span/>";
// Determine if at least one block list has been generated
$blocklistglob = glob("/etc/pihole/list.0.*.domains");
if ($blocklistglob === array()) {
die("[ERROR] There are no domain lists generated lists within <code>/etc/pihole/</code>! Please update gravity by running <code>pihole -g</code>, or repair Pi-hole using <code>pihole -r</code>.");
}
// Set location of adlists file
if (is_file("/etc/pihole/adlists.list")) {
$adLists = "/etc/pihole/adlists.list";
} elseif (is_file("/etc/pihole/adlists.default")) {
$adLists = "/etc/pihole/adlists.default";
} else {
die("[ERROR] File not found: <code>/etc/pihole/adlists.list</code>");
}
// Get all URLs starting with "http" or "www" from adlists and re-index array numerically
$adlistsUrls = array_values(preg_grep("/(^http)|(^www)/i", file($adLists, FILE_IGNORE_NEW_LINES)));
if (empty($adlistsUrls))
die("[ERROR]: There are no adlist URL's found within <code>$adLists</code>");
// Get total number of blocklists (Including Whitelist, Blacklist & Wildcard lists)
$adlistsCount = count($adlistsUrls) + 3;
// Set query timeout
ini_set("default_socket_timeout", 3);
// Logic for querying blocklists
function queryAds($serverName) {
// Determine the time it takes while querying adlists
$preQueryTime = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"];
$queryAds = file("http://127.0.0.1/admin/scripts/pi-hole/php/queryads.php?domain=$serverName&bp", FILE_IGNORE_NEW_LINES);
$queryAds = array_values(array_filter(preg_replace("/data:\s+/", "", $queryAds)));
$queryTime = sprintf("%.0f", (microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"]) - $preQueryTime);
// Exception Handling
try {
// Define Exceptions
if (strpos($queryAds[0], "No exact results") !== FALSE) {
// Return "none" into $queryAds array
return array("0" => "none");
} else if ($queryTime >= ini_get("default_socket_timeout")) {
// Connection Timeout
throw new Exception ("Connection timeout (".ini_get("default_socket_timeout")."s)");
} elseif (!strpos($queryAds[0], ".") !== false) {
// Unknown $queryAds output
throw new Exception ("Unhandled error message (<code>$queryAds[0]</code>)");
}
return $queryAds;
} catch (Exception $e) {
// Return exception as array
return array("0" => "error", "1" => $e->getMessage());
}
}
// Get results of queryads.php exact search
$queryAds = queryAds($serverName);
// Pass error through to Block Page
if ($queryAds[0] === "error")
die("[ERROR]: Unable to parse results from <i>queryads.php</i>: <code>".$queryAds[1]."</code>");
// Count total number of matching blocklists
$featuredTotal = count($queryAds);
// Place results into key => value array
$queryResults = null;
foreach ($queryAds as $str) {
$value = explode(" ", $str);
@$queryResults[$value[0]] .= "$value[1]";
}
// Determine if domain has been blacklisted, whitelisted, wildcarded or CNAME blocked
if (strpos($queryAds[0], "blacklist") !== FALSE) {
$notableFlagClass = "blacklist";
$adlistsUrls = array("π" => substr($queryAds[0], 2));
} elseif (strpos($queryAds[0], "whitelist") !== FALSE) {
$notableFlagClass = "noblock";
$adlistsUrls = array("π" => substr($queryAds[0], 2));
$wlInfo = "recentwl";
} elseif (strpos($queryAds[0], "wildcard") !== FALSE) {
$notableFlagClass = "wildcard";
$adlistsUrls = array("π" => substr($queryAds[0], 2));
} elseif ($queryAds[0] === "none") {
$featuredTotal = "0";
$notableFlagClass = "noblock";
// QoL addition: Determine appropriate info message if CNAME exists
// Suggests to the user that $serverName has a CNAME (alias) that may be blocked
$dnsRecord = dns_get_record("$serverName")[0];
if (array_key_exists("target", $dnsRecord)) {
$wlInfo = $dnsRecord['target'];
} else {
$wlInfo = "unknown";
}
}
// Set #bpOutput notification
$wlOutputClass = (isset($wlInfo) && $wlInfo === "recentwl") ? $wlInfo : "hidden";
$wlOutput = (isset($wlInfo) && $wlInfo !== "recentwl") ? "<a href='http://$wlInfo'>$wlInfo</a>" : "";
// Get Pi-hole Core version
$phVersion = exec("cd /etc/.pihole/ && git describe --long --tags");
// Print $execTime on development branches
// Testing for - is marginally faster than "git rev-parse --abbrev-ref HEAD"
if (explode("-", $phVersion)[1] != "0")
$execTime = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"];
// Please Note: Text is added via CSS to allow an admin to provide a localised
// language without the need to edit this file
setHeader();
?>
<!DOCTYPE html>
<!-- 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. -->
<html>
<head>
<meta charset="UTF-8">
<?=$viewPort ?>
<meta name="robots" content="noindex,nofollow"/>
<meta http-equiv="x-dns-prefetch-control" content="off">
<link rel="shortcut icon" href="//pi.hole/admin/img/favicon.png" type="image/x-icon"/>
<link rel="stylesheet" href="//pi.hole/pihole/blockingpage.css" type="text/css"/>
<title>● <?=$serverName ?></title>
<script src="//pi.hole/admin/scripts/vendor/jquery.min.js"></script>
<script>
window.onload = function () {
<?php
// Remove href fallback from "Back to safety" button
if ($featuredTotal > 0) {
echo '$("#bpBack").removeAttr("href");';
// Enable whitelisting if JS is available
echo '$("#bpWhitelist").prop("disabled", false);';
// Enable password input if necessary
if (!empty($svPasswd)) {
echo '$("#bpWLPassword").attr("placeholder", "Password");';
echo '$("#bpWLPassword").prop("disabled", false);';
}
// Otherwise hide the input
else {
echo '$("#bpWLPassword").hide();';
}
}
?>
}
</script>
</head>
<body id="blockpage"><div id="bpWrapper">
<header>
<h1 id="bpTitle">
<a class="title" href="/"><?php //Website Blocked ?></a>
</h1>
<div class="spc"></div>
<input id="bpAboutToggle" type="checkbox"/>
<div id="bpAbout">
<div class="aboutPH">
<div class="aboutImg"/></div>
<p>Open Source Ad Blocker
<small>Designed for Raspberry Pi</small>
</p>
</div>
<div class="aboutLink">
<a class="linkPH" href="https://github.com/pi-hole/pi-hole/wiki/What-is-Pi-hole%3F-A-simple-explanation"><?php //About PH ?></a>
<?php if (!empty($svEmail)) echo '<a class="linkEmail" href="mailto:'.$svEmail.'"></a>'; ?>
</div>
</div>
<div id="bpAlt">
<label class="altBtn" for="bpAboutToggle"><?php //Why am I here? ?></label>
</div>
</header>
<main>
<div id="bpOutput" class="<?=$wlOutputClass ?>"><?=$wlOutput ?></div>
<div id="bpBlock">
<p class="blockMsg"><?=$serverName ?></p>
</div>
<?php if(isset($notableFlagClass)) { ?>
<div id="bpFlag">
<p class="flagMsg <?=$notableFlagClass ?>"></p>
</div>
<?php } ?>
<div id="bpHelpTxt"><?=$bpAskAdmin ?></div>
<div id="bpButtons" class="buttons">
<a id="bpBack" onclick="javascript:history.back()" href="about:home"></a>
<?php if ($featuredTotal > 0) echo '<label id="bpInfo" for="bpMoreToggle"></label>'; ?>
</div>
<input id="bpMoreToggle" type="checkbox">
<div id="bpMoreInfo">
<span id="bpFoundIn"><span><?=$featuredTotal ?></span><?=$adlistsCount ?></span>
<pre id='bpQueryOutput'><?php if ($featuredTotal > 0) foreach ($queryResults as $num => $value) { echo "<span>[$num]:</span>$adlistsUrls[$num]\n"; } ?></pre>
<form id="bpWLButtons" class="buttons">
<input id="bpWLDomain" type="text" value="<?=$serverName ?>" disabled/>
<input id="bpWLPassword" type="password" placeholder="Javascript disabled" disabled/><button id="bpWhitelist" type="button" disabled></button>
</form>
</div>
</main>
<footer><span><?=date("l g:i A, F dS"); ?>.</span> Pi-hole <?=$phVersion ?> (<?=gethostname()."/".$_SERVER["SERVER_ADDR"]; if (isset($execTime)) printf("/%.2fs", $execTime); ?>)</footer>
</div>
<script>
function add() {
$("#bpOutput").removeClass("hidden error exception");
$("#bpOutput").addClass("add");
var domain = "<?=$serverName ?>";
var pw = $("#bpWLPassword");
if(domain.length === 0) {
return;
}
$.ajax({
url: "/admin/scripts/pi-hole/php/add.php",
method: "post",
data: {"domain":domain, "list":"white", "pw":pw.val()},
success: function(response) {
if(response.indexOf("Pi-hole blocking") !== -1) {
setTimeout(function(){window.location.reload(1);}, 10000);
$("#bpOutput").removeClass("add");
$("#bpOutput").addClass("success");
$("#bpOutput").html("");
} else {
$("#bpOutput").removeClass("add");
$("#bpOutput").addClass("error");
$("#bpOutput").html(""+response+"");
}
},
error: function(jqXHR, exception) {
$("#bpOutput").removeClass("add");
$("#bpOutput").addClass("exception");
$("#bpOutput").html("");
}
});
}
<?php if ($featuredTotal > 0) { ?>
$(document).keypress(function(e) {
if(e.which === 13 && $("#bpWLPassword").is(":focus")) {
add();
}
});
$("#bpWhitelist").on("click", function() {
add();
});
<?php } ?>
</script>
</body></html>

View File

@@ -16,20 +16,21 @@
###############################################################################
server.modules = (
"mod_access",
"mod_accesslog",
"mod_auth",
"mod_expire",
"mod_redirect",
"mod_setenv",
"mod_rewrite"
"mod_access",
"mod_accesslog",
"mod_auth",
"mod_expire",
"mod_compress",
"mod_redirect",
"mod_setenv",
"mod_rewrite"
)
server.document-root = "/var/www/html"
server.error-handler-404 = "/pihole/index.php"
server.error-handler-404 = "pihole/index.php"
server.upload-dirs = ( "/var/cache/lighttpd/uploads" )
server.errorlog = "/var/log/lighttpd/error.log"
server.pid-file = "/run/lighttpd.pid"
server.pid-file = "/var/run/lighttpd.pid"
server.username = "www-data"
server.groupname = "www-data"
server.port = 80
@@ -40,29 +41,18 @@ index-file.names = ( "index.php", "index.html", "index.lighttpd.html"
url.access-deny = ( "~", ".inc", ".md", ".yml", ".ini" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
mimetype.assign = (
".ico" => "image/x-icon",
".jpeg" => "image/jpeg",
".jpg" => "image/jpeg",
".png" => "image/png",
".svg" => "image/svg+xml",
".css" => "text/css; charset=utf-8",
".html" => "text/html; charset=utf-8",
".js" => "text/javascript; charset=utf-8",
".json" => "application/json; charset=utf-8",
".map" => "application/json; charset=utf-8",
".txt" => "text/plain; charset=utf-8",
".eot" => "application/vnd.ms-fontobject",
".otf" => "font/otf",
".ttc" => "font/collection",
".ttf" => "font/ttf",
".woff" => "font/woff",
".woff2" => "font/woff2"
)
compress.cache-dir = "/var/cache/lighttpd/compress/"
compress.filetype = ( "application/javascript", "text/css", "text/html", "text/plain" )
# Add user chosen options held in external file
# This uses include_shell instead of an include wildcard for compatibility
include_shell "cat external.conf 2>/dev/null"
mimetype.assign = ( ".png" => "image/png",
".jpg" => "image/jpeg",
".jpeg" => "image/jpeg",
".html" => "text/html",
".css" => "text/css; charset=utf-8",
".js" => "application/javascript",
".json" => "application/json",
".txt" => "text/plain",
".svg" => "image/svg+xml" )
# default listening port for IPv6 falls back to the IPv4 port
include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
@@ -78,12 +68,18 @@ $HTTP["url"] =~ "^/admin/" {
"X-Pi-hole" => "The Pi-hole Web interface is working!",
"X-Frame-Options" => "DENY"
)
$HTTP["url"] =~ ".ttf$" {
# Allow Block Page access to local fonts
setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" )
}
}
# Block . files from being served, such as .git, .github, .gitignore
$HTTP["url"] =~ "^/admin/\.(.*)" {
url.access-deny = ("")
url.access-deny = ("")
}
# Default expire header
expire.url = ( "" => "access plus 0 seconds" )
# Add user chosen options held in external file
# This uses include_shell instead of an include wildcard for compatibility
include_shell "cat external.conf 2>/dev/null"

View File

@@ -2,7 +2,7 @@
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware.
#
# Lighttpd config for Pi-hole
# lighttpd config for Pi-hole
#
# This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license.
@@ -16,82 +16,79 @@
###############################################################################
server.modules = (
"mod_access",
"mod_auth",
"mod_expire",
"mod_fastcgi",
"mod_accesslog",
"mod_redirect",
"mod_setenv",
"mod_rewrite"
"mod_access",
"mod_auth",
"mod_fastcgi",
"mod_accesslog",
"mod_expire",
"mod_compress",
"mod_redirect",
"mod_setenv",
"mod_rewrite"
)
server.document-root = "/var/www/html"
server.error-handler-404 = "/pihole/index.php"
server.error-handler-404 = "pihole/index.php"
server.upload-dirs = ( "/var/cache/lighttpd/uploads" )
server.errorlog = "/var/log/lighttpd/error.log"
server.pid-file = "/run/lighttpd.pid"
server.pid-file = "/var/run/lighttpd.pid"
server.username = "lighttpd"
server.groupname = "lighttpd"
server.port = 80
accesslog.filename = "/var/log/lighttpd/access.log"
accesslog.format = "%{%s}t|%V|%r|%s|%b"
index-file.names = ( "index.php", "index.html", "index.lighttpd.html" )
url.access-deny = ( "~", ".inc", ".md", ".yml", ".ini" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
mimetype.assign = (
".ico" => "image/x-icon",
".jpeg" => "image/jpeg",
".jpg" => "image/jpeg",
".png" => "image/png",
".svg" => "image/svg+xml",
".css" => "text/css; charset=utf-8",
".html" => "text/html; charset=utf-8",
".js" => "text/javascript; charset=utf-8",
".json" => "application/json; charset=utf-8",
".map" => "application/json; charset=utf-8",
".txt" => "text/plain; charset=utf-8",
".eot" => "application/vnd.ms-fontobject",
".otf" => "font/otf",
".ttc" => "font/collection",
".ttf" => "font/ttf",
".woff" => "font/woff",
".woff2" => "font/woff2"
)
compress.cache-dir = "/var/cache/lighttpd/compress/"
compress.filetype = ( "application/javascript", "text/css", "text/html", "text/plain" )
# Add user chosen options held in external file
# This uses include_shell instead of an include wildcard for compatibility
include_shell "cat external.conf 2>/dev/null"
mimetype.assign = ( ".png" => "image/png",
".jpg" => "image/jpeg",
".jpeg" => "image/jpeg",
".html" => "text/html",
".css" => "text/css; charset=utf-8",
".js" => "application/javascript",
".json" => "application/json",
".txt" => "text/plain",
".svg" => "image/svg+xml" )
# default listening port for IPv6 falls back to the IPv4 port
#include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
#include_shell "/usr/share/lighttpd/create-mime.assign.pl"
#include_shell "/usr/share/lighttpd/include-conf-enabled.pl"
fastcgi.server = (
".php" => (
"localhost" => (
"socket" => "/tmp/php-fastcgi.socket",
"bin-path" => "/usr/bin/php-cgi"
)
)
)
fastcgi.server = ( ".php" =>
( "localhost" =>
(
"socket" => "/tmp/php-fastcgi.socket",
"bin-path" => "/usr/bin/php-cgi"
)
)
)
# If the URL starts with /admin, it is the Web interface
$HTTP["url"] =~ "^/admin/" {
# Create a response header for debugging using curl -I
# Create a response header for debugging using curl -I
setenv.add-response-header = (
"X-Pi-hole" => "The Pi-hole Web interface is working!",
"X-Frame-Options" => "DENY"
)
$HTTP["url"] =~ ".ttf$" {
# Allow Block Page access to local fonts
setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" )
}
}
# Block . files from being served, such as .git, .github, .gitignore
$HTTP["url"] =~ "^/admin/\.(.*)" {
url.access-deny = ("")
url.access-deny = ("")
}
# Default expire header
expire.url = ( "" => "access plus 0 seconds" )
# Add user chosen options held in external file
# This uses include_shell instead of an include wildcard for compatibility
include_shell "cat external.conf 2>/dev/null"

File diff suppressed because it is too large Load Diff

View File

@@ -14,8 +14,8 @@ while true; do
read -rp " ${QST} Are you sure you would like to remove ${COL_WHITE}Pi-hole${COL_NC}? [y/N] " yn
case ${yn} in
[Yy]* ) break;;
[Nn]* ) echo -e "${OVER} ${COL_LIGHT_GREEN}Uninstall has been canceled${COL_NC}"; exit 0;;
* ) echo -e "${OVER} ${COL_LIGHT_GREEN}Uninstall has been canceled${COL_NC}"; exit 0;;
[Nn]* ) echo -e "${OVER} ${COL_LIGHT_GREEN}Uninstall has been cancelled${COL_NC}"; exit 0;;
* ) echo -e "${OVER} ${COL_LIGHT_GREEN}Uninstall has been cancelled${COL_NC}"; exit 0;;
esac
done
@@ -31,7 +31,7 @@ else
else
echo -e " ${CROSS} ${str}
Script called with non-root privileges
The Pi-hole requires elevated privileges to uninstall"
The Pi-hole requires elevated privleges to uninstall"
exit 1
fi
fi
@@ -42,8 +42,8 @@ source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh"
# setupVars set in basic-install.sh
source "${setupVars}"
# package_manager_detect() sourced from basic-install.sh
package_manager_detect
# distro_check() sourced from basic-install.sh
distro_check
# Install packages used by the Pi-hole
DEPS=("${INSTALLER_DEPS[@]}" "${PIHOLE_DEPS[@]}")
@@ -52,16 +52,16 @@ if [[ "${INSTALL_WEB_SERVER}" == true ]]; then
DEPS+=("${PIHOLE_WEB_DEPS[@]}")
fi
# Compatibility
# Compatability
if [ -x "$(command -v apt-get)" ]; then
# Debian Family
PKG_REMOVE=("${PKG_MANAGER}" -y remove --purge)
PKG_REMOVE="${PKG_MANAGER} -y remove --purge"
package_check() {
dpkg-query -W -f='${Status}' "$1" 2>/dev/null | grep -c "ok installed"
}
elif [ -x "$(command -v rpm)" ]; then
# Fedora Family
PKG_REMOVE=("${PKG_MANAGER}" remove -y)
PKG_REMOVE="${PKG_MANAGER} remove -y"
package_check() {
rpm -qa | grep "^$1-" > /dev/null
}
@@ -80,7 +80,7 @@ removeAndPurge() {
case ${yn} in
[Yy]* )
echo -ne " ${INFO} Removing ${i}...";
${SUDO} "${PKG_REMOVE[@]}" "${i}" &> /dev/null;
${SUDO} "${PKG_REMOVE} ${i}" &> /dev/null;
echo -e "${OVER} ${INFO} Removed ${i}";
break;;
[Nn]* ) echo -e " ${INFO} Skipped ${i}"; break;;
@@ -113,7 +113,7 @@ removeNoPurge() {
fi
fi
echo -e "${OVER} ${TICK} Removed Web Interface"
# Attempt to preserve backwards compatibility with older versions
# to guarantee no additional changes were made to /etc/crontab after
# the installation of pihole, /etc/crontab.pihole should be permanently
@@ -132,20 +132,16 @@ removeNoPurge() {
fi
if package_check lighttpd > /dev/null; then
if [[ -f /etc/lighttpd/lighttpd.conf.orig ]]; then
${SUDO} rm -rf /etc/lighttpd/ &> /dev/null
echo -e " ${TICK} Removed lighttpd"
else
if [ -f /etc/lighttpd/lighttpd.conf.orig ]; then
${SUDO} mv /etc/lighttpd/lighttpd.conf.orig /etc/lighttpd/lighttpd.conf
fi
if [[ -f /etc/lighttpd/external.conf ]]; then
${SUDO} rm /etc/lighttpd/external.conf
fi
echo -e " ${TICK} Removed lighttpd configs"
fi
${SUDO} rm -f /etc/dnsmasq.d/adList.conf &> /dev/null
${SUDO} rm -f /etc/dnsmasq.d/01-pihole.conf &> /dev/null
${SUDO} rm -f /etc/dnsmasq.d/06-rfc6761.conf &> /dev/null
${SUDO} rm -rf /var/log/*pihole* &> /dev/null
${SUDO} rm -rf /etc/pihole/ &> /dev/null
${SUDO} rm -rf /etc/.pihole/ &> /dev/null
@@ -157,7 +153,7 @@ removeNoPurge() {
# Restore Resolved
if [[ -e /etc/systemd/resolved.conf.orig ]]; then
${SUDO} cp -p /etc/systemd/resolved.conf.orig /etc/systemd/resolved.conf
${SUDO} cp /etc/systemd/resolved.conf.orig /etc/systemd/resolved.conf
systemctl reload-or-restart systemd-resolved
fi
@@ -189,17 +185,9 @@ removeNoPurge() {
echo -e " ${CROSS} Unable to remove 'pihole' user"
fi
fi
# If the pihole group exists, then remove
if getent group "pihole" &> /dev/null; then
if ${SUDO} groupdel pihole 2> /dev/null; then
echo -e " ${TICK} Removed 'pihole' group"
else
echo -e " ${CROSS} Unable to remove 'pihole' group"
fi
fi
echo -e "\\n We're sorry to see you go, but thanks for checking out Pi-hole!
If you need help, reach out to us on GitHub, Discourse, Reddit or Twitter
If you need help, reach out to us on Github, Discourse, Reddit or Twitter
Reinstall at any time: ${COL_WHITE}curl -sSL https://install.pi-hole.net | bash${COL_NC}
${COL_LIGHT_RED}Please reset the DNS on your router/clients to restore internet connectivity
@@ -207,7 +195,11 @@ removeNoPurge() {
}
######### SCRIPT ###########
echo -e " ${INFO} Be sure to confirm if any dependencies should not be removed"
if command -v vcgencmd &> /dev/null; then
echo -e " ${INFO} All dependencies are safe to remove on Raspbian"
else
echo -e " ${INFO} Be sure to confirm if any dependencies should not be removed"
fi
while true; do
echo -e " ${INFO} ${COL_YELLOW}The following dependencies may have been added by the Pi-hole install:"
echo -n " "

1
autotest Executable file
View File

@@ -0,0 +1 @@
py.test -v -f test/

View File

@@ -0,0 +1,43 @@
# Pi-hole: A black hole for Internet advertisements
# (c) 2015, 2016 by Jacob Salmela
# Network-wide ad blocking via your Raspberry Pi
# http://pi-hole.net
# Lighttpd config file for Pi-hole
#
# Pi-hole is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
server.modules = (
"mod_access",
"mod_alias",
"mod_compress",
"mod_redirect",
"mod_rewrite"
)
server.document-root = "/var/www"
server.upload-dirs = ( "/var/cache/lighttpd/uploads" )
server.errorlog = "/var/log/lighttpd/error.log"
server.pid-file = "/var/run/lighttpd.pid"
server.username = "www-data"
server.groupname = "www-data"
server.port = 80
index-file.names = ( "index.php", "index.html", "index.lighttpd.html" )
url.access-deny = ( "~", ".inc" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
compress.cache-dir = "/var/cache/lighttpd/compress/"
compress.filetype = ( "application/javascript", "text/css", "text/html", "text/plain" )
# default listening port for IPv6 falls back to the IPv4 port
include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
include_shell "/usr/share/lighttpd/create-mime.assign.pl"
include_shell "/usr/share/lighttpd/include-conf-enabled.pl"
$HTTP["host"] =~ "ads.hulu.com|ads-v-darwin.hulu.com|ads-e-darwin.hulu.com" {
url.redirect = ( ".*" => "http://192.168.1.101:8200/MediaItems/19.mov")
}

View File

@@ -0,0 +1,17 @@
# Pi-hole: A black hole for Internet advertisements
# (c) 2015, 2016 by Jacob Salmela
# Network-wide ad blocking via your Raspberry Pi
# http://pi-hole.net
# MiniDLNA config file for Pi-hole
#
# Pi-hole is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
media_dir=V,/var/lib/minidlna/videos/
port=8200
friendly_name=pihole
serial=12345678
model_number=1
inotify=yes

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
.TH "Pihole-FTL" "8" "pihole-FTL" "Pi-hole" "November 2020"
.TH "Pihole-FTL" "8" "pihole-FTL" "Pi-hole" "June 2018"
.SH "NAME"
pihole-FTL - Pi-hole : The Faster-Than-Light (FTL) Engine
.br
@@ -10,7 +10,7 @@ pihole-FTL - Pi-hole : The Faster-Than-Light (FTL) Engine
.br
\fBpihole-FTL test\fR
.br
\fBpihole-FTL -v|-vv\fR
\fBpihole-FTL -v\fR
.br
\fBpihole-FTL -t\fR
.br
@@ -22,16 +22,6 @@ pihole-FTL - Pi-hole : The Faster-Than-Light (FTL) Engine
.br
\fBpihole-FTL dnsmasq-test\fR
.br
\fBpihole-FTL regex-test str\fR
.br
\fBpihole-FTL regex-test str rgx\fR
.br
\fBpihole-FTL lua\fR
.br
\fBpihole-FTL luac\fR
.br
\fBpihole-FTL dhcp-discover\fR
.br
\fBpihole-FTL --\fR (\fBoptions\fR)
.br
@@ -75,11 +65,6 @@ Command line arguments
Don't start FTL, show only version
.br
\fB-vv\fR
.br
Don't start FTL, show verbose version information of embedded applications
.br
\fB-t, tag\fR
.br
Don't start FTL, show only git tag
@@ -105,31 +90,6 @@ Command line arguments
Test resolver config file syntax
.br
\fBregex-test str\fR
.br
Test str against all regular expressions in the database
.br
\fBregex-test str rgx\fR
.br
Test str against regular expression given by rgx
.br
\fBlua\fR
.br
Start the embedded Lua interpreter
.br
\fBluac\fR
.br
Execute the embedded Lua compiler
.br
\fBdhcp-discover\fR
.br
Discover DHCP servers in the local network
.br
\fB--\fR (options)
.br
Pass options to internal dnsmasq resolver

View File

@@ -1,4 +1,4 @@
.TH "pihole-FTL.conf" "5" "pihole-FTL.conf" "pihole-FTL.conf" "November 2020"
.TH "pihole-FTL.conf" "5" "pihole-FTL.conf" "pihole-FTL.conf" "June 2018"
.SH "NAME"
pihole-FTL.conf - FTL's config file
@@ -7,32 +7,49 @@ pihole-FTL.conf - FTL's config file
/etc/pihole/pihole-FTL.conf will be read by \fBpihole-FTL(8)\fR on startup.
.br
For each setting the option shown first is the default.
\fBSOCKET_LISTENING=localonly|all\fR
.br
Listen only for local socket connections or permit all connections
.br
\fBBLOCKINGMODE=IP|IP-AAAA-NODATA|NODATA|NXDOMAIN|NULL\fR
\fBQUERY_DISPLAY=yes|no\fR
.br
How should FTL reply to blocked queries?
IP - Pi-hole's IPs for blocked domains
IP-AAAA-NODATA - Pi-hole's IP + NODATA-IPv6 for blocked domains
NODATA - Using NODATA for blocked domains
NXDOMAIN - NXDOMAIN for blocked domains
NULL - Null IPs for blocked domains
Display all queries? Set to no to hide query display
.br
\fBCNAME_DEEP_INSPECT=true|false\fR
\fBAAAA_QUERY_ANALYSIS=yes|no\fR
.br
Use this option to disable deep CNAME inspection. This might be beneficial for very low-end devices.
Allow FTL to analyze AAAA queries from pihole.log?
.br
\fBBLOCK_ESNI=true|false\fR
\fBRESOLVE_IPV6=yes|no\fR
.br
Block requests to _esni.* sub-domains.
Should FTL try to resolve IPv6 addresses to host names?
.br
\fBRESOLVE_IPV4=yes|no\fR
.br
Should FTL try to resolve IPv4 addresses to host names?
.br
\fBMAXDBDAYS=365\fR
.br
How long should queries be stored in the database?
.br
Setting this to 0 disables the database
.br
\fBDBINTERVAL=1.0\fR
.br
How often do we store queries in FTL's database [minutes]?
.br
\fBDBFILE=/etc/pihole/pihole-FTL.db\fR
.br
Specify path and filename of FTL's SQLite long-term database.
.br
Setting this to DBFILE= disables the database altogether
.br
\fBMAXLOGAGE=24.0\fR
@@ -42,9 +59,14 @@ For each setting the option shown first is the default.
Maximum is 744 (31 days)
.br
\fBFTLPORT=4711\fR
.br
On which port should FTL be listening?
.br
\fBPRIVACYLEVEL=0|1|2|3|4\fR
.br
Privacy level used to collect Pi-hole statistics.
Which privacy level is used?
.br
0 - show everything
.br
@@ -62,244 +84,13 @@ For each setting the option shown first is the default.
Should FTL ignore queries coming from the local machine?
.br
\fBAAAA_QUERY_ANALYSIS=yes|no\fR
\fBBLOCKINGMODE=IP|IP-AAAA-NODATA|NXDOMAIN|NULL\fR
.br
Should FTL analyze AAAA queries?
How should FTL reply to blocked queries?
.br
\fBANALYZE_ONLY_A_AND_AAAA=false|true\fR
For each setting, the option shown first is the default.
.br
Should FTL only analyze A and AAAA queries?
.br
\fBSOCKET_LISTENING=localonly|all\fR
.br
Listen only for local socket connections on the API port or permit all connections.
.br
\fBFTLPORT=4711\fR
.br
On which port should FTL be listening?
.br
\fBRESOLVE_IPV6=yes|no\fR
.br
Should FTL try to resolve IPv6 addresses to hostnames?
.br
\fBRESOLVE_IPV4=yes|no\fR
.br
Should FTL try to resolve IPv4 addresses to hostnames?
.br
\fBDELAY_STARTUP=0\fR
.br
Time in seconds (between 0 and 300) to delay FTL startup.
.br
\fBNICE=-10\fR
.br
Set the niceness of the Pi-hole FTL process.
.br
Can be disabled altogether by setting a value of -999.
.br
\fBNAMES_FROM_NETDB=true|false\fR
.br
Control whether FTL should use a fallback option and try to obtain client names from checking the network table.
.br
E.g. IPv6 clients without a hostname will be compared via MAC address to known clients.
.br
\fB\fBREFRESH_HOSTNAMES=IPV4|ALL|NONE\fR
.br
Change how (and if) hourly PTR requests are made to check for changes in client and upstream server hostnames:
.br
IPV4 - Do the hourly PTR lookups only for IPv4 addresses resolving issues in networks with many short-lived PE IPv6 addresses.
.br
ALL - Do the hourly PTR lookups for all addresses. This can create a lot of PTR queries in networks with many IPv6 addresses.
.br
NONE - Don't do hourly PTR lookups. Look up hostnames once (when first seeing a client) and never again. Future hostname changes may be missed.
.br
\fBMAXNETAGE=365\fR
.br
IP addresses (and associated host names) older than the specified number of days are removed.
.br
This avoids dead entries in the network overview table.
.br
\fBEDNS0_ECS=true|false\fR
.br
Should we overwrite the query source when client information is provided through EDNS0 client subnet (ECS) information?
.br
\fBPARSE_ARP_CACHE=true|false\fR
.br
Parse ARP cache to fill network overview table.
.br
\fBDBIMPORT=yes|no\fR
.br
Should FTL load information from the database on startup to be aware of the most recent history?
.br
\fBMAXDBDAYS=365\fR
.br
How long should queries be stored in the database? Setting this to 0 disables the database
.br
\fBDBINTERVAL=1.0\fR
.br
How often do we store queries in FTL's database [minutes]?
.br
Accepts value between 0.1 (6 sec) and 1440 (1 day)
.br
\fBDBFILE=/etc/pihole/pihole-FTL.db\fR
.br
Specify path and filename of FTL's SQLite long-term database.
.br
Setting this to DBFILE= disables the database altogether
.br
\fBLOGFILE=/var/log/pihole-FTL.log\fR
.br
The location of FTL's log file.
.br
\fBPIDFILE=/run/pihole-FTL.pid\fR
.br
The file which contains the PID of FTL's main process.
.br
\fBPORTFILE=/run/pihole-FTL.port\fR
.br
Specify path and filename where the FTL process will write its API port number.
.br
\fBSOCKETFILE=/run/pihole/FTL.sock\fR
.br
The file containing the socket FTL's API is listening on.
.br
\fBSETUPVARSFILE=/etc/pihole/setupVars.conf\fR
.br
The config file of Pi-hole containing, e.g., the current blocking status (do not change).
.br
\fBMACVENDORDB=/etc/pihole/macvendor.db\fR
.br
The database containing MAC -> Vendor information for the network table.
.br
\fBGRAVITYDB=/etc/pihole/gravity.db\fR
.br
Specify path and filename of FTL's SQLite3 gravity database. This database contains all domains relevant for Pi-hole's DNS blocking.
.br
\fBDEBUG_ALL=false|true\fR
.br
Enable all debug flags. If this is set to true, all other debug config options are ignored.
.br
\fBDEBUG_DATABASE=false|true\fR
.br
Print debugging information about database actions such as SQL statements and performance.
.br
\fBDEBUG_NETWORKING=false|true\fR
.br
Prints a list of the detected network interfaces on the startup of FTL.
.br
\fBDEBUG_LOCKS=false|true\fR
.br
Print information about shared memory locks.
.br
Messages will be generated when waiting, obtaining, and releasing a lock.
.br
\fBDEBUG_QUERIES=false|true\fR
.br
Print extensive DNS query information (domains, types, replies, etc.).
.br
\fBDEBUG_FLAGS=false|true\fR
.br
Print flags of queries received by the DNS hooks.
.br
Only effective when \fBDEBUG_QUERIES\fR is enabled as well.
\fBDEBUG_SHMEM=false|true\fR
.br
Print information about shared memory buffers.
.br
Messages are either about creating or enlarging shmem objects or string injections.
.br
\fBDEBUG_GC=false|true\fR
.br
Print information about garbage collection (GC):
.br
What is to be removed, how many have been removed and how long did GC take.
.br
\fBDEBUG_ARP=false|true\fR
.br
Print information about ARP table processing:
.br
How long did parsing take, whether read MAC addresses are valid, and if the macvendor.db file exists.
.br
\fBDEBUG_REGEX=false|true\fR
.br
Controls if FTL should print extended details about regex matching.
.br
\fBDEBUG_API=false|true\fR
.br
Print extra debugging information during telnet API calls.
.br
Currently only used to send extra information when getting all queries.
.br
\fBDEBUG_OVERTIME=false|true\fR
.br
Print information about overTime memory operations, such as initializing or moving overTime slots.
.br
\fBDEBUG_EXTBLOCKED=false|true\fR
.br
Print information about why FTL decided that certain queries were recognized as being externally blocked.
.br
\fBDEBUG_CAPS=false|true\fR
.br
Print information about POSIX capabilities granted to the FTL process.
.br
The current capabilities are printed on receipt of SIGHUP i.e. after executing `killall -HUP pihole-FTL`.
.br
\fBDEBUG_DNSMASQ_LINES=false|true\fR
.br
Print file and line causing a dnsmasq event into FTL's log files.
.br
This is handy to implement additional hooks missing from FTL.
.br
\fBDEBUG_VECTORS=false|true\fR
.br
FTL uses dynamically allocated vectors for various tasks.
.br
This config option enables extensive debugging information such as information about allocation, referencing, deletion, and appending.
.br
\fBDEBUG_RESOLVER=false|true\fR
.br
Extensive information about hostname resolution like which DNS servers are used in the first and second hostname resolving tries.
.br
.SH "SEE ALSO"
\fBpihole\fR(8), \fBpihole-FTL\fR(8)

View File

@@ -1,4 +1,4 @@
.TH "Pi-hole" "8" "Pi-hole" "Pi-hole" "April 2020"
.TH "Pi-hole" "8" "Pi-hole" "Pi-hole" "May 2018"
.SH "NAME"
Pi-hole : A black-hole for internet advertisements
@@ -11,6 +11,8 @@ Pi-hole : A black-hole for internet advertisements
.br
\fBpihole -a\fR (\fB-c|-f|-k\fR)
.br
\fBpihole -a\fR [\fB-r\fR hostrecord]
.br
\fBpihole -a -e\fR email
.br
\fBpihole -a -i\fR interface
@@ -33,7 +35,7 @@ pihole -g\fR
.br
\fBpihole\fR \fB-l\fR (\fBon|off|off noflush\fR)
.br
\fBpihole -up \fR[--check-only]
\fBpihole -up \fR[--checkonly]
.br
\fBpihole -v\fR [-p|-a|-f] [-c|-l|-hash]
.br
@@ -41,7 +43,7 @@ pihole -g\fR
.br
pihole status
.br
pihole restartdns\fR [options]
pihole restartdns\fR
.br
\fBpihole\fR (\fBenable\fR|\fBdisable\fR [time])
.br
@@ -56,7 +58,7 @@ Available commands and options:
\fB-w, whitelist\fR [options] [<domain1> <domain2 ...>]
.br
Adds or removes specified domain or domains to the Whitelist
Adds or removes specified domain or domains tho the Whitelist
.br
\fB-b, blacklist\fR [options] [<domain1> <domain2 ...>]
@@ -64,24 +66,14 @@ Available commands and options:
Adds or removes specified domain or domains to the blacklist
.br
\fB--regex, regex\fR [options] [<regex1> <regex2 ...>]
.br
Add or removes specified regex filter to the regex blacklist
.br
\fB--white-regex\fR [options] [<regex1> <regex2 ...>]
.br
Add or removes specified regex filter to the regex whitelist
.br
\fB--wild, wildcard\fR [options] [<domain1> <domain2 ...>]
.br
Add or removes specified domain to the wildcard blacklist
.br
\fB--white-wild\fR [options] [<domain1> <domain2 ...>]
\fB--regex, regex\fR [options] [<regex1> <regex2 ...>]
.br
Add or removes specified domain to the wildcard whitelist
Add or removes specified regex filter to the regex blacklist
.br
(Whitelist/Blacklist manipulation options):
@@ -132,6 +124,9 @@ Available commands and options:
-f, fahrenheit Set Fahrenheit as preferred temperature unit
.br
-k, kelvin Set Kelvin as preferred temperature unit
.br
-r, hostrecord Add a name to the DNS associated to an
IPv4/IPv6 address
.br
-e, email Set an administrative contact address for the
Block Page
@@ -139,7 +134,7 @@ Available commands and options:
-i, interface Specify dnsmasq's interface listening behavior
.br
-l, privacylevel <level> Set privacy level
(0 = lowest, 3 = highest)
(0 = lowest, 4 = highest)
.br
\fB-c, chronometer\fR [options]
@@ -153,7 +148,7 @@ Available commands and options:
.br
-r, --refresh Set update frequency (in seconds)
.br
-e, --exit Output stats and exit without refreshing
-e, --exit Output stats and exit witout refreshing
.br
\fB-g, updateGravity\fR
@@ -224,7 +219,7 @@ Available commands and options:
.br
-l, --latest Return the latest version
.br
--hash Return the GitHub hash from your local
--hash Return the Github hash from your local
repositories
.br
@@ -255,21 +250,14 @@ Available commands and options:
#m Disable Pi-hole functionality for # minute(s)
.br
\fBrestartdns\fR [options]
\fBrestartdns\fR
.br
Full restart Pi-hole subsystems. Without any options (see below) a full restart causes config file parsing and history re-reading
.br
(restart options):
.br
reload Updates the lists (incl. HOSTS files) and flushes DNS cache. Does not reparse config files
.br
reload-lists Updates the lists (excl. HOSTS files) WITHOUT flushing the DNS cache. Does not reparse config files
Restart Pi-hole subsystems
.br
\fBcheckout\fR [repo] [branch]
.br
Switch Pi-hole subsystems to a different GitHub branch
Switch Pi-hole subsystems to a different Github branch
.br
(repo options):
@@ -363,12 +351,6 @@ Switching Pi-hole subsystem branches
.br
Switch to core development branch
.br
\fBpihole arpflush\fR
.br
Flush information stored in Pi-hole's network tables
.br
.SH "SEE ALSO"
\fBlighttpd\fR(8), \fBpihole-FTL\fR(8)

235
pihole
View File

@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/bash
# Pi-hole: A black hole for Internet advertisements
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
@@ -10,17 +10,19 @@
# Please see LICENSE file for your rights under this license.
readonly PI_HOLE_SCRIPT_DIR="/opt/pihole"
readonly gravitylist="/etc/pihole/gravity.list"
readonly blacklist="/etc/pihole/black.list"
# setupVars and PI_HOLE_BIN_DIR are not readonly here because in some functions (checkout),
# they might get set again when the installer is sourced. This causes an
# setupVars is not readonly here because in some funcitons (checkout),
# it might get set again when the installer is sourced. This causes an
# error due to modifying a readonly variable.
setupVars="/etc/pihole/setupVars.conf"
PI_HOLE_BIN_DIR="/usr/local/bin"
readonly FTL_PID_FILE="/run/pihole-FTL.pid"
readonly colfile="${PI_HOLE_SCRIPT_DIR}/COL_TABLE"
source "${colfile}"
resolver="pihole-FTL"
webpageFunc() {
source "${PI_HOLE_SCRIPT_DIR}/webpage.sh"
main "$@"
@@ -54,11 +56,6 @@ flushFunc() {
exit 0
}
arpFunc() {
"${PI_HOLE_SCRIPT_DIR}"/piholeARPTable.sh "$@"
exit 0
}
updatePiholeFunc() {
shift
"${PI_HOLE_SCRIPT_DIR}"/update.sh "$@"
@@ -99,71 +96,33 @@ versionFunc() {
exit 0
}
# Get PID of main pihole-FTL process
getFTLPID() {
local pid
if [ -s "${FTL_PID_FILE}" ]; then
# -s: FILE exists and has a size greater than zero
pid="$(<"$FTL_PID_FILE")"
# Exploit prevention: unset the variable if there is malicious content
# Verify that the value read from the file is numeric
[[ "$pid" =~ [^[:digit:]] ]] && unset pid
fi
# If FTL is not running, or the PID file contains malicious stuff, substitute
# negative PID to signal this to the caller
echo "${pid:=-1}"
}
restartDNS() {
local svcOption svc str output status pid icon
svcOption="${1:-restart}"
local svcOption svc str output status
svcOption="${1:-}"
# Determine if we should reload or restart
if [[ "${svcOption}" =~ "reload-lists" ]]; then
# Reloading of the lists has been requested
# Note 1: This will NOT re-read any *.conf files
# Note 2: We cannot use killall here as it does
# not know about real-time signals
pid="$(getFTLPID)"
if [[ "$pid" -eq "-1" ]]; then
svc="true"
str="FTL is not running"
icon="${INFO}"
else
svc="kill -RTMIN ${pid}"
str="Reloading DNS lists"
icon="${TICK}"
fi
elif [[ "${svcOption}" =~ "reload" ]]; then
# Reloading of the DNS cache has been requested
# Note: This will NOT re-read any *.conf files
pid="$(getFTLPID)"
if [[ "$pid" -eq "-1" ]]; then
svc="true"
str="FTL is not running"
icon="${INFO}"
else
svc="kill -HUP ${pid}"
str="Flushing DNS cache"
icon="${TICK}"
fi
# Determine if we should reload or restart restart
if [[ "${svcOption}" =~ "reload" ]]; then
# Using SIGHUP will NOT re-read any *.conf files
svc="killall -s SIGHUP ${resolver}"
else
# A full restart has been requested
svc="service pihole-FTL restart"
str="Restarting DNS server"
icon="${TICK}"
# Get PID of resolver to determine if it needs to start or restart
if pidof pihole-FTL &> /dev/null; then
svcOption="restart"
else
svcOption="start"
fi
svc="service ${resolver} ${svcOption}"
fi
# Print output to Terminal, but not to Web Admin
str="${svcOption^}ing DNS service"
[[ -t 1 ]] && echo -ne " ${INFO} ${str}..."
output=$( { ${svc}; } 2>&1 )
status="$?"
if [[ "${status}" -eq 0 ]]; then
[[ -t 1 ]] && echo -e "${OVER} ${icon} ${str}"
[[ -t 1 ]] && echo -e "${OVER} ${TICK} ${str}"
return 0
else
[[ ! -t 1 ]] && local OVER=""
@@ -189,6 +148,14 @@ Time:
echo -e " ${INFO} Blocking already disabled, nothing to do"
exit 0
fi
if [[ -e "${gravitylist}" ]]; then
mv "${gravitylist}" "${gravitylist}.bck"
echo "" > "${gravitylist}"
fi
if [[ -e "${blacklist}" ]]; then
mv "${blacklist}" "${blacklist}.bck"
echo "" > "${blacklist}"
fi
if [[ $# > 1 ]]; then
local error=false
if [[ "${2}" == *"s" ]]; then
@@ -197,7 +164,7 @@ Time:
local str="Disabling blocking for ${tt} seconds"
echo -e " ${INFO} ${str}..."
local str="Blocking will be re-enabled in ${tt} seconds"
nohup "${PI_HOLE_SCRIPT_DIR}"/pihole-reenable.sh ${tt} </dev/null &>/dev/null &
nohup bash -c "sleep ${tt}; pihole enable" </dev/null &>/dev/null &
else
local error=true
fi
@@ -208,7 +175,7 @@ Time:
echo -e " ${INFO} ${str}..."
local str="Blocking will be re-enabled in ${tt} minutes"
tt=$((${tt}*60))
nohup "${PI_HOLE_SCRIPT_DIR}"/pihole-reenable.sh ${tt} </dev/null &>/dev/null &
nohup bash -c "sleep ${tt}; pihole enable" </dev/null &>/dev/null &
else
local error=true
fi
@@ -230,7 +197,6 @@ Time:
fi
else
# Enable Pi-hole
killall -q pihole-reenable
if grep -cq "BLOCKING_ENABLED=true" "${setupVars}"; then
echo -e " ${INFO} Blocking already enabled, nothing to do"
exit 0
@@ -238,11 +204,17 @@ Time:
echo -e " ${INFO} Enabling blocking"
local str="Pi-hole Enabled"
if [[ -e "${gravitylist}.bck" ]]; then
mv "${gravitylist}.bck" "${gravitylist}"
fi
if [[ -e "${blacklist}.bck" ]]; then
mv "${blacklist}.bck" "${blacklist}"
fi
sed -i "/BLOCKING_ENABLED=/d" "${setupVars}"
echo "BLOCKING_ENABLED=true" >> "${setupVars}"
fi
restartDNS reload-lists
restartDNS reload
echo -e "${OVER} ${TICK} ${str}"
}
@@ -265,7 +237,7 @@ Options:
sed -i 's/^QUERY_LOGGING=true/QUERY_LOGGING=false/' /etc/pihole/setupVars.conf
if [[ "${2}" != "noflush" ]]; then
# Flush logs
"${PI_HOLE_BIN_DIR}"/pihole -f
pihole -f
fi
echo -e " ${INFO} Disabling logging..."
local str="Logging has been disabled!"
@@ -284,47 +256,16 @@ Options:
echo -e "${OVER} ${TICK} ${str}"
}
analyze_ports() {
# FTL is listening at least on at least one port when this
# function is getting called
echo -e " ${TICK} DNS service is listening"
# Check individual address family/protocol combinations
# For a healthy Pi-hole, they should all be up (nothing printed)
if grep -q "IPv4.*UDP" <<< "${1}"; then
echo -e " ${TICK} UDP (IPv4)"
else
echo -e " ${CROSS} UDP (IPv4)"
fi
if grep -q "IPv4.*TCP" <<< "${1}"; then
echo -e " ${TICK} TCP (IPv4)"
else
echo -e " ${CROSS} TCP (IPv4)"
fi
if grep -q "IPv6.*UDP" <<< "${1}"; then
echo -e " ${TICK} UDP (IPv6)"
else
echo -e " ${CROSS} UDP (IPv6)"
fi
if grep -q "IPv6.*TCP" <<< "${1}"; then
echo -e " ${TICK} TCP (IPv6)"
else
echo -e " ${CROSS} TCP (IPv6)"
fi
echo ""
}
statusFunc() {
# Determine if there is a pihole service is listening on port 53
local listening
listening="$(lsof -Pni:53)"
if grep -q "pihole" <<< "${listening}"; then
# Determine if service is running on port 53 (Cr: https://superuser.com/a/806331)
if (echo > /dev/tcp/127.0.0.1/53) >/dev/null 2>&1; then
if [[ "${1}" != "web" ]]; then
analyze_ports "${listening}"
echo -e " ${TICK} DNS service is running"
fi
else
case "${1}" in
"web") echo "-1";;
*) echo -e " ${CROSS} DNS service is NOT listening";;
*) echo -e " ${CROSS} DNS service is NOT running";;
esac
return 0
fi
@@ -334,13 +275,13 @@ statusFunc() {
# A config is commented out
case "${1}" in
"web") echo 0;;
*) echo -e " ${CROSS} Pi-hole blocking is disabled";;
*) echo -e " ${CROSS} Pi-hole blocking is Disabled";;
esac
elif grep -q "BLOCKING_ENABLED=true" /etc/pihole/setupVars.conf; then
# Configs are set
case "${1}" in
"web") echo 1;;
*) echo -e " ${TICK} Pi-hole blocking is enabled";;
*) echo -e " ${TICK} Pi-hole blocking is Enabled";;
esac
else
# No configs were found
@@ -349,7 +290,7 @@ statusFunc() {
*) echo -e " ${INFO} Pi-hole blocking will be enabled";;
esac
# Enable blocking
"${PI_HOLE_BIN_DIR}"/pihole enable
pihole enable
fi
}
@@ -363,13 +304,16 @@ tailFunc() {
fi
echo -e " ${INFO} Press Ctrl-C to exit"
# Retrieve IPv4/6 addresses
source /etc/pihole/setupVars.conf
# Strip date from each line
# Color blocklist/blacklist/wildcard entries as red
# Color A/AAAA/DHCP strings as white
# Color everything else as gray
tail -f /var/log/pihole.log | grep --line-buffered "${1}" | sed -E \
-e "s,($(date +'%b %d ')| dnsmasq\[[0-9]*\]),,g" \
-e "s,(.*(blacklisted |gravity blocked ).*),${COL_RED}&${COL_NC}," \
# Colour blocklist/blacklist/wildcard entries as red
# Colour A/AAAA/DHCP strings as white
# Colour everything else as gray
tail -f /var/log/pihole.log | sed -E \
-e "s,($(date +'%b %d ')| dnsmasq[.*[0-9]]),,g" \
-e "s,(.*(gravity.list|black.list|regex.list| config ).* is (0.0.0.0|::|NXDOMAIN|${IPV4_ADDRESS%/*}|${IPV6_ADDRESS:-NULL}).*),${COL_RED}&${COL_NC}," \
-e "s,.*(query\\[A|DHCP).*,${COL_NC}&${COL_NC}," \
-e "s,.*,${COL_GRAY}&${COL_NC},"
exit 0
@@ -379,7 +323,7 @@ piholeCheckoutFunc() {
if [[ "$2" == "-h" ]] || [[ "$2" == "--help" ]]; then
echo "Usage: pihole checkout [repo] [branch]
Example: 'pihole checkout master' or 'pihole checkout core dev'
Switch Pi-hole subsystems to a different GitHub branch
Switch Pi-hole subsystems to a different Github branch
Repositories:
core [branch] Change the branch of Pi-hole's core subsystem
@@ -399,24 +343,34 @@ Branches:
}
tricorderFunc() {
local tricorder_token
if [[ ! -p "/dev/stdin" ]]; then
echo -e " ${INFO} Please do not call Tricorder directly"
exit 1
fi
tricorder_token=$(curl --silent --fail --show-error --upload-file "-" https://tricorder.pi-hole.net/upload < /dev/stdin 2>&1)
if [[ "${tricorder_token}" != "https://tricorder.pi-hole.net/"* ]]; then
echo -e "${CROSS} uploading failed, contact Pi-hole support for assistance."
# Log curl error (if available)
if [ -n "${tricorder_token}" ]; then
echo -e "${INFO} Error message: ${COL_RED}${tricorder_token}${COL_NC}\\n"
tricorder_token=""
fi
exit 1
if ! (echo > /dev/tcp/tricorder.pi-hole.net/9998) >/dev/null 2>&1; then
echo -e " ${CROSS} Unable to connect to Pi-hole's Tricorder server"
exit 1
fi
if command -v openssl &> /dev/null; then
openssl s_client -quiet -connect tricorder.pi-hole.net:9998 2> /dev/null < /dev/stdin
exit "$?"
else
echo -e " ${INFO} ${COL_YELLOW}Security Notice${COL_NC}: ${COL_WHITE}openssl${COL_NC} is not installed
Your debug log will be transmitted unencrypted via plain-text
There is a possibility that this could be intercepted by a third party
If you wish to cancel, press Ctrl-C to exit within 10 seconds"
secs="10"
while [[ "$secs" -gt "0" ]]; do
echo -ne "."
sleep 1
: $((secs--))
done
echo " "
nc tricorder.pi-hole.net 9999 < /dev/stdin
exit "$?"
fi
echo "Upload successful, your token is: ${COL_GREEN}${tricorder_token}${COL_NC}"
exit 0
}
updateCheckFunc() {
@@ -432,21 +386,16 @@ Add '-h' after specific commands for more information on usage
Whitelist/Blacklist Options:
-w, whitelist Whitelist domain(s)
-b, blacklist Blacklist domain(s)
--regex, regex Regex blacklist domains(s)
--white-regex Regex whitelist domains(s)
--wild, wildcard Wildcard blacklist domain(s)
--white-wild Wildcard whitelist domain(s)
--wild, wildcard Wildcard blacklist domain(s)
--regex, regex Regex blacklist domains(s)
Add '-h' for more info on whitelist/blacklist usage
Debugging Options:
-d, debug Start a debugging session
Add '-a' to automatically upload the log to tricorder.pi-hole.net
Add '-a' to enable automated debugging
-f, flush Flush the Pi-hole log
-r, reconfigure Reconfigure or Repair Pi-hole subsystems
-t, tail [arg] View the live output of the Pi-hole log.
Add an optional argument to filter the log
(regular expressions are supported)
-t, tail View the live output of the Pi-hole log
Options:
-a, admin Web interface options
@@ -468,12 +417,9 @@ Options:
enable Enable Pi-hole subsystems
disable Disable Pi-hole subsystems
Add '-h' for more info on disable usage
restartdns Full restart Pi-hole subsystems
Add 'reload' to update the lists and flush the cache without restarting the DNS server
Add 'reload-lists' to only update the lists WITHOUT flushing the cache or restarting the DNS server
checkout Switch Pi-hole subsystems to a different GitHub branch
Add '-h' for more info on checkout usage
arpflush Flush information stored in Pi-hole's network tables";
restartdns Restart Pi-hole subsystems
checkout Switch Pi-hole subsystems to a different Github branch
Add '-h' for more info on checkout usage";
exit 0
}
@@ -500,10 +446,8 @@ fi
case "${1}" in
"-w" | "whitelist" ) listFunc "$@";;
"-b" | "blacklist" ) listFunc "$@";;
"--wild" | "wildcard" ) listFunc "$@";;
"--regex" | "regex" ) listFunc "$@";;
"--white-regex" | "white-regex" ) listFunc "$@";;
"--white-wild" | "white-wild" ) listFunc "$@";;
"--wild" | "wildcard" ) listFunc "$@";;
"--regex" | "regex" ) listFunc "$@";;
"-d" | "debug" ) debugFunc "$@";;
"-f" | "flush" ) flushFunc "$@";;
"-up" | "updatePihole" ) updatePiholeFunc "$@";;
@@ -520,10 +464,9 @@ case "${1}" in
"status" ) statusFunc "$2";;
"restartdns" ) restartDNS "$2";;
"-a" | "admin" ) webpageFunc "$@";;
"-t" | "tail" ) tailFunc "$2";;
"-t" | "tail" ) tailFunc;;
"checkout" ) piholeCheckoutFunc "$@";;
"tricorder" ) tricorderFunc;;
"updatechecker" ) updateCheckFunc "$@";;
"arpflush" ) arpFunc "$@";;
* ) helpFunc;;
esac

View File

@@ -7,11 +7,11 @@ From command line all you need to do is:
- `pip install tox`
- `tox`
Tox handles setting up a virtual environment for python dependencies, installing dependencies, building the docker images used by tests, and finally running tests. It's an easy way to have travis-ci like build behavior locally.
Tox handles setting up a virtual environment for python dependancies, installing dependancies, building the docker images used by tests, and finally running tests. It's an easy way to have travis-ci like build behavior locally.
## Alternative py.test method of running tests
You're responsible for setting up your virtual env and dependencies in this situation.
You're responsible for setting up your virtual env and dependancies in this situation.
```
py.test -vv -n auto -m "build_stage"

View File

@@ -1,17 +0,0 @@
FROM buildpack-deps:buster-scm
ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole
RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole
ADD . $GITDIR
RUN cp $GITDIR/advanced/Scripts/*.sh $GITDIR/gravity.sh $GITDIR/pihole $GITDIR/automated\ install/*.sh $SCRIPTDIR/
ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
RUN true && \
chmod +x $SCRIPTDIR/*
ENV PH_TEST true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View File

@@ -1,17 +0,0 @@
FROM buildpack-deps:bullseye-scm
ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole
RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole
ADD . $GITDIR
RUN cp $GITDIR/advanced/Scripts/*.sh $GITDIR/gravity.sh $GITDIR/pihole $GITDIR/automated\ install/*.sh $SCRIPTDIR/
ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
RUN true && \
chmod +x $SCRIPTDIR/*
ENV PH_TEST true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View File

@@ -1,17 +0,0 @@
FROM buildpack-deps:stretch-scm
ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole
RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole
ADD . $GITDIR
RUN cp $GITDIR/advanced/Scripts/*.sh $GITDIR/gravity.sh $GITDIR/pihole $GITDIR/automated\ install/*.sh $SCRIPTDIR/
ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
RUN true && \
chmod +x $SCRIPTDIR/*
ENV PH_TEST true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View File

@@ -1,17 +0,0 @@
FROM fedora:32
ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole
RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole
ADD . $GITDIR
RUN cp $GITDIR/advanced/Scripts/*.sh $GITDIR/gravity.sh $GITDIR/pihole $GITDIR/automated\ install/*.sh $SCRIPTDIR/
ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
RUN true && \
chmod +x $SCRIPTDIR/*
ENV PH_TEST true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View File

@@ -1,17 +0,0 @@
FROM buildpack-deps:xenial-scm
ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole
RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole
ADD . $GITDIR
RUN cp $GITDIR/advanced/Scripts/*.sh $GITDIR/gravity.sh $GITDIR/pihole $GITDIR/automated\ install/*.sh $SCRIPTDIR/
ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
RUN true && \
chmod +x $SCRIPTDIR/*
ENV PH_TEST true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View File

@@ -1,17 +0,0 @@
FROM buildpack-deps:bionic-scm
ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole
RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole
ADD . $GITDIR
RUN cp $GITDIR/advanced/Scripts/*.sh $GITDIR/gravity.sh $GITDIR/pihole $GITDIR/automated\ install/*.sh $SCRIPTDIR/
ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
RUN true && \
chmod +x $SCRIPTDIR/*
ENV PH_TEST true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View File

@@ -1,18 +0,0 @@
FROM buildpack-deps:focal-scm
ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole
RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole
ADD . $GITDIR
RUN cp $GITDIR/advanced/Scripts/*.sh $GITDIR/gravity.sh $GITDIR/pihole $GITDIR/automated\ install/*.sh $SCRIPTDIR/
ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
ENV DEBIAN_FRONTEND=noninteractive
RUN true && \
chmod +x $SCRIPTDIR/*
ENV PH_TEST true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View File

@@ -1,18 +0,0 @@
FROM buildpack-deps:hirsute-scm
ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole
RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole
ADD . $GITDIR
RUN cp $GITDIR/advanced/Scripts/*.sh $GITDIR/gravity.sh $GITDIR/pihole $GITDIR/automated\ install/*.sh $SCRIPTDIR/
ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
ENV DEBIAN_FRONTEND=noninteractive
RUN true && \
chmod +x $SCRIPTDIR/*
ENV PH_TEST true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View File

@@ -12,6 +12,5 @@ RUN true && \
chmod +x $SCRIPTDIR/*
ENV PH_TEST true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View File

@@ -8,13 +8,15 @@ check_output = testinfra.get_backend(
SETUPVARS = {
'PIHOLE_INTERFACE': 'eth99',
'IPV4_ADDRESS': '1.1.1.1',
'IPV6_ADDRESS': 'FE80::240:D0FF:FE48:4672',
'PIHOLE_DNS_1': '4.2.2.1',
'PIHOLE_DNS_2': '4.2.2.2'
}
tick_box = "[\x1b[1;32m\u2713\x1b[0m]"
cross_box = "[\x1b[1;31m\u2717\x1b[0m]"
info_box = "[i]"
tick_box = "[\x1b[1;32m\xe2\x9c\x93\x1b[0m]".decode("utf-8")
cross_box = "[\x1b[1;31m\xe2\x9c\x97\x1b[0m]".decode("utf-8")
info_box = "[i]".decode("utf-8")
@pytest.fixture
@@ -36,7 +38,9 @@ def Pihole(Docker):
return out
funcType = type(Docker.run)
Docker.run = funcType(run_bash, Docker)
Docker.run = funcType(run_bash,
Docker,
testinfra.backend.docker.DockerBackend)
return Docker
@@ -67,9 +71,7 @@ def args(request):
return '-t -d'
@pytest.fixture(params=[
'test_container'
])
@pytest.fixture(params=['debian', 'centos', 'fedora'])
def tag(request):
'''
consumed by image to make the test matrix
@@ -104,7 +106,7 @@ def mock_command(script, args, container):
#!/bin/bash -e
echo "\$0 \$@" >> /var/log/{script}
case "\$1" in'''.format(script=script))
for k, v in args.items():
for k, v in args.iteritems():
case = dedent('''
{arg})
echo {res}
@@ -131,7 +133,7 @@ def mock_command_2(script, args, container):
#!/bin/bash -e
echo "\$0 \$@" >> /var/log/{script}
case "\$1 \$2" in'''.format(script=script))
for k, v in args.items():
for k, v in args.iteritems():
case = dedent('''
\"{arg}\")
echo \"{res}\"

View File

@@ -1,4 +1,4 @@
FROM centos:8
FROM buildpack-deps:jessie-scm
ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole
@@ -12,6 +12,5 @@ RUN true && \
chmod +x $SCRIPTDIR/*
ENV PH_TEST true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View File

@@ -1,4 +1,4 @@
FROM fedora:33
FROM fedora:latest
ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole
@@ -12,6 +12,5 @@ RUN true && \
chmod +x $SCRIPTDIR/*
ENV PH_TEST true
ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View File

@@ -0,0 +1,23 @@
''' This file starts with 000 to make it run first '''
import pytest
import testinfra
run_local = testinfra.get_backend(
"local://"
).get_module("Command").run
@pytest.mark.parametrize("image,tag", [
('test/debian.Dockerfile', 'pytest_pihole:debian'),
('test/centos.Dockerfile', 'pytest_pihole:centos'),
('test/fedora.Dockerfile', 'pytest_pihole:fedora'),
])
# mark as 'build_stage' so we can ensure images are build first when tests
# are executed in parallel. (not required when tests are executed serially)
@pytest.mark.build_stage
def test_build_pihole_image(image, tag):
build_cmd = run_local('docker build -f {} -t {} .'.format(image, tag))
if build_cmd.rc != 0:
print build_cmd.stdout
print build_cmd.stderr
assert build_cmd.rc == 0

View File

@@ -1,6 +1,6 @@
from textwrap import dedent
import re
from .conftest import (
from conftest import (
SETUPVARS,
tick_box,
info_box,
@@ -11,20 +11,20 @@ from .conftest import (
)
def test_supported_package_manager(Pihole):
def test_supported_operating_system(Pihole):
'''
confirm installer exits when no supported package manager found
confirm installer exists on unsupported distribution
'''
# break supported package managers
# break supported package managers to emulate an unsupported distribution
Pihole.run('rm -rf /usr/bin/apt-get')
Pihole.run('rm -rf /usr/bin/rpm')
package_manager_detect = Pihole.run('''
distro_check = Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
distro_check
''')
expected_stdout = cross_box + ' No supported package manager found'
assert expected_stdout in package_manager_detect.stdout
# assert package_manager_detect.rc == 1
expected_stdout = cross_box + ' OS distribution not supported'
assert expected_stdout in distro_check.stdout
# assert distro_check.rc == 1
def test_setupVars_are_sourced_to_global_scope(Pihole):
@@ -34,7 +34,7 @@ def test_setupVars_are_sourced_to_global_scope(Pihole):
This confirms the sourced variables are in scope between functions
'''
setup_var_file = 'cat <<EOF> /etc/pihole/setupVars.conf\n'
for k, v in SETUPVARS.items():
for k, v in SETUPVARS.iteritems():
setup_var_file += "{}={}\n".format(k, v)
setup_var_file += "EOF\n"
Pihole.run(setup_var_file)
@@ -45,6 +45,8 @@ def test_setupVars_are_sourced_to_global_scope(Pihole):
# Currently debug test function only
echo "Outputting sourced variables"
echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}"
echo "IPV4_ADDRESS=${IPV4_ADDRESS}"
echo "IPV6_ADDRESS=${IPV6_ADDRESS}"
echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}"
echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}"
}
@@ -57,7 +59,7 @@ def test_setupVars_are_sourced_to_global_scope(Pihole):
output = run_script(Pihole, script).stdout
for k, v in SETUPVARS.items():
for k, v in SETUPVARS.iteritems():
assert "{}={}".format(k, v) in output
@@ -67,7 +69,7 @@ def test_setupVars_saved_to_file(Pihole):
'''
# dedent works better with this and padding matching script below
set_setup_vars = '\n'
for k, v in SETUPVARS.items():
for k, v in SETUPVARS.iteritems():
set_setup_vars += " {}={}\n".format(k, v)
Pihole.run(set_setup_vars).stdout
@@ -86,20 +88,239 @@ def test_setupVars_saved_to_file(Pihole):
output = run_script(Pihole, script).stdout
for k, v in SETUPVARS.items():
for k, v in SETUPVARS.iteritems():
assert "{}={}".format(k, v) in output
def test_selinux_not_detected(Pihole):
def test_configureFirewall_firewalld_running_no_errors(Pihole):
'''
confirms installer continues when SELinux configuration file does not exist
confirms firewalld rules are applied when firewallD is running
'''
# firewallD returns 'running' as status
mock_command('firewall-cmd', {'*': ('running', 0)}, Pihole)
# Whiptail dialog returns Ok for user prompt
mock_command('whiptail', {'*': ('', 0)}, Pihole)
configureFirewall = Pihole.run('''
source /opt/pihole/basic-install.sh
configureFirewall
''')
expected_stdout = 'Configuring FirewallD for httpd and pihole-FTL'
assert expected_stdout in configureFirewall.stdout
firewall_calls = Pihole.run('cat /var/log/firewall-cmd').stdout
assert 'firewall-cmd --state' in firewall_calls
assert ('firewall-cmd '
'--permanent '
'--add-service=http '
'--add-service=dns') in firewall_calls
assert 'firewall-cmd --reload' in firewall_calls
def test_configureFirewall_firewalld_disabled_no_errors(Pihole):
'''
confirms firewalld rules are not applied when firewallD is not running
'''
# firewallD returns non-running status
mock_command('firewall-cmd', {'*': ('not running', '1')}, Pihole)
configureFirewall = Pihole.run('''
source /opt/pihole/basic-install.sh
configureFirewall
''')
expected_stdout = ('No active firewall detected.. '
'skipping firewall configuration')
assert expected_stdout in configureFirewall.stdout
def test_configureFirewall_firewalld_enabled_declined_no_errors(Pihole):
'''
confirms firewalld rules are not applied when firewallD is running, user
declines ruleset
'''
# firewallD returns running status
mock_command('firewall-cmd', {'*': ('running', 0)}, Pihole)
# Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*': ('', 1)}, Pihole)
configureFirewall = Pihole.run('''
source /opt/pihole/basic-install.sh
configureFirewall
''')
expected_stdout = 'Not installing firewall rulesets.'
assert expected_stdout in configureFirewall.stdout
def test_configureFirewall_no_firewall(Pihole):
''' confirms firewall skipped no daemon is running '''
configureFirewall = Pihole.run('''
source /opt/pihole/basic-install.sh
configureFirewall
''')
expected_stdout = 'No active firewall detected'
assert expected_stdout in configureFirewall.stdout
def test_configureFirewall_IPTables_enabled_declined_no_errors(Pihole):
'''
confirms IPTables rules are not applied when IPTables is running, user
declines ruleset
'''
# iptables command exists
mock_command('iptables', {'*': ('', '0')}, Pihole)
# modinfo returns always true (ip_tables module check)
mock_command('modinfo', {'*': ('', '0')}, Pihole)
# Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*': ('', '1')}, Pihole)
configureFirewall = Pihole.run('''
source /opt/pihole/basic-install.sh
configureFirewall
''')
expected_stdout = 'Not installing firewall rulesets.'
assert expected_stdout in configureFirewall.stdout
def test_configureFirewall_IPTables_enabled_rules_exist_no_errors(Pihole):
'''
confirms IPTables rules are not applied when IPTables is running and rules
exist
'''
# iptables command exists and returns 0 on calls
# (should return 0 on iptables -C)
mock_command('iptables', {'-S': ('-P INPUT DENY', '0')}, Pihole)
# modinfo returns always true (ip_tables module check)
mock_command('modinfo', {'*': ('', '0')}, Pihole)
# Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*': ('', '0')}, Pihole)
configureFirewall = Pihole.run('''
source /opt/pihole/basic-install.sh
configureFirewall
''')
expected_stdout = 'Installing new IPTables firewall rulesets'
assert expected_stdout in configureFirewall.stdout
firewall_calls = Pihole.run('cat /var/log/iptables').stdout
# General call type occurances
assert len(re.findall(r'iptables -S', firewall_calls)) == 1
assert len(re.findall(r'iptables -C', firewall_calls)) == 4
assert len(re.findall(r'iptables -I', firewall_calls)) == 0
# Specific port call occurances
assert len(re.findall(r'tcp --dport 80', firewall_calls)) == 1
assert len(re.findall(r'tcp --dport 53', firewall_calls)) == 1
assert len(re.findall(r'udp --dport 53', firewall_calls)) == 1
assert len(re.findall(r'tcp --dport 4711:4720', firewall_calls)) == 1
def test_configureFirewall_IPTables_enabled_not_exist_no_errors(Pihole):
'''
confirms IPTables rules are applied when IPTables is running and rules do
not exist
'''
# iptables command and returns 0 on calls (should return 1 on iptables -C)
mock_command(
'iptables',
{
'-S': (
'-P INPUT DENY',
'0'
),
'-C': (
'',
1
),
'-I': (
'',
0
)
},
Pihole
)
# modinfo returns always true (ip_tables module check)
mock_command('modinfo', {'*': ('', '0')}, Pihole)
# Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*': ('', '0')}, Pihole)
configureFirewall = Pihole.run('''
source /opt/pihole/basic-install.sh
configureFirewall
''')
expected_stdout = 'Installing new IPTables firewall rulesets'
assert expected_stdout in configureFirewall.stdout
firewall_calls = Pihole.run('cat /var/log/iptables').stdout
# General call type occurances
assert len(re.findall(r'iptables -S', firewall_calls)) == 1
assert len(re.findall(r'iptables -C', firewall_calls)) == 4
assert len(re.findall(r'iptables -I', firewall_calls)) == 4
# Specific port call occurances
assert len(re.findall(r'tcp --dport 80', firewall_calls)) == 2
assert len(re.findall(r'tcp --dport 53', firewall_calls)) == 2
assert len(re.findall(r'udp --dport 53', firewall_calls)) == 2
assert len(re.findall(r'tcp --dport 4711:4720', firewall_calls)) == 2
def test_selinux_enforcing_default_exit(Pihole):
'''
confirms installer prompts to exit when SELinux is Enforcing by default
'''
# getenforce returns the running state of SELinux
mock_command('getenforce', {'*': ('Enforcing', '0')}, Pihole)
# Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*': ('', '1')}, Pihole)
check_selinux = Pihole.run('''
rm -f /etc/selinux/config
source /opt/pihole/basic-install.sh
checkSelinux
''')
expected_stdout = info_box + ' SELinux not detected'
expected_stdout = info_box + ' SELinux mode detected: Enforcing'
assert expected_stdout in check_selinux.stdout
expected_stdout = 'SELinux Enforcing detected, exiting installer'
assert expected_stdout in check_selinux.stdout
assert check_selinux.rc == 1
def test_selinux_enforcing_continue(Pihole):
'''
confirms installer prompts to continue with custom policy warning
'''
# getenforce returns the running state of SELinux
mock_command('getenforce', {'*': ('Enforcing', '0')}, Pihole)
# Whiptail dialog returns Continue for user prompt
mock_command('whiptail', {'*': ('', '0')}, Pihole)
check_selinux = Pihole.run('''
source /opt/pihole/basic-install.sh
checkSelinux
''')
expected_stdout = info_box + ' SELinux mode detected: Enforcing'
assert expected_stdout in check_selinux.stdout
expected_stdout = info_box + (' Continuing installation with SELinux '
'Enforcing')
assert expected_stdout in check_selinux.stdout
expected_stdout = info_box + (' Please refer to official SELinux '
'documentation to create a custom policy')
assert expected_stdout in check_selinux.stdout
assert check_selinux.rc == 0
def test_selinux_permissive(Pihole):
'''
confirms installer continues when SELinux is Permissive
'''
# getenforce returns the running state of SELinux
mock_command('getenforce', {'*': ('Permissive', '0')}, Pihole)
check_selinux = Pihole.run('''
source /opt/pihole/basic-install.sh
checkSelinux
''')
expected_stdout = info_box + ' SELinux mode detected: Permissive'
assert expected_stdout in check_selinux.stdout
assert check_selinux.rc == 0
def test_selinux_disabled(Pihole):
'''
confirms installer continues when SELinux is Disabled
'''
mock_command('getenforce', {'*': ('Disabled', '0')}, Pihole)
check_selinux = Pihole.run('''
source /opt/pihole/basic-install.sh
checkSelinux
''')
expected_stdout = info_box + ' SELinux mode detected: Disabled'
assert expected_stdout in check_selinux.stdout
assert check_selinux.rc == 0
@@ -112,12 +333,12 @@ def test_installPiholeWeb_fresh_install_no_errors(Pihole):
source /opt/pihole/basic-install.sh
installPiholeWeb
''')
expected_stdout = info_box + ' Installing 404 page...'
expected_stdout = info_box + ' Installing blocking page...'
assert expected_stdout in installWeb.stdout
expected_stdout = tick_box + (' Creating directory for 404 page, '
expected_stdout = tick_box + (' Creating directory for blocking page, '
'and copying files')
assert expected_stdout in installWeb.stdout
expected_stdout = info_box + ' Backing up index.lighttpd.html'
expected_stdout = cross_box + ' Backing up index.lighttpd.html'
assert expected_stdout in installWeb.stdout
expected_stdout = ('No default index.lighttpd.html file found... '
'not backing up')
@@ -126,6 +347,7 @@ def test_installPiholeWeb_fresh_install_no_errors(Pihole):
assert expected_stdout in installWeb.stdout
web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout
assert 'index.php' in web_directory
assert 'blockingpage.css' in web_directory
def test_update_package_cache_success_no_errors(Pihole):
@@ -134,7 +356,7 @@ def test_update_package_cache_success_no_errors(Pihole):
'''
updateCache = Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
distro_check
update_package_cache
''')
expected_stdout = tick_box + ' Update local cache of available packages'
@@ -149,7 +371,7 @@ def test_update_package_cache_failure_no_errors(Pihole):
mock_command('apt-get', {'update': ('', '1')}, Pihole)
updateCache = Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
distro_check
update_package_cache
''')
expected_stdout = cross_box + ' Update local cache of available packages'
@@ -176,63 +398,11 @@ def test_FTL_detect_aarch64_no_errors(Pihole):
)
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
create_pihole_user
funcOutput=$(get_binary_name)
binary="pihole-FTL${funcOutput##*pihole-FTL}"
theRest="${funcOutput%pihole-FTL*}"
FTLdetect "${binary}" "${theRest}"
FTLdetect
''')
expected_stdout = info_box + ' FTL Checks...'
assert expected_stdout in detectPlatform.stdout
expected_stdout = tick_box + ' Detected AArch64 (64 Bit ARM) processor'
assert expected_stdout in detectPlatform.stdout
expected_stdout = tick_box + ' Downloading and Installing FTL'
assert expected_stdout in detectPlatform.stdout
def test_FTL_detect_armv4t_no_errors(Pihole):
'''
confirms only armv4t package is downloaded for FTL engine
'''
# mock uname to return armv4t platform
mock_command('uname', {'-m': ('armv4t', '0')}, Pihole)
# mock ldd to respond with ld-linux shared library
mock_command('ldd', {'/bin/ls': ('/lib/ld-linux.so.3', '0')}, Pihole)
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
create_pihole_user
funcOutput=$(get_binary_name)
binary="pihole-FTL${funcOutput##*pihole-FTL}"
theRest="${funcOutput%pihole-FTL*}"
FTLdetect "${binary}" "${theRest}"
''')
expected_stdout = info_box + ' FTL Checks...'
assert expected_stdout in detectPlatform.stdout
expected_stdout = tick_box + (' Detected ARMv4 processor')
assert expected_stdout in detectPlatform.stdout
expected_stdout = tick_box + ' Downloading and Installing FTL'
assert expected_stdout in detectPlatform.stdout
def test_FTL_detect_armv5te_no_errors(Pihole):
'''
confirms only armv5te package is downloaded for FTL engine
'''
# mock uname to return armv5te platform
mock_command('uname', {'-m': ('armv5te', '0')}, Pihole)
# mock ldd to respond with ld-linux shared library
mock_command('ldd', {'/bin/ls': ('/lib/ld-linux.so.3', '0')}, Pihole)
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
create_pihole_user
funcOutput=$(get_binary_name)
binary="pihole-FTL${funcOutput##*pihole-FTL}"
theRest="${funcOutput%pihole-FTL*}"
FTLdetect "${binary}" "${theRest}"
''')
expected_stdout = info_box + ' FTL Checks...'
assert expected_stdout in detectPlatform.stdout
expected_stdout = tick_box + (' Detected ARMv5 (or newer) processor')
expected_stdout = tick_box + ' Detected ARM-aarch64 architecture'
assert expected_stdout in detectPlatform.stdout
expected_stdout = tick_box + ' Downloading and Installing FTL'
assert expected_stdout in detectPlatform.stdout
@@ -244,20 +414,16 @@ def test_FTL_detect_armv6l_no_errors(Pihole):
'''
# mock uname to return armv6l platform
mock_command('uname', {'-m': ('armv6l', '0')}, Pihole)
# mock ldd to respond with ld-linux-armhf shared library
# mock ldd to respond with aarch64 shared library
mock_command('ldd', {'/bin/ls': ('/lib/ld-linux-armhf.so.3', '0')}, Pihole)
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
create_pihole_user
funcOutput=$(get_binary_name)
binary="pihole-FTL${funcOutput##*pihole-FTL}"
theRest="${funcOutput%pihole-FTL*}"
FTLdetect "${binary}" "${theRest}"
FTLdetect
''')
expected_stdout = info_box + ' FTL Checks...'
assert expected_stdout in detectPlatform.stdout
expected_stdout = tick_box + (' Detected ARMv6 processor '
'(with hard-float support)')
expected_stdout = tick_box + (' Detected ARM-hf architecture '
'(armv6 or lower)')
assert expected_stdout in detectPlatform.stdout
expected_stdout = tick_box + ' Downloading and Installing FTL'
assert expected_stdout in detectPlatform.stdout
@@ -269,44 +435,15 @@ def test_FTL_detect_armv7l_no_errors(Pihole):
'''
# mock uname to return armv7l platform
mock_command('uname', {'-m': ('armv7l', '0')}, Pihole)
# mock ldd to respond with ld-linux-armhf shared library
# mock ldd to respond with aarch64 shared library
mock_command('ldd', {'/bin/ls': ('/lib/ld-linux-armhf.so.3', '0')}, Pihole)
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
create_pihole_user
funcOutput=$(get_binary_name)
binary="pihole-FTL${funcOutput##*pihole-FTL}"
theRest="${funcOutput%pihole-FTL*}"
FTLdetect "${binary}" "${theRest}"
FTLdetect
''')
expected_stdout = info_box + ' FTL Checks...'
assert expected_stdout in detectPlatform.stdout
expected_stdout = tick_box + (' Detected ARMv7 processor '
'(with hard-float support)')
assert expected_stdout in detectPlatform.stdout
expected_stdout = tick_box + ' Downloading and Installing FTL'
assert expected_stdout in detectPlatform.stdout
def test_FTL_detect_armv8a_no_errors(Pihole):
'''
confirms only armv8a package is downloaded for FTL engine
'''
# mock uname to return armv8a platform
mock_command('uname', {'-m': ('armv8a', '0')}, Pihole)
# mock ldd to respond with ld-linux-armhf shared library
mock_command('ldd', {'/bin/ls': ('/lib/ld-linux-armhf.so.3', '0')}, Pihole)
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
create_pihole_user
funcOutput=$(get_binary_name)
binary="pihole-FTL${funcOutput##*pihole-FTL}"
theRest="${funcOutput%pihole-FTL*}"
FTLdetect "${binary}" "${theRest}"
''')
expected_stdout = info_box + ' FTL Checks...'
assert expected_stdout in detectPlatform.stdout
expected_stdout = tick_box + ' Detected ARMv8 (or newer) processor'
expected_stdout = tick_box + ' Detected ARM-hf architecture (armv7+)'
assert expected_stdout in detectPlatform.stdout
expected_stdout = tick_box + ' Downloading and Installing FTL'
assert expected_stdout in detectPlatform.stdout
@@ -318,15 +455,11 @@ def test_FTL_detect_x86_64_no_errors(Pihole):
'''
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
create_pihole_user
funcOutput=$(get_binary_name)
binary="pihole-FTL${funcOutput##*pihole-FTL}"
theRest="${funcOutput%pihole-FTL*}"
FTLdetect "${binary}" "${theRest}"
FTLdetect
''')
expected_stdout = info_box + ' FTL Checks...'
assert expected_stdout in detectPlatform.stdout
expected_stdout = tick_box + ' Detected x86_64 processor'
expected_stdout = tick_box + ' Detected x86_64 architecture'
assert expected_stdout in detectPlatform.stdout
expected_stdout = tick_box + ' Downloading and Installing FTL'
assert expected_stdout in detectPlatform.stdout
@@ -338,13 +471,9 @@ def test_FTL_detect_unknown_no_errors(Pihole):
mock_command('uname', {'-m': ('mips', '0')}, Pihole)
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
create_pihole_user
funcOutput=$(get_binary_name)
binary="pihole-FTL${funcOutput##*pihole-FTL}"
theRest="${funcOutput%pihole-FTL*}"
FTLdetect "${binary}" "${theRest}"
FTLdetect
''')
expected_stdout = 'Not able to detect processor (unknown: mips)'
expected_stdout = 'Not able to detect architecture (unknown: mips)'
assert expected_stdout in detectPlatform.stdout
@@ -352,34 +481,56 @@ def test_FTL_download_aarch64_no_errors(Pihole):
'''
confirms only aarch64 package is downloaded for FTL engine
'''
# mock whiptail answers and ensure installer dependencies
mock_command('whiptail', {'*': ('', '0')}, Pihole)
Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
install_dependent_packages ${INSTALLER_DEPS[@]}
''')
download_binary = Pihole.run('''
source /opt/pihole/basic-install.sh
create_pihole_user
FTLinstall "pihole-FTL-aarch64-linux-gnu"
binary="pihole-FTL-aarch64-linux-gnu"
FTLinstall
''')
expected_stdout = tick_box + ' Downloading and Installing FTL'
assert expected_stdout in download_binary.stdout
assert 'error' not in download_binary.stdout.lower()
def test_FTL_download_unknown_fails_no_errors(Pihole):
'''
confirms unknown binary is not downloaded for FTL engine
'''
download_binary = Pihole.run('''
source /opt/pihole/basic-install.sh
binary="pihole-FTL-mips"
FTLinstall
''')
expected_stdout = cross_box + ' Downloading and Installing FTL'
assert expected_stdout in download_binary.stdout
error1 = 'Error: URL https://github.com/pi-hole/FTL/releases/download/'
assert error1 in download_binary.stdout
error2 = 'not found'
assert error2 in download_binary.stdout
def test_FTL_download_binary_unset_no_errors(Pihole):
'''
confirms unset binary variable does not download FTL engine
'''
download_binary = Pihole.run('''
source /opt/pihole/basic-install.sh
FTLinstall
''')
expected_stdout = cross_box + ' Downloading and Installing FTL'
assert expected_stdout in download_binary.stdout
error1 = 'Error: URL https://github.com/pi-hole/FTL/releases/download/'
assert error1 in download_binary.stdout
error2 = 'not found'
assert error2 in download_binary.stdout
def test_FTL_binary_installed_and_responsive_no_errors(Pihole):
'''
confirms FTL binary is copied and functional in installed location
'''
installed_binary = Pihole.run('''
source /opt/pihole/basic-install.sh
create_pihole_user
funcOutput=$(get_binary_name)
binary="pihole-FTL${funcOutput##*pihole-FTL}"
theRest="${funcOutput%pihole-FTL*}"
FTLdetect "${binary}" "${theRest}"
FTLdetect
pihole-FTL version
''')
expected_stdout = 'v'
@@ -420,9 +571,10 @@ def test_IPv6_only_link_local(Pihole):
)
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
find_IPv6_information
useIPv6dialog
''')
expected_stdout = ('Unable to find IPv6 ULA/GUA address')
expected_stdout = ('Unable to find IPv6 ULA/GUA address, '
'IPv6 adblocking will not be enabled')
assert expected_stdout in detectPlatform.stdout
@@ -443,9 +595,9 @@ def test_IPv6_only_ULA(Pihole):
)
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
find_IPv6_information
useIPv6dialog
''')
expected_stdout = 'Found IPv6 ULA address'
expected_stdout = 'Found IPv6 ULA address, using it for blocking IPv6 ads'
assert expected_stdout in detectPlatform.stdout
@@ -466,9 +618,9 @@ def test_IPv6_only_GUA(Pihole):
)
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
find_IPv6_information
useIPv6dialog
''')
expected_stdout = 'Found IPv6 GUA address'
expected_stdout = 'Found IPv6 GUA address, using it for blocking IPv6 ads'
assert expected_stdout in detectPlatform.stdout
@@ -490,9 +642,9 @@ def test_IPv6_GUA_ULA_test(Pihole):
)
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
find_IPv6_information
useIPv6dialog
''')
expected_stdout = 'Found IPv6 ULA address'
expected_stdout = 'Found IPv6 ULA address, using it for blocking IPv6 ads'
assert expected_stdout in detectPlatform.stdout
@@ -514,125 +666,7 @@ def test_IPv6_ULA_GUA_test(Pihole):
)
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
find_IPv6_information
useIPv6dialog
''')
expected_stdout = 'Found IPv6 ULA address'
expected_stdout = 'Found IPv6 ULA address, using it for blocking IPv6 ads'
assert expected_stdout in detectPlatform.stdout
def test_validate_ip(Pihole):
'''
Tests valid_ip for various IP addresses
'''
def test_address(addr, success=True):
output = Pihole.run('''
source /opt/pihole/basic-install.sh
valid_ip "{addr}"
'''.format(addr=addr))
assert output.rc == 0 if success else 1
test_address('192.168.1.1')
test_address('127.0.0.1')
test_address('255.255.255.255')
test_address('255.255.255.256', False)
test_address('255.255.256.255', False)
test_address('255.256.255.255', False)
test_address('256.255.255.255', False)
test_address('1092.168.1.1', False)
test_address('not an IP', False)
test_address('8.8.8.8#', False)
test_address('8.8.8.8#0')
test_address('8.8.8.8#1')
test_address('8.8.8.8#42')
test_address('8.8.8.8#888')
test_address('8.8.8.8#1337')
test_address('8.8.8.8#65535')
test_address('8.8.8.8#65536', False)
test_address('8.8.8.8#-1', False)
test_address('00.0.0.0', False)
test_address('010.0.0.0', False)
test_address('001.0.0.0', False)
test_address('0.0.0.0#00', False)
test_address('0.0.0.0#01', False)
test_address('0.0.0.0#001', False)
test_address('0.0.0.0#0001', False)
test_address('0.0.0.0#00001', False)
def test_os_check_fails(Pihole):
''' Confirms install fails on unsupported OS '''
Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
install_dependent_packages ${OS_CHECK_DEPS[@]}
install_dependent_packages ${INSTALLER_DEPS[@]}
cat <<EOT > /etc/os-release
ID=UnsupportedOS
VERSION_ID="2"
EOT
''')
detectOS = Pihole.run('''t
source /opt/pihole/basic-install.sh
os_check
''')
expected_stdout = 'Unsupported OS detected: UnsupportedOS'
assert expected_stdout in detectOS.stdout
def test_os_check_passes(Pihole):
''' Confirms OS meets the requirements '''
Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
install_dependent_packages ${OS_CHECK_DEPS[@]}
install_dependent_packages ${INSTALLER_DEPS[@]}
''')
detectOS = Pihole.run('''
source /opt/pihole/basic-install.sh
os_check
''')
expected_stdout = 'Supported OS detected'
assert expected_stdout in detectOS.stdout
def test_package_manager_has_installer_deps(Pihole):
''' Confirms OS is able to install the required packages for the installer'''
mock_command('whiptail', {'*': ('', '0')}, Pihole)
output = Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
install_dependent_packages ${INSTALLER_DEPS[@]}
''')
assert 'No package' not in output.stdout # centos7 still exits 0...
assert output.rc == 0
def test_package_manager_has_pihole_deps(Pihole):
''' Confirms OS is able to install the required packages for Pi-hole '''
mock_command('whiptail', {'*': ('', '0')}, Pihole)
output = Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
select_rpm_php
install_dependent_packages ${PIHOLE_DEPS[@]}
''')
assert 'No package' not in output.stdout # centos7 still exits 0...
assert output.rc == 0
def test_package_manager_has_web_deps(Pihole):
''' Confirms OS is able to install the required packages for web '''
mock_command('whiptail', {'*': ('', '0')}, Pihole)
output = Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
select_rpm_php
install_dependent_packages ${PIHOLE_WEB_DEPS[@]}
''')
assert 'No package' not in output.stdout # centos7 still exits 0...
assert output.rc == 0

View File

@@ -1,63 +0,0 @@
from .conftest import (
tick_box,
info_box,
mock_command,
)
def test_php_upgrade_default_optout_centos_eq_7(Pihole):
'''
confirms the default behavior to opt-out of installing PHP7 from REMI
'''
package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
select_rpm_php
''')
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
'Deprecated PHP may be in use.')
assert expected_stdout in package_manager_detect.stdout
remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed
def test_php_upgrade_user_optout_centos_eq_7(Pihole):
'''
confirms installer behavior when user opt-out of installing PHP7 from REMI
(php not currently installed)
'''
# Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*': ('', '1')}, Pihole)
package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
select_rpm_php
''')
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
'Deprecated PHP may be in use.')
assert expected_stdout in package_manager_detect.stdout
remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed
def test_php_upgrade_user_optin_centos_eq_7(Pihole):
'''
confirms installer behavior when user opt-in to installing PHP7 from REMI
(php not currently installed)
'''
# Whiptail dialog returns Continue for user prompt
mock_command('whiptail', {'*': ('', '0')}, Pihole)
package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
select_rpm_php
''')
assert 'opt-out' not in package_manager_detect.stdout
expected_stdout = info_box + (' Enabling Remi\'s RPM repository '
'(https://rpms.remirepo.net)')
assert expected_stdout in package_manager_detect.stdout
expected_stdout = tick_box + (' Remi\'s RPM repository has '
'been enabled for PHP7')
assert expected_stdout in package_manager_detect.stdout
remi_package = Pihole.package('remi-release')
assert remi_package.is_installed

View File

@@ -1,68 +0,0 @@
from .conftest import (
tick_box,
info_box,
mock_command,
)
def test_php_upgrade_default_continue_centos_gte_8(Pihole):
'''
confirms the latest version of CentOS continues / does not optout
(should trigger on CentOS7 only)
'''
package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
select_rpm_php
''')
unexpected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS.'
' Deprecated PHP may be in use.')
assert unexpected_stdout not in package_manager_detect.stdout
# ensure remi was not installed on latest CentOS
remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed
def test_php_upgrade_user_optout_skipped_centos_gte_8(Pihole):
'''
confirms installer skips user opt-out of installing PHP7 from REMI on
latest CentOS (should trigger on CentOS7 only)
(php not currently installed)
'''
# Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*': ('', '1')}, Pihole)
package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
select_rpm_php
''')
unexpected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS.'
' Deprecated PHP may be in use.')
assert unexpected_stdout not in package_manager_detect.stdout
# ensure remi was not installed on latest CentOS
remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed
def test_php_upgrade_user_optin_skipped_centos_gte_8(Pihole):
'''
confirms installer skips user opt-in to installing PHP7 from REMI on
latest CentOS (should trigger on CentOS7 only)
(php not currently installed)
'''
# Whiptail dialog returns Continue for user prompt
mock_command('whiptail', {'*': ('', '0')}, Pihole)
package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
select_rpm_php
''')
assert 'opt-out' not in package_manager_detect.stdout
unexpected_stdout = info_box + (' Enabling Remi\'s RPM repository '
'(https://rpms.remirepo.net)')
assert unexpected_stdout not in package_manager_detect.stdout
unexpected_stdout = tick_box + (' Remi\'s RPM repository has '
'been enabled for PHP7')
assert unexpected_stdout not in package_manager_detect.stdout
remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed

View File

@@ -1,65 +0,0 @@
from .conftest import (
tick_box,
cross_box,
mock_command,
)
def mock_selinux_config(state, Pihole):
'''
Creates a mock SELinux config file with expected content
'''
# validate state string
valid_states = ['enforcing', 'permissive', 'disabled']
assert state in valid_states
# getenforce returns the running state of SELinux
mock_command('getenforce', {'*': (state.capitalize(), '0')}, Pihole)
# create mock configuration with desired content
Pihole.run('''
mkdir /etc/selinux
echo "SELINUX={state}" > /etc/selinux/config
'''.format(state=state.lower()))
def test_selinux_enforcing_exit(Pihole):
'''
confirms installer prompts to exit when SELinux is Enforcing by default
'''
mock_selinux_config("enforcing", Pihole)
check_selinux = Pihole.run('''
source /opt/pihole/basic-install.sh
checkSelinux
''')
expected_stdout = cross_box + ' Current SELinux: Enforcing'
assert expected_stdout in check_selinux.stdout
expected_stdout = 'SELinux Enforcing detected, exiting installer'
assert expected_stdout in check_selinux.stdout
assert check_selinux.rc == 1
def test_selinux_permissive(Pihole):
'''
confirms installer continues when SELinux is Permissive
'''
mock_selinux_config("permissive", Pihole)
check_selinux = Pihole.run('''
source /opt/pihole/basic-install.sh
checkSelinux
''')
expected_stdout = tick_box + ' Current SELinux: Permissive'
assert expected_stdout in check_selinux.stdout
assert check_selinux.rc == 0
def test_selinux_disabled(Pihole):
'''
confirms installer continues when SELinux is Disabled
'''
mock_selinux_config("disabled", Pihole)
check_selinux = Pihole.run('''
source /opt/pihole/basic-install.sh
checkSelinux
''')
expected_stdout = tick_box + ' Current SELinux: Disabled'
assert expected_stdout in check_selinux.stdout
assert check_selinux.rc == 0

View File

@@ -1,47 +1,125 @@
import pytest
from .conftest import (
from conftest import (
tick_box,
info_box,
cross_box,
mock_command,
mock_command_2,
)
@pytest.mark.parametrize("tag", [('fedora'), ])
def test_epel_and_remi_not_installed_fedora(Pihole):
'''
confirms installer does not attempt to install EPEL/REMI repositories
on Fedora
'''
distro_check = Pihole.run('''
source /opt/pihole/basic-install.sh
distro_check
''')
assert distro_check.stdout == ''
epel_package = Pihole.package('epel-release')
assert not epel_package.is_installed
remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed
@pytest.mark.parametrize("tag", [('centos'), ])
def test_release_supported_version_check_centos(Pihole):
'''
confirms installer exits on unsupported releases of CentOS
'''
# modify /etc/redhat-release to mock an unsupported CentOS release
Pihole.run('echo "CentOS Linux release 6.9" > /etc/redhat-release')
package_manager_detect = Pihole.run('''
distro_check = Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
select_rpm_php
distro_check
''')
expected_stdout = cross_box + (' CentOS 6 is not supported.')
assert expected_stdout in package_manager_detect.stdout
assert expected_stdout in distro_check.stdout
expected_stdout = 'Please update to CentOS release 7 or later'
assert expected_stdout in package_manager_detect.stdout
assert expected_stdout in distro_check.stdout
@pytest.mark.parametrize("tag", [('centos'), ])
def test_enable_epel_repository_centos(Pihole):
'''
confirms the EPEL package repository is enabled when installed on CentOS
'''
package_manager_detect = Pihole.run('''
distro_check = Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
select_rpm_php
distro_check
''')
expected_stdout = info_box + (' Enabling EPEL package repository '
'(https://fedoraproject.org/wiki/EPEL)')
assert expected_stdout in package_manager_detect.stdout
assert expected_stdout in distro_check.stdout
expected_stdout = tick_box + ' Installed epel-release'
assert expected_stdout in package_manager_detect.stdout
assert expected_stdout in distro_check.stdout
epel_package = Pihole.package('epel-release')
assert epel_package.is_installed
@pytest.mark.parametrize("tag", [('centos'), ])
def test_php_upgrade_default_optout_centos(Pihole):
'''
confirms the default behavior to opt-out of installing PHP7 from REMI
'''
distro_check = Pihole.run('''
source /opt/pihole/basic-install.sh
distro_check
''')
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
'Deprecated PHP may be in use.')
assert expected_stdout in distro_check.stdout
remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed
@pytest.mark.parametrize("tag", [('centos'), ])
def test_php_upgrade_user_optout_centos(Pihole):
'''
confirms installer behavior when user opt-out of installing PHP7 from REMI
(php not currently installed)
'''
# Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*': ('', '1')}, Pihole)
distro_check = Pihole.run('''
source /opt/pihole/basic-install.sh
distro_check
''')
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
'Deprecated PHP may be in use.')
assert expected_stdout in distro_check.stdout
remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed
@pytest.mark.parametrize("tag", [('centos'), ])
def test_php_upgrade_user_optin_centos(Pihole):
'''
confirms installer behavior when user opt-in to installing PHP7 from REMI
(php not currently installed)
'''
# Whiptail dialog returns Continue for user prompt
mock_command('whiptail', {'*': ('', '0')}, Pihole)
distro_check = Pihole.run('''
source /opt/pihole/basic-install.sh
distro_check
''')
assert 'opt-out' not in distro_check.stdout
expected_stdout = info_box + (' Enabling Remi\'s RPM repository '
'(https://rpms.remirepo.net)')
assert expected_stdout in distro_check.stdout
expected_stdout = tick_box + (' Remi\'s RPM repository has '
'been enabled for PHP7')
assert expected_stdout in distro_check.stdout
remi_package = Pihole.package('remi-release')
assert remi_package.is_installed
@pytest.mark.parametrize("tag", [('centos'), ])
def test_php_version_lt_7_detected_upgrade_default_optout_centos(Pihole):
'''
confirms the default behavior to opt-out of upgrading to PHP7 from REMI
@@ -53,18 +131,18 @@ def test_php_version_lt_7_detected_upgrade_default_optout_centos(Pihole):
default_centos_php_version = php_package.version.split('.')[0]
if int(default_centos_php_version) >= 7: # PHP7 is supported/recommended
pytest.skip("Test deprecated . Detected default PHP version >= 7")
package_manager_detect = Pihole.run('''
distro_check = Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
select_rpm_php
distro_check
''')
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
'Deprecated PHP may be in use.')
assert expected_stdout in package_manager_detect.stdout
assert expected_stdout in distro_check.stdout
remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed
@pytest.mark.parametrize("tag", [('centos'), ])
def test_php_version_lt_7_detected_upgrade_user_optout_centos(Pihole):
'''
confirms installer behavior when user opt-out to upgrade to PHP7 via REMI
@@ -78,18 +156,18 @@ def test_php_version_lt_7_detected_upgrade_user_optout_centos(Pihole):
pytest.skip("Test deprecated . Detected default PHP version >= 7")
# Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*': ('', '1')}, Pihole)
package_manager_detect = Pihole.run('''
distro_check = Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
select_rpm_php
distro_check
''')
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
'Deprecated PHP may be in use.')
assert expected_stdout in package_manager_detect.stdout
assert expected_stdout in distro_check.stdout
remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed
@pytest.mark.parametrize("tag", [('centos'), ])
def test_php_version_lt_7_detected_upgrade_user_optin_centos(Pihole):
'''
confirms installer behavior when user opt-in to upgrade to PHP7 via REMI
@@ -103,21 +181,20 @@ def test_php_version_lt_7_detected_upgrade_user_optin_centos(Pihole):
pytest.skip("Test deprecated . Detected default PHP version >= 7")
# Whiptail dialog returns Continue for user prompt
mock_command('whiptail', {'*': ('', '0')}, Pihole)
package_manager_detect = Pihole.run('''
distro_check = Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
select_rpm_php
distro_check
install_dependent_packages PIHOLE_WEB_DEPS[@]
''')
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
'Deprecated PHP may be in use.')
assert expected_stdout not in package_manager_detect.stdout
assert expected_stdout not in distro_check.stdout
expected_stdout = info_box + (' Enabling Remi\'s RPM repository '
'(https://rpms.remirepo.net)')
assert expected_stdout in package_manager_detect.stdout
assert expected_stdout in distro_check.stdout
expected_stdout = tick_box + (' Remi\'s RPM repository has '
'been enabled for PHP7')
assert expected_stdout in package_manager_detect.stdout
assert expected_stdout in distro_check.stdout
remi_package = Pihole.package('remi-release')
assert remi_package.is_installed
updated_php_package = Pihole.package('php')

View File

@@ -1,16 +0,0 @@
def test_epel_and_remi_not_installed_fedora(Pihole):
'''
confirms installer does not attempt to install EPEL/REMI repositories
on Fedora
'''
package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh
package_manager_detect
select_rpm_php
''')
assert package_manager_detect.stdout == ''
epel_package = Pihole.package('epel-release')
assert not epel_package.is_installed
remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed

18
test/test_shellcheck.py Normal file
View File

@@ -0,0 +1,18 @@
import testinfra
run_local = testinfra.get_backend(
"local://"
).get_module("Command").run
def test_scripts_pass_shellcheck():
'''
Make sure shellcheck does not find anything wrong with our shell scripts
'''
shellcheck = ("find . -type f -name 'update.sh' "
"| while read file; do "
"shellcheck -x \"$file\" -e SC1090,SC1091; "
"done;")
results = run_local(shellcheck)
print results.stdout
assert '' == results.stdout

View File

@@ -1,8 +0,0 @@
[tox]
envlist = py37
[testenv]
whitelist_externals = docker
deps = -rrequirements.txt
commands = docker build -f _centos_7.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_automated_install.py ./test_centos_fedora_common_support.py ./test_centos_common_support.py ./test_centos_7_support.py

View File

@@ -1,8 +0,0 @@
[tox]
envlist = py37
[testenv]
whitelist_externals = docker
deps = -rrequirements.txt
commands = docker build -f _centos_8.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_automated_install.py ./test_centos_fedora_common_support.py ./test_centos_common_support.py ./test_centos_8_support.py

View File

@@ -1,8 +0,0 @@
[tox]
envlist = py37
[testenv]
whitelist_externals = docker
deps = -rrequirements.txt
commands = docker build -f _debian_10.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_automated_install.py

View File

@@ -1,8 +0,0 @@
[tox]
envlist = py37
[testenv]
whitelist_externals = docker
deps = -rrequirements.txt
commands = docker build -f _debian_11.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_automated_install.py

View File

@@ -1,8 +0,0 @@
[tox]
envlist = py37
[testenv]
whitelist_externals = docker
deps = -rrequirements.txt
commands = docker build -f _debian_9.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_automated_install.py

View File

@@ -1,8 +0,0 @@
[tox]
envlist = py37
[testenv]
whitelist_externals = docker
deps = -rrequirements.txt
commands = docker build -f _fedora_32.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_automated_install.py ./test_centos_fedora_common_support.py ./test_fedora_support.py

View File

@@ -1,8 +0,0 @@
[tox]
envlist = py37
[testenv]
whitelist_externals = docker
deps = -rrequirements.txt
commands = docker build -f _fedora_33.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_automated_install.py ./test_centos_fedora_common_support.py ./test_fedora_support.py

View File

@@ -1,8 +0,0 @@
[tox]
envlist = py37
[testenv]
whitelist_externals = docker
deps = -rrequirements.txt
commands = docker build -f _ubuntu_16.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_automated_install.py

View File

@@ -1,8 +0,0 @@
[tox]
envlist = py37
[testenv]
whitelist_externals = docker
deps = -rrequirements.txt
commands = docker build -f _ubuntu_18.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_automated_install.py

View File

@@ -1,8 +0,0 @@
[tox]
envlist = py37
[testenv]
whitelist_externals = docker
deps = -rrequirements.txt
commands = docker build -f _ubuntu_20.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_automated_install.py

View File

@@ -1,8 +0,0 @@
[tox]
envlist = py37
[testenv]
whitelist_externals = docker
deps = -rrequirements.txt
commands = docker build -f _ubuntu_21.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_automated_install.py

10
tox.ini Normal file
View File

@@ -0,0 +1,10 @@
[tox]
envlist = py27
[testenv]
whitelist_externals = docker
deps = -rrequirements.txt
commands = docker build -f test/debian.Dockerfile -t pytest_pihole:debian .
docker build -f test/centos.Dockerfile -t pytest_pihole:centos .
docker build -f test/fedora.Dockerfile -t pytest_pihole:fedora .
pytest {posargs:-vv -n auto} -m "not build_stage" ./test/