Merge branch 'development-v6' into fix/overTimeGraphs
This commit is contained in:
commit
66653ae0d5
|
@ -0,0 +1,126 @@
|
|||
name: Build and test
|
||||
description: Builds and tests FTL on all supported platforms
|
||||
|
||||
inputs:
|
||||
platform:
|
||||
required: true
|
||||
description: The platform to build for
|
||||
git_branch:
|
||||
required: true
|
||||
description: The branch to build from
|
||||
git_tag:
|
||||
required: true
|
||||
description: The tag to build from (if any)
|
||||
bin_name:
|
||||
required: true
|
||||
description: The name of the binary to build
|
||||
artifact_name:
|
||||
required: true
|
||||
description: The name of the artifact to upload
|
||||
event_name:
|
||||
required: true
|
||||
description: The name of the event that triggered the workflow run
|
||||
actor:
|
||||
required: true
|
||||
description: The name of the user or app that initiated the workflow run
|
||||
target_dir:
|
||||
required: true
|
||||
description: The directory to deploy the artifacts to
|
||||
# Secrets cannot be accessed in the action.yml file so we need to pass them as
|
||||
# inputs to the action.
|
||||
SSH_KEY:
|
||||
required: true
|
||||
description: The SSH private key to use for authentication
|
||||
KNOWN_HOSTS:
|
||||
required: true
|
||||
description: The SSH known hosts file
|
||||
SSH_USER:
|
||||
required: true
|
||||
description: The SSH user to use for authentication
|
||||
SSH_HOST:
|
||||
required: true
|
||||
description: The SSH host to connect to
|
||||
|
||||
# Both the definition of environment variables and checking out the code
|
||||
# needs to be done outside of the composite action as
|
||||
# - environment variables cannot be defined using inputs
|
||||
# - the checkout action needs to be the first step in the workflow, otherwise we
|
||||
# cannot use the composite action as the corresponding "action.yml" isn't
|
||||
# there yet
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.1.0
|
||||
-
|
||||
name: Print directory contents
|
||||
shell: bash
|
||||
run: ls -l
|
||||
-
|
||||
name: Build and test FTL in ftl-build container (QEMU)
|
||||
uses: Wandalen/wretry.action@v1.4.8
|
||||
with:
|
||||
attempt_limit: 3
|
||||
action: docker/build-push-action@v5.0.0
|
||||
with: |
|
||||
platforms: ${{ inputs.platform }}
|
||||
pull: true
|
||||
push: false
|
||||
context: .
|
||||
target: result
|
||||
file: .github/Dockerfile
|
||||
outputs: |
|
||||
type=tar,dest=build.tar
|
||||
build-args: |
|
||||
"CI_ARCH=${{ inputs.platform }}"
|
||||
"GIT_BRANCH=${{ inputs.git_branch }}"
|
||||
"GIT_TAG=${{ inputs.git_tag }}"
|
||||
-
|
||||
name: List files in current directory
|
||||
shell: bash
|
||||
run: ls -l
|
||||
-
|
||||
name: Extract FTL binary from container
|
||||
shell: bash
|
||||
run: |
|
||||
tar -xf build.tar pihole-FTL
|
||||
-
|
||||
name: "Generate checksum file"
|
||||
shell: bash
|
||||
run: |
|
||||
mv pihole-FTL "${{ inputs.bin_name }}"
|
||||
sha1sum pihole-FTL-* > ${{ inputs.bin_name }}.sha1
|
||||
-
|
||||
name: Store binary artifacts for later deployoment
|
||||
if: inputs.event_name != 'pull_request'
|
||||
uses: actions/upload-artifact@v4.3.1
|
||||
with:
|
||||
name: ${{ inputs.artifact_name }}
|
||||
path: '${{ inputs.bin_name }}*'
|
||||
-
|
||||
name: Extract documentation files from container
|
||||
if: inputs.event_name != 'pull_request' && inputs.platform == 'linux/amd64'
|
||||
shell: bash
|
||||
run: |
|
||||
tar -xf build.tar api-docs.tar.gz
|
||||
-
|
||||
name: Upload documentation artifacts for deployoment
|
||||
if: inputs.event_name != 'pull_request' && inputs.platform == 'linux/amd64'
|
||||
uses: actions/upload-artifact@v4.3.1
|
||||
with:
|
||||
name: pihole-api-docs
|
||||
path: 'api-docs.tar.gz'
|
||||
-
|
||||
name: Deploy
|
||||
if: inputs.event_name != 'pull_request'
|
||||
uses: ./.github/actions/deploy
|
||||
with:
|
||||
pattern: ${{ inputs.bin_name }}-binary
|
||||
target_dir: ${{ inputs.target_dir }}
|
||||
event_name: ${{ inputs.event_name }}
|
||||
actor: ${{ inputs.actor }}
|
||||
SSH_KEY: ${{ inputs.SSH_KEY }}
|
||||
KNOWN_HOSTS: ${{ inputs.KNOWN_HOSTS }}
|
||||
SSH_USER: ${{ inputs.SSH_USER }}
|
||||
SSH_HOST: ${{ inputs.SSH_HOST }}
|
|
@ -0,0 +1,96 @@
|
|||
name: Deploy
|
||||
description: Deploy the FTL binary and documentation
|
||||
|
||||
inputs:
|
||||
pattern:
|
||||
required: true
|
||||
description: The pattern to match the artifacts to download
|
||||
target_dir:
|
||||
required: true
|
||||
description: The directory to deploy the artifacts to
|
||||
event_name:
|
||||
required: true
|
||||
description: The name of the event that triggered the workflow run
|
||||
actor:
|
||||
required: true
|
||||
description: The name of the user or app that initiated the workflow run
|
||||
# Secrets cannot be accessed in the action.yml file so we need to pass them as
|
||||
# inputs to the action.
|
||||
SSH_KEY:
|
||||
required: true
|
||||
description: The SSH private key to use for authentication
|
||||
KNOWN_HOSTS:
|
||||
required: true
|
||||
description: The SSH known hosts file
|
||||
SSH_USER:
|
||||
required: true
|
||||
description: The SSH user to use for authentication
|
||||
SSH_HOST:
|
||||
required: true
|
||||
description: The SSH host to connect to
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
-
|
||||
name: Get binaries built in previous jobs
|
||||
uses: actions/download-artifact@v4.1.4
|
||||
id: download
|
||||
with:
|
||||
path: ftl_builds/
|
||||
pattern: ${{ inputs.pattern }}
|
||||
merge-multiple: true
|
||||
-
|
||||
name: Get documentation files built in previous jobs
|
||||
if: inputs.pattern == 'pihole-FTL-amd64-binary'
|
||||
uses: actions/download-artifact@v4.1.4
|
||||
with:
|
||||
path: ftl_builds/
|
||||
name: pihole-api-docs
|
||||
-
|
||||
name: Display structure of downloaded files
|
||||
shell: bash
|
||||
run: ls -R
|
||||
working-directory: ${{steps.download.outputs.download-path}}
|
||||
-
|
||||
name: Install SSH Key
|
||||
uses: benoitchantre/setup-ssh-authentication-action@1.0.1
|
||||
with:
|
||||
private-key: ${{ inputs.SSH_KEY }}
|
||||
private-key-name: id_rsa
|
||||
known-hosts: ${{ inputs.KNOWN_HOSTS }}
|
||||
-
|
||||
name: Set private key permissions
|
||||
shell: bash
|
||||
run: chmod 600 ~/.ssh/id_rsa
|
||||
-
|
||||
name: Untar documentation files
|
||||
if: inputs.pattern == 'pihole-FTL-amd64-binary'
|
||||
working-directory: ftl_builds/
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir docs/
|
||||
tar xzvf api-docs.tar.gz -C docs/
|
||||
-
|
||||
name: Display structure of files ready for upload
|
||||
working-directory: ftl_builds/
|
||||
shell: bash
|
||||
run: ls -R
|
||||
-
|
||||
name: Transfer Builds to Pi-hole server for pihole checkout
|
||||
if: inputs.actor != 'dependabot[bot]'
|
||||
env:
|
||||
USER: ${{ inputs.SSH_USER }}
|
||||
HOST: ${{ inputs.SSH_HOST }}
|
||||
TARGET_DIR: ${{ inputs.target_dir }}
|
||||
SOURCE_DIR: ftl_builds/
|
||||
shell: bash
|
||||
run: |
|
||||
bash ./deploy.sh
|
||||
-
|
||||
name: Attach binaries to release
|
||||
if: inputs.event_name == 'release'
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
ftl_builds/*
|
|
@ -13,3 +13,27 @@ updates:
|
|||
pull-request-branch-name:
|
||||
# Separate sections of the branch name with a hyphen
|
||||
separator: "-"
|
||||
groups:
|
||||
github_action-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
# As above, but for development-v6
|
||||
- package-ecosystem: github-actions
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: saturday
|
||||
time: "10:00"
|
||||
open-pull-requests-limit: 10
|
||||
target-branch: development-v6
|
||||
reviewers:
|
||||
- "pi-hole/ftl-maintainers"
|
||||
pull-request-branch-name:
|
||||
# Separate sections of the branch name with a hyphen
|
||||
separator: "-"
|
||||
groups:
|
||||
github_action-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ jobs:
|
|||
[[ $FAIL == 1 ]] && exit 1 || echo "Branch name depth check passed."
|
||||
shell: bash
|
||||
|
||||
build:
|
||||
gha:
|
||||
runs-on: ubuntu-latest
|
||||
needs: smoke-tests
|
||||
strategy:
|
||||
|
@ -61,132 +61,67 @@ jobs:
|
|||
bin_name: pihole-FTL-amd64
|
||||
- platform: linux/386
|
||||
bin_name: pihole-FTL-386
|
||||
- platform: linux/riscv64
|
||||
bin_name: pihole-FTL-riscv64
|
||||
env:
|
||||
CI_ARCH: ${{ matrix.platform }}
|
||||
GIT_BRANCH: ${{ needs.smoke-tests.outputs.GIT_BRANCH }}
|
||||
GIT_TAG: ${{ needs.smoke-tests.outputs.GIT_TAG }}
|
||||
steps:
|
||||
-
|
||||
name: Checkout code
|
||||
uses: actions/checkout@v4.1.1
|
||||
-
|
||||
name: Build and test and deploy FTL
|
||||
uses: ./.github/actions/build-and-test
|
||||
with:
|
||||
platform: ${{ matrix.platform }}
|
||||
bin_name: ${{ matrix.bin_name }}
|
||||
artifact_name: ${{ matrix.bin_name }}-binary
|
||||
target_dir: ${{ needs.smoke-tests.outputs.OUTPUT_DIR }}
|
||||
git_branch: ${{ needs.smoke-tests.outputs.GIT_BRANCH }}
|
||||
git_tag: ${{ needs.smoke-tests.outputs.GIT_TAG }}
|
||||
event_name: ${{ github.event_name }}
|
||||
actor: ${{ github.actor }}
|
||||
SSH_KEY: ${{ secrets.SSH_KEY }}
|
||||
KNOWN_HOSTS: ${{ secrets.KNOWN_HOSTS }}
|
||||
SSH_USER: ${{ secrets.SSH_USER }}
|
||||
SSH_HOST: ${{ secrets.SSH_HOST }}
|
||||
|
||||
self-hosted:
|
||||
runs-on: self-hosted
|
||||
needs: smoke-tests
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- platform: linux/arm/v6
|
||||
bin_name: pihole-FTL-armv6
|
||||
- platform: linux/arm/v7
|
||||
bin_name: pihole-FTL-armv7
|
||||
- platform: linux/arm64/v8
|
||||
bin_name: pihole-FTL-arm64
|
||||
- platform: linux/riscv64
|
||||
bin_name: pihole-FTL-riscv64
|
||||
|
||||
env:
|
||||
CI_ARCH: ${{ matrix.platform }}
|
||||
GIT_BRANCH: ${{ needs.smoke-tests.outputs.GIT_BRANCH }}
|
||||
GIT_TAG: ${{ needs.smoke-tests.outputs.GIT_TAG }}
|
||||
|
||||
steps:
|
||||
-
|
||||
name: Checkout code
|
||||
uses: actions/checkout@v4.1.1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.0.0
|
||||
-
|
||||
name: Print directory contents
|
||||
run: ls -l
|
||||
-
|
||||
name: Build and test FTL in ftl-build container (QEMU)
|
||||
uses: Wandalen/wretry.action@v1.4.4
|
||||
name: Build and test and deploy FTL
|
||||
uses: ./.github/actions/build-and-test
|
||||
with:
|
||||
attempt_limit: 3
|
||||
action: docker/build-push-action@v5.0.0
|
||||
with: |
|
||||
platforms: ${{ matrix.platform }}
|
||||
pull: true
|
||||
push: false
|
||||
context: .
|
||||
target: result
|
||||
file: .github/Dockerfile
|
||||
outputs: |
|
||||
type=tar,dest=build.tar
|
||||
build-args: |
|
||||
"CI_ARCH=${{ matrix.platform }}"
|
||||
"GIT_BRANCH=${{ needs.smoke-tests.outputs.GIT_BRANCH }}"
|
||||
"GIT_TAG=${{ needs.smoke-tests.outputs.GIT_TAG }}"
|
||||
-
|
||||
name: List files in current directory
|
||||
run: ls -l
|
||||
-
|
||||
name: Extract FTL binary from container
|
||||
run: |
|
||||
tar -xf build.tar pihole-FTL
|
||||
-
|
||||
name: "Generate checksum file"
|
||||
run: |
|
||||
mv pihole-FTL "${{ matrix.bin_name }}"
|
||||
sha1sum pihole-FTL-* > ${{ matrix.bin_name }}.sha1
|
||||
-
|
||||
name: Store binary artifacts for later deployoment
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: actions/upload-artifact@v4.3.1
|
||||
with:
|
||||
name: ${{ matrix.bin_name }}-binary
|
||||
path: '${{ matrix.bin_name }}*'
|
||||
-
|
||||
name: Extract documentation files from container
|
||||
if: github.event_name != 'pull_request' && matrix.platform == 'linux/amd64'
|
||||
run: |
|
||||
tar -xf build.tar api-docs.tar.gz
|
||||
-
|
||||
name: Upload documentation artifacts for deployoment
|
||||
if: github.event_name != 'pull_request' && matrix.platform == 'linux/amd64'
|
||||
uses: actions/upload-artifact@v4.3.1
|
||||
with:
|
||||
name: pihole-api-docs
|
||||
path: 'api-docs.tar.gz'
|
||||
|
||||
deploy:
|
||||
if: github.event_name != 'pull_request'
|
||||
needs: [smoke-tests, build]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout code
|
||||
uses: actions/checkout@v4.1.1
|
||||
-
|
||||
name: Get Binaries and documentation built in previous jobs
|
||||
uses: actions/download-artifact@v4.1.2
|
||||
id: download
|
||||
with:
|
||||
path: ftl_builds/
|
||||
pattern: pihole-*
|
||||
merge-multiple: true
|
||||
-
|
||||
name: Display structure of downloaded files
|
||||
run: ls -R
|
||||
working-directory: ${{steps.download.outputs.download-path}}
|
||||
|
||||
-
|
||||
name: Install SSH Key
|
||||
uses: benoitchantre/setup-ssh-authentication-action@1.0.1
|
||||
with:
|
||||
private-key: ${{ secrets.SSH_KEY }}
|
||||
known-hosts: ${{ secrets.KNOWN_HOSTS }}
|
||||
-
|
||||
name: Untar documentation files
|
||||
working-directory: ftl_builds/
|
||||
run: |
|
||||
mkdir docs/
|
||||
tar xzvf api-docs.tar.gz -C docs/
|
||||
-
|
||||
name: Display structure of files ready for upload
|
||||
run: ls -R
|
||||
working-directory: ftl_builds/
|
||||
-
|
||||
name: Transfer Builds to Pi-hole server for pihole checkout
|
||||
if: github.actor != 'dependabot[bot]'
|
||||
env:
|
||||
USER: ${{ secrets.SSH_USER }}
|
||||
HOST: ${{ secrets.SSH_HOST }}
|
||||
TARGET_DIR: ${{ needs.smoke-tests.outputs.OUTPUT_DIR }}
|
||||
SOURCE_DIR: ftl_builds/
|
||||
run: |
|
||||
bash ./deploy.sh
|
||||
-
|
||||
name: Attach binaries to release
|
||||
if: github.event_name == 'release'
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: |
|
||||
ftl_builds/*
|
||||
platform: ${{ matrix.platform }}
|
||||
bin_name: ${{ matrix.bin_name }}
|
||||
artifact_name: ${{ matrix.bin_name }}-binary
|
||||
target_dir: ${{ needs.smoke-tests.outputs.OUTPUT_DIR }}
|
||||
git_branch: ${{ needs.smoke-tests.outputs.GIT_BRANCH }}
|
||||
git_tag: ${{ needs.smoke-tests.outputs.GIT_TAG }}
|
||||
event_name: ${{ github.event_name }}
|
||||
actor: ${{ github.actor }}
|
||||
SSH_KEY: ${{ secrets.SSH_KEY }}
|
||||
KNOWN_HOSTS: ${{ secrets.KNOWN_HOSTS }}
|
||||
SSH_USER: ${{ secrets.SSH_USER }}
|
||||
SSH_HOST: ${{ secrets.SSH_HOST }}
|
||||
|
|
|
@ -23,14 +23,17 @@ jobs:
|
|||
days-before-stale: 30
|
||||
days-before-close: 5
|
||||
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Please comment or update this issue or it will be closed in 5 days.'
|
||||
stale-issue-label: $stale_label
|
||||
stale-issue-label: '${{ env.stale_label }}'
|
||||
exempt-issue-labels: 'Fixed in next release, Bug, Bug:Confirmed, Bugfix in progress, documentation needed, internal'
|
||||
exempt-all-issue-assignees: true
|
||||
operations-per-run: 300
|
||||
close-issue-reason: 'not_planned'
|
||||
|
||||
remove_stale: # trigger "stale" removal immediately when stale issues are commented on
|
||||
if: github.event_name == 'issue_comment'
|
||||
remove_stale:
|
||||
# trigger "stale" removal immediately when stale issues are commented on
|
||||
# we need to explicitly check that the trigger does not run on comment on a PR as
|
||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issue_comment-on-issues-only-or-pull-requests-only
|
||||
if: ${{ !github.event.issue.pull_request && github.event_name != 'schedule' }}
|
||||
permissions:
|
||||
contents: read # for actions/checkout
|
||||
issues: write # to edit issues label
|
||||
|
@ -39,7 +42,7 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Remove 'stale' label
|
||||
run: gh issue edit ${{ github.event.issue.number }} --remove-label $stale_label
|
||||
run: gh issue edit ${{ github.event.issue.number }} --remove-label ${{ env.stale_label }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
|
|
@ -12,6 +12,6 @@ cmake_minimum_required(VERSION 2.8.12)
|
|||
|
||||
project(PIHOLE_FTL C)
|
||||
|
||||
set(DNSMASQ_VERSION pi-hole-v2.89-e1de9c2)
|
||||
set(DNSMASQ_VERSION pi-hole-v2.90+1)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
|
|
@ -766,6 +766,17 @@ static int api_config_patch(struct ftl_conn *api)
|
|||
|
||||
// If we reach this point, a valid setting was found and changed
|
||||
|
||||
// Validate new value (if validation function is defined)
|
||||
char errbuf[VALIDATOR_ERRBUF_LEN] = { 0 };
|
||||
if(!conf_item->c(&new_item->v, new_item->k, errbuf))
|
||||
{
|
||||
free_config(&newconf);
|
||||
return send_json_error(api, 400,
|
||||
"bad_request",
|
||||
"Config item validation failed",
|
||||
errbuf);
|
||||
}
|
||||
|
||||
// Check if this item requires a config-rewrite + restart of dnsmasq
|
||||
if(conf_item->f & FLAG_RESTART_FTL)
|
||||
dnsmasq_changed = true;
|
||||
|
@ -949,6 +960,21 @@ static int api_config_put_delete(struct ftl_conn *api)
|
|||
}
|
||||
|
||||
// If we reach this point, a valid setting was found and changed
|
||||
|
||||
// Validate new value on PUT (if validation function is defined)
|
||||
if(api->method == HTTP_PUT)
|
||||
{
|
||||
char errbuf[VALIDATOR_ERRBUF_LEN] = { 0 };
|
||||
if(!new_item->c(&new_item->v, new_item->k, errbuf))
|
||||
{
|
||||
free_config(&newconf);
|
||||
return send_json_error(api, 400,
|
||||
"bad_request",
|
||||
"Invalid value",
|
||||
errbuf);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this item requires a config-rewrite + restart of dnsmasq
|
||||
if(new_item->f & FLAG_RESTART_FTL)
|
||||
dnsmasq_changed = true;
|
||||
|
|
|
@ -244,7 +244,10 @@ components:
|
|||
- Authentication
|
||||
operationId: "add_app"
|
||||
description: |
|
||||
Create a new application password. The generated password is shown only once and cannot be retrieved later so make sure to store it in a safe place. The application password can be used to authenticate against the API instead of the regular password. It does not require 2FA verification. Generating a new application password will invalidate all currently active sessions.
|
||||
Create a new application password. The generated password is shown only once and cannot be retrieved later - make sure to store it in a safe place. The application password can be used to authenticate against the API instead of the regular password.
|
||||
It does not require 2FA verification. Generating a new application password will invalidate all currently active sessions.
|
||||
|
||||
Note that this endpoint only generates an application password accompanied by its hash. To make this new password effective, the returned `hash` has to be set as `webserver.api.app_password` in the Pi-hole configuration in a follow-up step. This can be done in various ways, e.g. via the API (`PATCH /api/config/webserver/api/app_pwhash`), the graphical web interface (Settings -> All Settings) or by editing the configuration file directly.
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
|
|
|
@ -344,6 +344,8 @@ components:
|
|||
type: integer
|
||||
DBinterval:
|
||||
type: integer
|
||||
useWAL:
|
||||
type: boolean
|
||||
network:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -663,6 +665,7 @@ components:
|
|||
DBexport: true
|
||||
maxDBdays: 365
|
||||
DBinterval: 60
|
||||
useWAL: true
|
||||
network:
|
||||
parseARPcache: true
|
||||
expire: 365
|
||||
|
|
|
@ -752,6 +752,115 @@ components:
|
|||
type: boolean
|
||||
description: Whether or not FTL is allowed to perform destructive actions
|
||||
example: true
|
||||
dnsmasq:
|
||||
type: object
|
||||
description: Metrics from the embedded dnsmasq resolver
|
||||
properties:
|
||||
dns_cache_inserted:
|
||||
type: integer
|
||||
description: Number of inserted entries in DNS cache
|
||||
example: 8
|
||||
dns_cache_live_freed:
|
||||
type: integer
|
||||
description: Number of freed live entries in DNS cache
|
||||
example: 0
|
||||
dns_queries_forwarded:
|
||||
type: integer
|
||||
description: Number of forwarded DNS queries
|
||||
example: 2
|
||||
dns_auth_answered:
|
||||
type: integer
|
||||
description: Number of DNS queries for authoritative zones
|
||||
example: 0
|
||||
dns_local_answered:
|
||||
type: integer
|
||||
description: Number of DNS queries answered from local cache
|
||||
example: 74
|
||||
dns_stale_answered:
|
||||
type: integer
|
||||
description: Number of DNS queries answered from local cache (stale entries)
|
||||
example: 0
|
||||
dns_unanswered:
|
||||
type: integer
|
||||
description: Number of unanswered DNS queries
|
||||
example: 0
|
||||
bootp:
|
||||
type: integer
|
||||
description: Number of BOOTP requests
|
||||
example: 0
|
||||
pxe:
|
||||
type: integer
|
||||
description: Number of PXE requests
|
||||
example: 0
|
||||
dhcp_ack:
|
||||
type: integer
|
||||
description: Number of DHCP ACK
|
||||
example: 0
|
||||
dhcp_decline:
|
||||
type: integer
|
||||
description: Number of DHCP DECLINE
|
||||
example: 0
|
||||
dhcp_discover:
|
||||
type: integer
|
||||
description: Number of DHCP DISCOVER
|
||||
example: 0
|
||||
dhcp_inform:
|
||||
type: integer
|
||||
description: Number of DHCP INFORM
|
||||
example: 0
|
||||
dhcp_nak:
|
||||
type: integer
|
||||
description: Number of DHCP NAK
|
||||
example: 0
|
||||
dhcp_offer:
|
||||
type: integer
|
||||
description: Number of DHCP OFFER
|
||||
example: 0
|
||||
dhcp_release:
|
||||
type: integer
|
||||
description: Number of DHCP RELEASE
|
||||
example: 0
|
||||
dhcp_request:
|
||||
type: integer
|
||||
description: Number of DHCP REQUEST
|
||||
example: 0
|
||||
noanswer:
|
||||
type: integer
|
||||
description: Number of DHCP requests without answer (rapid commit)
|
||||
example: 0
|
||||
leases_allocated_4:
|
||||
type: integer
|
||||
description: Number of allocated IPv4 leases
|
||||
example: 0
|
||||
leases_pruned_4:
|
||||
type: integer
|
||||
description: Number of pruned IPv4 leases
|
||||
example: 0
|
||||
leases_allocated_6:
|
||||
type: integer
|
||||
description: Number of allocated IPv6 leases
|
||||
example: 0
|
||||
leases_pruned_6:
|
||||
type: integer
|
||||
description: Number of pruned IPv6 leases
|
||||
example: 0
|
||||
tcp_connections:
|
||||
type: integer
|
||||
description: Number of dedicated TCP workers
|
||||
example: 0
|
||||
dnssec_max_crypto_use:
|
||||
type: integer
|
||||
description: DNSSEC per-query crypto work HWM
|
||||
example: 0
|
||||
dnssec_max_sig_fail:
|
||||
type: integer
|
||||
description: DNSSEC per-RRSet signature fails HWM
|
||||
example: 0
|
||||
dnssec_max_work:
|
||||
type: integer
|
||||
description: DNSSEC per-query subqueries HWM
|
||||
example: 0
|
||||
|
||||
database:
|
||||
type: object
|
||||
properties:
|
||||
|
|
|
@ -5,6 +5,7 @@ components:
|
|||
summary: Modify list
|
||||
parameters:
|
||||
- $ref: 'lists.yaml#/components/parameters/list'
|
||||
- $ref: 'lists.yaml#/components/parameters/listtype'
|
||||
get:
|
||||
summary: Get lists
|
||||
tags:
|
||||
|
@ -449,3 +450,14 @@ components:
|
|||
required: true
|
||||
description: Address of the list
|
||||
example: https://hosts-file.net/ad_servers.txt
|
||||
listtype:
|
||||
in: query
|
||||
name: type
|
||||
schema:
|
||||
type: string
|
||||
enum:
|
||||
- "allow"
|
||||
- "block"
|
||||
required: false
|
||||
description: Type of list, optional
|
||||
example: block
|
||||
|
|
|
@ -368,7 +368,7 @@ static int get_hwmon_sensors(struct ftl_conn *api, cJSON *sensors)
|
|||
if(hwmon_dir == NULL)
|
||||
{
|
||||
// Nothing to read here, leave array empty
|
||||
log_warn("Cannot open %s: %s", dirname, strerror(errno));
|
||||
log_debug(DEBUG_API, "Cannot open %s: %s", dirname, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -590,6 +590,11 @@ static int get_ftl_obj(struct ftl_conn *api, cJSON *ftl)
|
|||
|
||||
JSON_ADD_BOOL_TO_OBJECT(ftl, "allow_destructive", config.webserver.api.allow_destructive.v.b);
|
||||
|
||||
// dnsmasq struct
|
||||
cJSON *dnsmasq = JSON_NEW_OBJECT();
|
||||
get_dnsmasq_metrics_obj(dnsmasq);
|
||||
JSON_ADD_ITEM_TO_OBJECT(ftl, "dnsmasq", dnsmasq);
|
||||
|
||||
// All okay
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ static int api_list_read(struct ftl_conn *api,
|
|||
sql_msg);
|
||||
}
|
||||
|
||||
tablerow table;
|
||||
tablerow table = { 0 };
|
||||
cJSON *rows = JSON_NEW_ARRAY();
|
||||
while(gravityDB_readTableGetRow(listtype, &table, &sql_msg))
|
||||
{
|
||||
|
@ -48,7 +48,9 @@ static int api_list_read(struct ftl_conn *api,
|
|||
JSON_COPY_STR_TO_OBJECT(row, "name", table.name);
|
||||
JSON_COPY_STR_TO_OBJECT(row, "comment", table.comment);
|
||||
}
|
||||
else if(listtype == GRAVITY_ADLISTS)
|
||||
else if(listtype == GRAVITY_ADLISTS ||
|
||||
listtype == GRAVITY_ADLISTS_BLOCK ||
|
||||
listtype == GRAVITY_ADLISTS_ALLOW)
|
||||
{
|
||||
JSON_COPY_STR_TO_OBJECT(row, "address", table.address);
|
||||
JSON_COPY_STR_TO_OBJECT(row, "comment", table.comment);
|
||||
|
@ -126,7 +128,9 @@ static int api_list_read(struct ftl_conn *api,
|
|||
JSON_ADD_NUMBER_TO_OBJECT(row, "date_modified", table.date_modified);
|
||||
|
||||
// Properties added in https://github.com/pi-hole/pi-hole/pull/3951
|
||||
if(listtype == GRAVITY_ADLISTS)
|
||||
if(listtype == GRAVITY_ADLISTS ||
|
||||
listtype == GRAVITY_ADLISTS_BLOCK ||
|
||||
listtype == GRAVITY_ADLISTS_ALLOW)
|
||||
{
|
||||
JSON_REF_STR_IN_OBJECT(row, "type", table.type);
|
||||
JSON_ADD_NUMBER_TO_OBJECT(row, "date_updated", table.date_updated);
|
||||
|
@ -147,7 +151,9 @@ static int api_list_read(struct ftl_conn *api,
|
|||
cJSON *json = JSON_NEW_OBJECT();
|
||||
if(listtype == GRAVITY_GROUPS)
|
||||
objname = "groups";
|
||||
else if(listtype == GRAVITY_ADLISTS)
|
||||
else if(listtype == GRAVITY_ADLISTS ||
|
||||
listtype == GRAVITY_ADLISTS_BLOCK ||
|
||||
listtype == GRAVITY_ADLISTS_ALLOW)
|
||||
objname = "lists";
|
||||
else if(listtype == GRAVITY_CLIENTS)
|
||||
objname = "clients";
|
||||
|
@ -268,6 +274,8 @@ static int api_list_write(struct ftl_conn *api,
|
|||
}
|
||||
|
||||
case GRAVITY_ADLISTS:
|
||||
case GRAVITY_ADLISTS_BLOCK:
|
||||
case GRAVITY_ADLISTS_ALLOW:
|
||||
{
|
||||
cJSON *json_address = cJSON_GetObjectItemCaseSensitive(api->payload.json, "address");
|
||||
if(cJSON_IsString(json_address) && strlen(json_address->valuestring) > 0)
|
||||
|
@ -331,6 +339,10 @@ static int api_list_write(struct ftl_conn *api,
|
|||
NULL);
|
||||
}
|
||||
}
|
||||
else if(listtype == GRAVITY_ADLISTS_BLOCK)
|
||||
row.type_int = ADLIST_BLOCK;
|
||||
else if(listtype == GRAVITY_ADLISTS_ALLOW)
|
||||
row.type_int = ADLIST_ALLOW;
|
||||
else
|
||||
{
|
||||
cJSON *json_type = cJSON_GetObjectItemCaseSensitive(api->payload.json, "type");
|
||||
|
@ -552,7 +564,9 @@ static int api_list_remove(struct ftl_conn *api,
|
|||
if(listtype == GRAVITY_DOMAINLIST_ALLOW_EXACT ||
|
||||
listtype == GRAVITY_DOMAINLIST_DENY_EXACT ||
|
||||
listtype == GRAVITY_DOMAINLIST_ALLOW_REGEX ||
|
||||
listtype == GRAVITY_DOMAINLIST_DENY_REGEX)
|
||||
listtype == GRAVITY_DOMAINLIST_DENY_REGEX ||
|
||||
listtype == GRAVITY_ADLISTS_BLOCK ||
|
||||
listtype == GRAVITY_ADLISTS_ALLOW)
|
||||
{
|
||||
int type = -1;
|
||||
switch (listtype)
|
||||
|
@ -568,12 +582,17 @@ static int api_list_remove(struct ftl_conn *api,
|
|||
break;
|
||||
case GRAVITY_DOMAINLIST_DENY_REGEX:
|
||||
type = 3;
|
||||
break;
|
||||
case GRAVITY_ADLISTS_BLOCK:
|
||||
type = ADLIST_BLOCK;
|
||||
break;
|
||||
case GRAVITY_ADLISTS_ALLOW:
|
||||
type = ADLIST_ALLOW;
|
||||
break;
|
||||
// Not handled herein
|
||||
case GRAVITY_GROUPS:
|
||||
case GRAVITY_ADLISTS:
|
||||
case GRAVITY_CLIENTS:
|
||||
// No type required for these tables
|
||||
break;
|
||||
// Aggregate types cannot be handled by this routine
|
||||
case GRAVITY_GRAVITY:
|
||||
case GRAVITY_ANTIGRAVITY:
|
||||
case GRAVITY_DOMAINLIST_ALLOW_ALL:
|
||||
|
@ -582,7 +601,7 @@ static int api_list_remove(struct ftl_conn *api,
|
|||
case GRAVITY_DOMAINLIST_ALL_REGEX:
|
||||
case GRAVITY_DOMAINLIST_ALL_ALL:
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Create new JSON array with the item and type:
|
||||
|
@ -827,6 +846,30 @@ int api_list(struct ftl_conn *api)
|
|||
api->request->local_uri_raw);
|
||||
}
|
||||
|
||||
// If this is a request for a list, we check if there is a request
|
||||
// parameter narrowing down which kind of list. If so, we modify the
|
||||
// list type accordingly
|
||||
if(listtype == GRAVITY_ADLISTS && api->request->query_string != NULL)
|
||||
{
|
||||
// Check if there is a type parameter
|
||||
char typestr[16] = { 0 };
|
||||
if(get_string_var(api->request->query_string, "type", typestr, sizeof(typestr)) > 0)
|
||||
{
|
||||
if(strcasecmp(typestr, "allow") == 0)
|
||||
listtype = GRAVITY_ADLISTS_ALLOW;
|
||||
else if(strcasecmp(typestr, "block") == 0)
|
||||
listtype = GRAVITY_ADLISTS_BLOCK;
|
||||
else
|
||||
{
|
||||
// Invalid type parameter
|
||||
return send_json_error(api, 400,
|
||||
"bad_request",
|
||||
"Invalid request: Invalid type parameter (should be either \"allow\" or \"block\")",
|
||||
api->request->query_string);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(api->method == HTTP_GET)
|
||||
{
|
||||
// Read list item identified by URI (or read them all)
|
||||
|
|
|
@ -293,6 +293,8 @@ int api_queries(struct ftl_conn *api)
|
|||
char sort_dir[16] = { 0 };
|
||||
char sort_col[16] = { 0 };
|
||||
|
||||
char search[2][512] = { { 0 }, { 0 } };
|
||||
|
||||
// We start with the most recent query at the beginning (until the cursor is changed)
|
||||
unsigned long cursor, largest_db_index, mem_dbnum, disk_dbnum;
|
||||
db_counts(&largest_db_index, &mem_dbnum, &disk_dbnum);
|
||||
|
@ -435,6 +437,72 @@ int api_queries(struct ftl_conn *api)
|
|||
log_warn("Sorting by column %d (%s) requested, but column name not found",
|
||||
sort_column, sort_dir);
|
||||
}
|
||||
|
||||
// Column searching?
|
||||
// ID 3 = domain, ID 4 = client, every other combination is requested
|
||||
for(unsigned int j = 0; j < 2; j++)
|
||||
{
|
||||
// Encoded URI string: %5B = [ and %5D = ]
|
||||
// columns[X][search][value] is the search string for column X
|
||||
char search_col[] = "columns%5BX%5D%5Bsearch%5D%5Bvalue%5D";
|
||||
search_col[10] = '3' + j;
|
||||
if(GET_STR(search_col, search[j], api->request->query_string) > 0)
|
||||
{
|
||||
// columns[X][data] is the name of column X
|
||||
char search_col_id[] = "columns%5BX%5D%5Bdata%5D";
|
||||
search_col_id[10] = '3' + j;
|
||||
|
||||
// Encoded URI string: %5B = [ and %5D = ]
|
||||
char search_col_id_str[32] = { 0 };
|
||||
if(GET_VAR(search_col_id, search_col_id_str, api->request->query_string) > 0)
|
||||
{
|
||||
size_t searchlen = min(strlen(search[j]), sizeof(search[j]) - 2);
|
||||
|
||||
// Replace "*" by SQLite3 wildcard character "%"
|
||||
for(unsigned int i = 0; i < searchlen; i++)
|
||||
{
|
||||
if(search[j][i] == '*')
|
||||
search[j][i] = '%';
|
||||
}
|
||||
|
||||
// Add % at the end of the search string to
|
||||
// make it a wildcard if there is none
|
||||
if(search[j][searchlen - 1] != '%')
|
||||
{
|
||||
search[j][searchlen] = '%';
|
||||
search[j][searchlen + 1] = '\0';
|
||||
searchlen++;
|
||||
}
|
||||
|
||||
// Add % at the beginning of the search
|
||||
// string to make it a wildcard if there
|
||||
// is none
|
||||
if(search[j][0] != '%')
|
||||
{
|
||||
memmove(search[j] + 1, search[j], searchlen + 1);
|
||||
search[j][0] = '%';
|
||||
}
|
||||
|
||||
// Apply the search string to the query if this is an allowed column
|
||||
if(j == 0 && strcasecmp(search_col_id_str, "domain") == 0)
|
||||
{
|
||||
log_debug(DEBUG_API, "Searching column domain: \"%s\"", search[j]);
|
||||
add_querystr_string(api, querystr, "d.domain LIKE", ":domain_search", &where);
|
||||
}
|
||||
else if(j == 1 && (strcasecmp(search_col_id_str, "client.ip") == 0 || strcasecmp(search_col_id_str, "client") == 0))
|
||||
{
|
||||
log_debug(DEBUG_API, "Searching column client: \"%s\"", search[j]);
|
||||
// We search both client IP and name
|
||||
add_querystr_string(api, querystr, "c.ip LIKE :client_search OR c.name LIKE", ":client_search", &where);
|
||||
}
|
||||
else
|
||||
log_warn("Column %u with name \"%s\" is not searchable (allowed: 3 = domain, 4 = client)",
|
||||
3 + j, search_col_id_str);
|
||||
}
|
||||
else
|
||||
log_warn("Column %u is not searchable (allowed: 3 = domain, 4 = client)", 3 + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We use this boolean to memorize if we are filtering at all. It is used
|
||||
|
@ -719,6 +787,36 @@ int api_queries(struct ftl_conn *api)
|
|||
sqlite3_errstr(rc));
|
||||
}
|
||||
}
|
||||
idx = sqlite3_bind_parameter_index(read_stmt, ":domain_search");
|
||||
if(idx > 0)
|
||||
{
|
||||
log_debug(DEBUG_API, "adding :domain_search = \"%s\" to query", search[0]);
|
||||
filtering = true;
|
||||
if((rc = sqlite3_bind_text(read_stmt, idx, search[0], -1, SQLITE_STATIC)) != SQLITE_OK)
|
||||
{
|
||||
sqlite3_reset(read_stmt);
|
||||
sqlite3_finalize(read_stmt);
|
||||
return send_json_error(api, 500,
|
||||
"internal_error",
|
||||
"Internal server error, failed to bind domain_search to SQL query",
|
||||
sqlite3_errstr(rc));
|
||||
}
|
||||
}
|
||||
idx = sqlite3_bind_parameter_index(read_stmt, ":client_search");
|
||||
if(idx > 0)
|
||||
{
|
||||
log_debug(DEBUG_API, "adding :client_search = \"%s\" to query", search[1]);
|
||||
filtering = true;
|
||||
if((rc = sqlite3_bind_text(read_stmt, idx, search[1], -1, SQLITE_STATIC)) != SQLITE_OK)
|
||||
{
|
||||
sqlite3_reset(read_stmt);
|
||||
sqlite3_finalize(read_stmt);
|
||||
return send_json_error(api, 500,
|
||||
"internal_error",
|
||||
"Internal server error, failed to bind client_search to SQL query",
|
||||
sqlite3_errstr(rc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Debug logging
|
||||
|
@ -1036,4 +1134,4 @@ bool compile_filter_regex(struct ftl_conn *api, const char *path, cJSON *json, r
|
|||
// We are filtering, so we have to continue to step over the
|
||||
// remaining rows to get the correct number of total records
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,8 @@ set(sources
|
|||
toml_reader.h
|
||||
toml_helper.c
|
||||
toml_helper.h
|
||||
validator.c
|
||||
validator.h
|
||||
)
|
||||
|
||||
add_library(config OBJECT ${sources})
|
||||
|
|
|
@ -455,6 +455,18 @@ int set_config_from_CLI(const char *key, const char *value)
|
|||
{
|
||||
// Config item changed
|
||||
|
||||
// Validate new value(if validation function is defined)
|
||||
if(new_item->c != NULL)
|
||||
{
|
||||
char errbuf[VALIDATOR_ERRBUF_LEN] = { 0 };
|
||||
if(!new_item->c(&new_item->v, new_item->k, errbuf))
|
||||
{
|
||||
free_config(&newconf);
|
||||
log_err("Invalid value: %s", errbuf);
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Is this a dnsmasq option we need to check?
|
||||
if(conf_item->f & FLAG_RESTART_FTL)
|
||||
{
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include "api/api.h"
|
||||
// exit_code
|
||||
#include "signals.h"
|
||||
// validation functions
|
||||
#include "config/validator.h"
|
||||
// getEnvVars()
|
||||
#include "config/env.h"
|
||||
// sha256sum()
|
||||
|
@ -383,47 +385,54 @@ void initConfig(struct config *conf)
|
|||
|
||||
// struct dns
|
||||
conf->dns.upstreams.k = "dns.upstreams";
|
||||
conf->dns.upstreams.h = "Array of upstream DNS servers used by Pi-hole\n Example: [ \"8.8.8.8\", \"127.0.0.1#5353\", \"docker-resolver\" ]";
|
||||
conf->dns.upstreams.h = "Array of upstream DNS servers used by Pi-hole\n Example: [ \"8.8.8.8\", \"127.0.0.1#5335\", \"docker-resolver\" ]";
|
||||
conf->dns.upstreams.a = cJSON_CreateStringReference("array of IP addresses and/or hostnames, optionally with a port (#...)");
|
||||
conf->dns.upstreams.t = CONF_JSON_STRING_ARRAY;
|
||||
conf->dns.upstreams.d.json = cJSON_CreateArray();
|
||||
conf->dns.upstreams.f = FLAG_RESTART_FTL;
|
||||
conf->dns.upstreams.c = validate_stub; // Type-based checking + dnsmasq syntax checking
|
||||
|
||||
conf->dns.CNAMEdeepInspect.k = "dns.CNAMEdeepInspect";
|
||||
conf->dns.CNAMEdeepInspect.h = "Use this option to control deep CNAME inspection. Disabling it might be beneficial for very low-end devices";
|
||||
conf->dns.CNAMEdeepInspect.t = CONF_BOOL;
|
||||
conf->dns.CNAMEdeepInspect.f = FLAG_ADVANCED_SETTING;
|
||||
conf->dns.CNAMEdeepInspect.d.b = true;
|
||||
conf->dns.CNAMEdeepInspect.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.blockESNI.k = "dns.blockESNI";
|
||||
conf->dns.blockESNI.h = "Should _esni. subdomains be blocked by default? Encrypted Server Name Indication (ESNI) is certainly a good step into the right direction to enhance privacy on the web. It prevents on-path observers, including ISPs, coffee shop owners and firewalls, from intercepting the TLS Server Name Indication (SNI) extension by encrypting it. This prevents the SNI from being used to determine which websites users are visiting.\n ESNI will obviously cause issues for pixelserv-tls which will be unable to generate matching certificates on-the-fly when it cannot read the SNI. Cloudflare and Firefox are already enabling ESNI. According to the IEFT draft (link above), we can easily restore piselserv-tls's operation by replying NXDOMAIN to _esni. subdomains of blocked domains as this mimics a \"not configured for this domain\" behavior.";
|
||||
conf->dns.blockESNI.t = CONF_BOOL;
|
||||
conf->dns.blockESNI.f = FLAG_ADVANCED_SETTING;
|
||||
conf->dns.blockESNI.d.b = true;
|
||||
conf->dns.blockESNI.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.EDNS0ECS.k = "dns.EDNS0ECS";
|
||||
conf->dns.EDNS0ECS.h = "Should we overwrite the query source when client information is provided through EDNS0 client subnet (ECS) information? This allows Pi-hole to obtain client IPs even if they are hidden behind the NAT of a router. This feature has been requested and discussed on Discourse where further information how to use it can be found: https://discourse.pi-hole.net/t/support-for-add-subnet-option-from-dnsmasq-ecs-edns0-client-subnet/35940";
|
||||
conf->dns.EDNS0ECS.t = CONF_BOOL;
|
||||
conf->dns.EDNS0ECS.f = FLAG_ADVANCED_SETTING;
|
||||
conf->dns.EDNS0ECS.d.b = true;
|
||||
conf->dns.EDNS0ECS.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.ignoreLocalhost.k = "dns.ignoreLocalhost";
|
||||
conf->dns.ignoreLocalhost.h = "Should FTL hide queries made by localhost?";
|
||||
conf->dns.ignoreLocalhost.t = CONF_BOOL;
|
||||
conf->dns.ignoreLocalhost.f = FLAG_ADVANCED_SETTING;
|
||||
conf->dns.ignoreLocalhost.d.b = false;
|
||||
conf->dns.ignoreLocalhost.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.showDNSSEC.k = "dns.showDNSSEC";
|
||||
conf->dns.showDNSSEC.h = "Should FTL should analyze and show internally generated DNSSEC queries?";
|
||||
conf->dns.showDNSSEC.t = CONF_BOOL;
|
||||
conf->dns.showDNSSEC.f = FLAG_ADVANCED_SETTING;
|
||||
conf->dns.showDNSSEC.d.b = true;
|
||||
conf->dns.showDNSSEC.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.analyzeOnlyAandAAAA.k = "dns.analyzeOnlyAandAAAA";
|
||||
conf->dns.analyzeOnlyAandAAAA.h = "Should FTL analyze *only* A and AAAA queries?";
|
||||
conf->dns.analyzeOnlyAandAAAA.t = CONF_BOOL;
|
||||
conf->dns.analyzeOnlyAandAAAA.f = FLAG_ADVANCED_SETTING;
|
||||
conf->dns.analyzeOnlyAandAAAA.d.b = false;
|
||||
conf->dns.analyzeOnlyAandAAAA.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.piholePTR.k = "dns.piholePTR";
|
||||
conf->dns.piholePTR.h = "Controls whether and how FTL will reply with for address for which a local interface exists.";
|
||||
|
@ -440,6 +449,7 @@ void initConfig(struct config *conf)
|
|||
conf->dns.piholePTR.t = CONF_ENUM_PTR_TYPE;
|
||||
conf->dns.piholePTR.f = FLAG_ADVANCED_SETTING;
|
||||
conf->dns.piholePTR.d.ptr_type = PTR_PIHOLE;
|
||||
conf->dns.piholePTR.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.replyWhenBusy.k = "dns.replyWhenBusy";
|
||||
conf->dns.replyWhenBusy.h = "How should FTL handle queries when the gravity database is not available?";
|
||||
|
@ -456,12 +466,14 @@ void initConfig(struct config *conf)
|
|||
conf->dns.replyWhenBusy.t = CONF_ENUM_BUSY_TYPE;
|
||||
conf->dns.replyWhenBusy.f = FLAG_ADVANCED_SETTING;
|
||||
conf->dns.replyWhenBusy.d.busy_reply = BUSY_ALLOW;
|
||||
conf->dns.replyWhenBusy.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.blockTTL.k = "dns.blockTTL";
|
||||
conf->dns.blockTTL.h = "FTL's internal TTL to be handed out for blocked queries in seconds. This settings allows users to select a value different from the dnsmasq config option local-ttl. This is useful in context of locally used hostnames that are known to stay constant over long times (printers, etc.).\n Note that large values may render whitelisting ineffective due to client-side caching of blocked queries.";
|
||||
conf->dns.blockTTL.t = CONF_UINT;
|
||||
conf->dns.blockTTL.f = FLAG_ADVANCED_SETTING;
|
||||
conf->dns.blockTTL.d.ui = 2;
|
||||
conf->dns.blockTTL.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.hosts.k = "dns.hosts";
|
||||
conf->dns.hosts.h = "Array of custom DNS records\n Example: hosts = [ \"127.0.0.1 mylocal\", \"192.168.0.1 therouter\" ]";
|
||||
|
@ -469,18 +481,21 @@ void initConfig(struct config *conf)
|
|||
conf->dns.hosts.t = CONF_JSON_STRING_ARRAY;
|
||||
conf->dns.hosts.f = FLAG_ADVANCED_SETTING;
|
||||
conf->dns.hosts.d.json = cJSON_CreateArray();
|
||||
conf->dns.hosts.c = validate_dns_hosts;
|
||||
|
||||
conf->dns.domainNeeded.k = "dns.domainNeeded";
|
||||
conf->dns.domainNeeded.h = "If set, A and AAAA queries for plain names, without dots or domain parts, are never forwarded to upstream nameservers";
|
||||
conf->dns.domainNeeded.t = CONF_BOOL;
|
||||
conf->dns.domainNeeded.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING;
|
||||
conf->dns.domainNeeded.d.b = false;
|
||||
conf->dns.domainNeeded.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.expandHosts.k = "dns.expandHosts";
|
||||
conf->dns.expandHosts.h = "If set, the domain is added to simple names (without a period) in /etc/hosts in the same way as for DHCP-derived names";
|
||||
conf->dns.expandHosts.t = CONF_BOOL;
|
||||
conf->dns.expandHosts.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING;
|
||||
conf->dns.expandHosts.d.b = false;
|
||||
conf->dns.expandHosts.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.domain.k = "dns.domain";
|
||||
conf->dns.domain.h = "The DNS domain used by your Pi-hole to expand hosts and for DHCP.\n\n Only if DHCP is enabled below: For DHCP, this has two effects; firstly it causes the DHCP server to return the domain to any hosts which request it, and secondly it sets the domain which it is legal for DHCP-configured hosts to claim. The intention is to constrain hostnames so that an untrusted host on the LAN cannot advertise its name via DHCP as e.g. \"google.com\" and capture traffic not meant for it. If no domain suffix is specified, then any DHCP hostname with a domain part (ie with a period) will be disallowed and logged. If a domain is specified, then hostnames with a domain part are allowed, provided the domain part matches the suffix. In addition, when a suffix is set then hostnames without a domain part have the suffix added as an optional domain part. For instance, we can set domain=mylab.com and have a machine whose DHCP hostname is \"laptop\". The IP address for that machine is available both as \"laptop\" and \"laptop.mylab.com\".\n\n You can disable setting a domain by setting this option to an empty string.";
|
||||
|
@ -488,17 +503,20 @@ void initConfig(struct config *conf)
|
|||
conf->dns.domain.t = CONF_STRING;
|
||||
conf->dns.domain.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING;
|
||||
conf->dns.domain.d.s = (char*)"lan";
|
||||
conf->dns.domain.c = validate_domain;
|
||||
|
||||
conf->dns.bogusPriv.k = "dns.bogusPriv";
|
||||
conf->dns.bogusPriv.h = "Should all reverse lookups for private IP ranges (i.e., 192.168.x.y, etc) which are not found in /etc/hosts or the DHCP leases file be answered with \"no such domain\" rather than being forwarded upstream?";
|
||||
conf->dns.bogusPriv.t = CONF_BOOL;
|
||||
conf->dns.bogusPriv.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING;
|
||||
conf->dns.bogusPriv.d.b = true;
|
||||
conf->dns.bogusPriv.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.dnssec.k = "dns.dnssec";
|
||||
conf->dns.dnssec.h = "Validate DNS replies using DNSSEC?";
|
||||
conf->dns.dnssec.t = CONF_BOOL;
|
||||
conf->dns.dnssec.f = FLAG_RESTART_FTL;
|
||||
conf->dns.dnssec.c = validate_stub; // Only type-based checking
|
||||
conf->dns.dnssec.d.b = false;
|
||||
|
||||
conf->dns.interface.k = "dns.interface";
|
||||
|
@ -507,6 +525,7 @@ void initConfig(struct config *conf)
|
|||
conf->dns.interface.t = CONF_STRING;
|
||||
conf->dns.interface.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING;
|
||||
conf->dns.interface.d.s = (char*)"";
|
||||
conf->dns.interface.c = validate_stub; // Type-based checking + dnsmasq syntax checking
|
||||
|
||||
conf->dns.hostRecord.k = "dns.hostRecord";
|
||||
conf->dns.hostRecord.h = "Add A, AAAA and PTR records to the DNS. This adds one or more names to the DNS with associated IPv4 (A) and IPv6 (AAAA) records";
|
||||
|
@ -514,6 +533,7 @@ void initConfig(struct config *conf)
|
|||
conf->dns.hostRecord.t = CONF_STRING;
|
||||
conf->dns.hostRecord.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING;
|
||||
conf->dns.hostRecord.d.s = (char*)"";
|
||||
conf->dns.hostRecord.c = validate_stub; // Type-based checking + dnsmasq syntax checking
|
||||
|
||||
conf->dns.listeningMode.k = "dns.listeningMode";
|
||||
conf->dns.listeningMode.h = "Pi-hole interface listening modes";
|
||||
|
@ -531,12 +551,14 @@ void initConfig(struct config *conf)
|
|||
conf->dns.listeningMode.t = CONF_ENUM_LISTENING_MODE;
|
||||
conf->dns.listeningMode.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING;
|
||||
conf->dns.listeningMode.d.listeningMode = LISTEN_LOCAL;
|
||||
conf->dns.listeningMode.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.queryLogging.k = "dns.queryLogging";
|
||||
conf->dns.queryLogging.h = "Log DNS queries and replies to pihole.log";
|
||||
conf->dns.queryLogging.t = CONF_BOOL;
|
||||
conf->dns.queryLogging.f = FLAG_RESTART_FTL;
|
||||
conf->dns.queryLogging.d.b = true;
|
||||
conf->dns.queryLogging.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.cnameRecords.k = "dns.cnameRecords";
|
||||
conf->dns.cnameRecords.h = "List of CNAME records which indicate that <cname> is really <target>. If the <TTL> is given, it overwrites the value of local-ttl";
|
||||
|
@ -544,12 +566,14 @@ void initConfig(struct config *conf)
|
|||
conf->dns.cnameRecords.t = CONF_JSON_STRING_ARRAY;
|
||||
conf->dns.cnameRecords.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING;
|
||||
conf->dns.cnameRecords.d.json = cJSON_CreateArray();
|
||||
conf->dns.cnameRecords.c = validate_dns_cnames;
|
||||
|
||||
conf->dns.port.k = "dns.port";
|
||||
conf->dns.port.h = "Port used by the DNS server";
|
||||
conf->dns.port.t = CONF_UINT16;
|
||||
conf->dns.port.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING;
|
||||
conf->dns.port.d.ui = 53u;
|
||||
conf->dns.port.c = validate_stub; // Only type-based checking
|
||||
|
||||
// sub-struct dns.cache
|
||||
conf->dns.cache.size.k = "dns.cache.size";
|
||||
|
@ -557,18 +581,21 @@ void initConfig(struct config *conf)
|
|||
conf->dns.cache.size.t = CONF_UINT;
|
||||
conf->dns.cache.size.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING;
|
||||
conf->dns.cache.size.d.ui = 10000u;
|
||||
conf->dns.cache.size.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.cache.optimizer.k = "dns.cache.optimizer";
|
||||
conf->dns.cache.optimizer.h = "Query cache optimizer: If a DNS name exists in the cache, but its time-to-live has expired only recently, the data will be used anyway (a refreshing from upstream is triggered). This can improve DNS query delays especially over unreliable Internet connections. This feature comes at the expense of possibly sometimes returning out-of-date data and less efficient cache utilization, since old data cannot be flushed when its TTL expires, so the cache becomes mostly least-recently-used. To mitigate issues caused by massively outdated DNS replies, the maximum overaging of cached records is limited. We strongly recommend staying below 86400 (1 day) with this option.\n Setting the TTL excess time to zero will serve stale cache data regardless how long it has expired. This is not recommended as it may lead to stale data being served for a long time. Setting this option to any negative value will disable this feature altogether.";
|
||||
conf->dns.cache.optimizer.t = CONF_INT;
|
||||
conf->dns.cache.optimizer.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING;
|
||||
conf->dns.cache.optimizer.d.i = 3600u;
|
||||
conf->dns.cache.optimizer.c = validate_stub; // Only type-based checking
|
||||
|
||||
// sub-struct dns.blocking
|
||||
conf->dns.blocking.active.k = "dns.blocking.active";
|
||||
conf->dns.blocking.active.h = "Should FTL block queries?";
|
||||
conf->dns.blocking.active.t = CONF_BOOL;
|
||||
conf->dns.blocking.active.d.b = true;
|
||||
conf->dns.blocking.active.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.blocking.mode.k = "dns.blocking.mode";
|
||||
conf->dns.blocking.mode.h = "How should FTL reply to blocked queries?";
|
||||
|
@ -585,12 +612,14 @@ void initConfig(struct config *conf)
|
|||
}
|
||||
conf->dns.blocking.mode.t = CONF_ENUM_BLOCKING_MODE;
|
||||
conf->dns.blocking.mode.d.blocking_mode = MODE_NULL;
|
||||
conf->dns.blocking.mode.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.revServers.k = "dns.revServers";
|
||||
conf->dns.revServers.h = "Reverse server (former also called \"conditional forwarding\") feature\n Array of reverse servers each one in one of the following forms: \"<enabled>,<ip-address>[/<prefix-len>],<server>[#<port>],<domain>\"\n\n Individual components:\n\n <enabled>: either \"true\" or \"false\"\n\n <ip-address>[/<prefix-len>]: Address range for the reverse server feature in CIDR notation. If the prefix length is omitted, either 32 (IPv4) or 128 (IPv6) are substituted (exact address match). This is almost certainly not what you want here.\n Example: \"192.168.0.0/24\" for the range 192.168.0.1 - 192.168.0.255\n\n <server>[#<port>]: Target server to be used for the reverse server feature\n Example: \"192.168.0.1#53\"\n\n <domain>: Domain used for the reverse server feature (e.g., \"fritz.box\")\n Example: \"fritz.box\"";
|
||||
conf->dns.revServers.a = cJSON_CreateStringReference("array of reverse servers each one in one of the following forms: \"<enabled>,<ip-address>[/<prefix-len>],<server>[#<port>],<domain>\", e.g., \"true,192.168.0.0/24,192.168.0.1,fritz.box\"");
|
||||
conf->dns.revServers.t = CONF_JSON_STRING_ARRAY;
|
||||
conf->dns.revServers.d.json = cJSON_CreateArray();
|
||||
conf->dns.revServers.c = validate_dns_revServers;
|
||||
conf->dns.revServers.f = FLAG_RESTART_FTL;
|
||||
|
||||
// sub-struct dns.rate_limit
|
||||
|
@ -598,22 +627,26 @@ void initConfig(struct config *conf)
|
|||
conf->dns.rateLimit.count.h = "Rate-limited queries are answered with a REFUSED reply and not further processed by FTL.\n The default settings for FTL's rate-limiting are to permit no more than 1000 queries in 60 seconds. Both numbers can be customized independently. It is important to note that rate-limiting is happening on a per-client basis. Other clients can continue to use FTL while rate-limited clients are short-circuited at the same time.\n For this setting, both numbers, the maximum number of queries within a given time, and the length of the time interval (seconds) have to be specified. For instance, if you want to set a rate limit of 1 query per hour, the option should look like RATE_LIMIT=1/3600. The time interval is relative to when FTL has finished starting (start of the daemon + possible delay by DELAY_STARTUP) then it will advance in steps of the rate-limiting interval. If a client reaches the maximum number of queries it will be blocked until the end of the current interval. This will be logged to /var/log/pihole/FTL.log, e.g. Rate-limiting 10.0.1.39 for at least 44 seconds. If the client continues to send queries while being blocked already and this number of queries during the blocking exceeds the limit the client will continue to be blocked until the end of the next interval (FTL.log will contain lines like Still rate-limiting 10.0.1.39 as it made additional 5007 queries). As soon as the client requests less than the set limit, it will be unblocked (Ending rate-limitation of 10.0.1.39).\n Rate-limiting may be disabled altogether by setting both values to zero (this results in the same behavior as before FTL v5.7).\n How many queries are permitted...";
|
||||
conf->dns.rateLimit.count.t = CONF_UINT;
|
||||
conf->dns.rateLimit.count.d.ui = 1000;
|
||||
conf->dns.rateLimit.count.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.rateLimit.interval.k = "dns.rateLimit.interval";
|
||||
conf->dns.rateLimit.interval.h = "... in the set interval before rate-limiting?";
|
||||
conf->dns.rateLimit.interval.t = CONF_UINT;
|
||||
conf->dns.rateLimit.interval.d.ui = 60;
|
||||
conf->dns.rateLimit.interval.c = validate_stub; // Only type-based checking
|
||||
|
||||
// sub-struct dns.special_domains
|
||||
conf->dns.specialDomains.mozillaCanary.k = "dns.specialDomains.mozillaCanary";
|
||||
conf->dns.specialDomains.mozillaCanary.h = "Should Pi-hole always replies with NXDOMAIN to A and AAAA queries of use-application-dns.net to disable Firefox automatic DNS-over-HTTP? This is following the recommendation on https://support.mozilla.org/en-US/kb/configuring-networks-disable-dns-over-https";
|
||||
conf->dns.specialDomains.mozillaCanary.t = CONF_BOOL;
|
||||
conf->dns.specialDomains.mozillaCanary.d.b = true;
|
||||
conf->dns.specialDomains.mozillaCanary.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.specialDomains.iCloudPrivateRelay.k = "dns.specialDomains.iCloudPrivateRelay";
|
||||
conf->dns.specialDomains.iCloudPrivateRelay.h = "Should Pi-hole always replies with NXDOMAIN to A and AAAA queries of mask.icloud.com and mask-h2.icloud.com to disable Apple's iCloud Private Relay to prevent Apple devices from bypassing Pi-hole? This is following the recommendation on https://developer.apple.com/support/prepare-your-network-for-icloud-private-relay";
|
||||
conf->dns.specialDomains.iCloudPrivateRelay.t = CONF_BOOL;
|
||||
conf->dns.specialDomains.iCloudPrivateRelay.d.b = true;
|
||||
conf->dns.specialDomains.iCloudPrivateRelay.c = validate_stub; // Only type-based checking
|
||||
|
||||
// sub-struct dns.reply_addr
|
||||
conf->dns.reply.host.force4.k = "dns.reply.host.force4";
|
||||
|
@ -621,6 +654,7 @@ void initConfig(struct config *conf)
|
|||
conf->dns.reply.host.force4.t = CONF_BOOL;
|
||||
conf->dns.reply.host.force4.f = FLAG_ADVANCED_SETTING;
|
||||
conf->dns.reply.host.force4.d.b = false;
|
||||
conf->dns.reply.host.force4.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.reply.host.v4.k = "dns.reply.host.IPv4";
|
||||
conf->dns.reply.host.v4.h = "Custom IPv4 address for the Pi-hole host";
|
||||
|
@ -628,12 +662,14 @@ void initConfig(struct config *conf)
|
|||
conf->dns.reply.host.v4.t = CONF_STRUCT_IN_ADDR;
|
||||
conf->dns.reply.host.v4.f = FLAG_ADVANCED_SETTING;
|
||||
memset(&conf->dns.reply.host.v4.d.in_addr, 0, sizeof(struct in_addr));
|
||||
conf->dns.reply.host.v4.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.reply.host.force6.k = "dns.reply.host.force6";
|
||||
conf->dns.reply.host.force6.h = "Use a specific IPv6 address for the Pi-hole host? See description for the IPv4 variant above for further details.";
|
||||
conf->dns.reply.host.force6.t = CONF_BOOL;
|
||||
conf->dns.reply.host.force6.f = FLAG_ADVANCED_SETTING;
|
||||
conf->dns.reply.host.force6.d.b = false;
|
||||
conf->dns.reply.host.force6.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.reply.host.v6.k = "dns.reply.host.IPv6";
|
||||
conf->dns.reply.host.v6.h = "Custom IPv6 address for the Pi-hole host";
|
||||
|
@ -641,12 +677,14 @@ void initConfig(struct config *conf)
|
|||
conf->dns.reply.host.v6.t = CONF_STRUCT_IN6_ADDR;
|
||||
conf->dns.reply.host.v6.f = FLAG_ADVANCED_SETTING;
|
||||
memset(&conf->dns.reply.host.v6.d.in6_addr, 0, sizeof(struct in6_addr));
|
||||
conf->dns.reply.host.v6.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.reply.blocking.force4.k = "dns.reply.blocking.force4";
|
||||
conf->dns.reply.blocking.force4.h = "Use a specific IPv4 address in IP blocking mode? By default, FTL determines the address of the interface a query arrived on and uses this address for replying to A queries with the most suitable address for the requesting client. This setting can be used to use a fixed, rather than the dynamically obtained, address when Pi-hole responds in the following cases: IP blocking mode is used and this query is to be blocked, regular expressions with the ;reply=IP regex extension.";
|
||||
conf->dns.reply.blocking.force4.t = CONF_BOOL;
|
||||
conf->dns.reply.blocking.force4.f = FLAG_ADVANCED_SETTING;
|
||||
conf->dns.reply.blocking.force4.d.b = false;
|
||||
conf->dns.reply.blocking.force4.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.reply.blocking.v4.k = "dns.reply.blocking.IPv4";
|
||||
conf->dns.reply.blocking.v4.h = "Custom IPv4 address for IP blocking mode";
|
||||
|
@ -654,12 +692,14 @@ void initConfig(struct config *conf)
|
|||
conf->dns.reply.blocking.v4.t = CONF_STRUCT_IN_ADDR;
|
||||
conf->dns.reply.blocking.v4.f = FLAG_ADVANCED_SETTING;
|
||||
memset(&conf->dns.reply.blocking.v4.d.in_addr, 0, sizeof(struct in_addr));
|
||||
conf->dns.reply.blocking.v4.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.reply.blocking.force6.k = "dns.reply.blocking.force6";
|
||||
conf->dns.reply.blocking.force6.h = "Use a specific IPv6 address in IP blocking mode? See description for the IPv4 variant above for further details.";
|
||||
conf->dns.reply.blocking.force6.t = CONF_BOOL;
|
||||
conf->dns.reply.blocking.force6.f = FLAG_ADVANCED_SETTING;
|
||||
conf->dns.reply.blocking.force6.d.b = false;
|
||||
conf->dns.reply.blocking.force6.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dns.reply.blocking.v6.k = "dns.reply.blocking.IPv6";
|
||||
conf->dns.reply.blocking.v6.h = "Custom IPv6 address for IP blocking mode";
|
||||
|
@ -667,6 +707,7 @@ void initConfig(struct config *conf)
|
|||
conf->dns.reply.blocking.v6.t = CONF_STRUCT_IN6_ADDR;
|
||||
conf->dns.reply.blocking.v6.f = FLAG_ADVANCED_SETTING;
|
||||
memset(&conf->dns.reply.blocking.v6.d.in6_addr, 0, sizeof(struct in6_addr));
|
||||
conf->dns.reply.blocking.v6.c = validate_stub; // Only type-based checking
|
||||
|
||||
// sub-struct dhcp
|
||||
conf->dhcp.active.k = "dhcp.active";
|
||||
|
@ -674,6 +715,7 @@ void initConfig(struct config *conf)
|
|||
conf->dhcp.active.t = CONF_BOOL;
|
||||
conf->dhcp.active.f = FLAG_RESTART_FTL;
|
||||
conf->dhcp.active.d.b = false;
|
||||
conf->dhcp.active.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dhcp.start.k = "dhcp.start";
|
||||
conf->dhcp.start.h = "Start address of the DHCP address pool";
|
||||
|
@ -681,6 +723,7 @@ void initConfig(struct config *conf)
|
|||
conf->dhcp.start.t = CONF_STRUCT_IN_ADDR;
|
||||
conf->dhcp.start.f = FLAG_RESTART_FTL;
|
||||
memset(&conf->dhcp.start.d.in_addr, 0, sizeof(struct in_addr));
|
||||
conf->dhcp.start.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dhcp.end.k = "dhcp.end";
|
||||
conf->dhcp.end.h = "End address of the DHCP address pool";
|
||||
|
@ -688,6 +731,7 @@ void initConfig(struct config *conf)
|
|||
conf->dhcp.end.t = CONF_STRUCT_IN_ADDR;
|
||||
conf->dhcp.end.f = FLAG_RESTART_FTL;
|
||||
memset(&conf->dhcp.end.d.in_addr, 0, sizeof(struct in_addr));
|
||||
conf->dhcp.end.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dhcp.router.k = "dhcp.router";
|
||||
conf->dhcp.router.h = "Address of the gateway to be used (typically the address of your router in a home installation)";
|
||||
|
@ -695,6 +739,7 @@ void initConfig(struct config *conf)
|
|||
conf->dhcp.router.t = CONF_STRUCT_IN_ADDR;
|
||||
conf->dhcp.router.f = FLAG_RESTART_FTL;
|
||||
memset(&conf->dhcp.router.d.in_addr, 0, sizeof(struct in_addr));
|
||||
conf->dhcp.router.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dhcp.netmask.k = "dhcp.netmask";
|
||||
conf->dhcp.netmask.h = "The netmask used by your Pi-hole. For directly connected networks (i.e., networks on which the machine running Pi-hole has an interface) the netmask is optional and may be set to an empty string (\"\"): it will then be determined from the interface configuration itself. For networks which receive DHCP service via a relay agent, we cannot determine the netmask itself, so it should explicitly be specified, otherwise Pi-hole guesses based on the class (A, B or C) of the network address.";
|
||||
|
@ -702,6 +747,7 @@ void initConfig(struct config *conf)
|
|||
conf->dhcp.netmask.t = CONF_STRUCT_IN_ADDR;
|
||||
conf->dhcp.netmask.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING;
|
||||
memset(&conf->dhcp.netmask.d.in_addr, 0, sizeof(struct in_addr));
|
||||
conf->dhcp.netmask.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dhcp.leaseTime.k = "dhcp.leaseTime";
|
||||
conf->dhcp.leaseTime.h = "If the lease time is given, then leases will be given for that length of time. If not given, the default lease time is one hour for IPv4 and one day for IPv6.";
|
||||
|
@ -709,24 +755,28 @@ void initConfig(struct config *conf)
|
|||
conf->dhcp.leaseTime.t = CONF_STRING;
|
||||
conf->dhcp.leaseTime.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING;
|
||||
conf->dhcp.leaseTime.d.s = (char*)"";
|
||||
conf->dhcp.leaseTime.c = validate_stub; // Type-based checking + dnsmasq syntax checking
|
||||
|
||||
conf->dhcp.ipv6.k = "dhcp.ipv6";
|
||||
conf->dhcp.ipv6.h = "Should Pi-hole make an attempt to also satisfy IPv6 address requests (be aware that IPv6 works a whole lot different than IPv4)";
|
||||
conf->dhcp.ipv6.t = CONF_BOOL;
|
||||
conf->dhcp.ipv6.f = FLAG_RESTART_FTL;
|
||||
conf->dhcp.ipv6.d.b = false;
|
||||
conf->dhcp.ipv6.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dhcp.multiDNS.k = "dhcp.multiDNS";
|
||||
conf->dhcp.multiDNS.h = "Advertise DNS server multiple times to clients. Some devices will add their own proprietary DNS servers to the list of DNS servers, which can cause issues with Pi-hole. This option will advertise the Pi-hole DNS server multiple times to clients, which should prevent this from happening.";
|
||||
conf->dhcp.multiDNS.t = CONF_BOOL;
|
||||
conf->dhcp.multiDNS.f = FLAG_RESTART_FTL;
|
||||
conf->dhcp.multiDNS.d.b = false;
|
||||
conf->dhcp.multiDNS.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dhcp.rapidCommit.k = "dhcp.rapidCommit";
|
||||
conf->dhcp.rapidCommit.h = "Enable DHCPv4 Rapid Commit Option specified in RFC 4039. Should only be enabled if either the server is the only server for the subnet to avoid conflicts";
|
||||
conf->dhcp.rapidCommit.t = CONF_BOOL;
|
||||
conf->dhcp.rapidCommit.f = FLAG_RESTART_FTL;
|
||||
conf->dhcp.rapidCommit.d.b = false;
|
||||
conf->dhcp.rapidCommit.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->dhcp.hosts.k = "dhcp.hosts";
|
||||
conf->dhcp.hosts.h = "Per host parameters for the DHCP server. This allows a machine with a particular hardware address to be always allocated the same hostname, IP address and lease time or to specify static DHCP leases";
|
||||
|
@ -734,6 +784,7 @@ void initConfig(struct config *conf)
|
|||
conf->dhcp.hosts.t = CONF_JSON_STRING_ARRAY;
|
||||
conf->dhcp.hosts.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING;
|
||||
conf->dhcp.hosts.d.json = cJSON_CreateArray();
|
||||
conf->dhcp.hosts.c = validate_stub; // Type-based checking + dnsmasq syntax checking
|
||||
|
||||
|
||||
// struct resolver
|
||||
|
@ -741,17 +792,20 @@ void initConfig(struct config *conf)
|
|||
conf->resolver.resolveIPv6.h = "Should FTL try to resolve IPv6 addresses to hostnames?";
|
||||
conf->resolver.resolveIPv6.t = CONF_BOOL;
|
||||
conf->resolver.resolveIPv6.d.b = true;
|
||||
conf->resolver.resolveIPv6.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->resolver.resolveIPv4.k = "resolver.resolveIPv4";
|
||||
conf->resolver.resolveIPv4.h = "Should FTL try to resolve IPv4 addresses to hostnames?";
|
||||
conf->resolver.resolveIPv4.t = CONF_BOOL;
|
||||
conf->resolver.resolveIPv4.d.b = true;
|
||||
conf->resolver.resolveIPv4.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->resolver.networkNames.k = "resolver.networkNames";
|
||||
conf->resolver.networkNames.h = "Control whether FTL should use the fallback option to try to obtain client names from checking the network table. This behavior can be disabled with this option.\n Assume an IPv6 client without a host names. However, the network table knows - though the client's MAC address - that this is the same device where we have a host name for another IP address (e.g., a DHCP server managed IPv4 address). In this case, we use the host name associated to the other address as this is the same device.";
|
||||
conf->resolver.networkNames.t = CONF_BOOL;
|
||||
conf->resolver.networkNames.f = FLAG_ADVANCED_SETTING;
|
||||
conf->resolver.networkNames.d.b = true;
|
||||
conf->resolver.networkNames.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->resolver.refreshNames.k = "resolver.refreshNames";
|
||||
conf->resolver.refreshNames.h = "With this option, you can change how (and if) hourly PTR requests are made to check for changes in client and upstream server hostnames.";
|
||||
|
@ -768,6 +822,7 @@ void initConfig(struct config *conf)
|
|||
conf->resolver.refreshNames.t = CONF_ENUM_REFRESH_HOSTNAMES;
|
||||
conf->resolver.refreshNames.f = FLAG_ADVANCED_SETTING;
|
||||
conf->resolver.refreshNames.d.refresh_hostnames = REFRESH_IPV4_ONLY;
|
||||
conf->resolver.refreshNames.c = validate_stub; // Only type-based checking
|
||||
|
||||
|
||||
// struct database
|
||||
|
@ -775,21 +830,43 @@ void initConfig(struct config *conf)
|
|||
conf->database.DBimport.h = "Should FTL load information from the database on startup to be aware of the most recent history?";
|
||||
conf->database.DBimport.t = CONF_BOOL;
|
||||
conf->database.DBimport.d.b = true;
|
||||
conf->database.DBimport.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->database.DBexport.k = "database.DBexport";
|
||||
conf->database.DBexport.h = "Should FTL store queries in the long-term database?";
|
||||
conf->database.DBexport.t = CONF_BOOL;
|
||||
conf->database.DBexport.d.b = true;
|
||||
conf->database.DBexport.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->database.maxDBdays.k = "database.maxDBdays";
|
||||
conf->database.maxDBdays.h = "How long should queries be stored in the database [days]?";
|
||||
conf->database.maxDBdays.t = CONF_INT;
|
||||
conf->database.maxDBdays.d.i = (365/4);
|
||||
conf->database.maxDBdays.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->database.DBinterval.k = "database.DBinterval";
|
||||
conf->database.DBinterval.h = "How often do we store queries in FTL's database [seconds]?";
|
||||
conf->database.DBinterval.t = CONF_UINT;
|
||||
conf->database.DBinterval.d.ui = 60;
|
||||
conf->database.DBinterval.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->database.useWAL.k = "database.useWAL";
|
||||
conf->database.useWAL.h = "Should FTL enable Write-Ahead Log (WAL) mode for the on-disk query database (configured via files.database)?\n It is recommended to leave this setting enabled for performance reasons. About the only reason to disable WAL mode is if you are experiencing specific issues with it, e.g., when using a database that is accessed from multiple hosts via a network share. When this setting is disabled, FTL will use SQLite3's default journal mode (rollback journal in DELETE mode).";
|
||||
conf->database.useWAL.t = CONF_BOOL;
|
||||
// Note: We would not necessarily need to restart FTL when this setting
|
||||
// is changed, but we do it anyway as this ensures the database is
|
||||
// properly re-initialized and the new journal mode is used. As this is
|
||||
// a setting that will be changed very rarely, this seems the better
|
||||
// compromise than adding special code that can transform the database
|
||||
// while being active.
|
||||
// The in-memory database is not affected by this setting as it uses a
|
||||
// MEMORY journal mode anyway (there is nothing to be restored after power
|
||||
// loss). The gravity database is also not affected as it is only written
|
||||
// to on an individual basis (explicit API calls) and not continuously
|
||||
// (like the query database).
|
||||
conf->database.useWAL.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL;
|
||||
conf->database.useWAL.d.b = true;
|
||||
conf->database.useWAL.c = validate_stub; // Only type-based checking
|
||||
|
||||
// sub-struct database.network
|
||||
conf->database.network.parseARPcache.k = "database.network.parseARPcache";
|
||||
|
@ -797,12 +874,14 @@ void initConfig(struct config *conf)
|
|||
conf->database.network.parseARPcache.t = CONF_BOOL;
|
||||
conf->database.network.parseARPcache.f = FLAG_ADVANCED_SETTING;
|
||||
conf->database.network.parseARPcache.d.b = true;
|
||||
conf->database.network.parseARPcache.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->database.network.expire.k = "database.network.expire";
|
||||
conf->database.network.expire.h = "How long should IP addresses be kept in the network_addresses table [days]? IP addresses (and associated host names) older than the specified number of days are removed to avoid dead entries in the network overview table.";
|
||||
conf->database.network.expire.t = CONF_UINT;
|
||||
conf->database.network.expire.f = FLAG_ADVANCED_SETTING;
|
||||
conf->database.network.expire.d.ui = conf->database.maxDBdays.d.ui;
|
||||
conf->database.network.expire.c = validate_stub; // Only type-based checking
|
||||
|
||||
|
||||
// struct http
|
||||
|
@ -812,6 +891,7 @@ void initConfig(struct config *conf)
|
|||
conf->webserver.domain.t = CONF_STRING;
|
||||
conf->webserver.domain.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL;
|
||||
conf->webserver.domain.d.s = (char*)"pi.hole";
|
||||
conf->webserver.domain.c = validate_domain;
|
||||
|
||||
conf->webserver.acl.k = "webserver.acl";
|
||||
conf->webserver.acl.h = "Webserver access control list (ACL) allowing for restrictions to be put on the list of IP addresses which have access to the web server. The ACL is a comma separated list of IP subnets, where each subnet is prepended by either a - or a + sign. A plus sign means allow, where a minus sign means deny. If a subnet mask is omitted, such as -1.2.3.4, this means to deny only that single IP address. If this value is not set (empty string), all accesses are allowed. Otherwise, the default setting is to deny all accesses. On each request the full list is traversed, and the last (!) match wins. IPv6 addresses may be specified in CIDR-form [a:b::c]/64.\n\n Example 1: acl = \"+127.0.0.1,+[::1]\"\n ---> deny all access, except from 127.0.0.1 and ::1,\n Example 2: acl = \"+192.168.0.0/16\"\n ---> deny all accesses, except from the 192.168.0.0/16 subnet,\n Example 3: acl = \"+[::]/0\" ---> allow only IPv6 access.";
|
||||
|
@ -819,6 +899,7 @@ void initConfig(struct config *conf)
|
|||
conf->webserver.acl.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL;
|
||||
conf->webserver.acl.t = CONF_STRING;
|
||||
conf->webserver.acl.d.s = (char*)"";
|
||||
conf->webserver.acl.c = validate_stub; // Type-based checking + civetweb syntax checking
|
||||
|
||||
conf->webserver.port.k = "webserver.port";
|
||||
conf->webserver.port.h = "Ports to be used by the webserver.\n Comma-separated list of ports to listen on. It is possible to specify an IP address to bind to. In this case, an IP address and a colon must be prepended to the port number. For example, to bind to the loopback interface on port 80 (IPv4) and to all interfaces port 8080 (IPv4), use \"127.0.0.1:80,8080\". \"[::]:80\" can be used to listen to IPv6 connections to port 80. IPv6 addresses of network interfaces can be specified as well, e.g. \"[::1]:80\" for the IPv6 loopback interface. [::]:80 will bind to port 80 IPv6 only.\n In order to use port 80 for all interfaces, both IPv4 and IPv6, use either the configuration \"80,[::]:80\" (create one socket for IPv4 and one for IPv6 only), or \"+80\" (create one socket for both, IPv4 and IPv6). The + notation to use IPv4 and IPv6 will only work if no network interface is specified. Depending on your operating system version and IPv6 network environment, some configurations might not work as expected, so you have to test to find the configuration most suitable for your needs. In case \"+80\" does not work for your environment, you need to use \"80,[::]:80\".\n If the port is TLS/SSL, a letter 's' must be appended, for example, \"80,443s\" will open port 80 and port 443, and connections on port 443 will be encrypted. For non-encrypted ports, it is allowed to append letter 'r' (as in redirect). Redirected ports will redirect all their traffic to the first configured SSL port. For example, if webserver.port is \"80r,443s\", then all HTTP traffic coming at port 80 will be redirected to HTTPS port 443. If this value is not set (empty string), the web server will not be started and, hence, the API will not be available.";
|
||||
|
@ -826,12 +907,14 @@ void initConfig(struct config *conf)
|
|||
conf->webserver.port.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL;
|
||||
conf->webserver.port.t = CONF_STRING;
|
||||
conf->webserver.port.d.s = (char*)"80,[::]:80,443s,[::]:443s";
|
||||
conf->webserver.port.c = validate_stub; // Type-based checking + civetweb syntax checking
|
||||
|
||||
conf->webserver.tls.rev_proxy.k = "webserver.tls.rev_proxy";
|
||||
conf->webserver.tls.rev_proxy.h = "Is Pi-hole running behind a reverse proxy? If yes, Pi-hole will not consider HTTP-only connections being insecure. This is useful if you are running Pi-hole in a trusted environment, for example, in a local network, and you are using a reverse proxy to provide TLS encryption, e.g., by using Traefik (docker). If you are using a reverse proxy, you can alternatively set webserver.tls.cert to the path of the TLS certificate file and let Pi-hole handle true end-to-end encryption.";
|
||||
conf->webserver.tls.rev_proxy.f = FLAG_ADVANCED_SETTING;
|
||||
conf->webserver.tls.rev_proxy.t = CONF_BOOL;
|
||||
conf->webserver.tls.rev_proxy.d.b = false;
|
||||
conf->webserver.tls.rev_proxy.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->webserver.tls.cert.k = "webserver.tls.cert";
|
||||
conf->webserver.tls.cert.h = "Path to the TLS (SSL) certificate file. This option is only required when at least one of webserver.port is TLS. The file must be in PEM format, and it must have both, private key and certificate (the *.pem file created must contain a 'CERTIFICATE' section as well as a 'RSA PRIVATE KEY' section).\n The *.pem file can be created using\n cp server.crt server.pem\n cat server.key >> server.pem\n if you have these files instead";
|
||||
|
@ -839,16 +922,19 @@ void initConfig(struct config *conf)
|
|||
conf->webserver.tls.cert.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL;
|
||||
conf->webserver.tls.cert.t = CONF_STRING;
|
||||
conf->webserver.tls.cert.d.s = (char*)"/etc/pihole/tls.pem";
|
||||
conf->webserver.tls.cert.c = validate_filepath;
|
||||
|
||||
conf->webserver.session.timeout.k = "webserver.session.timeout";
|
||||
conf->webserver.session.timeout.h = "Session timeout in seconds. If a session is inactive for more than this time, it will be terminated. Sessions are continuously refreshed by the web interface, preventing sessions from timing out while the web interface is open.\n This option may also be used to make logins persistent for long times, e.g. 86400 seconds (24 hours), 604800 seconds (7 days) or 2592000 seconds (30 days). Note that the total number of concurrent sessions is limited so setting this value too high may result in users being rejected and unable to log in if there are already too many sessions active.";
|
||||
conf->webserver.session.timeout.t = CONF_UINT;
|
||||
conf->webserver.session.timeout.d.ui = 1800u;
|
||||
conf->webserver.session.timeout.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->webserver.session.restore.k = "webserver.session.restore";
|
||||
conf->webserver.session.restore.h = "Should Pi-hole backup and restore sessions from the database? This is useful if you want to keep your sessions after a restart of the web interface.";
|
||||
conf->webserver.session.restore.t = CONF_BOOL;
|
||||
conf->webserver.session.restore.d.b = true;
|
||||
conf->webserver.session.restore.c = validate_stub; // Only type-based checking
|
||||
|
||||
// sub-struct paths
|
||||
conf->webserver.paths.webroot.k = "webserver.paths.webroot";
|
||||
|
@ -857,6 +943,7 @@ void initConfig(struct config *conf)
|
|||
conf->webserver.paths.webroot.t = CONF_STRING;
|
||||
conf->webserver.paths.webroot.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL;
|
||||
conf->webserver.paths.webroot.d.s = (char*)"/var/www/html";
|
||||
conf->webserver.paths.webroot.c = validate_filepath;
|
||||
|
||||
conf->webserver.paths.webhome.k = "webserver.paths.webhome";
|
||||
conf->webserver.paths.webhome.h = "Sub-directory of the root containing the web interface";
|
||||
|
@ -864,12 +951,14 @@ void initConfig(struct config *conf)
|
|||
conf->webserver.paths.webhome.t = CONF_STRING;
|
||||
conf->webserver.paths.webhome.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL;
|
||||
conf->webserver.paths.webhome.d.s = (char*)"/admin/";
|
||||
conf->webserver.paths.webhome.c = validate_filepath;
|
||||
|
||||
// sub-struct interface
|
||||
conf->webserver.interface.boxed.k = "webserver.interface.boxed";
|
||||
conf->webserver.interface.boxed.h = "Should the web interface use the boxed layout?";
|
||||
conf->webserver.interface.boxed.t = CONF_BOOL;
|
||||
conf->webserver.interface.boxed.d.b = true;
|
||||
conf->webserver.interface.boxed.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->webserver.interface.theme.k = "webserver.interface.theme";
|
||||
conf->webserver.interface.theme.h = "Theme used by the Pi-hole web interface";
|
||||
|
@ -884,29 +973,34 @@ void initConfig(struct config *conf)
|
|||
}
|
||||
conf->webserver.interface.theme.t = CONF_ENUM_WEB_THEME;
|
||||
conf->webserver.interface.theme.d.web_theme = THEME_DEFAULT_AUTO;
|
||||
conf->webserver.interface.theme.c = validate_stub; // Only type-based checking
|
||||
|
||||
// sub-struct api
|
||||
conf->webserver.api.searchAPIauth.k = "webserver.api.searchAPIauth";
|
||||
conf->webserver.api.searchAPIauth.h = "Do local clients need to authenticate to access the search API? This settings allows local clients to use pihole -q ... without authentication. Note that \"local\" in the sense of the option means only 127.0.0.1 and [::1]";
|
||||
conf->webserver.api.searchAPIauth.t = CONF_BOOL;
|
||||
conf->webserver.api.searchAPIauth.d.b = false;
|
||||
conf->webserver.api.searchAPIauth.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->webserver.api.localAPIauth.k = "webserver.api.localAPIauth";
|
||||
conf->webserver.api.localAPIauth.h = "Do local clients need to authenticate to access the API? This settings allows local clients to use the API without authentication.";
|
||||
conf->webserver.api.localAPIauth.t = CONF_BOOL;
|
||||
conf->webserver.api.localAPIauth.d.b = true;
|
||||
conf->webserver.api.localAPIauth.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->webserver.api.max_sessions.k = "webserver.api.max_sessions";
|
||||
conf->webserver.api.max_sessions.h = "Number of concurrent sessions allowed for the API. If the number of sessions exceeds this value, no new sessions will be allowed until the number of sessions drops due to session expiration or logout. Note that the number of concurrent sessions is irrelevant if authentication is disabled as no sessions are used in this case.";
|
||||
conf->webserver.api.max_sessions.t = CONF_UINT16;
|
||||
conf->webserver.api.max_sessions.d.u16 = 16;
|
||||
conf->webserver.api.max_sessions.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL;
|
||||
conf->webserver.api.max_sessions.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->webserver.api.prettyJSON.k = "webserver.api.prettyJSON";
|
||||
conf->webserver.api.prettyJSON.h = "Should FTL prettify the API output (add extra spaces, newlines and indentation)?";
|
||||
conf->webserver.api.prettyJSON.t = CONF_BOOL;
|
||||
conf->webserver.api.prettyJSON.f = FLAG_ADVANCED_SETTING;
|
||||
conf->webserver.api.prettyJSON.d.b = false;
|
||||
conf->webserver.api.prettyJSON.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->webserver.api.pwhash.k = "webserver.api.pwhash";
|
||||
conf->webserver.api.pwhash.h = "API password hash";
|
||||
|
@ -914,6 +1008,7 @@ void initConfig(struct config *conf)
|
|||
conf->webserver.api.pwhash.t = CONF_STRING;
|
||||
conf->webserver.api.pwhash.f = FLAG_INVALIDATE_SESSIONS;
|
||||
conf->webserver.api.pwhash.d.s = (char*)"";
|
||||
conf->webserver.api.pwhash.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->webserver.api.password.k = "webserver.api.password";
|
||||
conf->webserver.api.password.h = "Pi-hole web interface and API password. When set to something different than \""PASSWORD_VALUE"\", this property will compute the corresponding password hash to set webserver.api.pwhash";
|
||||
|
@ -921,6 +1016,7 @@ void initConfig(struct config *conf)
|
|||
conf->webserver.api.password.t = CONF_PASSWORD;
|
||||
conf->webserver.api.password.f = FLAG_PSEUDO_ITEM | FLAG_INVALIDATE_SESSIONS;
|
||||
conf->webserver.api.password.d.s = (char*)"";
|
||||
conf->webserver.api.password.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->webserver.api.totp_secret.k = "webserver.api.totp_secret";
|
||||
conf->webserver.api.totp_secret.h = "Pi-hole 2FA TOTP secret. When set to something different than \"""\", 2FA authentication will be enforced for the API and the web interface. This setting is write-only, you can not read the secret back.";
|
||||
|
@ -928,6 +1024,7 @@ void initConfig(struct config *conf)
|
|||
conf->webserver.api.totp_secret.t = CONF_STRING;
|
||||
conf->webserver.api.totp_secret.f = FLAG_WRITE_ONLY | FLAG_INVALIDATE_SESSIONS;
|
||||
conf->webserver.api.totp_secret.d.s = (char*)"";
|
||||
conf->webserver.api.totp_secret.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->webserver.api.app_pwhash.k = "webserver.api.app_pwhash";
|
||||
conf->webserver.api.app_pwhash.h = "Pi-hole application password.\n After you turn on two-factor (2FA) verification and set up an Authenticator app, you may run into issues if you use apps or other services that don't support two-step verification. In this case, you can create and use an app password to sign in. An app password is a long, randomly generated password that can be used instead of your regular password + TOTP token when signing in to the API. The app password can be generated through the API and will be shown only once. You can revoke the app password at any time. If you revoke the app password, be sure to generate a new one and update your app with the new password.";
|
||||
|
@ -935,28 +1032,33 @@ void initConfig(struct config *conf)
|
|||
conf->webserver.api.app_pwhash.t = CONF_STRING;
|
||||
conf->webserver.api.app_pwhash.f = FLAG_INVALIDATE_SESSIONS;
|
||||
conf->webserver.api.app_pwhash.d.s = (char*)"";
|
||||
conf->webserver.api.app_pwhash.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->webserver.api.excludeClients.k = "webserver.api.excludeClients";
|
||||
conf->webserver.api.excludeClients.h = "Array of clients to be excluded from certain API responses (regex):\n - Query Log (/api/queries)\n - Top Clients (/api/stats/top_clients)\n This setting accepts both IP addresses (IPv4 and IPv6) as well as hostnames.\n Note that backslashes \"\\\" need to be escaped, i.e. \"\\\\\" in this setting\n\n Example: [ \"^192\\\\.168\\\\.2\\\\.56$\", \"^fe80::341:[0-9a-f]*$\", \"^localhost$\" ]";
|
||||
conf->webserver.api.excludeClients.a = cJSON_CreateStringReference("array of regular expressions describing clients");
|
||||
conf->webserver.api.excludeClients.t = CONF_JSON_STRING_ARRAY;
|
||||
conf->webserver.api.excludeClients.d.json = cJSON_CreateArray();
|
||||
conf->webserver.api.excludeClients.c = validate_regex_array;
|
||||
|
||||
conf->webserver.api.excludeDomains.k = "webserver.api.excludeDomains";
|
||||
conf->webserver.api.excludeDomains.h = "Array of domains to be excluded from certain API responses (regex):\n - Query Log (/api/queries)\n - Top Clients (/api/stats/top_domains)\n Note that backslashes \"\\\" need to be escaped, i.e. \"\\\\\" in this setting\n\n Example: [ \"(^|\\\\.)\\\\.google\\\\.de$\", \"\\\\.pi-hole\\\\.net$\" ]";
|
||||
conf->webserver.api.excludeDomains.a = cJSON_CreateStringReference("array of regular expressions describing domains");
|
||||
conf->webserver.api.excludeDomains.t = CONF_JSON_STRING_ARRAY;
|
||||
conf->webserver.api.excludeDomains.d.json = cJSON_CreateArray();
|
||||
conf->webserver.api.excludeDomains.c = validate_regex_array;
|
||||
|
||||
conf->webserver.api.maxHistory.k = "webserver.api.maxHistory";
|
||||
conf->webserver.api.maxHistory.h = "How much history should be imported from the database and returned by the API [seconds]? (max 24*60*60 = 86400)";
|
||||
conf->webserver.api.maxHistory.t = CONF_UINT;
|
||||
conf->webserver.api.maxHistory.d.ui = MAXLOGAGE*3600;
|
||||
conf->webserver.api.maxHistory.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->webserver.api.maxClients.k = "webserver.api.maxClients";
|
||||
conf->webserver.api.maxClients.h = "Up to how many clients should be returned in the activity graph endpoint (/api/history/clients)?\n This setting can be overwritten at run-time using the parameter N. Setting this to 0 will always send all clients. Be aware that this may be challenging for the GUI if you have many (think > 1.000 clients) in your network";
|
||||
conf->webserver.api.maxClients.t = CONF_UINT16;
|
||||
conf->webserver.api.maxClients.d.u16 = 10;
|
||||
conf->webserver.api.maxClients.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->webserver.api.client_history_global_max.k = "webserver.api.client_history_global_max";
|
||||
conf->webserver.api.client_history_global_max.h = "How should the API compute the most active clients? If set to true, the API will return the clients with the most queries globally (within 24 hours). If set to false, the API will return the clients with the most queries per time slot individually.";
|
||||
|
@ -967,12 +1069,14 @@ void initConfig(struct config *conf)
|
|||
conf->webserver.api.allow_destructive.h = "Allow destructive API calls (e.g. deleting all queries, powering off the system, ...)";
|
||||
conf->webserver.api.allow_destructive.t = CONF_BOOL;
|
||||
conf->webserver.api.allow_destructive.d.b = true;
|
||||
conf->webserver.api.allow_destructive.c = validate_stub; // Only type-based checking
|
||||
|
||||
// sub-struct webserver.api.temp
|
||||
conf->webserver.api.temp.limit.k = "webserver.api.temp.limit";
|
||||
conf->webserver.api.temp.limit.h = "Which upper temperature limit should be used by Pi-hole? Temperatures above this limit will be shown as \"hot\". The number specified here is in the unit defined below";
|
||||
conf->webserver.api.temp.limit.t = CONF_DOUBLE;
|
||||
conf->webserver.api.temp.limit.d.d = 60.0; // °C
|
||||
conf->webserver.api.temp.limit.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->webserver.api.temp.unit.k = "webserver.api.temp.unit";
|
||||
conf->webserver.api.temp.unit.h = "Which temperature unit should be used for temperatures processed by FTL?";
|
||||
|
@ -987,7 +1091,7 @@ void initConfig(struct config *conf)
|
|||
}
|
||||
conf->webserver.api.temp.unit.t = CONF_ENUM_TEMP_UNIT;
|
||||
conf->webserver.api.temp.unit.d.temp_unit = TEMP_UNIT_C;
|
||||
|
||||
conf->webserver.api.temp.unit.c = validate_stub; // Only type-based checking
|
||||
|
||||
// struct files
|
||||
conf->files.pid.k = "files.pid";
|
||||
|
@ -996,6 +1100,7 @@ void initConfig(struct config *conf)
|
|||
conf->files.pid.t = CONF_STRING;
|
||||
conf->files.pid.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL;
|
||||
conf->files.pid.d.s = (char*)"/run/pihole-FTL.pid";
|
||||
conf->files.pid.c = validate_filepath;
|
||||
|
||||
conf->files.database.k = "files.database";
|
||||
conf->files.database.h = "The location of FTL's long-term database";
|
||||
|
@ -1003,6 +1108,7 @@ void initConfig(struct config *conf)
|
|||
conf->files.database.t = CONF_STRING;
|
||||
conf->files.database.f = FLAG_ADVANCED_SETTING;
|
||||
conf->files.database.d.s = (char*)"/etc/pihole/pihole-FTL.db";
|
||||
conf->files.database.c = validate_filepath;
|
||||
|
||||
conf->files.gravity.k = "files.gravity";
|
||||
conf->files.gravity.h = "The location of Pi-hole's gravity database";
|
||||
|
@ -1010,6 +1116,7 @@ void initConfig(struct config *conf)
|
|||
conf->files.gravity.t = CONF_STRING;
|
||||
conf->files.gravity.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL;
|
||||
conf->files.gravity.d.s = (char*)"/etc/pihole/gravity.db";
|
||||
conf->files.gravity.c = validate_filepath;
|
||||
|
||||
conf->files.gravity_tmp.k = "files.gravity_tmp";
|
||||
conf->files.gravity_tmp.h = "A temporary directory where Pi-hole can store files during gravity updates. This directory must be writable by the user running gravity (typically pihole).";
|
||||
|
@ -1017,6 +1124,7 @@ void initConfig(struct config *conf)
|
|||
conf->files.gravity_tmp.t = CONF_STRING;
|
||||
conf->files.gravity_tmp.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL;
|
||||
conf->files.gravity_tmp.d.s = (char*)"/tmp";
|
||||
conf->files.gravity_tmp.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->files.macvendor.k = "files.macvendor";
|
||||
conf->files.macvendor.h = "The database containing MAC -> Vendor information for the network table";
|
||||
|
@ -1024,6 +1132,7 @@ void initConfig(struct config *conf)
|
|||
conf->files.macvendor.t = CONF_STRING;
|
||||
conf->files.macvendor.f = FLAG_ADVANCED_SETTING;
|
||||
conf->files.macvendor.d.s = (char*)"/etc/pihole/macvendor.db";
|
||||
conf->files.macvendor.c = validate_filepath;
|
||||
|
||||
conf->files.setupVars.k = "files.setupVars";
|
||||
conf->files.setupVars.h = "The old config file of Pi-hole used before v6.0";
|
||||
|
@ -1031,6 +1140,7 @@ void initConfig(struct config *conf)
|
|||
conf->files.setupVars.t = CONF_STRING;
|
||||
conf->files.setupVars.f = FLAG_ADVANCED_SETTING;
|
||||
conf->files.setupVars.d.s = (char*)"/etc/pihole/setupVars.conf";
|
||||
conf->files.setupVars.c = validate_filepath;
|
||||
|
||||
conf->files.pcap.k = "files.pcap";
|
||||
conf->files.pcap.h = "An optional file containing a pcap capture of the network traffic. This file is used for debugging purposes only. If you don't know what this is, you don't need it.\n Setting this to an empty string disables pcap recording. The file must be writable by the user running FTL (typically pihole). Failure to write to this file will prevent the DNS resolver from starting. The file is appended to if it already exists.";
|
||||
|
@ -1038,6 +1148,10 @@ void initConfig(struct config *conf)
|
|||
conf->files.pcap.t = CONF_STRING;
|
||||
conf->files.pcap.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL;
|
||||
conf->files.pcap.d.s = (char*)"";
|
||||
conf->files.pcap.c = validate_filepath_empty;
|
||||
|
||||
// sub-struct files.log
|
||||
// conf->files.log.ftl is set in a separate function
|
||||
|
||||
conf->files.log.webserver.k = "files.log.webserver";
|
||||
conf->files.log.webserver.h = "The log file used by the webserver";
|
||||
|
@ -1045,9 +1159,7 @@ void initConfig(struct config *conf)
|
|||
conf->files.log.webserver.t = CONF_STRING;
|
||||
conf->files.log.webserver.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL;
|
||||
conf->files.log.webserver.d.s = (char*)"/var/log/pihole/webserver.log";
|
||||
|
||||
// sub-struct files.log
|
||||
// conf->files.log.ftl is set in a separate function
|
||||
conf->files.log.webserver.c = validate_filepath;
|
||||
|
||||
conf->files.log.dnsmasq.k = "files.log.dnsmasq";
|
||||
conf->files.log.dnsmasq.h = "The log file used by the embedded dnsmasq DNS server";
|
||||
|
@ -1055,6 +1167,7 @@ void initConfig(struct config *conf)
|
|||
conf->files.log.dnsmasq.t = CONF_STRING;
|
||||
conf->files.log.dnsmasq.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL;
|
||||
conf->files.log.dnsmasq.d.s = (char*)"/var/log/pihole/pihole.log";
|
||||
conf->files.log.dnsmasq.c = validate_filepath_dash;
|
||||
|
||||
|
||||
// struct misc
|
||||
|
@ -1072,29 +1185,34 @@ void initConfig(struct config *conf)
|
|||
}
|
||||
conf->misc.privacylevel.t = CONF_ENUM_PRIVACY_LEVEL;
|
||||
conf->misc.privacylevel.d.privacy_level = PRIVACY_SHOW_ALL;
|
||||
conf->misc.privacylevel.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->misc.delay_startup.k = "misc.delay_startup";
|
||||
conf->misc.delay_startup.h = "During startup, in some configurations, network interfaces appear only late during system startup and are not ready when FTL tries to bind to them. Therefore, you may want FTL to wait a given amount of time before trying to start the DNS revolver. This setting takes any integer value between 0 and 300 seconds. To prevent delayed startup while the system is already running and FTL is restarted, the delay only takes place within the first 180 seconds (hard-coded) after booting.";
|
||||
conf->misc.delay_startup.t = CONF_UINT;
|
||||
conf->misc.delay_startup.d.ui = 0;
|
||||
conf->misc.delay_startup.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->misc.nice.k = "misc.nice";
|
||||
conf->misc.nice.h = "Set niceness of pihole-FTL. Defaults to -10 and can be disabled altogether by setting a value of -999. The nice value is an attribute that can be used to influence the CPU scheduler to favor or disfavor a process in scheduling decisions. The range of the nice value varies across UNIX systems. On modern Linux, the range is -20 (high priority = not very nice to other processes) to +19 (low priority).";
|
||||
conf->misc.nice.t = CONF_INT;
|
||||
conf->misc.nice.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL;
|
||||
conf->misc.nice.d.i = -10;
|
||||
conf->misc.nice.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->misc.addr2line.k = "misc.addr2line";
|
||||
conf->misc.addr2line.h = "Should FTL translate its own stack addresses into code lines during the bug backtrace? This improves the analysis of crashed significantly. It is recommended to leave the option enabled. This option should only be disabled when addr2line is known to not be working correctly on the machine because, in this case, the malfunctioning addr2line can prevent from generating any backtrace at all.";
|
||||
conf->misc.addr2line.t = CONF_BOOL;
|
||||
conf->misc.addr2line.f = FLAG_ADVANCED_SETTING;
|
||||
conf->misc.addr2line.d.b = true;
|
||||
conf->misc.addr2line.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->misc.etc_dnsmasq_d.k = "misc.etc_dnsmasq_d";
|
||||
conf->misc.etc_dnsmasq_d.h = "Should FTL load additional dnsmasq configuration files from /etc/dnsmasq.d/?";
|
||||
conf->misc.etc_dnsmasq_d.t = CONF_BOOL;
|
||||
conf->misc.etc_dnsmasq_d.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING;
|
||||
conf->misc.etc_dnsmasq_d.d.b = false;
|
||||
conf->misc.etc_dnsmasq_d.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->misc.dnsmasq_lines.k = "misc.dnsmasq_lines";
|
||||
conf->misc.dnsmasq_lines.h = "Additional lines to inject into the generated dnsmasq configuration.\n Warning: This is an advanced setting and should only be used with care. Incorrectly formatted or duplicated lines as well as lines conflicting with the automatic configuration of Pi-hole can break the embedded dnsmasq and will stop DNS resolution from working.\n Use this option with extra care.";
|
||||
|
@ -1102,28 +1220,33 @@ void initConfig(struct config *conf)
|
|||
conf->misc.dnsmasq_lines.t = CONF_JSON_STRING_ARRAY;
|
||||
conf->misc.dnsmasq_lines.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL;
|
||||
conf->misc.dnsmasq_lines.d.json = cJSON_CreateArray();
|
||||
conf->misc.dnsmasq_lines.c = validate_stub; // Type-based checking + dnsmasq syntax checking
|
||||
|
||||
conf->misc.extraLogging.k = "misc.extraLogging";
|
||||
conf->misc.extraLogging.h = "Log additional information about queries and replies to pihole.log\n When this setting is enabled, the log has extra information at the start of each line. This consists of a serial number which ties together the log lines associated with an individual query, and the IP address of the requestor. This setting is only effective if dns.queryLogging is enabled, too. This option is only useful for debugging and is not recommended for normal use.";
|
||||
conf->misc.extraLogging.t = CONF_BOOL;
|
||||
conf->misc.extraLogging.f = FLAG_RESTART_FTL;
|
||||
conf->misc.extraLogging.d.b = false;
|
||||
conf->misc.extraLogging.c = validate_stub; // Only type-based checking
|
||||
|
||||
// sub-struct misc.check
|
||||
conf->misc.check.load.k = "misc.check.load";
|
||||
conf->misc.check.load.h = "Pi-hole is very lightweight on resources. Nevertheless, this does not mean that you should run Pi-hole on a server that is otherwise extremely busy as queuing on the system can lead to unnecessary delays in DNS operation as the system becomes less and less usable as the system load increases because all resources are permanently in use. To account for this, FTL regularly checks the system load. To bring this to your attention, FTL warns about excessive load when the 15 minute system load average exceeds the number of cores.\n This check can be disabled with this setting.";
|
||||
conf->misc.check.load.t = CONF_BOOL;
|
||||
conf->misc.check.load.d.b = true;
|
||||
conf->misc.check.load.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->misc.check.disk.k = "misc.check.disk";
|
||||
conf->misc.check.disk.h = "FTL stores its long-term history in a database file on disk. Furthermore, FTL stores log files. By default, FTL warns if usage of the disk holding any crucial file exceeds 90%. You can set any integer limit between 0 to 100 (interpreted as percentages) where 0 means that checking of disk usage is disabled.";
|
||||
conf->misc.check.disk.t = CONF_UINT;
|
||||
conf->misc.check.disk.d.ui = 90;
|
||||
conf->misc.check.disk.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->misc.check.shmem.k = "misc.check.shmem";
|
||||
conf->misc.check.shmem.h = "FTL stores history in shared memory to allow inter-process communication with forked dedicated TCP workers. If FTL runs out of memory, it cannot continue to work as queries cannot be analyzed any further. Hence, FTL checks if enough shared memory is available on your system and warns you if this is not the case.\n By default, FTL warns if the shared-memory usage exceeds 90%. You can set any integer limit between 0 to 100 (interpreted as percentages) where 0 means that checking of shared-memory usage is disabled.";
|
||||
conf->misc.check.shmem.t = CONF_UINT;
|
||||
conf->misc.check.shmem.d.ui = 90;
|
||||
conf->misc.check.shmem.c = validate_stub; // Only type-based checking
|
||||
|
||||
|
||||
// struct debug
|
||||
|
@ -1132,168 +1255,196 @@ void initConfig(struct config *conf)
|
|||
conf->debug.database.t = CONF_BOOL;
|
||||
conf->debug.database.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.database.d.b = false;
|
||||
conf->debug.database.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.networking.k = "debug.networking";
|
||||
conf->debug.networking.h = "Prints a list of the detected interfaces on the startup of pihole-FTL. Also, prints whether these interfaces are IPv4 or IPv6 interfaces.";
|
||||
conf->debug.networking.t = CONF_BOOL;
|
||||
conf->debug.networking.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.networking.d.b = false;
|
||||
conf->debug.networking.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.locks.k = "debug.locks";
|
||||
conf->debug.locks.h = "Print information about shared memory locks. Messages will be generated when waiting, obtaining, and releasing a lock.";
|
||||
conf->debug.locks.t = CONF_BOOL;
|
||||
conf->debug.locks.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.locks.d.b = false;
|
||||
conf->debug.locks.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.queries.k = "debug.queries";
|
||||
conf->debug.queries.h = "Print extensive query information (domains, types, replies, etc.). This has always been part of the legacy debug mode of pihole-FTL.";
|
||||
conf->debug.queries.t = CONF_BOOL;
|
||||
conf->debug.queries.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.queries.d.b = false;
|
||||
conf->debug.queries.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.flags.k = "debug.flags";
|
||||
conf->debug.flags.h = "Print flags of queries received by the DNS hooks. Only effective when DEBUG_QUERIES is enabled as well.";
|
||||
conf->debug.flags.t = CONF_BOOL;
|
||||
conf->debug.flags.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.flags.d.b = false;
|
||||
conf->debug.flags.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.shmem.k = "debug.shmem";
|
||||
conf->debug.shmem.h = "Print information about shared memory buffers. Messages are either about creating or enlarging shmem objects or string injections.";
|
||||
conf->debug.shmem.t = CONF_BOOL;
|
||||
conf->debug.shmem.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.shmem.d.b = false;
|
||||
conf->debug.shmem.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.gc.k = "debug.gc";
|
||||
conf->debug.gc.h = "Print information about garbage collection (GC): What is to be removed, how many have been removed and how long did GC take.";
|
||||
conf->debug.gc.t = CONF_BOOL;
|
||||
conf->debug.gc.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.gc.d.b = false;
|
||||
conf->debug.gc.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.arp.k = "debug.arp";
|
||||
conf->debug.arp.h = "Print information about ARP table processing: How long did parsing take, whether read MAC addresses are valid, and if the macvendor.db file exists.";
|
||||
conf->debug.arp.t = CONF_BOOL;
|
||||
conf->debug.arp.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.arp.d.b = false;
|
||||
conf->debug.arp.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.regex.k = "debug.regex";
|
||||
conf->debug.regex.h = "Controls if FTLDNS should print extended details about regex matching into FTL.log.";
|
||||
conf->debug.regex.t = CONF_BOOL;
|
||||
conf->debug.regex.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.regex.d.b = false;
|
||||
conf->debug.regex.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.api.k = "debug.api";
|
||||
conf->debug.api.h = "Print extra debugging information concerning API calls. This includes the request, the request parameters, and the internal details about how the algorithms decide which data to present and in what form. This very verbose output should only be used when debugging specific API issues and can be helpful, e.g., when a client cannot connect due to an obscure API error. Furthermore, this setting enables logging of all API requests (auth log) and details about user authentication attempts.";
|
||||
conf->debug.api.t = CONF_BOOL;
|
||||
conf->debug.api.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.api.d.b = false;
|
||||
conf->debug.api.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.tls.k = "debug.tls";
|
||||
conf->debug.tls.h = "Print extra debugging information about TLS connections. This includes the TLS version, the cipher suite, the certificate chain and much more. This very verbose output should only be used when debugging specific TLS issues and can be helpful, e.g., when a client cannot connect due to an obscure TLS error as modern browsers do not provide much information about the underlying TLS connection and most often give only very generic error messages without much/any underlying technical information.";
|
||||
conf->debug.tls.t = CONF_BOOL;
|
||||
conf->debug.tls.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.tls.d.b = false;
|
||||
conf->debug.tls.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.overtime.k = "debug.overtime";
|
||||
conf->debug.overtime.h = "Print information about overTime memory operations, such as initializing or moving overTime slots.";
|
||||
conf->debug.overtime.t = CONF_BOOL;
|
||||
conf->debug.overtime.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.overtime.d.b = false;
|
||||
conf->debug.overtime.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.status.k = "debug.status";
|
||||
conf->debug.status.h = "Print information about status changes for individual queries. This can be useful to identify unexpected unknown queries.";
|
||||
conf->debug.status.t = CONF_BOOL;
|
||||
conf->debug.status.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.status.d.b = false;
|
||||
conf->debug.status.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.caps.k = "debug.caps";
|
||||
conf->debug.caps.h = "Print information about capabilities granted to the pihole-FTL process. The current capabilities are printed on receipt of SIGHUP, i.e., the current set of capabilities can be queried without restarting pihole-FTL (by setting DEBUG_CAPS=true and thereafter sending killall -HUP pihole-FTL).";
|
||||
conf->debug.caps.t = CONF_BOOL;
|
||||
conf->debug.caps.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.caps.d.b = false;
|
||||
conf->debug.caps.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.dnssec.k = "debug.dnssec";
|
||||
conf->debug.dnssec.h = "Print information about DNSSEC activity";
|
||||
conf->debug.dnssec.t = CONF_BOOL;
|
||||
conf->debug.dnssec.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.dnssec.d.b = false;
|
||||
conf->debug.dnssec.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.vectors.k = "debug.vectors";
|
||||
conf->debug.vectors.h = "FTL uses dynamically allocated vectors for various tasks. This config option enables extensive debugging information such as information about allocation, referencing, deletion, and appending.";
|
||||
conf->debug.vectors.t = CONF_BOOL;
|
||||
conf->debug.vectors.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.vectors.d.b = false;
|
||||
conf->debug.vectors.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.resolver.k = "debug.resolver";
|
||||
conf->debug.resolver.h = "Extensive information about hostname resolution like which DNS servers are used in the first and second hostname resolving tries (only affecting internally generated PTR queries).";
|
||||
conf->debug.resolver.t = CONF_BOOL;
|
||||
conf->debug.resolver.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.resolver.d.b = false;
|
||||
conf->debug.resolver.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.edns0.k = "debug.edns0";
|
||||
conf->debug.edns0.h = "Print debugging information about received EDNS(0) data.";
|
||||
conf->debug.edns0.t = CONF_BOOL;
|
||||
conf->debug.edns0.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.edns0.d.b = false;
|
||||
conf->debug.edns0.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.clients.k = "debug.clients";
|
||||
conf->debug.clients.h = "Log various important client events such as change of interface (e.g., client switching from WiFi to wired or VPN connection), as well as extensive reporting about how clients were assigned to its groups.";
|
||||
conf->debug.clients.t = CONF_BOOL;
|
||||
conf->debug.clients.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.clients.d.b = false;
|
||||
conf->debug.clients.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.aliasclients.k = "debug.aliasclients";
|
||||
conf->debug.aliasclients.h = "Log information related to alias-client processing.";
|
||||
conf->debug.aliasclients.t = CONF_BOOL;
|
||||
conf->debug.aliasclients.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.aliasclients.d.b = false;
|
||||
conf->debug.aliasclients.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.events.k = "debug.events";
|
||||
conf->debug.events.h = "Log information regarding FTL's embedded event handling queue.";
|
||||
conf->debug.events.t = CONF_BOOL;
|
||||
conf->debug.events.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.events.d.b = false;
|
||||
conf->debug.events.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.helper.k = "debug.helper";
|
||||
conf->debug.helper.h = "Log information about script helpers, e.g., due to dhcp-script.";
|
||||
conf->debug.helper.t = CONF_BOOL;
|
||||
conf->debug.helper.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.helper.d.b = false;
|
||||
conf->debug.helper.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.config.k = "debug.config";
|
||||
conf->debug.config.h = "Print config parsing details";
|
||||
conf->debug.config.t = CONF_BOOL;
|
||||
conf->debug.config.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.config.d.b = false;
|
||||
conf->debug.config.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.inotify.k = "debug.inotify";
|
||||
conf->debug.inotify.h = "Debug monitoring of /etc/pihole filesystem events";
|
||||
conf->debug.inotify.t = CONF_BOOL;
|
||||
conf->debug.inotify.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.inotify.d.b = false;
|
||||
conf->debug.inotify.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.webserver.k = "debug.webserver";
|
||||
conf->debug.webserver.h = "Debug monitoring of the webserver (CivetWeb) events";
|
||||
conf->debug.webserver.t = CONF_BOOL;
|
||||
conf->debug.webserver.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.webserver.d.b = false;
|
||||
conf->debug.webserver.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.extra.k = "debug.extra";
|
||||
conf->debug.extra.h = "Temporary flag that may print additional information. This debug flag is meant to be used whenever needed for temporary investigations. The logged content may change without further notice at any time.";
|
||||
conf->debug.extra.t = CONF_BOOL;
|
||||
conf->debug.extra.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.extra.d.b = false;
|
||||
conf->debug.extra.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.reserved.k = "debug.reserved";
|
||||
conf->debug.reserved.h = "Reserved debug flag";
|
||||
conf->debug.reserved.t = CONF_BOOL;
|
||||
conf->debug.reserved.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.reserved.d.b = false;
|
||||
conf->debug.reserved.c = validate_stub; // Only type-based checking
|
||||
|
||||
conf->debug.all.k = "debug.all";
|
||||
conf->debug.all.h = "Set all debug flags at once. This is a convenience option to enable all debug flags at once. Note that this option is not persistent, setting it to true will enable all *remaining* debug flags but unsetting it will disable *all* debug flags.";
|
||||
conf->debug.all.t = CONF_ALL_DEBUG_BOOL;
|
||||
conf->debug.all.f = FLAG_ADVANCED_SETTING;
|
||||
conf->debug.all.d.b = false;
|
||||
conf->debug.all.c = validate_stub; // Only type-based checking
|
||||
|
||||
// Post-processing:
|
||||
// Initialize and verify config data
|
||||
|
@ -1343,6 +1494,13 @@ void initConfig(struct config *conf)
|
|||
log_err("Config option %s has NULL default JSON array!", conf_item->k);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Verify that all config options have a validator function
|
||||
if(conf_item->c == NULL)
|
||||
{
|
||||
log_err("Config option %s has no validator function!", conf_item->k);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1491,6 +1649,7 @@ bool getLogFilePath(void)
|
|||
config.files.log.ftl.f = FLAG_ADVANCED_SETTING;
|
||||
config.files.log.ftl.d.s = (char*)"/var/log/pihole/FTL.log";
|
||||
config.files.log.ftl.v.s = config.files.log.ftl.d.s;
|
||||
config.files.log.ftl.c = validate_filepath;
|
||||
|
||||
// Check if the config file contains a different path
|
||||
if(!getLogFilePathTOML())
|
||||
|
|
|
@ -38,6 +38,9 @@
|
|||
// characters will be replaced by their UTF-8 escape sequences (UCS-2)
|
||||
#define TOML_UTF8
|
||||
|
||||
// Size of the buffer used to report possible errors during config validation
|
||||
#define VALIDATOR_ERRBUF_LEN 256
|
||||
|
||||
// Location of the legacy (pre-v6.0) config file
|
||||
#define GLOBALCONFFILE_LEGACY "/etc/pihole/pihole-FTL.conf"
|
||||
|
||||
|
@ -109,6 +112,7 @@ struct conf_item {
|
|||
uint8_t f; // additional Flags
|
||||
union conf_value v; // current Value
|
||||
union conf_value d; // Default value
|
||||
bool (*c)(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]); // Function pointer to validate the value
|
||||
};
|
||||
|
||||
struct enum_options {
|
||||
|
@ -202,6 +206,7 @@ struct config {
|
|||
struct conf_item DBexport;
|
||||
struct conf_item maxDBdays;
|
||||
struct conf_item DBinterval;
|
||||
struct conf_item useWAL;
|
||||
struct {
|
||||
struct conf_item parseARPcache;
|
||||
struct conf_item expire;
|
||||
|
|
|
@ -473,7 +473,7 @@ bool __attribute__((const)) write_dnsmasq_config(struct config *conf, bool test_
|
|||
|
||||
if(active == NULL || cidr == NULL || target == NULL || domain == NULL)
|
||||
{
|
||||
log_err("Invalid reverse server string: %s", revServer->valuestring);
|
||||
log_err("Skipped invalid dns.revServers[%u]: %s", i, revServer->valuestring);
|
||||
free(copy);
|
||||
continue;
|
||||
}
|
||||
|
@ -658,6 +658,15 @@ bool __attribute__((const)) write_dnsmasq_config(struct config *conf, bool test_
|
|||
}
|
||||
}
|
||||
|
||||
// Add ANY filtering
|
||||
fputs("# RFC 8482: Providing Minimal-Sized Responses to DNS Queries That Have QTYPE=ANY\n", pihole_conf);
|
||||
fputs("# Filters replies to queries for type ANY. Everything other than A, AAAA, MX and CNAME\n", pihole_conf);
|
||||
fputs("# records are removed. Since ANY queries with forged source addresses can be used in DNS amplification attacks\n", pihole_conf);
|
||||
fputs("# replies to ANY queries can be large) this defangs such attacks, whilst still supporting the\n", pihole_conf);
|
||||
fputs("# one remaining possible use of ANY queries. See RFC 8482 para 4.3 for details.\n", pihole_conf);
|
||||
fputs("filter-rr=ANY\n", pihole_conf);
|
||||
fputs("\n", pihole_conf);
|
||||
|
||||
// Add additional config lines to disk (if present)
|
||||
if(conf->misc.dnsmasq_lines.v.json != NULL &&
|
||||
cJSON_GetArraySize(conf->misc.dnsmasq_lines.v.json) > 0)
|
||||
|
@ -697,6 +706,14 @@ bool __attribute__((const)) write_dnsmasq_config(struct config *conf, bool test_
|
|||
if(test_config && !test_dnsmasq_config(errbuf))
|
||||
{
|
||||
log_warn("New dnsmasq configuration is not valid (%s), config remains unchanged", errbuf);
|
||||
|
||||
// Remove temporary config file
|
||||
if(remove(DNSMASQ_TEMP_CONF) != 0)
|
||||
{
|
||||
log_err("Cannot remove temporary dnsmasq config file: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -707,8 +724,14 @@ bool __attribute__((const)) write_dnsmasq_config(struct config *conf, bool test_
|
|||
if(rename(DNSMASQ_TEMP_CONF, DNSMASQ_PH_CONFIG) != 0)
|
||||
{
|
||||
log_err("Cannot install dnsmasq config file: %s", strerror(errno));
|
||||
|
||||
// Remove temporary config file
|
||||
if(remove(DNSMASQ_TEMP_CONF) != 0)
|
||||
log_err("Cannot remove temporary dnsmasq config file: %s", strerror(errno));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
log_debug(DEBUG_CONFIG, "Config file written to "DNSMASQ_PH_CONFIG);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -48,6 +48,13 @@ void getEnvVars(void)
|
|||
char *key = strtok(*env, "=");
|
||||
char *value = strtok(NULL, "=");
|
||||
|
||||
// Log warning if value is missing
|
||||
if(value == NULL)
|
||||
{
|
||||
log_warn("Environment variable %s has no value, substituting with empty string", key);
|
||||
value = (char*)"";
|
||||
}
|
||||
|
||||
// Add to list
|
||||
struct env_item *new_item = calloc(1, sizeof(struct env_item));
|
||||
new_item->used = false;
|
||||
|
|
|
@ -175,14 +175,17 @@ static void get_revServer_from_setupVars(void)
|
|||
// Free memory, harmless to call if read_setupVarsconf() didn't return a result
|
||||
clearSetupVarsArray();
|
||||
|
||||
// Only add the entry if all values are present and active
|
||||
if(active && cidr != NULL && target != NULL && domain != NULL)
|
||||
{
|
||||
// Build comma-separated string of all values
|
||||
char *old = calloc(strlen(active_str) + strlen(cidr) + strlen(target) + strlen(domain) + 4, sizeof(char));
|
||||
// 8 = 3 commas, "true", and null terminator
|
||||
char *old = calloc(strlen(cidr) + strlen(target) + strlen(domain) + 8, sizeof(char));
|
||||
if(old)
|
||||
{
|
||||
// Add to new config
|
||||
sprintf(old, "%s,%s,%s,%s", active_str, cidr, target, domain);
|
||||
// active is always true as we only add active entries
|
||||
sprintf(old, "true,%s,%s,%s", cidr, target, domain);
|
||||
cJSON_AddItemToArray(config.dns.revServers.v.json, cJSON_CreateString(old));
|
||||
free(old);
|
||||
}
|
||||
|
|
|
@ -30,8 +30,8 @@
|
|||
static toml_table_t *parseTOML(const unsigned int version);
|
||||
static void reportDebugFlags(void);
|
||||
|
||||
// Migrate config from old to new, returns true if a restart is required
|
||||
static bool migrate_config(toml_table_t *toml, struct config *newconf)
|
||||
// Migrate dns.revServer -> dns.revServers[0]
|
||||
static bool migrate_dns_revServer(toml_table_t *toml, struct config *newconf)
|
||||
{
|
||||
bool restart = false;
|
||||
toml_table_t *dns = toml_table_in(toml, "dns");
|
||||
|
@ -55,19 +55,48 @@ static bool migrate_config(toml_table_t *toml, struct config *newconf)
|
|||
{
|
||||
// Add to new config
|
||||
sprintf(old, "%s,%s,%s,%s", active.u.s ? "true" : "false", cidr.u.s, target.u.s, domain.u.s);
|
||||
log_debug(DEBUG_CONFIG, "Config setting dns.revServer MIGRATED: %s", old);
|
||||
log_debug(DEBUG_CONFIG, "Config setting dns.revServer MIGRATED to dns.revServers[0]: %s", old);
|
||||
cJSON_AddItemToArray(newconf->dns.revServers.v.json, cJSON_CreateString(old));
|
||||
restart = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
log_warn("Config setting dns.revServer INVALID - ignoring: %s %s %s %s", active.ok ? active.u.s : "NULL", cidr.ok ? cidr.u.s : "NULL", target.ok ? target.u.s : "NULL", domain.ok ? domain.u.s : "NULL");
|
||||
{
|
||||
// Invalid config - ignored but logged in case
|
||||
// the user wants to know and restore it later
|
||||
// manually after fixing whatever the problem is
|
||||
log_warn("Config setting dns.revServer INVALID - ignoring: %s %s %s %s",
|
||||
active.ok ? active.u.s : "NULL",
|
||||
cidr.ok ? cidr.u.s : "NULL",
|
||||
target.ok ? target.u.s : "NULL",
|
||||
domain.ok ? domain.u.s : "NULL");
|
||||
}
|
||||
}
|
||||
else
|
||||
log_info("dns.revServer DOES NOT EXIST");
|
||||
{
|
||||
// Perfectly fine - it just means this old option does
|
||||
// not exist and, hence, does not need to be migrated
|
||||
log_debug(DEBUG_CONFIG, "dns.revServer does not exist - nothing to migrate");
|
||||
}
|
||||
}
|
||||
else
|
||||
log_info("dns DOES NOT EXIST");
|
||||
{
|
||||
// This is actually a problem as the old config file
|
||||
// should always contain a "dns" section
|
||||
log_warn("dns config tab does not exist - config file corrupt or incomplete");
|
||||
}
|
||||
|
||||
return restart;
|
||||
}
|
||||
|
||||
// Migrate config from old to new, returns true if a restart is required to
|
||||
// apply the changes
|
||||
static bool migrate_config(toml_table_t *toml, struct config *newconf)
|
||||
{
|
||||
bool restart = false;
|
||||
|
||||
// Migrate dns.revServer -> dns.revServers[0]
|
||||
restart |= migrate_dns_revServer(toml, newconf);
|
||||
|
||||
return restart;
|
||||
}
|
||||
|
@ -86,17 +115,10 @@ bool readFTLtoml(struct config *oldconf, struct config *newconf,
|
|||
return false;
|
||||
}
|
||||
|
||||
// Check if we are in Adam mode
|
||||
// (only read the env vars)
|
||||
const char *envvar = getenv("FTLCONF_ENV_ONLY");
|
||||
const bool adam_mode = (envvar != NULL &&
|
||||
(strcmp(envvar, "true") == 0 ||
|
||||
strcmp(envvar, "yes") == 0));
|
||||
|
||||
// Try to read debug config. This is done before the full config
|
||||
// parsing to allow for debug output further down
|
||||
// First try to read env variable, if this fails, read TOML
|
||||
if((teleporter || !readEnvValue(&newconf->debug.config, newconf)) && !adam_mode)
|
||||
if(teleporter || !readEnvValue(&newconf->debug.config, newconf))
|
||||
{
|
||||
toml_table_t *conf_debug = toml_table_in(toml, "debug");
|
||||
if(conf_debug)
|
||||
|
@ -124,10 +146,6 @@ bool readFTLtoml(struct config *oldconf, struct config *newconf,
|
|||
continue;
|
||||
}
|
||||
|
||||
// Do not read TOML file when in Adam mode
|
||||
if(adam_mode)
|
||||
continue;
|
||||
|
||||
// Get config path depth
|
||||
unsigned int level = config_path_depth(new_conf_item->p);
|
||||
|
||||
|
|
|
@ -0,0 +1,556 @@
|
|||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2023 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* FTL Engine
|
||||
* Config validation routines
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
#include "validator.h"
|
||||
#include "log.h"
|
||||
// valid_domain()
|
||||
#include "tools/gravity-parseList.h"
|
||||
// regex
|
||||
#include "regex_r.h"
|
||||
|
||||
// Stub validator for config types that need to dedicated validation as they can
|
||||
// be tested by their type only (e.g., integers, strings, booleans, enums, etc.)
|
||||
bool __attribute__((const)) validate_stub(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Validate the dns.hosts array
|
||||
// Each entry needs to be a string in form "IP HOSTNAME"
|
||||
bool validate_dns_hosts(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN])
|
||||
{
|
||||
if(!cJSON_IsArray(val->json))
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not an array", key);
|
||||
return false;
|
||||
}
|
||||
|
||||
for(int i = 0; i < cJSON_GetArraySize(val->json); i++)
|
||||
{
|
||||
// Get array item
|
||||
cJSON *item = cJSON_GetArrayItem(val->json, i);
|
||||
|
||||
// Check if it's a string
|
||||
if(!cJSON_IsString(item))
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: not a string",
|
||||
key, i);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if it's in the form "IP HOSTNAME"
|
||||
char *str = strdup(item->valuestring);
|
||||
char *tmp = str;
|
||||
char *ip = strsep(&tmp, " ");
|
||||
|
||||
if(!ip || !*ip)
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: not an IP address (\"%s\")",
|
||||
key, i, item->valuestring);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if IP is valid
|
||||
struct in_addr addr;
|
||||
struct in6_addr addr6;
|
||||
if(inet_pton(AF_INET, ip, &addr) != 1 && inet_pton(AF_INET6, ip, &addr6) != 1)
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: neither a valid IPv4 nor IPv6 address (\"%s\")",
|
||||
key, i, ip);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if all hostnames are valid
|
||||
// The HOSTS format allows any number of space-separated
|
||||
// hostnames to come after the IP address
|
||||
unsigned int hosts = 0;
|
||||
char *host = NULL;
|
||||
while((host = strsep(&tmp, " ")) != NULL)
|
||||
{
|
||||
if(!valid_domain(host, strlen(host), false))
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: invalid hostname (\"%s\")",
|
||||
key, i, host);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
hosts++;
|
||||
}
|
||||
|
||||
// Check if there is at least one hostname in this record
|
||||
if(hosts < 1)
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: entry does not have at least one hostname (\"%s\")",
|
||||
key, i, item->valuestring);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
|
||||
free(str);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Validate the dns.cnames array
|
||||
// Each entry needs to be a string in form "<cname>,[<cname>,]<target>[,<TTL>]"
|
||||
bool validate_dns_cnames(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN])
|
||||
{
|
||||
if(!cJSON_IsArray(val->json))
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not an array", key);
|
||||
return false;
|
||||
}
|
||||
|
||||
for(int i = 0; i < cJSON_GetArraySize(val->json); i++)
|
||||
{
|
||||
// Get array item
|
||||
cJSON *item = cJSON_GetArrayItem(val->json, i);
|
||||
|
||||
// Check if it's a string
|
||||
if(!cJSON_IsString(item))
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: not a string", key, i);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Count the number of elements in the string
|
||||
unsigned int elements = 1;
|
||||
for(unsigned int j = 0; j < strlen(item->valuestring); j++)
|
||||
if(item->valuestring[j] == ',')
|
||||
elements++;
|
||||
|
||||
// Check if it's in the form "<cname>,[<cnameX>,]<target>[,<TTL>]"
|
||||
// <cnameX> is optional and may be repeated
|
||||
char *str = strdup(item->valuestring);
|
||||
char *tmp = str, *s = NULL;
|
||||
unsigned int j = 0;
|
||||
|
||||
while((s = strsep(&tmp, ",")) != NULL)
|
||||
{
|
||||
// Check if it's a valid cname
|
||||
if(strlen(s) == 0)
|
||||
{
|
||||
// Contains an empty string
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: contains an empty string at position %u", key, i, j);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
|
||||
j++;
|
||||
}
|
||||
free(str);
|
||||
|
||||
// Check if there are at least one cname and a target
|
||||
if(j < 2)
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: not a valid CNAME definition (too few elements)", key, i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Validate IPs in CIDR notation
|
||||
bool validate_cidr(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN])
|
||||
{
|
||||
// Check if it's a valid CIDR
|
||||
char *str = strdup(val->s);
|
||||
char *tmp = str;
|
||||
char *ip = strsep(&tmp, "/");
|
||||
char *cidr = strsep(&tmp, "/");
|
||||
char *tail = strsep(&tmp, "/");
|
||||
|
||||
// Check if there is an IP and no tail
|
||||
if(!ip || !*ip || tail)
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not a valid IP in CIDR notation (\"%s\")", key, val->s);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if IP is valid
|
||||
struct in_addr addr;
|
||||
struct in6_addr addr6;
|
||||
int ip4 = 0, ip6 = 0;
|
||||
if((ip4 = inet_pton(AF_INET, ip, &addr) != 1) && (ip6 = inet_pton(AF_INET6, ip, &addr6)) != 1)
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not a valid IPv4 nor IPv6 address (\"%s\")", key, ip);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if CIDR is valid
|
||||
if(cidr)
|
||||
{
|
||||
if(strlen(cidr) == 0)
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: empty CIDR value", key);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
int cidr_int = atoi(cidr);
|
||||
if(ip4 && (cidr_int < 0 || cidr_int > 32))
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not a valid IPv4 CIDR (\"%s\")", key, cidr);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
else if(ip6 && (cidr_int < 0 || cidr_int > 128))
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not a valid IPv6 CIDR (\"%s\")", key, cidr);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
free(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Validate IP address optionally followed by a port (separator is "#")
|
||||
bool validate_ip_port(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN])
|
||||
{
|
||||
// Check if it's a valid IP
|
||||
char *str = strdup(val->s);
|
||||
char *tmp = str;
|
||||
char *ip = strsep(&tmp, "#");
|
||||
char *port = strsep(&tmp, "#");
|
||||
char *tail = strsep(&tmp, "#");
|
||||
|
||||
// Check if there is an IP and no tail
|
||||
if(!ip || !*ip || tail)
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not a valid IP (\"%s\")", key, val->s);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if IP is valid
|
||||
struct in_addr addr;
|
||||
struct in6_addr addr6;
|
||||
int ip4 = 0, ip6 = 0;
|
||||
if((ip4 = inet_pton(AF_INET, ip, &addr) != 1) && (ip6 = inet_pton(AF_INET6, ip, &addr6)) != 1)
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not a valid IPv4 nor IPv6 address (\"%s\")", key, ip);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if port is valid
|
||||
if(port)
|
||||
{
|
||||
if(strlen(port) == 0)
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: empty port value", key);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
int port_int = atoi(port);
|
||||
if(port_int < 0 || port_int > 65535)
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not a valid port (\"%s\")", key, port);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
free(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Validate domain
|
||||
bool validate_domain(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN])
|
||||
{
|
||||
// Check if domain is valid
|
||||
if(!valid_domain(val->s, strlen(val->s), false))
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not a valid domain (\"%s\")", key, val->s);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Validate file path
|
||||
bool validate_filepath(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN])
|
||||
{
|
||||
// Check if the path contains only valid characters
|
||||
for(unsigned int i = 0; i < strlen(val->s); i++)
|
||||
{
|
||||
if(!isalnum(val->s[i]) && val->s[i] != '/' && val->s[i] != '.' && val->s[i] != '-' && val->s[i] != '_' && val->s[i] != ' ')
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not a valid file path (\"%s\")", key, val->s);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Validate file path (empty allowed)
|
||||
bool validate_filepath_empty(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN])
|
||||
{
|
||||
// Empty paths are allowed, e.g., to disable a feature like PCAP
|
||||
if(strlen(val->s) == 0)
|
||||
return true;
|
||||
|
||||
// else:
|
||||
return validate_filepath(val, key, err);
|
||||
}
|
||||
|
||||
// Validate file path (dash allowed), used by files.log.dnsmasq
|
||||
bool validate_filepath_dash(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN])
|
||||
{
|
||||
// Dash is allowed, this enabled printing to stderr
|
||||
if(strlen(val->s) == 1 && val->s[0] == '-')
|
||||
return true;
|
||||
|
||||
// else:
|
||||
return validate_filepath(val, key, err);
|
||||
}
|
||||
|
||||
// Validate a single regular expression
|
||||
static bool validate_regex(const char *regex, char err[VALIDATOR_ERRBUF_LEN])
|
||||
{
|
||||
// Compile regex
|
||||
regex_t preg = { 0 };
|
||||
const int ret = regcomp(&preg, regex, REG_EXTENDED);
|
||||
if(ret != 0)
|
||||
{
|
||||
regerror(ret, &preg, err, VALIDATOR_ERRBUF_LEN);
|
||||
regfree(&preg);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Free regex
|
||||
regfree(&preg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Validate array of regexes
|
||||
bool validate_regex_array(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN])
|
||||
{
|
||||
if(val == NULL || !cJSON_IsArray(val->json))
|
||||
{
|
||||
strncat(err, "%s: not an array", VALIDATOR_ERRBUF_LEN);
|
||||
return false;
|
||||
}
|
||||
|
||||
for(int i = 0; i < cJSON_GetArraySize(val->json); i++)
|
||||
{
|
||||
// Get array item
|
||||
cJSON *item = cJSON_GetArrayItem(val->json, i);
|
||||
|
||||
// Check if it's a string
|
||||
if(!cJSON_IsString(item))
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: not a string",
|
||||
key, i);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if it's a valid regex
|
||||
char errbuf[VALIDATOR_ERRBUF_LEN] = { 0 };
|
||||
if(!validate_regex(item->valuestring, errbuf))
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: not a valid regex (\"%s\"): %s",
|
||||
key, i, item->valuestring, errbuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Validate dns.revServers array
|
||||
// Each entry has to be of form "<enabled>,<ip-address>[/<prefix-len>],<server>[#<port>],<domain>"
|
||||
bool validate_dns_revServers(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN])
|
||||
{
|
||||
// Check if it's an array
|
||||
if(!cJSON_IsArray(val->json))
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not an array", key);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Iterate over all array items
|
||||
for(int i = 0; i < cJSON_GetArraySize(val->json); i++)
|
||||
{
|
||||
// Get array item
|
||||
cJSON *item = cJSON_GetArrayItem(val->json, i);
|
||||
|
||||
// Check if it's a string
|
||||
if(!cJSON_IsString(item))
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: not a string", key, i);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Count the number of elements in the string
|
||||
unsigned int elements = 1;
|
||||
for(unsigned int j = 0; j < strlen(item->valuestring); j++)
|
||||
if(item->valuestring[j] == ',')
|
||||
elements++;
|
||||
|
||||
// Check if it's in the form "<enabled>,<ip-address>[/<prefix-len>],<server>[#<port>],<domain>"
|
||||
// Mandatory elements are: <enabled>, <ip-address>, <server>, and <domain>
|
||||
// Optional elements are: [/<prefix-len>] and [#<port>]
|
||||
char *str = strdup(item->valuestring);
|
||||
char *tmp = str, *s = NULL;
|
||||
unsigned int e = 0;
|
||||
|
||||
while((s = strsep(&tmp, ",")) != NULL)
|
||||
{
|
||||
// Check if it's a valid element
|
||||
if(strlen(s) == 0)
|
||||
{
|
||||
// Contains an empty string
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: contains two commas following each other immediately", key, i);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
// Check if the zeroth element is a boolean
|
||||
if(e == 0)
|
||||
{
|
||||
if(strcmp(s, "true") != 0 && strcmp(s, "false") != 0)
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: <enabled> not a boolean (\"%s\")", key, i, s);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Check if the first element is an IP address
|
||||
else if(e == 1)
|
||||
{
|
||||
// Extract IP and prefix length (if present)
|
||||
char *ip = strsep(&s, "/");
|
||||
char *prefix = strsep(&s, "/");
|
||||
if(strlen(ip) == 0)
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: <ip-address> empty", key, i);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if IP is valid
|
||||
struct in_addr addr = { 0 };
|
||||
struct in6_addr addr6 = { 0 };
|
||||
const bool ipv4 = inet_pton(AF_INET, ip, &addr) == 1;
|
||||
const bool ipv6 = inet_pton(AF_INET6, ip, &addr6) == 1;
|
||||
if(!ipv4 && !ipv6)
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: <ip-address> neither a valid IPv4 nor IPv6 address (\"%s\")", key, i, ip);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if prefix length is valid (if present)
|
||||
if(prefix != NULL)
|
||||
{
|
||||
if(strlen(prefix) == 0)
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: <prefix-len> empty", key, i);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
const int prefix_int = atoi(prefix);
|
||||
if(prefix_int < 0 || (ipv4 && prefix_int > 32) || (ipv6 && prefix_int > 128))
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: <prefix-len> not a valid %sprefix length (\"%s\")",
|
||||
key, i, ipv4 ? "IPv4 " : ipv6 ? "IPv6 " : "", prefix);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check if the second element is a valid server (either an IP address or a domain, optionally with a port)
|
||||
else if(e == 2)
|
||||
{
|
||||
// Extract server and port (if present)
|
||||
char *server = strsep(&s, "#");
|
||||
char *port = strsep(&s, "#");
|
||||
if(strlen(server) == 0)
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: <server> empty", key, i);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct in_addr addr = { 0 };
|
||||
struct in6_addr addr6 = { 0 };
|
||||
const bool server_ipv4 = inet_pton(AF_INET, server, &addr) == 1;
|
||||
const bool server_ipv6 = inet_pton(AF_INET6, server, &addr6) == 1;
|
||||
const bool server_domain = valid_domain(server, strlen(server), false);
|
||||
|
||||
// Check if server is valid
|
||||
if(!server_ipv4 && !server_ipv6 && !server_domain)
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: <server> neither a valid domain nor an IPv4 or IPv6 address (\"%s\")", key, i, server);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if port is valid (if present)
|
||||
if(port != NULL)
|
||||
{
|
||||
if(strlen(port) == 0)
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: specified server <port> empty", key, i);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
const int port_int = atoi(port);
|
||||
if(port_int < 0 || port_int > 65535)
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: server <port> not a valid port (\"%s\")", key, i, port);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check if the third element is a valid domain
|
||||
else if(e == 3)
|
||||
{
|
||||
if(!valid_domain(s, strlen(s), false))
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: <domain> not a valid domain (\"%s\")", key, i, s);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Check if there are too many elements
|
||||
else
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: too many elements", key, i);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Increment element counter
|
||||
e++;
|
||||
}
|
||||
|
||||
// Check if there are all required elements
|
||||
if(e < 4)
|
||||
{
|
||||
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: entry does not have all required elements (<enabled>,<ip-address>[/<prefix-len>],<server>[#<port>],<domain>)", key, i);
|
||||
free(str);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Return success
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2023 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* FTL Engine
|
||||
* Config validation routines
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
#ifndef CONFIG_VALIDATOR_H
|
||||
#define CONFIG_VALIDATOR_H
|
||||
|
||||
#include "FTL.h"
|
||||
#include "config/config.h"
|
||||
|
||||
bool validate_stub(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]) __attribute__((const));
|
||||
bool validate_dns_hosts(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]);
|
||||
bool validate_dns_cnames(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]);
|
||||
bool validate_cidr(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]);
|
||||
bool validate_ip_port(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]);
|
||||
bool validate_domain(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]);
|
||||
bool validate_filepath(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]);
|
||||
bool validate_filepath_empty(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]);
|
||||
bool validate_filepath_dash(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]);
|
||||
bool validate_regex_array(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]);
|
||||
bool validate_dns_revServers(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]);
|
||||
|
||||
#endif // CONFIG_VALIDATOR_H
|
|
@ -1575,6 +1575,8 @@ bool gravityDB_addToTable(const enum gravity_list_type listtype, tablerow *row,
|
|||
// Nothing to be done for these tables
|
||||
case GRAVITY_GROUPS:
|
||||
case GRAVITY_ADLISTS:
|
||||
case GRAVITY_ADLISTS_BLOCK:
|
||||
case GRAVITY_ADLISTS_ALLOW:
|
||||
case GRAVITY_CLIENTS:
|
||||
break;
|
||||
|
||||
|
@ -1597,7 +1599,9 @@ bool gravityDB_addToTable(const enum gravity_list_type listtype, tablerow *row,
|
|||
{
|
||||
querystr = "INSERT INTO \"group\" (name,enabled,description) VALUES (:item,:enabled,:comment);";
|
||||
}
|
||||
else if(listtype == GRAVITY_ADLISTS)
|
||||
else if(listtype == GRAVITY_ADLISTS ||
|
||||
listtype == GRAVITY_ADLISTS_BLOCK ||
|
||||
listtype == GRAVITY_ADLISTS_ALLOW)
|
||||
{
|
||||
querystr = "INSERT INTO adlist (address,enabled,comment,type) VALUES (:item,:enabled,:comment,:type);";
|
||||
}
|
||||
|
@ -1605,7 +1609,7 @@ bool gravityDB_addToTable(const enum gravity_list_type listtype, tablerow *row,
|
|||
{
|
||||
querystr = "INSERT INTO client (ip,comment) VALUES (:item,:comment);";
|
||||
}
|
||||
else // domainlis
|
||||
else // domainlist
|
||||
{
|
||||
querystr = "INSERT INTO domainlist (domain,type,enabled,comment) VALUES (:item,:type,:enabled,:comment);";
|
||||
}
|
||||
|
@ -1625,9 +1629,11 @@ bool gravityDB_addToTable(const enum gravity_list_type listtype, tablerow *row,
|
|||
querystr = "UPDATE \"group\" SET name = :name, enabled = :enabled, description = :comment "
|
||||
"WHERE name = :item";
|
||||
}
|
||||
else if(listtype == GRAVITY_ADLISTS)
|
||||
else if(listtype == GRAVITY_ADLISTS ||
|
||||
listtype == GRAVITY_ADLISTS_BLOCK ||
|
||||
listtype == GRAVITY_ADLISTS_ALLOW)
|
||||
querystr = "INSERT INTO adlist (address,enabled,comment,type) VALUES (:item,:enabled,:comment,:type) "\
|
||||
"ON CONFLICT(address) DO UPDATE SET enabled = :enabled, comment = :comment, type = :type;";
|
||||
"ON CONFLICT(address,type) DO UPDATE SET enabled = :enabled, comment = :comment, type = :type;";
|
||||
else if(listtype == GRAVITY_CLIENTS)
|
||||
querystr = "INSERT INTO client (ip,comment) VALUES (:item,:comment) "\
|
||||
"ON CONFLICT(ip) DO UPDATE SET comment = :comment;";
|
||||
|
@ -1825,11 +1831,14 @@ bool gravityDB_delFromTable(const enum gravity_list_type listtype, const cJSON*
|
|||
return false;
|
||||
}
|
||||
|
||||
const bool isDomain = listtype == GRAVITY_DOMAINLIST_ALLOW_EXACT ||
|
||||
listtype == GRAVITY_DOMAINLIST_DENY_EXACT ||
|
||||
listtype == GRAVITY_DOMAINLIST_ALLOW_REGEX ||
|
||||
listtype == GRAVITY_DOMAINLIST_DENY_REGEX ||
|
||||
listtype == GRAVITY_DOMAINLIST_ALL_ALL; // batch delete
|
||||
const bool hasType = listtype == GRAVITY_DOMAINLIST_ALLOW_EXACT ||
|
||||
listtype == GRAVITY_DOMAINLIST_DENY_EXACT ||
|
||||
listtype == GRAVITY_DOMAINLIST_ALLOW_REGEX ||
|
||||
listtype == GRAVITY_DOMAINLIST_DENY_REGEX ||
|
||||
listtype == GRAVITY_DOMAINLIST_ALL_ALL ||
|
||||
listtype == GRAVITY_ADLISTS ||
|
||||
listtype == GRAVITY_ADLISTS_BLOCK ||
|
||||
listtype == GRAVITY_ADLISTS_ALLOW;
|
||||
|
||||
// Begin transaction
|
||||
const char *querystr = "BEGIN TRANSACTION;";
|
||||
|
@ -1843,7 +1852,7 @@ bool gravityDB_delFromTable(const enum gravity_list_type listtype, const cJSON*
|
|||
}
|
||||
|
||||
// Create temporary table for JSON argument
|
||||
if(isDomain)
|
||||
if(hasType)
|
||||
// Create temporary table for domains to be deleted
|
||||
querystr = "CREATE TEMPORARY TABLE deltable (type INT, item TEXT);";
|
||||
else
|
||||
|
@ -1885,7 +1894,7 @@ bool gravityDB_delFromTable(const enum gravity_list_type listtype, const cJSON*
|
|||
sqlite3_finalize(stmt);
|
||||
|
||||
// Prepare statement for inserting items into virtual table
|
||||
if(isDomain)
|
||||
if(hasType)
|
||||
querystr = "INSERT INTO deltable (type, item) VALUES (:type, :item);";
|
||||
else
|
||||
querystr = "INSERT INTO deltable (item) VALUES (:item);";
|
||||
|
@ -1910,12 +1919,24 @@ bool gravityDB_delFromTable(const enum gravity_list_type listtype, const cJSON*
|
|||
{
|
||||
// Bind type to prepared statement
|
||||
cJSON *type = cJSON_GetObjectItemCaseSensitive(it, "type");
|
||||
int type_int = cJSON_IsNumber(type) ? type->valueint : -1;
|
||||
if(listtype == GRAVITY_ADLISTS_BLOCK)
|
||||
type_int = ADLIST_BLOCK;
|
||||
else if(listtype == GRAVITY_ADLISTS_ALLOW)
|
||||
type_int = ADLIST_ALLOW;
|
||||
else if(listtype == GRAVITY_ADLISTS && cJSON_IsString(type))
|
||||
{
|
||||
if(strcasecmp(type->valuestring, "block") == 0)
|
||||
type_int = ADLIST_BLOCK;
|
||||
else if(strcasecmp(type->valuestring, "allow") == 0)
|
||||
type_int = ADLIST_ALLOW;
|
||||
}
|
||||
const int type_idx = sqlite3_bind_parameter_index(stmt, ":type");
|
||||
if(type_idx > 0 && (!cJSON_IsNumber(type) || (rc = sqlite3_bind_int(stmt, type_idx, type->valueint)) != SQLITE_OK))
|
||||
if(type_idx > 0 && (rc = sqlite3_bind_int(stmt, type_idx, type_int)) != SQLITE_OK)
|
||||
{
|
||||
*message = sqlite3_errmsg(gravity_db);
|
||||
log_err("gravityDB_delFromTable(%d): Failed to bind type (error %d) - %s",
|
||||
type->valueint, rc, *message);
|
||||
type_int, rc, *message);
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
|
@ -1981,12 +2002,15 @@ bool gravityDB_delFromTable(const enum gravity_list_type listtype, const cJSON*
|
|||
const char *querystrs[4] = {NULL, NULL, NULL, NULL};
|
||||
if(listtype == GRAVITY_GROUPS)
|
||||
querystrs[0] = "DELETE FROM \"group\" WHERE name IN (SELECT item FROM deltable);";
|
||||
else if(listtype == GRAVITY_ADLISTS)
|
||||
else if(listtype == GRAVITY_ADLISTS ||
|
||||
listtype == GRAVITY_ADLISTS_BLOCK ||
|
||||
listtype == GRAVITY_ADLISTS_ALLOW)
|
||||
{
|
||||
// This is actually a three-step deletion to satisfy foreign-key constraints
|
||||
querystrs[0] = "DELETE FROM gravity WHERE adlist_id IN (SELECT id FROM adlist WHERE address IN (SELECT item FROM deltable));";
|
||||
querystrs[1] = "DELETE FROM antigravity WHERE adlist_id IN (SELECT id FROM adlist WHERE address IN (SELECT item FROM deltable));";
|
||||
querystrs[2] = "DELETE FROM adlist WHERE address IN (SELECT item FROM deltable);";
|
||||
// This is actually a four-step deletion to satisfy foreign-key constraints
|
||||
querystrs[0] = "DELETE FROM gravity WHERE adlist_id IN (SELECT id FROM adlist WHERE address IN (SELECT item FROM deltable WHERE type = 0));";
|
||||
querystrs[1] = "DELETE FROM antigravity WHERE adlist_id IN (SELECT id FROM adlist WHERE address IN (SELECT item FROM deltable WHERE type = 1));";
|
||||
querystrs[2] = "DELETE FROM adlist WHERE address IN (SELECT item FROM deltable WHERE type = 0) AND type = 0;";
|
||||
querystrs[3] = "DELETE FROM adlist WHERE address IN (SELECT item FROM deltable WHERE type = 1) AND type = 1;";
|
||||
}
|
||||
else if(listtype == GRAVITY_CLIENTS)
|
||||
querystrs[0] = "DELETE FROM client WHERE ip IN (SELECT item FROM deltable);";
|
||||
|
@ -2096,12 +2120,16 @@ bool gravityDB_readTable(const enum gravity_list_type listtype,
|
|||
case GRAVITY_DOMAINLIST_ALL_ALL:
|
||||
type = "0,1,2,3";
|
||||
break;
|
||||
|
||||
// No type required for these tables
|
||||
case GRAVITY_GRAVITY:
|
||||
case GRAVITY_ANTIGRAVITY:
|
||||
case GRAVITY_GROUPS:
|
||||
case GRAVITY_ADLISTS:
|
||||
case GRAVITY_CLIENTS:
|
||||
// No type required for these tables
|
||||
case GRAVITY_ADLISTS:
|
||||
// Type is set in the SQL query directly
|
||||
case GRAVITY_ADLISTS_BLOCK:
|
||||
case GRAVITY_ADLISTS_ALLOW:
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2133,19 +2161,29 @@ bool gravityDB_readTable(const enum gravity_list_type listtype,
|
|||
}
|
||||
snprintf(querystr, buflen, "SELECT id,name,enabled,date_added,date_modified,description AS comment FROM \"group\"%s;", filter);
|
||||
}
|
||||
else if(listtype == GRAVITY_ADLISTS)
|
||||
else if(listtype == GRAVITY_ADLISTS ||
|
||||
listtype == GRAVITY_ADLISTS_BLOCK ||
|
||||
listtype == GRAVITY_ADLISTS_ALLOW)
|
||||
{
|
||||
if(listtype == GRAVITY_ADLISTS_BLOCK)
|
||||
filter = "type = 0";
|
||||
else if(listtype == GRAVITY_ADLISTS_ALLOW)
|
||||
filter = "type = 1";
|
||||
else
|
||||
filter = "TRUE";
|
||||
|
||||
const char *filter2 = "";
|
||||
if(item != NULL && item[0] != '\0')
|
||||
{
|
||||
if(exact)
|
||||
filter = " WHERE address = :item";
|
||||
filter2 = " AND address = :item";
|
||||
else
|
||||
filter = " WHERE address LIKE :item";
|
||||
filter2 = " AND address LIKE :item";
|
||||
}
|
||||
snprintf(querystr, buflen, "SELECT id,type,address,enabled,date_added,date_modified,comment,"
|
||||
"(SELECT GROUP_CONCAT(group_id) FROM adlist_by_group g WHERE g.adlist_id = a.id) AS group_ids,"
|
||||
"date_updated,number,invalid_domains,status,abp_entries "
|
||||
"FROM adlist a%s;", filter);
|
||||
"FROM adlist a WHERE %s%s;", filter, filter2);
|
||||
}
|
||||
else if(listtype == GRAVITY_CLIENTS)
|
||||
{
|
||||
|
@ -2312,6 +2350,8 @@ bool gravityDB_readTableGetRow(const enum gravity_list_type listtype, tablerow *
|
|||
}
|
||||
}
|
||||
else if(listtype == GRAVITY_ADLISTS ||
|
||||
listtype == GRAVITY_ADLISTS_ALLOW ||
|
||||
listtype == GRAVITY_ADLISTS_BLOCK ||
|
||||
listtype == GRAVITY_GRAVITY ||
|
||||
listtype == GRAVITY_ANTIGRAVITY)
|
||||
{
|
||||
|
@ -2328,6 +2368,10 @@ bool gravityDB_readTableGetRow(const enum gravity_list_type listtype, tablerow *
|
|||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
row->type = "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
else if(strcasecmp(cname, "domain") == 0)
|
||||
|
@ -2425,9 +2469,14 @@ bool gravityDB_edit_groups(const enum gravity_list_type listtype, cJSON *groups,
|
|||
del_querystr = "DELETE FROM client_by_group WHERE client_id = :id;";
|
||||
add_querystr = "INSERT INTO client_by_group (client_id,group_id) VALUES (:id,:gid);";
|
||||
}
|
||||
else if(listtype == GRAVITY_ADLISTS)
|
||||
else if(listtype == GRAVITY_ADLISTS ||
|
||||
listtype == GRAVITY_ADLISTS_BLOCK ||
|
||||
listtype == GRAVITY_ADLISTS_ALLOW)
|
||||
{
|
||||
get_querystr = "SELECT id FROM adlist WHERE address = :item";
|
||||
if(listtype == GRAVITY_ADLISTS)
|
||||
get_querystr = "SELECT id FROM adlist WHERE address = :item";
|
||||
else
|
||||
get_querystr = "SELECT id FROM adlist WHERE address = :item AND type = :type";
|
||||
del_querystr = "DELETE FROM adlist_by_group WHERE adlist_id = :id;";
|
||||
add_querystr = "INSERT INTO adlist_by_group (adlist_id,group_id) VALUES (:id,:gid);";
|
||||
}
|
||||
|
|
|
@ -753,7 +753,7 @@ static int update_netDB_interface(sqlite3 *db, const int network_id, const char
|
|||
|
||||
// Loop over all clients known to FTL and ensure we add them all to the database
|
||||
static bool add_FTL_clients_to_network_table(sqlite3 *db, const enum arp_status *client_status,
|
||||
const int clients, time_t now, unsigned int *additional_entries)
|
||||
const int clients, const time_t now, unsigned int *additional_entries)
|
||||
{
|
||||
// Return early if database is known to be broken
|
||||
if(FTLDBerror())
|
||||
|
@ -1546,6 +1546,7 @@ void parse_neighbor_cache(sqlite3* db)
|
|||
free(client_status);
|
||||
return;
|
||||
}
|
||||
|
||||
free(client_status);
|
||||
client_status = NULL;
|
||||
|
||||
|
|
|
@ -112,19 +112,47 @@ bool init_memory_database(void)
|
|||
if(!attach_database(_memdb, NULL, config.files.database.v.s, "disk"))
|
||||
return false;
|
||||
|
||||
// Change journal mode to WAL
|
||||
// - WAL is significantly faster in most scenarios.
|
||||
// - WAL provides more concurrency as readers do not block writers and a
|
||||
// writer does not block readers. Reading and writing can proceed
|
||||
// concurrently.
|
||||
// - Disk I/O operations tends to be more sequential using WAL.
|
||||
rc = sqlite3_exec(_memdb, "PRAGMA disk.journal_mode=WAL", NULL, NULL, NULL);
|
||||
if( rc != SQLITE_OK )
|
||||
// Enable WAL mode for the on-disk database (pihole-FTL.db) if
|
||||
// configured (default is yes). User may not want to enable WAL
|
||||
// mode if the database is on a network share as all processes
|
||||
// accessing the database must be on the same host in WAL mode.
|
||||
if(config.database.useWAL.v.b)
|
||||
{
|
||||
log_err("init_memory_database(): Step error while trying to set journal mode: %s",
|
||||
sqlite3_errstr(rc));
|
||||
sqlite3_close(_memdb);
|
||||
return false;
|
||||
// Change journal mode to WAL
|
||||
// - WAL is significantly faster in most scenarios.
|
||||
// - WAL provides more concurrency as readers do not block writers and a
|
||||
// writer does not block readers. Reading and writing can proceed
|
||||
// concurrently.
|
||||
// - Disk I/O operations tend to be more sequential using WAL.
|
||||
rc = sqlite3_exec(_memdb, "PRAGMA disk.journal_mode=WAL", NULL, NULL, NULL);
|
||||
if( rc != SQLITE_OK )
|
||||
{
|
||||
log_err("init_memory_database(): Step error while trying to set journal mode: %s",
|
||||
sqlite3_errstr(rc));
|
||||
sqlite3_close(_memdb);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unlike the other journaling modes, PRAGMA journal_mode=WAL is
|
||||
// persistent. If a process sets WAL mode, then closes and
|
||||
// reopens the database, the database will come back in WAL
|
||||
// mode. In contrast, if a process sets (for example) PRAGMA
|
||||
// journal_mode=TRUNCATE and then closes and reopens the
|
||||
// database will come back up in the default rollback mode of
|
||||
// DELETE rather than the previous TRUNCATE setting.
|
||||
|
||||
// Change journal mode back to DELETE due to user configuration
|
||||
// (might have been changed to WAL before)
|
||||
rc = sqlite3_exec(_memdb, "PRAGMA disk.journal_mode=DELETE", NULL, NULL, NULL);
|
||||
if( rc != SQLITE_OK )
|
||||
{
|
||||
log_err("init_memory_database(): Step error while trying to set journal mode: %s",
|
||||
sqlite3_errstr(rc));
|
||||
sqlite3_close(_memdb);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Everything went well
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -126,6 +126,7 @@ static const struct {
|
|||
{ 258, "AVC" }, /* Application Visibility and Control [Wolfgang_Riedel] AVC/avc-completed-template 2016-02-26*/
|
||||
{ 259, "DOA" }, /* Digital Object Architecture [draft-durand-doa-over-dns] DOA/doa-completed-template 2017-08-30*/
|
||||
{ 260, "AMTRELAY" }, /* Automatic Multicast Tunneling Relay [RFC8777] AMTRELAY/amtrelay-completed-template 2019-02-06*/
|
||||
{ 261, "RESINFO" }, /* Resolver Information as Key/Value Pairs https://datatracker.ietf.org/doc/draft-ietf-add-resolver-info/06/ */
|
||||
{ 32768, "TA" }, /* DNSSEC Trust Authorities [Sam_Weiler][http://cameo.library.cmu.edu/][ Deploying DNSSEC Without a Signed Root. Technical Report 1999-19, Information Networking Institute, Carnegie Mellon University, April 2004.] 2005-12-13*/
|
||||
{ 32769, "DLV" }, /* DNSSEC Lookaside Validation (OBSOLETE) [RFC8749][RFC4431] */
|
||||
};
|
||||
|
@ -441,18 +442,21 @@ unsigned int cache_remove_uid(const unsigned int uid)
|
|||
{
|
||||
int i;
|
||||
unsigned int removed = 0;
|
||||
struct crec *crecp, **up;
|
||||
struct crec *crecp, *tmp, **up;
|
||||
|
||||
for (i = 0; i < hash_size; i++)
|
||||
for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = crecp->hash_next)
|
||||
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && crecp->uid == uid)
|
||||
{
|
||||
*up = crecp->hash_next;
|
||||
free(crecp);
|
||||
removed++;
|
||||
}
|
||||
else
|
||||
up = &crecp->hash_next;
|
||||
for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = tmp)
|
||||
{
|
||||
tmp = crecp->hash_next;
|
||||
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && crecp->uid == uid)
|
||||
{
|
||||
*up = tmp;
|
||||
free(crecp);
|
||||
removed++;
|
||||
}
|
||||
else
|
||||
up = &crecp->hash_next;
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
@ -815,32 +819,28 @@ void cache_end_insert(void)
|
|||
read_write(daemon->pipe_to_parent, (unsigned char *)name, m, 0);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->ttd, sizeof(new_chain->ttd), 0);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&flags, sizeof(flags), 0);
|
||||
|
||||
if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_RR))
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr, sizeof(new_chain->addr), 0);
|
||||
|
||||
if (flags & F_RR)
|
||||
{
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr, sizeof(new_chain->addr), 0);
|
||||
|
||||
if (flags & F_RR)
|
||||
{
|
||||
/* A negative RR entry is possible and has no data, obviously. */
|
||||
if (!(flags & F_NEG) && (flags & F_KEYTAG))
|
||||
blockdata_write(new_chain->addr.rrblock.rrdata, new_chain->addr.rrblock.datalen, daemon->pipe_to_parent);
|
||||
}
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (flags & F_DNSKEY)
|
||||
{
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), 0);
|
||||
blockdata_write(new_chain->addr.key.keydata, new_chain->addr.key.keylen, daemon->pipe_to_parent);
|
||||
}
|
||||
else if (flags & F_DS)
|
||||
{
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), 0);
|
||||
/* A negative DS entry is possible and has no data, obviously. */
|
||||
if (!(flags & F_NEG))
|
||||
blockdata_write(new_chain->addr.ds.keydata, new_chain->addr.ds.keylen, daemon->pipe_to_parent);
|
||||
}
|
||||
#endif
|
||||
/* A negative RR entry is possible and has no data, obviously. */
|
||||
if (!(flags & F_NEG) && (flags & F_KEYTAG))
|
||||
blockdata_write(new_chain->addr.rrblock.rrdata, new_chain->addr.rrblock.datalen, daemon->pipe_to_parent);
|
||||
}
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (flags & F_DNSKEY)
|
||||
{
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), 0);
|
||||
blockdata_write(new_chain->addr.key.keydata, new_chain->addr.key.keylen, daemon->pipe_to_parent);
|
||||
}
|
||||
else if (flags & F_DS)
|
||||
{
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), 0);
|
||||
/* A negative DS entry is possible and has no data, obviously. */
|
||||
if (!(flags & F_NEG))
|
||||
blockdata_write(new_chain->addr.ds.keydata, new_chain->addr.ds.keylen, daemon->pipe_to_parent);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -851,7 +851,18 @@ void cache_end_insert(void)
|
|||
if (daemon->pipe_to_parent != -1)
|
||||
{
|
||||
ssize_t m = -1;
|
||||
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), 0);
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* Sneak out possibly updated crypto HWM values. */
|
||||
m = daemon->metrics[METRIC_CRYPTO_HWM];
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), 0);
|
||||
m = daemon->metrics[METRIC_SIG_FAIL_HWM];
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), 0);
|
||||
m = daemon->metrics[METRIC_WORK_HWM];
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
new_chain = NULL;
|
||||
|
@ -870,7 +881,7 @@ int cache_recv_insert(time_t now, int fd)
|
|||
|
||||
cache_start_insert();
|
||||
|
||||
while(1)
|
||||
while (1)
|
||||
{
|
||||
|
||||
if (!read_write(fd, (unsigned char *)&m, sizeof(m), 1))
|
||||
|
@ -878,13 +889,29 @@ int cache_recv_insert(time_t now, int fd)
|
|||
|
||||
if (m == -1)
|
||||
{
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* Sneak in possibly updated crypto HWM. */
|
||||
if (!read_write(fd, (unsigned char *)&m, sizeof(m), 1))
|
||||
return 0;
|
||||
if (m > daemon->metrics[METRIC_CRYPTO_HWM])
|
||||
daemon->metrics[METRIC_CRYPTO_HWM] = m;
|
||||
if (!read_write(fd, (unsigned char *)&m, sizeof(m), 1))
|
||||
return 0;
|
||||
if (m > daemon->metrics[METRIC_SIG_FAIL_HWM])
|
||||
daemon->metrics[METRIC_SIG_FAIL_HWM] = m;
|
||||
if (!read_write(fd, (unsigned char *)&m, sizeof(m), 1))
|
||||
return 0;
|
||||
if (m > daemon->metrics[METRIC_WORK_HWM])
|
||||
daemon->metrics[METRIC_WORK_HWM] = m;
|
||||
#endif
|
||||
cache_end_insert();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!read_write(fd, (unsigned char *)daemon->namebuff, m, 1) ||
|
||||
!read_write(fd, (unsigned char *)&ttd, sizeof(ttd), 1) ||
|
||||
!read_write(fd, (unsigned char *)&flags, sizeof(flags), 1))
|
||||
!read_write(fd, (unsigned char *)&flags, sizeof(flags), 1) ||
|
||||
!read_write(fd, (unsigned char *)&addr, sizeof(addr), 1))
|
||||
return 0;
|
||||
|
||||
daemon->namebuff[m] = 0;
|
||||
|
@ -915,30 +942,23 @@ int cache_recv_insert(time_t now, int fd)
|
|||
{
|
||||
unsigned short class = C_IN;
|
||||
|
||||
if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_RR))
|
||||
{
|
||||
if (!read_write(fd, (unsigned char *)&addr, sizeof(addr), 1))
|
||||
return 0;
|
||||
|
||||
if ((flags & F_RR) && !(flags & F_NEG) && (flags & F_KEYTAG)
|
||||
&& !(addr.rrblock.rrdata = blockdata_read(fd, addr.rrblock.datalen)))
|
||||
return 0;
|
||||
if ((flags & F_RR) && !(flags & F_NEG) && (flags & F_KEYTAG)
|
||||
&& !(addr.rrblock.rrdata = blockdata_read(fd, addr.rrblock.datalen)))
|
||||
return 0;
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (flags & F_DNSKEY)
|
||||
{
|
||||
if (!read_write(fd, (unsigned char *)&class, sizeof(class), 1) ||
|
||||
!(addr.key.keydata = blockdata_read(fd, addr.key.keylen)))
|
||||
return 0;
|
||||
}
|
||||
else if (flags & F_DS)
|
||||
{
|
||||
if (!read_write(fd, (unsigned char *)&class, sizeof(class), 1) ||
|
||||
(!(flags & F_NEG) && !(addr.key.keydata = blockdata_read(fd, addr.key.keylen))))
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if (flags & F_DNSKEY)
|
||||
{
|
||||
if (!read_write(fd, (unsigned char *)&class, sizeof(class), 1) ||
|
||||
!(addr.key.keydata = blockdata_read(fd, addr.key.keylen)))
|
||||
return 0;
|
||||
}
|
||||
|
||||
else if (flags & F_DS)
|
||||
{
|
||||
if (!read_write(fd, (unsigned char *)&class, sizeof(class), 1) ||
|
||||
(!(flags & F_NEG) && !(addr.key.keydata = blockdata_read(fd, addr.key.keylen))))
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
crecp = really_insert(daemon->namebuff, &addr, class, now, ttl, flags);
|
||||
}
|
||||
}
|
||||
|
@ -1839,8 +1859,18 @@ static void dump_cache_entry(struct crec *cache, time_t now)
|
|||
p = buff;
|
||||
|
||||
*a = 0;
|
||||
if (strlen(n) == 0 && !(cache->flags & F_REVERSE))
|
||||
n = "<Root>";
|
||||
|
||||
if (cache->flags & F_REVERSE)
|
||||
{
|
||||
if ((cache->flags & F_NEG))
|
||||
n = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strlen(n) == 0)
|
||||
n = "<Root>";
|
||||
}
|
||||
|
||||
p += sprintf(p, "%-30.30s ", sanitise(n));
|
||||
if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache))
|
||||
a = sanitise(cache_get_cname_target(cache));
|
||||
|
@ -2005,9 +2035,19 @@ void dump_cache(time_t now)
|
|||
#ifdef HAVE_AUTH
|
||||
my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->metrics[METRIC_DNS_AUTH_ANSWERED]);
|
||||
#endif
|
||||
#ifdef HAVE_DNSSEC
|
||||
my_syslog(LOG_INFO, _("DNSSEC per-query subqueries HWM %u"), daemon->metrics[METRIC_WORK_HWM]);
|
||||
my_syslog(LOG_INFO, _("DNSSEC per-query crypto work HWM %u"), daemon->metrics[METRIC_CRYPTO_HWM]);
|
||||
my_syslog(LOG_INFO, _("DNSSEC per-RRSet signature fails HWM %u"), daemon->metrics[METRIC_SIG_FAIL_HWM]);
|
||||
#endif
|
||||
|
||||
blockdata_report();
|
||||
|
||||
my_syslog(LOG_INFO, _("child processes for TCP requests: in use %zu, highest since last SIGUSR1 %zu, max allowed %zu."),
|
||||
daemon->metrics[METRIC_TCP_CONNECTIONS],
|
||||
daemon->max_procs_used,
|
||||
daemon->max_procs);
|
||||
daemon->max_procs_used = daemon->metrics[METRIC_TCP_CONNECTIONS];
|
||||
|
||||
/* sum counts from different records for same server */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
serv->flags &= ~SERV_MARK;
|
||||
|
@ -2161,6 +2201,11 @@ const char *edestr(int ede)
|
|||
case EDE_NO_AUTH: return "no reachable authority";
|
||||
case EDE_NETERR: return "network error";
|
||||
case EDE_INVALID_DATA: return "invalid data";
|
||||
case EDE_SIG_E_B_V: return "signature expired before valid";
|
||||
case EDE_TOO_EARLY: return "too early";
|
||||
case EDE_UNS_NS3_ITER: return "unsupported NSEC3 iterations value";
|
||||
case EDE_UNABLE_POLICY: return "uanble to conform to policy";
|
||||
case EDE_SYNTHESIZED: return "synthesized";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -15,14 +15,17 @@
|
|||
*/
|
||||
|
||||
#define FTABSIZ 150 /* max number of outstanding requests (default) */
|
||||
#define MAX_PROCS 60 /* max no children for TCP requests */
|
||||
#define MAX_PROCS 60 /* default max no children for TCP requests */
|
||||
#define CHILD_LIFETIME 300 /* secs 'till terminated (RFC1035 suggests > 120s) */
|
||||
#define TCP_MAX_QUERIES 100 /* Maximum number of queries per incoming TCP connection */
|
||||
#define TCP_BACKLOG 32 /* kernel backlog limit for TCP connections */
|
||||
#define EDNS_PKTSZ 1232 /* default max EDNS.0 UDP packet from from /dnsflagday.net/2020 */
|
||||
#define SAFE_PKTSZ 1232 /* "go anywhere" UDP packet size, see https://dnsflagday.net/2020/ */
|
||||
#define KEYBLOCK_LEN 40 /* choose to minimise fragmentation when storing DNSSEC keys */
|
||||
#define DNSSEC_WORK 50 /* Max number of queries to validate one question */
|
||||
#define DNSSEC_LIMIT_WORK 40 /* Max number of queries to validate one question */
|
||||
#define DNSSEC_LIMIT_SIG_FAIL 20 /* Number of signature that can fail to validate in one answer */
|
||||
#define DNSSEC_LIMIT_CRYPTO 200 /* max no. of crypto operations to validate one query. */
|
||||
#define DNSSEC_LIMIT_NSEC3_ITERS 150 /* Max. number if iterations allowed in NSEC3 record. */
|
||||
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
|
||||
#define SMALL_PORT_RANGE 30 /* If DNS port range is smaller than this, use different allocation. */
|
||||
#define FORWARD_TEST 1000 /* try all servers every 1000 queries */
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -92,7 +92,7 @@ void dhcp6_packet(time_t now)
|
|||
struct iface_param parm;
|
||||
struct cmsghdr *cmptr;
|
||||
struct msghdr msg;
|
||||
int if_index = 0;
|
||||
uint32_t if_index = 0;
|
||||
union {
|
||||
struct cmsghdr align; /* this ensures alignment */
|
||||
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -112,8 +112,11 @@
|
|||
#define EDE_NO_AUTH 22 /* No Reachable Authority */
|
||||
#define EDE_NETERR 23 /* Network error */
|
||||
#define EDE_INVALID_DATA 24 /* Invalid Data */
|
||||
|
||||
|
||||
#define EDE_SIG_E_B_V 25 /* Signature Expired before Valid */
|
||||
#define EDE_TOO_EARLY 26 /* To Early */
|
||||
#define EDE_UNS_NS3_ITER 27 /* Unsupported NSEC3 Iterations Value */
|
||||
#define EDE_UNABLE_POLICY 28 /* Unable to conform to policy */
|
||||
#define EDE_SYNTHESIZED 29 /* Synthesized */
|
||||
|
||||
|
||||
struct dns_header {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -36,12 +36,14 @@ static volatile int pipewrite;
|
|||
volatile char FTL_terminate = 0;
|
||||
|
||||
static void set_dns_listeners(void);
|
||||
static void set_tftp_listeners(void);
|
||||
static void check_dns_listeners(time_t now);
|
||||
static void sig_handler(int sig);
|
||||
static void async_event(int pipe, time_t now);
|
||||
static void fatal_event(struct event_desc *ev, char *msg);
|
||||
static int read_event(int fd, struct event_desc *evp, char **msg);
|
||||
static void poll_resolv(int force, int do_reload, time_t now);
|
||||
static void tcp_init(void);
|
||||
|
||||
int main_dnsmasq (int argc, char **argv)
|
||||
{
|
||||
|
@ -135,19 +137,11 @@ int main_dnsmasq (int argc, char **argv)
|
|||
'.' or NAME_ESCAPE then all would have to be escaped, so the
|
||||
presentation format would be twice as long as the spec. */
|
||||
daemon->keyname = safe_malloc((MAXDNAME * 2) + 1);
|
||||
daemon->workspacename = safe_malloc((MAXDNAME * 2) + 1);
|
||||
/* one char flag per possible RR in answer section (may get extended). */
|
||||
daemon->rr_status_sz = 64;
|
||||
daemon->rr_status = safe_malloc(sizeof(*daemon->rr_status) * daemon->rr_status_sz);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||
/* CONNTRACK UBUS code uses this buffer, so if not allocated above,
|
||||
we need to allocate it here. */
|
||||
if (option_bool(OPT_CMARK_ALST_EN) && !daemon->workspacename)
|
||||
daemon->workspacename = safe_malloc((MAXDNAME * 2) + 1);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
if (!daemon->lease_file)
|
||||
|
@ -378,6 +372,13 @@ int main_dnsmasq (int argc, char **argv)
|
|||
|
||||
if (!enumerate_interfaces(1) || !enumerate_interfaces(0))
|
||||
die(_("failed to find list of interfaces: %s"), NULL, EC_MISC);
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
/* Determine lease FQDNs after enumerate_interfaces() call, since it needs
|
||||
to call get_domain and that's only valid for some domain configs once we
|
||||
have interface addresses. */
|
||||
lease_calc_fqdns();
|
||||
#endif
|
||||
|
||||
if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
|
||||
{
|
||||
|
@ -421,11 +422,13 @@ int main_dnsmasq (int argc, char **argv)
|
|||
daemon->numrrand = max_fd/3;
|
||||
/* safe_malloc returns zero'd memory */
|
||||
daemon->randomsocks = safe_malloc(daemon->numrrand * sizeof(struct randfd));
|
||||
|
||||
tcp_init();
|
||||
}
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
if ((daemon->port != 0 || daemon->dhcp || daemon->doing_dhcp6)
|
||||
&& (!option_bool(OPT_NO_RESOLV) || daemon->dynamic_dirs))
|
||||
if ((daemon->port != 0 && !option_bool(OPT_NO_RESOLV)) ||
|
||||
daemon->dynamic_dirs)
|
||||
inotify_dnsmasq_init();
|
||||
else
|
||||
daemon->inotifyfd = -1;
|
||||
|
@ -865,6 +868,8 @@ int main_dnsmasq (int argc, char **argv)
|
|||
|
||||
if (option_bool(OPT_LOCAL_SERVICE))
|
||||
my_syslog(LOG_INFO, _("DNS service limited to local subnets"));
|
||||
else if (option_bool(OPT_LOCALHOST_SERVICE))
|
||||
my_syslog(LOG_INFO, _("DNS service limited to localhost"));
|
||||
}
|
||||
|
||||
my_syslog(LOG_INFO, _("compile time options: %s"), compile_opts);
|
||||
|
@ -1051,8 +1056,10 @@ int main_dnsmasq (int argc, char **argv)
|
|||
pid = getpid();
|
||||
|
||||
daemon->pipe_to_parent = -1;
|
||||
for (i = 0; i < MAX_PROCS; i++)
|
||||
daemon->tcp_pipes[i] = -1;
|
||||
|
||||
if (daemon->port != 0)
|
||||
for (i = 0; i < daemon->max_procs; i++)
|
||||
daemon->tcp_pipes[i] = -1;
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
/* Using inotify, have to select a resolv file at startup */
|
||||
|
@ -1079,7 +1086,12 @@ int main_dnsmasq (int argc, char **argv)
|
|||
(timeout == -1 || timeout > 1000))
|
||||
timeout = 1000;
|
||||
|
||||
set_dns_listeners();
|
||||
if (daemon->port != 0)
|
||||
set_dns_listeners();
|
||||
|
||||
#ifdef HAVE_TFTP
|
||||
set_tftp_listeners();
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DBUS
|
||||
if (option_bool(OPT_DBUS))
|
||||
|
@ -1264,8 +1276,9 @@ int main_dnsmasq (int argc, char **argv)
|
|||
check_ubus_listeners();
|
||||
}
|
||||
#endif
|
||||
|
||||
check_dns_listeners(now);
|
||||
|
||||
if (daemon->port != 0)
|
||||
check_dns_listeners(now);
|
||||
|
||||
#ifdef HAVE_TFTP
|
||||
check_tftp_listeners(now);
|
||||
|
@ -1538,10 +1551,15 @@ static void async_event(int pipe, time_t now)
|
|||
if (errno != EINTR)
|
||||
break;
|
||||
}
|
||||
else
|
||||
for (i = 0 ; i < MAX_PROCS; i++)
|
||||
else if (daemon->port != 0)
|
||||
for (i = 0 ; i < daemon->max_procs; i++)
|
||||
if (daemon->tcp_pids[i] == p)
|
||||
daemon->tcp_pids[i] = 0;
|
||||
{
|
||||
daemon->tcp_pids[i] = 0;
|
||||
/* tcp_pipes == -1 && tcp_pids == 0 required to free slot */
|
||||
if (daemon->tcp_pipes[i] == -1)
|
||||
daemon->metrics[METRIC_TCP_CONNECTIONS]--;
|
||||
}
|
||||
break;
|
||||
|
||||
#if defined(HAVE_SCRIPT)
|
||||
|
@ -1604,9 +1622,10 @@ static void async_event(int pipe, time_t now)
|
|||
|
||||
case EVENT_TERM:
|
||||
/* Knock all our children on the head. */
|
||||
for (i = 0; i < MAX_PROCS; i++)
|
||||
if (daemon->tcp_pids[i] != 0)
|
||||
kill(daemon->tcp_pids[i], SIGALRM);
|
||||
if (daemon->port != 0)
|
||||
for (i = 0; i < daemon->max_procs; i++)
|
||||
if (daemon->tcp_pids[i] != 0)
|
||||
kill(daemon->tcp_pids[i], SIGALRM);
|
||||
|
||||
#if defined(HAVE_SCRIPT) && defined(HAVE_DHCP)
|
||||
/* handle pending lease transitions */
|
||||
|
@ -1756,23 +1775,33 @@ void clear_cache_and_reload(time_t now)
|
|||
#endif
|
||||
}
|
||||
|
||||
static void set_dns_listeners(void)
|
||||
{
|
||||
struct serverfd *serverfdp;
|
||||
struct listener *listener;
|
||||
struct randfd_list *rfl;
|
||||
int i;
|
||||
|
||||
#ifdef HAVE_TFTP
|
||||
static void set_tftp_listeners(void)
|
||||
{
|
||||
int tftp = 0;
|
||||
struct tftp_transfer *transfer;
|
||||
struct listener *listener;
|
||||
|
||||
if (!option_bool(OPT_SINGLE_PORT))
|
||||
for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next)
|
||||
{
|
||||
tftp++;
|
||||
poll_listen(transfer->sockfd, POLLIN);
|
||||
}
|
||||
|
||||
for (listener = daemon->listeners; listener; listener = listener->next)
|
||||
/* tftp == 0 in single-port mode. */
|
||||
if (tftp <= daemon->tftp_max && listener->tftpfd != -1)
|
||||
poll_listen(listener->tftpfd, POLLIN);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void set_dns_listeners(void)
|
||||
{
|
||||
struct serverfd *serverfdp;
|
||||
struct listener *listener;
|
||||
struct randfd_list *rfl;
|
||||
int i;
|
||||
|
||||
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
|
||||
poll_listen(serverfdp->fd, POLLIN);
|
||||
|
@ -1786,7 +1815,7 @@ static void set_dns_listeners(void)
|
|||
poll_listen(rfl->rfd->fd, POLLIN);
|
||||
|
||||
/* check to see if we have free tcp process slots. */
|
||||
for (i = MAX_PROCS - 1; i >= 0; i--)
|
||||
for (i = daemon->max_procs - 1; i >= 0; i--)
|
||||
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
|
||||
break;
|
||||
|
||||
|
@ -1801,16 +1830,10 @@ static void set_dns_listeners(void)
|
|||
we'll be called again when a slot becomes available. */
|
||||
if (listener->tcpfd != -1 && i >= 0)
|
||||
poll_listen(listener->tcpfd, POLLIN);
|
||||
|
||||
#ifdef HAVE_TFTP
|
||||
/* tftp == 0 in single-port mode. */
|
||||
if (tftp <= daemon->tftp_max && listener->tftpfd != -1)
|
||||
poll_listen(listener->tftpfd, POLLIN);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
for (i = 0; i < MAX_PROCS; i++)
|
||||
for (i = 0; i < daemon->max_procs; i++)
|
||||
if (daemon->tcp_pipes[i] != -1)
|
||||
poll_listen(daemon->tcp_pipes[i], POLLIN);
|
||||
}
|
||||
|
@ -1845,13 +1868,16 @@ static void check_dns_listeners(time_t now)
|
|||
to free the process slot. Once the child process has gone, poll()
|
||||
returns POLLHUP, not POLLIN, so have to check for both here. */
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
for (i = 0; i < MAX_PROCS; i++)
|
||||
for (i = 0; i < daemon->max_procs; i++)
|
||||
if (daemon->tcp_pipes[i] != -1 &&
|
||||
poll_check(daemon->tcp_pipes[i], POLLIN | POLLHUP) &&
|
||||
!cache_recv_insert(now, daemon->tcp_pipes[i]))
|
||||
{
|
||||
close(daemon->tcp_pipes[i]);
|
||||
daemon->tcp_pipes[i] = -1;
|
||||
/* tcp_pipes == -1 && tcp_pids == 0 required to free slot */
|
||||
if (daemon->tcp_pids[i] == 0)
|
||||
daemon->metrics[METRIC_TCP_CONNECTIONS]--;
|
||||
}
|
||||
|
||||
for (listener = daemon->listeners; listener; listener = listener->next)
|
||||
|
@ -1860,17 +1886,12 @@ static void check_dns_listeners(time_t now)
|
|||
if (listener->fd != -1 && poll_check(listener->fd, POLLIN))
|
||||
receive_query(listener, now);
|
||||
|
||||
#ifdef HAVE_TFTP
|
||||
if (listener->tftpfd != -1 && poll_check(listener->tftpfd, POLLIN))
|
||||
tftp_request(listener, now);
|
||||
#endif
|
||||
|
||||
/* check to see if we have a free tcp process slot.
|
||||
Note that we can't assume that because we had
|
||||
at least one a poll() time, that we still do.
|
||||
There may be more waiting connections after
|
||||
poll() returns then free process slots. */
|
||||
for (i = MAX_PROCS - 1; i >= 0; i--)
|
||||
for (i = daemon->max_procs - 1; i >= 0; i--)
|
||||
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
|
||||
break;
|
||||
|
||||
|
@ -1986,6 +2007,9 @@ static void check_dns_listeners(time_t now)
|
|||
/* i holds index of free slot */
|
||||
daemon->tcp_pids[i] = p;
|
||||
daemon->tcp_pipes[i] = pipefd[0];
|
||||
daemon->metrics[METRIC_TCP_CONNECTIONS]++;
|
||||
if (daemon->metrics[METRIC_TCP_CONNECTIONS] > daemon->max_procs_used)
|
||||
daemon->max_procs_used = daemon->metrics[METRIC_TCP_CONNECTIONS];
|
||||
}
|
||||
close(confd);
|
||||
|
||||
|
@ -2171,7 +2195,11 @@ int delay_dhcp(time_t start, int sec, int fd, uint32_t addr, unsigned short id)
|
|||
poll_reset();
|
||||
if (fd != -1)
|
||||
poll_listen(fd, POLLIN);
|
||||
set_dns_listeners();
|
||||
if (daemon->port != 0)
|
||||
set_dns_listeners();
|
||||
#ifdef HAVE_TFTP
|
||||
set_tftp_listeners();
|
||||
#endif
|
||||
set_log_writer();
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
|
@ -2189,7 +2217,8 @@ int delay_dhcp(time_t start, int sec, int fd, uint32_t addr, unsigned short id)
|
|||
now = dnsmasq_time();
|
||||
|
||||
check_log_writer(0);
|
||||
check_dns_listeners(now);
|
||||
if (daemon->port != 0)
|
||||
check_dns_listeners(now);
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (daemon->doing_ra && poll_check(daemon->icmp6fd, POLLIN))
|
||||
|
@ -2232,3 +2261,9 @@ void print_dnsmasq_version(const char *yellow, const char *green, const char *bo
|
|||
printf(_("Features: %s\n\n"), compile_opts);
|
||||
}
|
||||
/**************************************************************************************/
|
||||
|
||||
void tcp_init(void)
|
||||
{
|
||||
daemon->tcp_pids = safe_malloc(daemon->max_procs*sizeof(pid_t));
|
||||
daemon->tcp_pipes = safe_malloc(daemon->max_procs*sizeof(int));
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -14,7 +14,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define COPYRIGHT "Copyright (c) 2000-2023 Simon Kelley"
|
||||
#define COPYRIGHT "Copyright (c) 2000-2024 Simon Kelley"
|
||||
|
||||
/* We do defines that influence behavior of stdio.h, so complain
|
||||
if included too early. */
|
||||
|
@ -281,7 +281,8 @@ struct event_desc {
|
|||
#define OPT_NORR 69
|
||||
#define OPT_NO_IDENT 70
|
||||
#define OPT_CACHE_RR 71
|
||||
#define OPT_LAST 72
|
||||
#define OPT_LOCALHOST_SERVICE 72
|
||||
#define OPT_LAST 73
|
||||
|
||||
#define OPTION_BITS (sizeof(unsigned int)*8)
|
||||
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
|
||||
|
@ -340,7 +341,7 @@ union all_addr {
|
|||
in the cache flags. */
|
||||
struct datablock {
|
||||
unsigned short rrtype;
|
||||
unsigned char datalen;
|
||||
unsigned char datalen; /* also length of SOA in negative records. */
|
||||
char data[];
|
||||
} rrdata;
|
||||
};
|
||||
|
@ -762,6 +763,9 @@ struct dyndir {
|
|||
#define DNSSEC_FAIL_NONSEC 0x0040 /* No NSEC */
|
||||
#define DNSSEC_FAIL_NODSSUP 0x0080 /* no supported DS algo. */
|
||||
#define DNSSEC_FAIL_NOKEY 0x0100 /* no DNSKEY */
|
||||
#define DNSSEC_FAIL_NSEC3_ITERS 0x0200 /* too many iterations in NSEC3 */
|
||||
#define DNSSEC_FAIL_BADPACKET 0x0400 /* bad packet */
|
||||
#define DNSSEC_FAIL_WORK 0x0800 /* too much crypto */
|
||||
|
||||
#define STAT_ISEQUAL(a, b) (((a) & 0xffff0000) == (b))
|
||||
|
||||
|
@ -799,7 +803,7 @@ struct frec {
|
|||
struct blockdata *stash; /* Saved reply, whilst we validate */
|
||||
size_t stash_len;
|
||||
#ifdef HAVE_DNSSEC
|
||||
int class, work_counter;
|
||||
int class, work_counter, validate_counter;
|
||||
struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */
|
||||
struct frec *next_dependent; /* list of above. */
|
||||
struct frec *blocking_query; /* Query which is blocking us. */
|
||||
|
@ -836,6 +840,12 @@ struct frec {
|
|||
#define LEASE_HAVE_HWADDR 128 /* Have set hwaddress */
|
||||
#define LEASE_EXP_CHANGED 256 /* Lease expiry time changed */
|
||||
|
||||
#define LIMIT_SIG_FAIL 0
|
||||
#define LIMIT_CRYPTO 1
|
||||
#define LIMIT_WORK 2
|
||||
#define LIMIT_NSEC3_ITERS 3
|
||||
#define LIMIT_MAX 4
|
||||
|
||||
struct dhcp_lease {
|
||||
int clid_len; /* length of client identifier */
|
||||
unsigned char *clid; /* clientid */
|
||||
|
@ -1238,16 +1248,14 @@ extern struct daemon {
|
|||
char *packet; /* packet buffer */
|
||||
int packet_buff_sz; /* size of above */
|
||||
char *namebuff; /* MAXDNAME size buffer */
|
||||
#if (defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)) || defined(HAVE_DNSSEC)
|
||||
/* CONNTRACK UBUS code uses this buffer, as well as DNSSEC code. */
|
||||
char *workspacename;
|
||||
#endif
|
||||
#ifdef HAVE_DNSSEC
|
||||
char *keyname; /* MAXDNAME size buffer */
|
||||
unsigned long *rr_status; /* ceiling in TTL from DNSSEC or zero for insecure */
|
||||
int rr_status_sz;
|
||||
int dnssec_no_time_check;
|
||||
int back_to_the_future;
|
||||
int limit[LIMIT_MAX];
|
||||
#endif
|
||||
struct frec *frec_list;
|
||||
struct frec_src *free_frec_src;
|
||||
|
@ -1258,8 +1266,8 @@ extern struct daemon {
|
|||
struct server *srv_save; /* Used for resend on DoD */
|
||||
size_t packet_len; /* " " */
|
||||
int fd_save; /* " " */
|
||||
pid_t tcp_pids[MAX_PROCS];
|
||||
int tcp_pipes[MAX_PROCS];
|
||||
pid_t *tcp_pids;
|
||||
int *tcp_pipes;
|
||||
int pipe_to_parent;
|
||||
int numrrand;
|
||||
struct randfd *randomsocks;
|
||||
|
@ -1319,6 +1327,8 @@ extern struct daemon {
|
|||
/* file for packet dumps. */
|
||||
int dumpfd;
|
||||
#endif
|
||||
int max_procs;
|
||||
uint max_procs_used;
|
||||
} *daemon;
|
||||
|
||||
struct server_details {
|
||||
|
@ -1383,6 +1393,7 @@ int is_name_synthetic(int flags, char *name, union all_addr *addr);
|
|||
int is_rev_synth(int flag, union all_addr *addr, char *name);
|
||||
|
||||
/* rfc1035.c */
|
||||
int do_doctor(struct dns_header *header, size_t qlen, char *namebuff);
|
||||
int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
char *name, int isExtract, int extrabytes);
|
||||
unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes);
|
||||
|
@ -1393,7 +1404,7 @@ unsigned int extract_request(struct dns_header *header, size_t qlen,
|
|||
void setup_reply(struct dns_header *header, unsigned int flags, int ede);
|
||||
int extract_addresses(struct dns_header *header, size_t qlen, char *name,
|
||||
time_t now, struct ipsets *ipsets, struct ipsets *nftsets, int is_sign,
|
||||
int check_rebind, int no_cache_dnssec, int secure, int *doctored);
|
||||
int check_rebind, int no_cache_dnssec, int secure);
|
||||
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||
void report_addresses(struct dns_header *header, size_t len, u32 mark);
|
||||
#endif
|
||||
|
@ -1424,10 +1435,12 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
|
|||
/* dnssec.c */
|
||||
#ifdef HAVE_DNSSEC
|
||||
size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int type, int edns_pktsz);
|
||||
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
|
||||
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
|
||||
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name,
|
||||
char *keyname, int class, int *validate_count);
|
||||
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name,
|
||||
char *keyname, int class, int *validate_count);
|
||||
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class,
|
||||
int check_unsigned, int *neganswer, int *nons, int *nsec_ttl);
|
||||
int check_unsigned, int *neganswer, int *nons, int *nsec_ttl, int *validate_count);
|
||||
int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen);
|
||||
size_t filter_rrsigs(struct dns_header *header, size_t plen);
|
||||
int setup_timestamp(void);
|
||||
|
@ -1613,6 +1626,7 @@ void lease_update_from_configs(void);
|
|||
int do_script_run(time_t now);
|
||||
void rerun_scripts(void);
|
||||
void lease_find_interfaces(time_t now);
|
||||
void lease_calc_fqdns(void);
|
||||
#ifdef HAVE_SCRIPT
|
||||
void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data,
|
||||
unsigned int len, int delim);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -22,12 +22,13 @@ static int match_domain(struct in_addr addr, struct cond_domain *c);
|
|||
static struct cond_domain *search_domain6(struct in6_addr *addr, struct cond_domain *c);
|
||||
static int match_domain6(struct in6_addr *addr, struct cond_domain *c);
|
||||
|
||||
int is_name_synthetic(int flags, char *name, union all_addr *addr)
|
||||
int is_name_synthetic(int flags, char *name, union all_addr *addrp)
|
||||
{
|
||||
char *p;
|
||||
struct cond_domain *c = NULL;
|
||||
int prot = (flags & F_IPV6) ? AF_INET6 : AF_INET;
|
||||
|
||||
union all_addr addr;
|
||||
|
||||
for (c = daemon->synth_domains; c; c = c->next)
|
||||
{
|
||||
int found = 0;
|
||||
|
@ -74,7 +75,7 @@ int is_name_synthetic(int flags, char *name, union all_addr *addr)
|
|||
if (!c->is6 &&
|
||||
index <= ntohl(c->end.s_addr) - ntohl(c->start.s_addr))
|
||||
{
|
||||
addr->addr4.s_addr = htonl(ntohl(c->start.s_addr) + index);
|
||||
addr.addr4.s_addr = htonl(ntohl(c->start.s_addr) + index);
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
|
@ -86,8 +87,8 @@ int is_name_synthetic(int flags, char *name, union all_addr *addr)
|
|||
index <= addr6part(&c->end6) - addr6part(&c->start6))
|
||||
{
|
||||
u64 start = addr6part(&c->start6);
|
||||
addr->addr6 = c->start6;
|
||||
setaddr6part(&addr->addr6, start + index);
|
||||
addr.addr6 = c->start6;
|
||||
setaddr6part(&addr.addr6, start + index);
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
|
@ -135,8 +136,8 @@ int is_name_synthetic(int flags, char *name, union all_addr *addr)
|
|||
}
|
||||
}
|
||||
|
||||
if (hostname_isequal(c->domain, p+1) && inet_pton(prot, tail, addr))
|
||||
found = (prot == AF_INET) ? match_domain(addr->addr4, c) : match_domain6(&addr->addr6, c);
|
||||
if (hostname_isequal(c->domain, p+1) && inet_pton(prot, tail, &addr))
|
||||
found = (prot == AF_INET) ? match_domain(addr.addr4, c) : match_domain6(&addr.addr6, c);
|
||||
}
|
||||
|
||||
/* restore name */
|
||||
|
@ -148,7 +149,12 @@ int is_name_synthetic(int flags, char *name, union all_addr *addr)
|
|||
|
||||
|
||||
if (found)
|
||||
return 1;
|
||||
{
|
||||
if (addrp)
|
||||
*addrp = addr;
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -344,7 +344,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
|||
if (ad_reqd)
|
||||
forward->flags |= FREC_AD_QUESTION;
|
||||
#ifdef HAVE_DNSSEC
|
||||
forward->work_counter = DNSSEC_WORK;
|
||||
forward->work_counter = daemon->limit[LIMIT_WORK];
|
||||
forward->validate_counter = daemon->limit[LIMIT_CRYPTO];
|
||||
if (do_bit)
|
||||
forward->flags |= FREC_DO_QUESTION;
|
||||
#endif
|
||||
|
@ -697,7 +698,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
|||
{
|
||||
unsigned char *pheader, *sizep;
|
||||
struct ipsets *ipsets = NULL, *nftsets = NULL;
|
||||
int munged = 0, is_sign;
|
||||
int is_sign;
|
||||
unsigned int rcode = RCODE(header);
|
||||
size_t plen;
|
||||
/******** Pi-hole modification ********/
|
||||
|
@ -706,8 +707,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
|||
|
||||
(void)ad_reqd;
|
||||
(void)do_bit;
|
||||
(void)bogusanswer;
|
||||
|
||||
|
||||
#ifdef HAVE_IPSET
|
||||
if (daemon->ipsets && extract_request(header, n, daemon->namebuff, NULL))
|
||||
ipsets = domain_find_sets(daemon->ipsets, daemon->namebuff);
|
||||
|
@ -801,91 +801,108 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
|||
server->flags |= SERV_WARNED_RECURSIVE;
|
||||
}
|
||||
|
||||
if (daemon->bogus_addr && rcode != NXDOMAIN &&
|
||||
check_for_bogus_wildcard(header, n, daemon->namebuff, now))
|
||||
if (header->hb3 & HB3_TC)
|
||||
{
|
||||
munged = 1;
|
||||
SET_RCODE(header, NXDOMAIN);
|
||||
header->hb3 &= ~HB3_AA;
|
||||
cache_secure = 0;
|
||||
ede = EDE_BLOCKED;
|
||||
log_query(F_UPSTREAM, NULL, NULL, "truncated", 0);
|
||||
header->ancount = htons(0);
|
||||
header->nscount = htons(0);
|
||||
header->arcount = htons(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
int doctored = 0;
|
||||
|
||||
if (rcode == NXDOMAIN &&
|
||||
extract_request(header, n, daemon->namebuff, NULL))
|
||||
{
|
||||
if (check_for_local_domain(daemon->namebuff, now) ||
|
||||
lookup_domain(daemon->namebuff, F_CONFIG, NULL, NULL))
|
||||
{
|
||||
/* if we forwarded a query for a locally known name (because it was for
|
||||
an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
|
||||
since we know that the domain exists, even if upstream doesn't */
|
||||
munged = 1;
|
||||
header->hb3 |= HB3_AA;
|
||||
SET_RCODE(header, NOERROR);
|
||||
cache_secure = 0;
|
||||
}
|
||||
}
|
||||
|
||||
switch (extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure, &doctored))
|
||||
if (!(header->hb3 & HB3_TC) && (!bogusanswer || (header->hb4 & HB4_CD)))
|
||||
{
|
||||
if (rcode == NXDOMAIN && extract_request(header, n, daemon->namebuff, NULL) &&
|
||||
(check_for_local_domain(daemon->namebuff, now) || lookup_domain(daemon->namebuff, F_CONFIG, NULL, NULL)))
|
||||
{
|
||||
case 1:
|
||||
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
|
||||
munged = 1;
|
||||
/* if we forwarded a query for a locally known name (because it was for
|
||||
an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
|
||||
since we know that the domain exists, even if upstream doesn't */
|
||||
header->hb3 |= HB3_AA;
|
||||
SET_RCODE(header, NOERROR);
|
||||
cache_secure = 0;
|
||||
}
|
||||
|
||||
if (daemon->doctors && do_doctor(header, n, daemon->namebuff))
|
||||
cache_secure = 0;
|
||||
|
||||
/* check_for_bogus_wildcard() does it's own caching, so
|
||||
don't call extract_addresses() if it triggers. */
|
||||
if (daemon->bogus_addr && rcode != NXDOMAIN &&
|
||||
check_for_bogus_wildcard(header, n, daemon->namebuff, now))
|
||||
{
|
||||
header->ancount = htons(0);
|
||||
header->nscount = htons(0);
|
||||
header->arcount = htons(0);
|
||||
SET_RCODE(header, NXDOMAIN);
|
||||
header->hb3 &= ~HB3_AA;
|
||||
cache_secure = 0;
|
||||
ede = EDE_BLOCKED;
|
||||
break;
|
||||
|
||||
/* extract_addresses() found a malformed answer. */
|
||||
case 2:
|
||||
munged = 1;
|
||||
SET_RCODE(header, SERVFAIL);
|
||||
cache_secure = 0;
|
||||
ede = EDE_OTHER;
|
||||
break;
|
||||
|
||||
/* Pi-hole modification */
|
||||
case 99:
|
||||
cache_secure = 0;
|
||||
// Make a private copy of the pheader to ensure
|
||||
// we are not accidentially rewriting what is in
|
||||
// the pheader when we're creating a crafted reply
|
||||
// further below (when a query is to be blocked)
|
||||
if (pheader)
|
||||
{
|
||||
pheader_copy = calloc(1, plen);
|
||||
memcpy(pheader_copy, pheader, plen);
|
||||
}
|
||||
|
||||
// Generate DNS packet for reply, a possibly existing pseudo header
|
||||
// will be restored later inside resize_packet()
|
||||
n = FTL_make_answer(header, ((char *) header) + 65536, n, &ede);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
int rc = extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure);
|
||||
|
||||
if (rcode == NOERROR && rrfilter(header, &n, RRFILTER_CONF) > 0)
|
||||
ede = EDE_FILTERED;
|
||||
if (rc != 0)
|
||||
{
|
||||
header->ancount = htons(0);
|
||||
header->nscount = htons(0);
|
||||
header->arcount = htons(0);
|
||||
cache_secure = 0;
|
||||
}
|
||||
|
||||
if (rc == 1)
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
|
||||
ede = EDE_BLOCKED;
|
||||
}
|
||||
|
||||
if (rc == 2)
|
||||
{
|
||||
/* extract_addresses() found a malformed answer. */
|
||||
SET_RCODE(header, SERVFAIL);
|
||||
ede = EDE_OTHER;
|
||||
}
|
||||
|
||||
/* Pi-hole modification */
|
||||
if(rc == 99)
|
||||
{
|
||||
cache_secure = 0;
|
||||
// Make a private copy of the pheader to ensure
|
||||
// we are not accidentially rewriting what is in
|
||||
// the pheader when we're creating a crafted reply
|
||||
// further below (when a query is to be blocked)
|
||||
if (pheader)
|
||||
{
|
||||
pheader_copy = calloc(1, plen);
|
||||
memcpy(pheader_copy, pheader, plen);
|
||||
}
|
||||
|
||||
// Generate DNS packet for reply, a possibly existing pseudo header
|
||||
// will be restored later inside resize_packet()
|
||||
n = FTL_make_answer(header, ((char *) header) + 65536, n, &ede);
|
||||
}
|
||||
}
|
||||
|
||||
if (doctored)
|
||||
cache_secure = 0;
|
||||
if (RCODE(header) == NOERROR && rrfilter(header, &n, RRFILTER_CONF) > 0)
|
||||
ede = EDE_FILTERED;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (bogusanswer && !(header->hb4 & HB4_CD) && !option_bool(OPT_DNSSEC_DEBUG))
|
||||
{
|
||||
/* Bogus reply, turn into SERVFAIL */
|
||||
SET_RCODE(header, SERVFAIL);
|
||||
munged = 1;
|
||||
}
|
||||
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
header->hb4 &= ~HB4_AD;
|
||||
|
||||
if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure)
|
||||
if (bogusanswer)
|
||||
{
|
||||
if (!(header->hb4 & HB4_CD) && !option_bool(OPT_DNSSEC_DEBUG))
|
||||
{
|
||||
/* Bogus reply, turn into SERVFAIL */
|
||||
SET_RCODE(header, SERVFAIL);
|
||||
header->ancount = htons(0);
|
||||
header->nscount = htons(0);
|
||||
header->arcount = htons(0);
|
||||
ede = EDE_DNSSEC_BOGUS;
|
||||
}
|
||||
}
|
||||
else if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure)
|
||||
header->hb4 |= HB4_AD;
|
||||
|
||||
/* If the requestor didn't set the DO bit, don't return DNSSEC info. */
|
||||
|
@ -893,20 +910,9 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
|||
rrfilter(header, &n, RRFILTER_DNSSEC);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* do this after extract_addresses. Ensure NODATA reply and remove
|
||||
nameserver info. */
|
||||
if (munged)
|
||||
{
|
||||
header->ancount = htons(0);
|
||||
header->nscount = htons(0);
|
||||
header->arcount = htons(0);
|
||||
header->hb3 &= ~HB3_TC;
|
||||
}
|
||||
|
||||
/* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide
|
||||
sections of the packet. Find the new length here and put back pseudoheader
|
||||
if it was removed. */
|
||||
/* the code above can elide sections of the packet. Find the new length here
|
||||
and put back pseudoheader if it was removed. */
|
||||
n = resize_packet(header, n, pheader_copy ? pheader_copy : pheader, plen);
|
||||
/******** Pi-hole modification ********/
|
||||
// The line above was modified to use
|
||||
|
@ -931,6 +937,9 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
|||
static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
||||
ssize_t plen, int status, time_t now)
|
||||
{
|
||||
struct frec *orig;
|
||||
int log_resource = 0;
|
||||
|
||||
daemon->log_display_id = forward->frec_src.log_id;
|
||||
|
||||
/* We've had a reply already, which we're validating. Ignore this duplicate */
|
||||
|
@ -955,6 +964,9 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
|||
log_query(F_UPSTREAM | F_NOEXTRA, daemon->namebuff, NULL, "truncated", (forward->flags & FREC_DNSKEY_QUERY) ? T_DNSKEY : T_DS);
|
||||
}
|
||||
}
|
||||
|
||||
/* Find the original query that started it all.... */
|
||||
for (orig = forward; orig->dependent; orig = orig->dependent);
|
||||
|
||||
/* As soon as anything returns BOGUS, we stop and unwind, to do otherwise
|
||||
would invite infinite loops, since the answers to DNSKEY and DS queries
|
||||
|
@ -962,18 +974,16 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
|||
if (!STAT_ISEQUAL(status, STAT_BOGUS) && !STAT_ISEQUAL(status, STAT_TRUNCATED) && !STAT_ISEQUAL(status, STAT_ABANDONED))
|
||||
{
|
||||
if (forward->flags & FREC_DNSKEY_QUERY)
|
||||
status = dnssec_validate_by_ds(now, header, plen, daemon->namebuff, daemon->keyname, forward->class);
|
||||
status = dnssec_validate_by_ds(now, header, plen, daemon->namebuff, daemon->keyname, forward->class, &orig->validate_counter);
|
||||
else if (forward->flags & FREC_DS_QUERY)
|
||||
status = dnssec_validate_ds(now, header, plen, daemon->namebuff, daemon->keyname, forward->class);
|
||||
status = dnssec_validate_ds(now, header, plen, daemon->namebuff, daemon->keyname, forward->class, &orig->validate_counter);
|
||||
else
|
||||
status = dnssec_validate_reply(now, header, plen, daemon->namebuff, daemon->keyname, &forward->class,
|
||||
!option_bool(OPT_DNSSEC_IGN_NS) && (forward->sentto->flags & SERV_DO_DNSSEC),
|
||||
NULL, NULL, NULL);
|
||||
#ifdef HAVE_DUMPFILE
|
||||
if (STAT_ISEQUAL(status, STAT_BOGUS))
|
||||
dump_packet_udp((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_BOGUS : DUMP_BOGUS,
|
||||
header, (size_t)plen, &forward->sentto->addr, NULL, -daemon->port);
|
||||
#endif
|
||||
NULL, NULL, NULL, &orig->validate_counter);
|
||||
|
||||
if (STAT_ISEQUAL(status, STAT_ABANDONED))
|
||||
log_resource = 1;
|
||||
}
|
||||
|
||||
/* Can't validate, as we're missing key data. Put this
|
||||
|
@ -1018,18 +1028,19 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
|||
return;
|
||||
}
|
||||
}
|
||||
else if (orig->work_counter-- == 0)
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("limit exceeded: per-query subqueries"));
|
||||
log_resource = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct server *server;
|
||||
struct frec *orig;
|
||||
void *hash;
|
||||
size_t nn;
|
||||
int serverind, fd;
|
||||
struct randfd_list *rfds = NULL;
|
||||
|
||||
/* Find the original query that started it all.... */
|
||||
for (orig = forward; orig->dependent; orig = orig->dependent);
|
||||
|
||||
/* Make sure we don't expire and free the orig frec during the
|
||||
allocation of a new one: third arg of get_new_frec() does that. */
|
||||
if ((serverind = dnssec_server(forward->sentto, daemon->keyname, NULL, NULL)) != -1 &&
|
||||
|
@ -1038,7 +1049,6 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
|||
daemon->keyname, forward->class,
|
||||
STAT_ISEQUAL(status, STAT_NEED_KEY) ? T_DNSKEY : T_DS, server->edns_pktsz)) &&
|
||||
(hash = hash_questions(header, nn, daemon->namebuff)) &&
|
||||
--orig->work_counter != 0 &&
|
||||
(fd = allocate_rfd(&rfds, server)) != -1 &&
|
||||
(new = get_new_frec(now, server, 1)))
|
||||
{
|
||||
|
@ -1104,6 +1114,21 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
|||
status = STAT_ABANDONED;
|
||||
}
|
||||
|
||||
if (log_resource)
|
||||
{
|
||||
/* Log the actual validation that made us barf. */
|
||||
unsigned char *p = (unsigned char *)(header+1);
|
||||
if (extract_name(header, plen, &p, daemon->namebuff, 0, 4) == 1)
|
||||
my_syslog(LOG_WARNING, _("validation of %s failed: resource limit exceeded."),
|
||||
daemon->namebuff[0] ? daemon->namebuff : ".");
|
||||
}
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
if (STAT_ISEQUAL(status, STAT_BOGUS) || STAT_ISEQUAL(status, STAT_ABANDONED))
|
||||
dump_packet_udp((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_BOGUS : DUMP_BOGUS,
|
||||
header, (size_t)plen, &forward->sentto->addr, NULL, -daemon->port);
|
||||
#endif
|
||||
|
||||
/* Validated original answer, all done. */
|
||||
if (!forward->dependent)
|
||||
return_reply(now, forward, header, plen, status);
|
||||
|
@ -1112,7 +1137,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
|||
/* validated subsidiary query/queries, (and cached result)
|
||||
pop that and return to the previous query/queries we were working on. */
|
||||
struct frec *prev, *nxt = forward->dependent;
|
||||
|
||||
|
||||
free_frec(forward);
|
||||
|
||||
while ((prev = nxt))
|
||||
|
@ -1378,6 +1403,12 @@ static void return_reply(time_t now, struct frec *forward, struct dns_header *he
|
|||
log_query(F_SECSTAT, domain, &a, result, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if ((daemon->limit[LIMIT_CRYPTO] - forward->validate_counter) > (int)daemon->metrics[METRIC_CRYPTO_HWM])
|
||||
daemon->metrics[METRIC_CRYPTO_HWM] = daemon->limit[LIMIT_CRYPTO] - forward->validate_counter;
|
||||
|
||||
if ((daemon->limit[LIMIT_WORK] - forward->work_counter) > (int)daemon->metrics[METRIC_WORK_HWM])
|
||||
daemon->metrics[METRIC_WORK_HWM] = daemon->limit[LIMIT_WORK] - forward->work_counter;
|
||||
#endif
|
||||
|
||||
if (option_bool(OPT_NO_REBIND))
|
||||
|
@ -2107,7 +2138,7 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
|
|||
/* Recurse down the key hierarchy */
|
||||
static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n,
|
||||
int class, char *name, char *keyname, struct server *server,
|
||||
int have_mark, unsigned int mark, int *keycount)
|
||||
int have_mark, unsigned int mark, int *keycount, int *validatecount)
|
||||
{
|
||||
int first, last, start, new_status;
|
||||
unsigned char *packet = NULL;
|
||||
|
@ -2121,20 +2152,34 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
|||
int log_save;
|
||||
|
||||
/* limit the amount of work we do, to avoid cycling forever on loops in the DNS */
|
||||
if (--(*keycount) == 0)
|
||||
new_status = STAT_ABANDONED;
|
||||
else if (STAT_ISEQUAL(status, STAT_NEED_KEY))
|
||||
new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
|
||||
if (STAT_ISEQUAL(status, STAT_NEED_KEY))
|
||||
new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class, validatecount);
|
||||
else if (STAT_ISEQUAL(status, STAT_NEED_DS))
|
||||
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
|
||||
new_status = dnssec_validate_ds(now, header, n, name, keyname, class, validatecount);
|
||||
else
|
||||
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class,
|
||||
!option_bool(OPT_DNSSEC_IGN_NS) && (server->flags & SERV_DO_DNSSEC),
|
||||
NULL, NULL, NULL);
|
||||
NULL, NULL, NULL, validatecount);
|
||||
|
||||
if (!STAT_ISEQUAL(new_status, STAT_NEED_DS) && !STAT_ISEQUAL(new_status, STAT_NEED_KEY))
|
||||
if (!STAT_ISEQUAL(new_status, STAT_NEED_DS) && !STAT_ISEQUAL(new_status, STAT_NEED_KEY) && !STAT_ISEQUAL(new_status, STAT_ABANDONED))
|
||||
break;
|
||||
|
||||
|
||||
if ((*keycount)-- == 0)
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("limit exceeded: per-query subqueries"));
|
||||
new_status = STAT_ABANDONED;
|
||||
}
|
||||
|
||||
if (STAT_ISEQUAL(new_status, STAT_ABANDONED))
|
||||
{
|
||||
/* Log the actual validation that made us barf. */
|
||||
unsigned char *p = (unsigned char *)(header+1);
|
||||
if (extract_name(header, n, &p, daemon->namebuff, 0, 4) == 1)
|
||||
my_syslog(LOG_WARNING, _("validation of %s failed: resource limit exceeded."),
|
||||
daemon->namebuff[0] ? daemon->namebuff : ".");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Can't validate because we need a key/DS whose name now in keyname.
|
||||
Make query for same, and recurse to validate */
|
||||
if (!packet)
|
||||
|
@ -2148,7 +2193,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
|||
new_status = STAT_ABANDONED;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class,
|
||||
STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? T_DNSKEY : T_DS, server->edns_pktsz);
|
||||
|
||||
|
@ -2163,10 +2208,11 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
|||
daemon->log_display_id = ++daemon->log_id;
|
||||
|
||||
log_query_mysockaddr(F_NOEXTRA | F_DNSSEC | F_SERVER, keyname, &server->addr,
|
||||
STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? "dnssec-query[DNSKEY]" : "dnssec-query[DS]", 0);
|
||||
|
||||
new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, have_mark, mark, keycount);
|
||||
|
||||
STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? "dnssec-query[DNSKEY]" : "dnssec-query[DS]", 0);
|
||||
|
||||
new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server,
|
||||
have_mark, mark, keycount, validatecount);
|
||||
|
||||
daemon->log_display_id = log_save;
|
||||
|
||||
if (!STAT_ISEQUAL(new_status, STAT_OK))
|
||||
|
@ -2508,9 +2554,10 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled && (master->flags & SERV_DO_DNSSEC))
|
||||
{
|
||||
int keycount = DNSSEC_WORK; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
|
||||
int keycount = daemon->limit[LIMIT_WORK]; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
|
||||
int validatecount = daemon->limit[LIMIT_CRYPTO];
|
||||
int status = tcp_key_recurse(now, STAT_OK, header, m, 0, daemon->namebuff, daemon->keyname,
|
||||
serv, have_mark, mark, &keycount);
|
||||
serv, have_mark, mark, &keycount, &validatecount);
|
||||
char *result, *domain = "result";
|
||||
|
||||
union all_addr a;
|
||||
|
@ -2536,6 +2583,12 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||
}
|
||||
|
||||
log_query(F_SECSTAT, domain, &a, result, 0);
|
||||
|
||||
if ((daemon->limit[LIMIT_CRYPTO] - validatecount) > (int)daemon->metrics[METRIC_CRYPTO_HWM])
|
||||
daemon->metrics[METRIC_CRYPTO_HWM] = daemon->limit[LIMIT_CRYPTO] - validatecount;
|
||||
|
||||
if ((daemon->limit[LIMIT_WORK] - keycount) > (int)daemon->metrics[METRIC_WORK_HWM])
|
||||
daemon->metrics[METRIC_WORK_HWM] = daemon->limit[LIMIT_WORK] - keycount;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -165,7 +165,7 @@ static void sha256_transform(SHA256_CTX *ctx, const BYTE data[])
|
|||
WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
|
||||
|
||||
for (i = 0, j = 0; i < 16; ++i, j += 4)
|
||||
m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
|
||||
m[i] = (((WORD)data[j]) << 24) | (((WORD)data[j + 1]) << 16) | (((WORD)data[j + 2]) << 8) | (((WORD)data[j + 3]));
|
||||
for ( ; i < 64; ++i)
|
||||
m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -94,7 +94,7 @@ void inotify_dnsmasq_init()
|
|||
if (daemon->inotifyfd == -1)
|
||||
die(_("failed to create inotify: %s"), NULL, EC_MISC);
|
||||
|
||||
if (option_bool(OPT_NO_RESOLV))
|
||||
if (daemon->port == 0 || option_bool(OPT_NO_RESOLV))
|
||||
return;
|
||||
|
||||
for (res = daemon->resolv_files; res; res = res->next)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
|
||||
static struct dhcp_lease *leases = NULL, *old_leases = NULL;
|
||||
|
@ -28,8 +27,7 @@ static int read_leases(time_t now, FILE *leasestream)
|
|||
struct dhcp_lease *lease;
|
||||
int clid_len, hw_len, hw_type;
|
||||
int items;
|
||||
char *domain = NULL;
|
||||
|
||||
|
||||
*daemon->dhcp_buff3 = *daemon->dhcp_buff2 = '\0';
|
||||
|
||||
/* client-id max length is 255 which is 255*2 digits + 254 colons
|
||||
|
@ -69,8 +67,8 @@ static int read_leases(time_t now, FILE *leasestream)
|
|||
|
||||
if (inet_pton(AF_INET, daemon->namebuff, &addr.addr4))
|
||||
{
|
||||
if ((lease = lease4_allocate(addr.addr4)))
|
||||
domain = get_domain(lease->addr);
|
||||
lease = lease4_allocate(addr.addr4);
|
||||
|
||||
|
||||
hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, DHCP_CHADDR_MAX, NULL, &hw_type);
|
||||
/* For backwards compatibility, no explicit MAC address type means ether. */
|
||||
|
@ -90,10 +88,7 @@ static int read_leases(time_t now, FILE *leasestream)
|
|||
}
|
||||
|
||||
if ((lease = lease6_allocate(&addr.addr6, lease_type)))
|
||||
{
|
||||
lease_set_iaid(lease, strtoul(s, NULL, 10));
|
||||
domain = get_domain6(&lease->addr6);
|
||||
}
|
||||
lease_set_iaid(lease, strtoul(s, NULL, 10));
|
||||
}
|
||||
#endif
|
||||
else
|
||||
|
@ -114,7 +109,7 @@ static int read_leases(time_t now, FILE *leasestream)
|
|||
hw_len, hw_type, clid_len, now, 0);
|
||||
|
||||
if (strcmp(daemon->dhcp_buff, "*") != 0)
|
||||
lease_set_hostname(lease, daemon->dhcp_buff, 0, domain, NULL);
|
||||
lease_set_hostname(lease, daemon->dhcp_buff, 0, NULL, NULL);
|
||||
|
||||
ei = atol(daemon->dhcp_buff3);
|
||||
|
||||
|
@ -946,6 +941,36 @@ static void kill_name(struct dhcp_lease *lease)
|
|||
lease->hostname = lease->fqdn = NULL;
|
||||
}
|
||||
|
||||
void lease_calc_fqdns(void)
|
||||
{
|
||||
struct dhcp_lease *lease;
|
||||
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
{
|
||||
char *domain;
|
||||
|
||||
if (lease->hostname)
|
||||
{
|
||||
#ifdef HAVE_DHCP6
|
||||
if (lease->flags & (LEASE_TA | LEASE_NA))
|
||||
domain = get_domain6(&lease->addr6);
|
||||
else
|
||||
#endif
|
||||
domain = get_domain(lease->addr);
|
||||
|
||||
if (domain)
|
||||
{
|
||||
/* This is called only during startup, before forking, hence safe_malloc() */
|
||||
lease->fqdn = safe_malloc(strlen(lease->hostname) + strlen(domain) + 2);
|
||||
|
||||
strcpy(lease->fqdn, lease->hostname);
|
||||
strcat(lease->fqdn, ".");
|
||||
strcat(lease->fqdn, domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lease_set_hostname(struct dhcp_lease *lease, const char *name, int auth, char *domain, char *config_domain)
|
||||
{
|
||||
struct dhcp_lease *lease_tmp;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -24,6 +24,9 @@ const char * metric_names[] = {
|
|||
"dns_local_answered",
|
||||
"dns_stale_answered",
|
||||
"dns_unanswered",
|
||||
"dnssec_max_crypto_use",
|
||||
"dnssec_max_sig_fail",
|
||||
"dnssec_max_work",
|
||||
"bootp",
|
||||
"pxe",
|
||||
"dhcp_ack",
|
||||
|
@ -39,6 +42,7 @@ const char * metric_names[] = {
|
|||
"leases_pruned_4",
|
||||
"leases_allocated_6",
|
||||
"leases_pruned_6",
|
||||
"tcp_connections",
|
||||
};
|
||||
|
||||
const char* get_metric_name(int i) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -23,6 +23,9 @@ enum {
|
|||
METRIC_DNS_LOCAL_ANSWERED,
|
||||
METRIC_DNS_STALE_ANSWERED,
|
||||
METRIC_DNS_UNANSWERED_QUERY,
|
||||
METRIC_CRYPTO_HWM,
|
||||
METRIC_SIG_FAIL_HWM,
|
||||
METRIC_WORK_HWM,
|
||||
METRIC_BOOTP,
|
||||
METRIC_PXE,
|
||||
METRIC_DHCPACK,
|
||||
|
@ -38,6 +41,7 @@ enum {
|
|||
METRIC_LEASES_PRUNED_4,
|
||||
METRIC_LEASES_ALLOCATED_6,
|
||||
METRIC_LEASES_PRUNED_6,
|
||||
METRIC_TCP_CONNECTIONS,
|
||||
|
||||
__METRIC_MAX,
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -929,15 +929,24 @@ static int make_sock(union mysockaddr *addr, int type, int dienow)
|
|||
|
||||
errno = errsave;
|
||||
|
||||
if (dienow)
|
||||
/* Failure to bind addresses given by --listen-address at this point
|
||||
because there's no interface with the address is OK if we're doing bind-dynamic.
|
||||
If/when an interface is created with the relevant address we'll notice
|
||||
and attempt to bind it then. This is in the generic error path so we close the socket,
|
||||
but EADDRNOTAVAIL is only a possible error from bind()
|
||||
|
||||
When a new address is created and we call this code again (dienow == 0) there
|
||||
may still be configured addresses when don't exist, (consider >1 --listen-address,
|
||||
when the first is created, the second will still be missing) so we suppress
|
||||
EADDRNOTAVAIL even in that case to avoid confusing log entries.
|
||||
*/
|
||||
if (!option_bool(OPT_CLEVERBIND) || errno != EADDRNOTAVAIL)
|
||||
{
|
||||
/* failure to bind addresses given by --listen-address at this point
|
||||
is OK if we're doing bind-dynamic */
|
||||
if (!option_bool(OPT_CLEVERBIND))
|
||||
if (dienow)
|
||||
die(s, daemon->addrbuff, EC_BADNET);
|
||||
else
|
||||
my_syslog(LOG_WARNING, s, daemon->addrbuff, strerror(errno));
|
||||
}
|
||||
else
|
||||
my_syslog(LOG_WARNING, s, daemon->addrbuff, strerror(errno));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -43,7 +43,8 @@ int add_to_nftset(const char *setname, const union all_addr *ipaddr, int flags,
|
|||
const char *cmd = remove ? cmd_del : cmd_add;
|
||||
int ret, af = (flags & F_IPV4) ? AF_INET : AF_INET6;
|
||||
size_t new_sz;
|
||||
char *new, *err, *nl;
|
||||
char *err_str, *new, *nl;
|
||||
const char *err;
|
||||
static char *cmd_buf = NULL;
|
||||
static size_t cmd_buf_sz = 0;
|
||||
|
||||
|
@ -78,14 +79,19 @@ int add_to_nftset(const char *setname, const union all_addr *ipaddr, int flags,
|
|||
}
|
||||
|
||||
ret = nft_run_cmd_from_buffer(ctx, cmd_buf);
|
||||
err = (char *)nft_ctx_get_error_buffer(ctx);
|
||||
err = nft_ctx_get_error_buffer(ctx);
|
||||
|
||||
if (ret != 0)
|
||||
{
|
||||
/* Log only first line of error return. */
|
||||
if ((nl = strchr(err, '\n')))
|
||||
*nl = 0;
|
||||
my_syslog(LOG_ERR, "nftset %s %s", setname, err);
|
||||
if ((err_str = whine_malloc(strlen(err) + 1)))
|
||||
{
|
||||
strcpy(err_str, err);
|
||||
if ((nl = strchr(err_str, '\n')))
|
||||
*nl = 0;
|
||||
my_syslog(LOG_ERR, "nftset %s %s", setname, err_str);
|
||||
free(err_str);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -194,6 +194,8 @@ struct myoption {
|
|||
#define LOPT_FILTER_RR 381
|
||||
#define LOPT_NO_DHCP6 382
|
||||
#define LOPT_NO_DHCP4 383
|
||||
#define LOPT_MAX_PROCS 384
|
||||
#define LOPT_DNSSEC_LIMITS 385
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
static const struct option opts[] =
|
||||
|
@ -225,7 +227,7 @@ static const struct myoption opts[] =
|
|||
{ "domain-suffix", 1, 0, 's' },
|
||||
{ "interface", 1, 0, 'i' },
|
||||
{ "listen-address", 1, 0, 'a' },
|
||||
{ "local-service", 0, 0, LOPT_LOCAL_SERVICE },
|
||||
{ "local-service", 2, 0, LOPT_LOCAL_SERVICE },
|
||||
{ "bogus-priv", 0, 0, 'b' },
|
||||
{ "bogus-nxdomain", 1, 0, 'B' },
|
||||
{ "ignore-address", 1, 0, LOPT_IGNORE_ADDR },
|
||||
|
@ -367,6 +369,7 @@ static const struct myoption opts[] =
|
|||
{ "dnssec-check-unsigned", 2, 0, LOPT_DNSSEC_CHECK },
|
||||
{ "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
|
||||
{ "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
|
||||
{ "dnssec-limits", 1, 0, LOPT_DNSSEC_LIMITS },
|
||||
{ "dhcp-relay", 1, 0, LOPT_RELAY },
|
||||
{ "ra-param", 1, 0, LOPT_RA_PARAM },
|
||||
{ "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP },
|
||||
|
@ -388,6 +391,7 @@ static const struct myoption opts[] =
|
|||
{ "fast-dns-retry", 2, 0, LOPT_FAST_RETRY },
|
||||
{ "use-stale-cache", 2, 0 , LOPT_STALE_CACHE },
|
||||
{ "no-ident", 0, 0, LOPT_NO_IDENT },
|
||||
{ "max-tcp-connections", 1, 0, LOPT_MAX_PROCS },
|
||||
{ NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
|
@ -570,12 +574,13 @@ static struct {
|
|||
{ LOPT_DNSSEC_CHECK, ARG_DUP, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
|
||||
{ LOPT_DNSSEC_TIME, OPT_DNSSEC_TIME, NULL, gettext_noop("Don't check DNSSEC signature timestamps until first cache-reload"), NULL },
|
||||
{ LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL },
|
||||
{ LOPT_DNSSEC_LIMITS, ARG_ONE, "<limit>,..", gettext_noop("Set resource limits for DNSSEC validation"), NULL },
|
||||
{ LOPT_RA_PARAM, ARG_DUP, "<iface>,[mtu:<value>|<interface>|off,][<prio>,]<intval>[,<lifetime>]", gettext_noop("Set MTU, priority, resend-interval and router-lifetime"), NULL },
|
||||
{ LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
|
||||
{ LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
|
||||
{ LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
|
||||
{ LOPT_LOG_DEBUG, OPT_LOG_DEBUG, NULL, gettext_noop("Log debugging information."), NULL },
|
||||
{ LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL },
|
||||
{ LOPT_LOCAL_SERVICE, ARG_ONE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL },
|
||||
{ LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL },
|
||||
{ LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL },
|
||||
{ LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL },
|
||||
|
@ -589,6 +594,7 @@ static struct {
|
|||
{ LOPT_NORR, OPT_NORR, NULL, gettext_noop("Suppress round-robin ordering of DNS records."), NULL },
|
||||
{ LOPT_NO_IDENT, OPT_NO_IDENT, NULL, gettext_noop("Do not add CHAOS TXT records."), NULL },
|
||||
{ LOPT_CACHE_RR, ARG_DUP, "<RR-type>", gettext_noop("Cache this DNS resource record type."), NULL },
|
||||
{ LOPT_MAX_PROCS, ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent tcp connections."), NULL },
|
||||
{ 0, 0, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
|
@ -1283,6 +1289,17 @@ static char *domain_rev6(int from_file, char *server, struct in6_addr *addr6, in
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void if_names_add(const char *ifname)
|
||||
{
|
||||
struct iname *new = opt_malloc(sizeof(struct iname));
|
||||
new->next = daemon->if_names;
|
||||
daemon->if_names = new;
|
||||
/* new->name may be NULL if someone does
|
||||
"interface=" to disable all interfaces except loop. */
|
||||
new->name = opt_string_alloc(ifname);
|
||||
new->flags = 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
|
||||
static int is_tag_prefix(char *arg)
|
||||
|
@ -1412,7 +1429,6 @@ static void dhcp_opt_free(struct dhcp_opt *opt)
|
|||
free(opt);
|
||||
}
|
||||
|
||||
|
||||
/* This is too insanely large to keep in-line in the switch */
|
||||
static int parse_dhcp_opt(char *errstr, char *arg, int flags)
|
||||
{
|
||||
|
@ -2836,14 +2852,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
|||
|
||||
case 'i': /* --interface */
|
||||
do {
|
||||
struct iname *new = opt_malloc(sizeof(struct iname));
|
||||
comma = split(arg);
|
||||
new->next = daemon->if_names;
|
||||
daemon->if_names = new;
|
||||
/* new->name may be NULL if someone does
|
||||
"interface=" to disable all interfaces except loop. */
|
||||
new->name = opt_string_alloc(arg);
|
||||
new->flags = 0;
|
||||
comma = split(arg);
|
||||
if_names_add(arg);
|
||||
arg = comma;
|
||||
} while (arg);
|
||||
break;
|
||||
|
@ -3409,6 +3419,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
|||
ret_err(gen_err);
|
||||
else if (daemon->max_logs > 100)
|
||||
daemon->max_logs = 100;
|
||||
break;
|
||||
|
||||
case LOPT_LOCAL_SERVICE: /* --local-service */
|
||||
if (!arg || !strcmp(arg, "net"))
|
||||
set_option_bool(OPT_LOCAL_SERVICE);
|
||||
else if (!strcmp(arg, "host"))
|
||||
set_option_bool(OPT_LOCALHOST_SERVICE);
|
||||
else
|
||||
ret_err(gen_err);
|
||||
break;
|
||||
|
||||
case 'P': /* --edns-packet-max */
|
||||
|
@ -5246,6 +5265,24 @@ err:
|
|||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
case LOPT_DNSSEC_LIMITS:
|
||||
{
|
||||
int lim, val;
|
||||
|
||||
for (lim = LIMIT_SIG_FAIL; arg && lim < LIMIT_MAX ; lim++, arg = comma)
|
||||
{
|
||||
comma = split(arg);
|
||||
|
||||
if (!atoi_check(arg, &val))
|
||||
ret_err(gen_err);
|
||||
|
||||
if (val != 0)
|
||||
daemon->limit[lim] = val;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
|
||||
daemon->timestamp_file = opt_string_alloc(arg);
|
||||
break;
|
||||
|
@ -5317,7 +5354,17 @@ err:
|
|||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
case LOPT_MAX_PROCS: /* --max-tcp-connections */
|
||||
{
|
||||
int max_procs;
|
||||
/* Don't accept numbers less than 1. */
|
||||
if (!atoi_check(arg, &max_procs) || max_procs < 1)
|
||||
ret_err(gen_err);
|
||||
daemon->max_procs = max_procs;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
|
||||
|
||||
|
@ -5822,6 +5869,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
|||
daemon = opt_malloc(sizeof(struct daemon));
|
||||
memset(daemon, 0, sizeof(struct daemon));
|
||||
daemon->namebuff = buff;
|
||||
daemon->workspacename = safe_malloc((MAXDNAME * 2) + 1);
|
||||
daemon->addrbuff = safe_malloc(ADDRSTRLEN);
|
||||
|
||||
/* Set defaults - everything else is zero or NULL */
|
||||
|
@ -5845,6 +5893,13 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
|||
daemon->soa_expiry = SOA_EXPIRY;
|
||||
daemon->randport_limit = 1;
|
||||
daemon->host_index = SRC_AH;
|
||||
daemon->max_procs = MAX_PROCS;
|
||||
#ifdef HAVE_DNSSEC
|
||||
daemon->limit[LIMIT_SIG_FAIL] = DNSSEC_LIMIT_SIG_FAIL;
|
||||
daemon->limit[LIMIT_CRYPTO] = DNSSEC_LIMIT_CRYPTO;
|
||||
daemon->limit[LIMIT_WORK] = DNSSEC_LIMIT_WORK;
|
||||
daemon->limit[LIMIT_NSEC3_ITERS] = DNSSEC_LIMIT_NSEC3_ITERS;
|
||||
#endif
|
||||
|
||||
/* See comment above make_servers(). Optimises server-read code. */
|
||||
mark_servers(0);
|
||||
|
@ -6148,7 +6203,16 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
|||
/* If there's access-control config, then ignore --local-service, it's intended
|
||||
as a system default to keep otherwise unconfigured installations safe. */
|
||||
if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver)
|
||||
reset_option_bool(OPT_LOCAL_SERVICE);
|
||||
{
|
||||
reset_option_bool(OPT_LOCAL_SERVICE);
|
||||
reset_option_bool(OPT_LOCALHOST_SERVICE);
|
||||
}
|
||||
else if (option_bool(OPT_LOCALHOST_SERVICE) && !option_bool(OPT_LOCAL_SERVICE))
|
||||
{
|
||||
/* listen only on localhost, emulate --interface=lo --bind-interfaces */
|
||||
if_names_add(NULL);
|
||||
set_option_bool(OPT_NOWILD);
|
||||
}
|
||||
|
||||
if (testmode)
|
||||
{
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -213,6 +213,14 @@ size_t rrfilter(struct dns_header *header, size_t *plen, int mode)
|
|||
if (i < ntohs(header->ancount) && type == qtype && class == qclass)
|
||||
continue;
|
||||
}
|
||||
else if (qtype == T_ANY && rr_on_list(daemon->filter_rr, T_ANY))
|
||||
{
|
||||
/* Filter replies to ANY queries in the spirit of
|
||||
RFC RFC 8482 para 4.3 */
|
||||
if (class != C_IN ||
|
||||
type == T_A || type == T_AAAA || type == T_MX || type == T_CNAME)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Only looking at answer section now. */
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -585,8 +585,13 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *c
|
|||
|
||||
void check_tftp_listeners(time_t now)
|
||||
{
|
||||
struct listener *listener;
|
||||
struct tftp_transfer *transfer, *tmp, **up;
|
||||
|
||||
for (listener = daemon->listeners; listener; listener = listener->next)
|
||||
if (listener->tftpfd != -1 && poll_check(listener->tftpfd, POLLIN))
|
||||
tftp_request(listener, now);
|
||||
|
||||
/* In single port mode, all packets come via port 69 and tftp_request() */
|
||||
if (!option_bool(OPT_SINGLE_PORT))
|
||||
for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* dnsmasq is Copyright (c) 2000-2023 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -119,7 +119,7 @@ int rr_on_list(struct rrlist *list, unsigned short rr)
|
|||
{
|
||||
while (list)
|
||||
{
|
||||
if (list->rr == rr || list->rr == T_ANY)
|
||||
if (list->rr == rr)
|
||||
return 1;
|
||||
|
||||
list = list->next;
|
||||
|
|
|
@ -1417,10 +1417,10 @@ static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const c
|
|||
break;
|
||||
}
|
||||
|
||||
// Skip all checks and continue if we hit already at least one whitelist in the chain
|
||||
// Skip all checks and continue if we hit already at least one allowlist in the chain
|
||||
if(query->flags.allowed)
|
||||
{
|
||||
log_debug(DEBUG_QUERIES, "Query is permitted as at least one whitelist entry matched");
|
||||
log_debug(DEBUG_QUERIES, "Query is permitted as at least one allowlist entry matched");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2423,6 +2423,17 @@ static void FTL_dnssec(const char *arg, const union all_addr *addr, const int id
|
|||
else
|
||||
log_warn("Unknown DNSSEC status \"%s\"", arg);
|
||||
|
||||
// Set reply to NONE (if not already set) as we will not reply to this
|
||||
// query when the status is neither SECURE nor INSECURE
|
||||
if (query->reply == REPLY_UNKNOWN &&
|
||||
query->dnssec != DNSSEC_SECURE &&
|
||||
query->dnssec != DNSSEC_INSECURE)
|
||||
{
|
||||
struct timeval response;
|
||||
gettimeofday(&response, 0);
|
||||
query_set_reply(0, REPLY_NONE, addr, query, response);
|
||||
}
|
||||
|
||||
// Mark query for updating in the database
|
||||
query->flags.database.changed = true;
|
||||
|
||||
|
@ -3486,3 +3497,9 @@ static const char *check_dnsmasq_name(const char *name)
|
|||
// else
|
||||
return name;
|
||||
}
|
||||
|
||||
void get_dnsmasq_metrics_obj(cJSON *json)
|
||||
{
|
||||
for (unsigned int i = 0; i < __METRIC_MAX; i++)
|
||||
cJSON_AddNumberToObject(json, get_metric_name(i), daemon->metrics[i]);
|
||||
}
|
|
@ -189,7 +189,9 @@ enum gravity_list_type {
|
|||
GRAVITY_ADLISTS,
|
||||
GRAVITY_CLIENTS,
|
||||
GRAVITY_GRAVITY,
|
||||
GRAVITY_ANTIGRAVITY
|
||||
GRAVITY_ANTIGRAVITY,
|
||||
GRAVITY_ADLISTS_BLOCK,
|
||||
GRAVITY_ADLISTS_ALLOW
|
||||
} __attribute__ ((packed));
|
||||
|
||||
enum gravity_tables {
|
||||
|
|
|
@ -221,7 +221,7 @@ void ls_dir(const char* path)
|
|||
format_memory_size(prefix, (unsigned long long)st.st_size, &formatted);
|
||||
|
||||
// Log output for this file
|
||||
log_info("%s %-15s %3.0f%s %s", permissions, usergroup, formatted, prefix, filename);
|
||||
log_info("%s %-15s %3.0f%s %s", permissions, usergroup, formatted, strlen(prefix) > 0 ? prefix : " ", filename);
|
||||
}
|
||||
|
||||
log_info("---------------------------------------------------");
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#ifndef METRICS_H
|
||||
#define METRICS_H
|
||||
|
||||
#include "webserver/cJSON/cJSON.h"
|
||||
|
||||
// defined in src/dnsmasq/cache.c
|
||||
const char *rrtype_name(unsigned short type);
|
||||
|
||||
|
@ -88,4 +90,6 @@ struct metrics
|
|||
|
||||
void get_dnsmasq_metrics(struct metrics *ci);
|
||||
|
||||
void get_dnsmasq_metrics_obj(cJSON *json);
|
||||
|
||||
#endif // METRICS_H
|
||||
|
|
|
@ -54,7 +54,7 @@ inline bool __attribute__((pure)) valid_domain(const char *domain, const size_t
|
|||
// Domain must not contain any character other than [a-zA-Z0-9.-_]
|
||||
if(domain[i] != '-' && domain[i] != '.' && domain[i] != '_' &&
|
||||
(domain[i] < 'a' || domain[i] > 'z') &&
|
||||
// (domain[i] < 'A' || domain[i] > 'Z') && // not needed as all domains are converted to lowercase
|
||||
(domain[i] < 'A' || domain[i] > 'Z') &&
|
||||
(domain[i] < '0' || domain[i] > '9'))
|
||||
return false;
|
||||
|
||||
|
|
|
@ -78,6 +78,8 @@ src/dnsmasq/dnsmasq.c
|
|||
my_syslog(LOG_WARNING, _("failed to access %s: %s"), res->name, strerror(errno));
|
||||
src/dnsmasq/dnsmasq.c
|
||||
my_syslog(LOG_WARNING, _("no servers found in %s, will retry"), latest->name);
|
||||
src/dnsmasq/dnssec.c
|
||||
my_syslog(LOG_WARNING, "limit exceeded: %s", message ? message : _("per-query crypto work"));
|
||||
src/dnsmasq/dnssec.c
|
||||
my_syslog(LOG_WARNING, _("Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name);
|
||||
src/dnsmasq/forward.c
|
||||
|
@ -85,11 +87,21 @@ src/dnsmasq/forward.c
|
|||
src/dnsmasq/forward.c
|
||||
my_syslog(LOG_WARNING, _("nameserver %s refused to do a recursive query"), daemon->namebuff);
|
||||
src/dnsmasq/forward.c
|
||||
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
|
||||
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
|
||||
src/dnsmasq/forward.c
|
||||
my_syslog(LOG_WARNING, _("limit exceeded: per-query subqueries"));
|
||||
src/dnsmasq/forward.c
|
||||
my_syslog(LOG_WARNING, _("validation of %s failed: resource limit exceeded."),
|
||||
daemon->namebuff[0] ? daemon->namebuff : ".");
|
||||
src/dnsmasq/forward.c
|
||||
my_syslog(LOG_WARNING, _("reducing DNS packet size for nameserver %s to %d"), daemon->addrbuff, SAFE_PKTSZ);
|
||||
src/dnsmasq/forward.c
|
||||
my_syslog(LOG_WARNING, _("ignoring query from non-local network %s (logged only once)"), daemon->addrbuff);
|
||||
src/dnsmasq/forward.c
|
||||
my_syslog(LOG_WARNING, _("limit exceeded: per-query subqueries"));
|
||||
src/dnsmasq/forward.c
|
||||
my_syslog(LOG_WARNING, _("validation of %s failed: resource limit exceeded."),
|
||||
daemon->namebuff[0] ? daemon->namebuff : ".");
|
||||
src/dnsmasq/forward.c
|
||||
my_syslog(LOG_WARNING, _("ignoring query from non-local network %s"), daemon->addrbuff);
|
||||
src/dnsmasq/forward.c
|
||||
|
@ -108,7 +120,7 @@ src/dnsmasq/lease.c
|
|||
src/dnsmasq/log.c
|
||||
my_syslog(LOG_WARNING, _("overflow: %d log entries lost"), e);
|
||||
src/dnsmasq/network.c
|
||||
my_syslog(LOG_WARNING, s, daemon->addrbuff, strerror(errno));
|
||||
my_syslog(LOG_WARNING, s, daemon->addrbuff, strerror(errno));
|
||||
src/dnsmasq/network.c
|
||||
my_syslog(LOG_WARNING,
|
||||
_("LOUD WARNING: listening on %s may accept requests via interfaces other than %s"),
|
||||
|
|
|
@ -114,6 +114,9 @@ pdnsutil add-record ftl. regex-multiple AAAA fe80::3f41
|
|||
pdnsutil add-record ftl. regex-notMultiple A 192.168.3.12
|
||||
pdnsutil add-record ftl. regex-notMultiple AAAA fe80::3f41
|
||||
|
||||
# TXT
|
||||
pdnsutil add-record ftl. any TXT "\"Some example text\""
|
||||
|
||||
# Create reverse lookup zone
|
||||
pdnsutil create-zone arpa ns1.ftl
|
||||
pdnsutil add-record arpa. 1.1.168.192.in-addr PTR ftl.
|
||||
|
|
|
@ -487,6 +487,15 @@
|
|||
# How often do we store queries in FTL's database [seconds]?
|
||||
DBinterval = 60
|
||||
|
||||
# Should FTL enable Write-Ahead Log (WAL) mode for the on-disk query database
|
||||
# (configured via files.database)?
|
||||
# It is recommended to leave this setting enabled for performance reasons. About the
|
||||
# only reason to disable WAL mode is if you are experiencing specific issues with it,
|
||||
# e.g., when using a database that is accessed from multiple hosts via a network
|
||||
# share. When this setting is disabled, FTL will use SQLite3's default journal mode
|
||||
# (rollback journal in DELETE mode).
|
||||
useWAL = true
|
||||
|
||||
[database.network]
|
||||
# Should FTL analyze the local ARP cache? When disabled, client identification and the
|
||||
# network table will stop working reliably.
|
||||
|
|
|
@ -311,6 +311,8 @@
|
|||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[@]} == *"192.168.3.1"* ]]
|
||||
[[ ${lines[@]} == *"fe80::3c01"* ]]
|
||||
# TXT records should not be returned due to filter-rr=ANY
|
||||
[[ ${lines[@]} != *"Some example text"* ]]
|
||||
}
|
||||
|
||||
@test "Local DNS test: CNAME cname-ok.ftl" {
|
||||
|
@ -1420,12 +1422,135 @@
|
|||
[[ ${lines[0]} == "145" ]]
|
||||
}
|
||||
|
||||
@test "Check /api/lists?type=block returning only blocking lists" {
|
||||
run bash -c 'curl -s 127.0.0.1/api/lists?type=block | jq ".lists[].type"'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
# Check no allow entries are present
|
||||
[[ ${lines[@]} != *"allow"* ]]
|
||||
}
|
||||
|
||||
@test "Check /api/lists?type=allow returning only allowing lists" {
|
||||
run bash -c 'curl -s 127.0.0.1/api/lists?type=allow | jq ".lists[].type"'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
# Check no block entries are present
|
||||
[[ ${lines[@]} != *"block"* ]]
|
||||
}
|
||||
|
||||
@test "Check /api/lists without type parameter returning all lists" {
|
||||
run bash -c 'curl -s 127.0.0.1/api/lists | jq ".lists[].type"'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
# Check both block and allow entries are present
|
||||
[[ ${lines[@]} == *"allow"* ]]
|
||||
[[ ${lines[@]} == *"block"* ]]
|
||||
}
|
||||
|
||||
@test "API: No UNKNOWN reply in API" {
|
||||
run bash -c 'curl -s 127.0.0.1/api/queries?reply=UNKNOWN | jq .queries'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
run bash -c 'curl -s 127.0.0.1/api/queries?reply=UNKNOWN | jq ".queries | length"'
|
||||
[[ ${lines[0]} == "0" ]]
|
||||
}
|
||||
|
||||
@test "API: No UNKNOWN status in API" {
|
||||
run bash -c 'curl -s 127.0.0.1/api/queries?status=UNKNOWN | jq .queries'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
run bash -c 'curl -s 127.0.0.1/api/queries?status=UNKNOWN | jq ".queries | length"'
|
||||
[[ ${lines[0]} == "0" ]]
|
||||
}
|
||||
|
||||
@test "API authorization (without password): No login required" {
|
||||
run bash -c 'curl -s 127.0.0.1/api/auth'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == '{"session":{"valid":true,"totp":false,"sid":null,"validity":-1},"took":'*'}' ]]
|
||||
}
|
||||
|
||||
@test "Config validation working on the CLI (type-based checking)" {
|
||||
run bash -c './pihole-FTL --config dns.port true'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == 'Config setting dns.port is invalid, allowed options are: unsigned integer (16 bit)' ]]
|
||||
[[ $status == 2 ]]
|
||||
|
||||
run bash -c './pihole-FTL --config dns.revServers "abc"'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == 'Config setting dns.revServers is invalid: not valid JSON, error before: abc' ]]
|
||||
[[ $status == 2 ]]
|
||||
}
|
||||
|
||||
@test "Config validation working on the API (type-based checking)" {
|
||||
run bash -c 'curl -s -X PATCH http://127.0.0.1/api/config -d "{\"config\":{\"dns\":{\"blockESNI\":15.5}}}"'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == "{\"error\":{\"key\":\"bad_request\",\"message\":\"Config item is invalid\",\"hint\":\"dns.blockESNI: not of type bool\"},\"took\":"*"}" ]]
|
||||
|
||||
run bash -c 'curl -s -X PATCH http://127.0.0.1/api/config -d "{\"config\":{\"dns\":{\"piholePTR\":\"something_else\"}}}"'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == "{\"error\":{\"key\":\"bad_request\",\"message\":\"Config item is invalid\",\"hint\":\"dns.piholePTR: invalid option\"},\"took\":"*"}" ]]
|
||||
}
|
||||
|
||||
@test "Config validation working on the CLI (validator-based checking)" {
|
||||
run bash -c './pihole-FTL --config dns.hosts "[\"111.222.333.444 abc\"]"'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == 'Invalid value: dns.hosts[0]: neither a valid IPv4 nor IPv6 address ("111.222.333.444")' ]]
|
||||
[[ $status == 3 ]]
|
||||
|
||||
run bash -c './pihole-FTL --config dns.hosts "[\"1.1.1.1 cf\",\"8.8.8.8 google\",\"1.2.3.4\"]"'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == 'Invalid value: dns.hosts[2]: entry does not have at least one hostname ("1.2.3.4")' ]]
|
||||
[[ $status == 3 ]]
|
||||
|
||||
run bash -c './pihole-FTL --config dns.revServers "[\"abc,def,ghi\"]"'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == 'Invalid value: dns.revServers[0]: <enabled> not a boolean ("abc")' ]]
|
||||
[[ $status == 3 ]]
|
||||
|
||||
run bash -c './pihole-FTL --config dns.revServers "[\"true,abc,def,ghi\"]"'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == 'Invalid value: dns.revServers[0]: <ip-address> neither a valid IPv4 nor IPv6 address ("abc")' ]]
|
||||
[[ $status == 3 ]]
|
||||
|
||||
run bash -c './pihole-FTL --config dns.revServers "[\"true,1.2.3.4/55,def,ghi\"]"'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == 'Invalid value: dns.revServers[0]: <prefix-len> not a valid IPv4 prefix length ("55")' ]]
|
||||
[[ $status == 3 ]]
|
||||
|
||||
run bash -c './pihole-FTL --config dns.revServers "[\"true,::1/255,def,ghi\"]"'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == 'Invalid value: dns.revServers[0]: <prefix-len> not a valid IPv6 prefix length ("255")' ]]
|
||||
[[ $status == 3 ]]
|
||||
|
||||
run bash -c './pihole-FTL --config dns.revServers "[\"true,1.1.1.1,def\"]"'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == 'Invalid value: dns.revServers[0]: entry does not have all required elements (<enabled>,<ip-address>[/<prefix-len>],<server>[#<port>],<domain>)' ]]
|
||||
[[ $status == 3 ]]
|
||||
|
||||
run bash -c './pihole-FTL --config dns.revServers "[\"true,1.1.1.1,def,ghi\"]"'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == 'New dnsmasq configuration is not valid ('*'Name does not resolve at line '*' of /etc/pihole/dnsmasq.conf.temp: "rev-server=1.1.1.1,def"), config remains unchanged' ]]
|
||||
[[ $status == 3 ]]
|
||||
|
||||
run bash -c './pihole-FTL --config webserver.api.excludeClients "[\".*\",\"$$$\",\"[[[\"]"'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == 'Invalid value: webserver.api.excludeClients[2]: not a valid regex ("[[["): Missing '\'']'\' ]]
|
||||
[[ $status == 3 ]]
|
||||
}
|
||||
|
||||
@test "Config validation working on the API (validator-based checking)" {
|
||||
run bash -c 'curl -s -X PATCH http://127.0.0.1/api/config -d "{\"config\":{\"files\":{\"pcap\":\"%gh4b\"}}}"'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == "{\"error\":{\"key\":\"bad_request\",\"message\":\"Config item validation failed\",\"hint\":\"files.pcap: not a valid file path (\\\"%gh4b\\\")\"},\"took\":"*"}" ]]
|
||||
|
||||
run bash -c 'curl -s -X PATCH http://127.0.0.1/api/config -d "{\"config\":{\"dns\":{\"cnameRecords\":[\"a\"]}}}"'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == "{\"error\":{\"key\":\"bad_request\",\"message\":\"Config item validation failed\",\"hint\":\"dns.cnameRecords[0]: not a valid CNAME definition (too few elements)\"},\"took\":"*"}" ]]
|
||||
|
||||
run bash -c 'curl -s -X PATCH http://127.0.0.1/api/config -d "{\"config\":{\"dns\":{\"cnameRecords\":[\"a,b,c\",\"a,b,c,,c\"]}}}"'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == "{\"error\":{\"key\":\"bad_request\",\"message\":\"Config item validation failed\",\"hint\":\"dns.cnameRecords[1]: contains an empty string at position 3\"},\"took\":"*"}" ]]
|
||||
|
||||
run bash -c 'curl -s -X PATCH http://127.0.0.1/api/config -d "{\"config\":{\"dns\":{\"cnameRecords\":[\"a,b,c\",\"a,b,c\",5]}}}"'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == "{\"error\":{\"key\":\"bad_request\",\"message\":\"Config item is invalid\",\"hint\":\"dns.cnameRecords: array has invalid elements\"},\"took\":"*"}" ]]
|
||||
}
|
||||
|
||||
@test "Create, set, and use application password" {
|
||||
run bash -c 'curl -s 127.0.0.1/api/auth/app'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
|
|
Loading…
Reference in New Issue