mirror of
https://github.com/pi-hole/FTL.git
synced 2024-10-26 16:52:18 +02:00
Merge branch 'development' into new/http
Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "FTL x86_64 Build Env",
|
||||
"image": "ghcr.io/pi-hole/ftl-build:x86_64",
|
||||
"extensions": [
|
||||
"jetmartin.bats",
|
||||
"ms-vscode.cpptools",
|
||||
"ms-vscode.cmake-tools",
|
||||
"eamodio.gitlens"
|
||||
],
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
|
||||
**In raising this issue, I confirm the following (please check boxes, eg [X]) Failure to fill the template will close your issue:**
|
||||
|
||||
- [] I have read and understood the [contributors guide](https://github.com/pi-hole/pi-hole/blob/master/CONTRIBUTING.md).
|
||||
- [] The issue I am reporting can be *replicated*
|
||||
- [] The issue I am reporting isn't a duplicate
|
||||
|
||||
**How familiar are you with the codebase?:**
|
||||
|
||||
_{replace this text with a number from 1 to 10, with 1 being not familiar, and 10 being very familiar}_
|
||||
|
||||
---
|
||||
|
||||
**[BUG | ISSUE] Expected Behaviour:**
|
||||
|
||||
|
||||
**[BUG | ISSUE] Actual Behaviour:**
|
||||
|
||||
|
||||
**[BUG | ISSUE] Steps to reproduce:**
|
||||
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
**Log file output [if available]**
|
||||
|
||||
`output`
|
||||
|
||||
**Device specifics**
|
||||
|
||||
Hardware Type: rPi, VPS, etc
|
||||
OS:
|
||||
|
||||
|
||||
_This template was created based on the work of [`udemy-dl`](https://github.com/nishad/udemy-dl/blob/master/LICENSE)._
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
**By submitting this pull request, I confirm the following (please check boxes, eg [X]) _Failure to fill the template will close your PR_:**
|
||||
|
||||
***Please submit all pull requests against the `development` branch. Failure to do so will delay or deny your request***
|
||||
|
||||
- [] I have read and understood the [contributors guide](https://github.com/pi-hole/pi-hole/blob/master/CONTRIBUTING.md).
|
||||
- [] I have checked that [another pull request](https://github.com/pi-hole/FTL/pulls) for this purpose does not exist.
|
||||
- [] I have considered, and confirmed that this submission will be valuable to others.
|
||||
- [] I accept that this submission may not be used, and the pull request closed at the will of the maintainer.
|
||||
- [] I give this submission freely, and claim no ownership to its content.
|
||||
|
||||
**How familiar are you with the codebase?:** _{replace this text with a number from 1 to 10, with 1 being not familiar, and 10 being very familiar}_
|
||||
|
||||
---
|
||||
|
||||
_{replace this line with your pull request content}_
|
||||
|
||||
|
||||
_This template was created based on the work of [`udemy-dl`](https://github.com/nishad/udemy-dl/blob/master/LICENSE)._
|
||||
@@ -10,3 +10,6 @@ updates:
|
||||
target-branch: development
|
||||
reviewers:
|
||||
- "pi-hole/ftl-maintainers"
|
||||
pull-request-branch-name:
|
||||
# Separate sections of the branch name with a hyphen
|
||||
separator: "-"
|
||||
|
||||
+79
-23
@@ -4,15 +4,12 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
- '!dependabot/**'
|
||||
pull_request:
|
||||
branches-ignore:
|
||||
- 'dependabot/**'
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
smoke-tests:
|
||||
if: |
|
||||
github.event_name == 'push'
|
||||
|| github.event_name == 'release'
|
||||
@@ -24,8 +21,39 @@ jobs:
|
||||
OUTPUT_DIR: ${{ steps.variables.outputs.OUTPUT_DIR }}
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout code
|
||||
uses: actions/checkout@v3.2.0
|
||||
-
|
||||
name: "Calculate required variables"
|
||||
id: variables
|
||||
run: |
|
||||
GIT_TAG=${{ github.event.release.tag_name }}
|
||||
# If GIT_TAG is set then GIT BRANCH should be "master", else set it from GITHUB_REF
|
||||
GIT_BRANCH=$([ -n "${GIT_TAG}" ] && echo "master" || echo "${GITHUB_REF#refs/*/}")
|
||||
echo "GIT_BRANCH=${GIT_BRANCH}" >> $GITHUB_OUTPUT
|
||||
echo "GIT_TAG=${GIT_TAG}" >> $GITHUB_OUTPUT
|
||||
echo "OUTPUT_DIR=${GIT_TAG:-${GIT_BRANCH}}" >> $GITHUB_OUTPUT
|
||||
-
|
||||
name: "Check git branch name depth"
|
||||
env:
|
||||
GIT_BRANCH: ${{ steps.variables.outputs.GIT_BRANCH }}
|
||||
run: |
|
||||
IFS='/';
|
||||
read -r -a branch <<<"${GIT_BRANCH}";
|
||||
if [[ "${#branch[@]}" -gt 2 ]]; then echo "Error: Your branch name contains more than one subdir, which will cause issues with the build process." && FAIL=1; fi;
|
||||
unset IFS;
|
||||
# If FAIL is 1 then we fail.
|
||||
[[ $FAIL == 1 ]] && exit 1 || echo "Branch name depth check passed."
|
||||
shell: bash
|
||||
|
||||
container: ghcr.io/pi-hole/ftl-build:v1.16-${{ matrix.arch }}
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
needs: smoke-tests
|
||||
|
||||
container: ghcr.io/pi-hole/ftl-build:v1.23-${{ matrix.arch }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -34,6 +62,9 @@ jobs:
|
||||
include:
|
||||
- arch: x86_64
|
||||
bin_name: pihole-FTL-linux-x86_64
|
||||
- arch: x86_64
|
||||
arch_extra: _full
|
||||
bin_name: pihole-FTL-linux-x86_64_full
|
||||
- arch: x86_64-musl
|
||||
bin_name: pihole-FTL-musl-linux-x86_64
|
||||
- arch: x86_32
|
||||
@@ -52,27 +83,23 @@ jobs:
|
||||
bin_name: pihole-FTL-aarch64-linux-gnu
|
||||
|
||||
env:
|
||||
CI_ARCH: ${{ matrix.arch }}
|
||||
CI_ARCH: ${{ matrix.arch }}${{ matrix.arch_extra }}
|
||||
|
||||
steps:
|
||||
-
|
||||
name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v3.2.0
|
||||
-
|
||||
name: Calculate required build variables
|
||||
id: variables
|
||||
run: |
|
||||
GIT_TAG=${{ github.event.release.tag_name }}
|
||||
# If GIT_TAG is set then GIT BRANCH should be "master", else set it from GITHUB_REF
|
||||
GIT_BRANCH=$([ -n "${GIT_TAG}" ] && echo "master" || echo "${GITHUB_REF#refs/*/}")
|
||||
echo ::set-output name=GIT_BRANCH::${GIT_BRANCH}
|
||||
echo ::set-output name=GIT_TAG::${GIT_TAG}
|
||||
echo ::set-output name=OUTPUT_DIR::${GIT_TAG:-${GIT_BRANCH}}
|
||||
name: "Fix ownership of repository"
|
||||
run: chown -R root .
|
||||
-
|
||||
name: "Fix ownership of repository"
|
||||
run: chown -R root .
|
||||
-
|
||||
name: "Build"
|
||||
env:
|
||||
GIT_BRANCH: ${{ steps.variables.outputs.GIT_BRANCH }}
|
||||
GIT_TAG: ${{ steps.variables.outputs.GIT_TAG }}
|
||||
GIT_BRANCH: ${{ needs.smoke-tests.outputs.GIT_BRANCH }}
|
||||
GIT_TAG: ${{ needs.smoke-tests.outputs.GIT_TAG }}
|
||||
run: |
|
||||
bash build.sh "-DSTATIC=${STATIC}"
|
||||
-
|
||||
@@ -92,7 +119,7 @@ jobs:
|
||||
-
|
||||
name: Store binary artifacts for deployoment
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v3.1.1
|
||||
with:
|
||||
name: tmp-storage
|
||||
path: '${{ matrix.bin_name }}*'
|
||||
@@ -110,23 +137,38 @@ jobs:
|
||||
|
||||
deploy:
|
||||
if: github.event_name != 'pull_request'
|
||||
needs: build
|
||||
needs: [smoke-tests, build]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
<<<<<<< HEAD
|
||||
- name: Get Binaries and documentation built in previous jobs
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: tmp-storage
|
||||
=======
|
||||
-
|
||||
name: Checkout code
|
||||
uses: actions/checkout@v3.2.0
|
||||
-
|
||||
name: Get Binaries built in previous jobs
|
||||
uses: actions/download-artifact@v3.0.1
|
||||
id: download
|
||||
with:
|
||||
name: tmp-binary-storage
|
||||
path: ftl-builds/
|
||||
>>>>>>> development
|
||||
-
|
||||
name: Display structure of downloaded files
|
||||
run: ls -R
|
||||
working-directory: ${{steps.download.outputs.download-path}}
|
||||
-
|
||||
name: Install SSH Key
|
||||
uses: shimataro/ssh-key-action@v2
|
||||
uses: benoitchantre/setup-ssh-authentication-action@1.0.0
|
||||
with:
|
||||
key: ${{ secrets.SSH_KEY }}
|
||||
known_hosts: ${{ secrets.KNOWN_HOSTS }}
|
||||
private-key: ${{ secrets.SSH_KEY }}
|
||||
known-hosts: ${{ secrets.KNOWN_HOSTS }}
|
||||
-
|
||||
<<<<<<< HEAD
|
||||
name: Untar documentation files
|
||||
run: |
|
||||
mkdir docs/
|
||||
@@ -142,11 +184,25 @@ jobs:
|
||||
-mkdir ${{ needs.build.outputs.OUTPUT_DIR }}/docs/specs
|
||||
put pihole* ${{ needs.build.outputs.OUTPUT_DIR }}
|
||||
put docs/* ${{ needs.build.outputs.OUTPUT_DIR }}/docs/"
|
||||
=======
|
||||
name: Transfer Builds to Pi-hole server for pihole checkout
|
||||
env:
|
||||
USER: ${{ secrets.SSH_USER }}
|
||||
HOST: ${{ secrets.SSH_HOST }}
|
||||
TARGET_DIR: ${{ needs.smoke-tests.outputs.OUTPUT_DIR }}
|
||||
SOURCE_DIR: ${{ steps.download.outputs.download-path }}
|
||||
run: |
|
||||
bash ./deploy.sh
|
||||
>>>>>>> development
|
||||
-
|
||||
name: Attach binaries to release
|
||||
if: github.event_name == 'release'
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: |
|
||||
<<<<<<< HEAD
|
||||
pihole-FTL*
|
||||
api-docs.tar.gz
|
||||
=======
|
||||
${{ steps.download.outputs.download-path }}/*
|
||||
>>>>>>> development
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v3.2.0
|
||||
-
|
||||
name: Spell-Checking
|
||||
uses: codespell-project/actions-codespell@master
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
name: "Check for merge conflicts"
|
||||
on:
|
||||
# So that PRs touching the same files as the push are updated
|
||||
push:
|
||||
# So that the `dirtyLabel` is removed if conflicts are resolve
|
||||
# We recommend `pull_request_target` so that github secrets are available.
|
||||
# In `pull_request` we wouldn't be able to change labels of fork PRs
|
||||
pull_request_target:
|
||||
types: [synchronize]
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check if PRs are have merge conflicts
|
||||
uses: eps1lon/actions-label-merge-conflict@v2.1.0
|
||||
with:
|
||||
dirtyLabel: "Merge conflicts"
|
||||
repoToken: "${{ secrets.GITHUB_TOKEN }}"
|
||||
commentOnDirty: "This pull request has conflicts, please resolve those before we can evaluate the pull request."
|
||||
commentOnClean: "Conflicts have been resolved."
|
||||
@@ -2,7 +2,7 @@ name: Mark stale issues
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 * * * *'
|
||||
- cron: '0 8 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
issues: write
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v5
|
||||
- uses: actions/stale@v7.0.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 30
|
||||
@@ -23,4 +23,4 @@ jobs:
|
||||
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
|
||||
stale-issue-reason: 'not_planned'
|
||||
close-issue-reason: 'not_planned'
|
||||
|
||||
@@ -11,17 +11,8 @@ jobs:
|
||||
name: Syncing branches
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v3.2.0
|
||||
- name: Opening pull request
|
||||
id: pull
|
||||
uses: tretuna/sync-branches@1.4.0
|
||||
with:
|
||||
run: gh pr create -B development -H master --title 'Sync master back into development' --body 'Created by Github action' --label 'internal'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
FROM_BRANCH: 'master'
|
||||
TO_BRANCH: 'development'
|
||||
- name: Label the pull request to ignore for release note generation
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: internal
|
||||
repo: ${{ github.repository }}
|
||||
number: ${{ steps.pull.outputs.PULL_REQUEST_NUMBER }}
|
||||
|
||||
@@ -27,3 +27,10 @@ src/api/docs/hex
|
||||
|
||||
# Test dependencies
|
||||
/node_modules/
|
||||
|
||||
# Test Leftovers
|
||||
dig.log
|
||||
ptr.log
|
||||
|
||||
# Compiled LUA scripts
|
||||
/src/lua/scripts/*.hex
|
||||
|
||||
+1
-1
@@ -11,6 +11,6 @@
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
project(PIHOLE_FTL C)
|
||||
|
||||
set(DNSMASQ_VERSION pi-hole-2.87test8)
|
||||
set(DNSMASQ_VERSION pi-hole-v2.88)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Pi-hole: A black hole for Internet advertisements
|
||||
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
#
|
||||
# Simple speed test bench for FTL
|
||||
#
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
function GetFTLData {
|
||||
# Open connection to FTL
|
||||
exec 3<>/dev/tcp/localhost/"$(cat /run/pihole-FTL.port)"
|
||||
|
||||
# Test if connection is open
|
||||
if { true >&3; } 2> /dev/null; then
|
||||
# Send command to FTL
|
||||
echo -e ">$1" >&3
|
||||
echo -e ">quit" >&3
|
||||
|
||||
# Read all input
|
||||
if [ "$1" == "stats" ]; then
|
||||
cat <&3
|
||||
else
|
||||
cat <&3 &> /dev/null
|
||||
fi
|
||||
|
||||
# Close connection
|
||||
exec 3>&-
|
||||
exec 3<&-
|
||||
fi
|
||||
}
|
||||
|
||||
echo "Getting statistics data (output to stdout)"
|
||||
time GetFTLData "stats"
|
||||
|
||||
echo "Getting over time data (output to /dev/null)"
|
||||
time GetFTLData "overTime"
|
||||
|
||||
echo "Getting all queries (output to /dev/null)"
|
||||
time GetFTLData "getallqueries"
|
||||
@@ -1,8 +1,21 @@
|
||||
<p align="center">
|
||||
<a href="https://pi-hole.net"><img src="https://pi-hole.github.io/graphics/Vortex/Vortex_with_text.png" width="80" alt="Pi-hole"></a>
|
||||
<br/>
|
||||
<b>Network-wide ad blocking via your own Linux hardware</b><br/><br/>
|
||||
<a href="https://pi-hole.net"><img src="https://pi-hole.github.io/graphics/FTLDNS/FTLDNS.png" width="550" alt="FTLDNS"></a><br/>
|
||||
<a href="https://pi-hole.net">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://pi-hole.github.io/graphics/Vortex/Vortex_Vertical_wordmark_darkmode.png">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://pi-hole.github.io/graphics/Vortex/Vortex_Vertical_wordmark_lightmode.png">
|
||||
<img src="https://pi-hole.github.io/graphics/Vortex/Vortex_Vertical_wordmark_lightmode.png" width="80" alt="Pi-hole website">
|
||||
</picture>
|
||||
</a>
|
||||
<br/>
|
||||
<b>Network-wide ad blocking via your own Linux hardware</b><br/><br/>
|
||||
<a href="https://pi-hole.net">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://pi-hole.github.io/graphics/FTLDNS/FTLDNS_darkmode.png">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://pi-hole.github.io/graphics/FTLDNS/FTLDNS.png">
|
||||
<img src="https://pi-hole.github.io/graphics/FTLDNS/FTLDNS.png" alt="FTLDNS">
|
||||
</picture>
|
||||
</a>
|
||||
<br/>
|
||||
</p>
|
||||
|
||||
FTLDNS (`pihole-FTL`) provides an interactive API and also generates statistics for Pi-hole[®](https://pi-hole.net/trademark-rules-and-brand-guidelines/)'s Web interface.
|
||||
@@ -13,15 +26,14 @@ FTLDNS (`pihole-FTL`) provides an interactive API and also generates statistics
|
||||
- **Interactive**: our API can be used to interface with your projects
|
||||
- **Insightful**: stats normally reserved inside of `dnsmasq` are made available so you can see what's really happening on your network
|
||||
|
||||
|
||||
# Official documentation
|
||||
## Official documentation
|
||||
|
||||
The official *FTL*DNS documentation can be found [here](https://docs.pi-hole.net/ftldns/).
|
||||
|
||||
# Installation
|
||||
## Installation
|
||||
|
||||
FTLDNS (`pihole-FTL`) is installed by default when you choose to enable the Web interface when installing Pi-hole.
|
||||
FTLDNS (`pihole-FTL`) is automatically installed when installing Pi-hole.
|
||||
|
||||
> IMPORTANT!
|
||||
### IMPORTANT
|
||||
|
||||
>FTLDNS will _disable_ any existing installations of `dnsmasq`. This is because FTLDNS _is_ `dnsmasq` + Pi-hole's code, so both cannot run simultaneously.
|
||||
>FTLDNS will *disable* any existing installations of `dnsmasq`. This is because FTLDNS *is* `dnsmasq` + Pi-hole's code, so both cannot run simultaneously.
|
||||
|
||||
@@ -34,6 +34,14 @@ fi
|
||||
# Remove possibly generated api/docs elements
|
||||
rm -rf src/api/docs/hex
|
||||
|
||||
# Remove compiled LUA scripts if older than the plain ones
|
||||
for scriptname in src/lua/scripts/*.lua; do
|
||||
if [ -f "${scriptname}.hex" ] && [ "${scriptname}.hex" -ot "${scriptname}" ]; then
|
||||
echo "INFO: ${scriptname} is outdated and will be recompiled"
|
||||
rm "${scriptname}.hex"
|
||||
fi
|
||||
done
|
||||
|
||||
# Configure build, pass CMake CACHE entries if present
|
||||
# Wrap multiple options in "" as first argument to ./build.sh:
|
||||
# ./build.sh "-DA=1 -DB=2" install
|
||||
@@ -52,7 +60,7 @@ cmake --build . -- -j $(nproc)
|
||||
# Otherwise, we simply copy the binary one level up
|
||||
if [[ -n "${install}" ]]; then
|
||||
echo "Installing pihole-FTL"
|
||||
SUDO=$(which sudo)
|
||||
SUDO=$(command -v sudo)
|
||||
${SUDO} cmake --install .
|
||||
else
|
||||
echo "Copying compiled pihole-FTL binary to repository root"
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#!/bin/bash
|
||||
# Pi-hole: A black hole for Internet advertisements
|
||||
# (c) 2022 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
#
|
||||
# FTL Engine
|
||||
# Deploy script for FTL
|
||||
#
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
|
||||
# Transfer Builds to Pi-hole server for pihole checkout
|
||||
# We use sftp for secure transfer and use the branch name as dir on the server.
|
||||
# The branch name could contain slashes, creating hierarchical dirs. However,
|
||||
# this is not supported by sftp's `mkdir` (option -p) is not available. Therefore,
|
||||
# we need to loop over each dir level and create them one by one.
|
||||
|
||||
|
||||
# Safeguard: do not deploy if TARGET_DIR is empty
|
||||
if [[ -z ${TARGET_DIR} ]]; then
|
||||
echo "Error: Empty target dir."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
IFS='/'
|
||||
read -r -a path <<<"${TARGET_DIR}"
|
||||
|
||||
# Safeguard: do not deploy if more than one subdir (eg. /tweak/feature/subfeature) needs to be created
|
||||
if [[ "${#path[@]}" -gt 2 ]]; then
|
||||
echo "Error: Your branch name contains more then one subdir. We won't deploy that."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
unset IFS
|
||||
|
||||
old_path="."
|
||||
|
||||
for dir in "${path[@]}"; do
|
||||
mapfile -t dir_content <<< "$(
|
||||
sftp -b - "${USER}"@"${HOST}" <<< "cd ${old_path}
|
||||
ls -1"
|
||||
)"
|
||||
|
||||
# Only try to create the subdir if does not already exist
|
||||
if [[ "${dir_content[*]}" =~ "${dir}" ]]; then
|
||||
echo "Dir: ${old_path}/${dir} already exists"
|
||||
else
|
||||
echo "Creating dir: ${old_path}/${dir}"
|
||||
sftp -b - "${USER}"@"${HOST}" <<< "cd ${old_path}
|
||||
-mkdir ${dir}"
|
||||
fi
|
||||
|
||||
old_path="${old_path}/${dir}"
|
||||
done
|
||||
|
||||
sftp -r -b - "${USER}"@"${HOST}" <<< "cd ${old_path}
|
||||
put ${SOURCE_DIR}/* ./"
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
patch -p1 < patch/lua/0001-add-pihole-library.patch
|
||||
@@ -0,0 +1,94 @@
|
||||
diff --git a/src/lua/linit.c b/src/lua/linit.c
|
||||
index 69808f84..83b89555 100644
|
||||
--- a/src/lua/linit.c
|
||||
+++ b/src/lua/linit.c
|
||||
@@ -50,6 +50,9 @@ static const luaL_Reg loadedlibs[] = {
|
||||
{LUA_MATHLIBNAME, luaopen_math},
|
||||
{LUA_UTF8LIBNAME, luaopen_utf8},
|
||||
{LUA_DBLIBNAME, luaopen_debug},
|
||||
+ /****** Pi-hole modification ******/
|
||||
+ {LUA_PIHOLELIBNAME, luaopen_pihole},
|
||||
+ /**********************************/
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
diff --git a/src/lua/lua.c b/src/lua/lua.c
|
||||
index 454ce12f..a363925c 100644
|
||||
--- a/src/lua/lua.c
|
||||
+++ b/src/lua/lua.c
|
||||
@@ -20,6 +20,10 @@
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
+/** Pi-hole modification **/
|
||||
+#include "ftl_lua.h"
|
||||
+/**************************/
|
||||
+
|
||||
|
||||
#if !defined(LUA_PROGNAME)
|
||||
#define LUA_PROGNAME "lua"
|
||||
@@ -190,7 +194,9 @@ static int dostring (lua_State *L, const char *s, const char *name) {
|
||||
/*
|
||||
** Receives 'globname[=modname]' and runs 'globname = require(modname)'.
|
||||
*/
|
||||
-static int dolibrary (lua_State *L, char *globname) {
|
||||
+/************** Pi-hole modification ***************/
|
||||
+int dolibrary (lua_State *L, char *globname) {
|
||||
+/***************************************************/
|
||||
int status;
|
||||
char *modname = strchr(globname, '=');
|
||||
if (modname == NULL) /* no explicit name? */
|
||||
@@ -597,6 +599,12 @@ static int pmain (lua_State *L) {
|
||||
if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */
|
||||
return 0; /* error running LUA_INIT */
|
||||
}
|
||||
+
|
||||
+ /************** Pi-hole modification ***************/
|
||||
+ // Load and enable libraries bundled with Pi-hole
|
||||
+ ftl_lua_init(L);
|
||||
+ /***************************************************/
|
||||
+
|
||||
if (!runargs(L, argv, script)) /* execute arguments -e and -l */
|
||||
return 0; /* something failed */
|
||||
if (script < argc && /* execute main script (if there is one) */
|
||||
@@ -616,7 +622,9 @@ static int pmain (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
-int main (int argc, char **argv) {
|
||||
+/******* Pi-hole modification ********/
|
||||
+int lua_main (int argc, char **argv) {
|
||||
+/*************************************/
|
||||
int status, result;
|
||||
lua_State *L = luaL_newstate(); /* create state */
|
||||
if (L == NULL) {
|
||||
diff --git a/src/lua/luac.c b/src/lua/luac.c
|
||||
index 56ddc414..d7d219e4 100644
|
||||
--- a/src/lua/luac.c
|
||||
+++ b/src/lua/luac.c
|
||||
@@ -194,7 +194,9 @@ static int pmain(lua_State* L)
|
||||
return 0;
|
||||
}
|
||||
|
||||
-int main(int argc, char* argv[])
|
||||
+/******* Pi-hole modification ********/
|
||||
+int luac_main(int argc, char* argv[])
|
||||
+/*************************************/
|
||||
{
|
||||
lua_State* L;
|
||||
int i=doargs(argc,argv);
|
||||
diff --git a/src/lua/lualib.h b/src/lua/lualib.h
|
||||
index eb08b530..e6dfbf6c 100644
|
||||
--- a/src/lua/lualib.h
|
||||
+++ b/src/lua/lualib.h
|
||||
@@ -44,6 +44,10 @@ LUAMOD_API int (luaopen_debug) (lua_State *L);
|
||||
#define LUA_LOADLIBNAME "package"
|
||||
LUAMOD_API int (luaopen_package) (lua_State *L);
|
||||
|
||||
+/************ Pi-hole modification *************/
|
||||
+#define LUA_PIHOLELIBNAME "pihole"
|
||||
+LUAMOD_API int (luaopen_pihole) (lua_State *L);
|
||||
+/***********************************************/
|
||||
|
||||
/* open all previous libraries */
|
||||
LUALIB_API void (luaL_openlibs) (lua_State *L);
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
patch -p1 < patch/sqlite3/0001-print-FTL-version-in-interactive-shell.patch
|
||||
patch -p1 < patch/sqlite3/0002-make-sqlite3ErrName-public.patch
|
||||
@@ -0,0 +1,30 @@
|
||||
diff --git a/src/database/shell.c b/src/database/shell.c
|
||||
index 6280ebf6..a5e82f70 100644
|
||||
--- a/src/database/shell.c
|
||||
+++ b/src/database/shell.c
|
||||
@@ -126,6 +126,8 @@ typedef unsigned char u8;
|
||||
#endif
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
+// print_FTL_version()
|
||||
+#include "../log.h"
|
||||
|
||||
#if !defined(_WIN32) && !defined(WIN32)
|
||||
# include <signal.h>
|
||||
@@ -25855,7 +25857,7 @@ static char *cmdline_option_value(int argc, char **argv, int i){
|
||||
#endif
|
||||
|
||||
#if SQLITE_SHELL_IS_UTF8
|
||||
-int SQLITE_CDECL main(int argc, char **argv){
|
||||
+int SQLITE_CDECL sqlite3_shell_main(int argc, char **argv){
|
||||
#else
|
||||
int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
|
||||
char **argv;
|
||||
@@ -26377,6 +26379,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
|
||||
char *zHome;
|
||||
char *zHistory;
|
||||
int nHistory;
|
||||
+ print_FTL_version();
|
||||
printf(
|
||||
"SQLite version %s %.19s\n" /*extra-version-info*/
|
||||
"Enter \".help\" for usage hints.\n",
|
||||
@@ -0,0 +1,22 @@
|
||||
diff --git a/src/database/sqlite3.c b/src/database/sqlite3.c
|
||||
index 2763b1b4..55f88efb 100644
|
||||
--- a/src/database/sqlite3.c
|
||||
+++ b/src/database/sqlite3.c
|
||||
@@ -173885,8 +173885,7 @@ SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3 *db, int tripCode){
|
||||
** Return a static string containing the name corresponding to the error code
|
||||
** specified in the argument.
|
||||
*/
|
||||
-#if defined(SQLITE_NEED_ERR_NAME)
|
||||
-SQLITE_PRIVATE const char *sqlite3ErrName(int rc){
|
||||
+SQLITE_API const char *sqlite3ErrName(int rc){
|
||||
const char *zName = 0;
|
||||
int i, origRc = rc;
|
||||
for(i=0; i<2 && zName==0; i++, rc &= 0xff){
|
||||
@@ -173991,7 +173990,6 @@ SQLITE_PRIVATE const char *sqlite3ErrName(int rc){
|
||||
}
|
||||
return zName;
|
||||
}
|
||||
-#endif
|
||||
|
||||
/*
|
||||
** Return a static string that describes the kind of error specified in the
|
||||
+21
-1
@@ -32,7 +32,8 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
|
||||
# SQLITE_USE_URI=1: The advantage of using a URI filename is that query parameters on the URI can be used to control details of the newly created database connection.
|
||||
# SQLITE_OMIT_DESERIALIZE: This option causes the the sqlite3_serialize() and sqlite3_deserialize() interfaces to be omitted from the build (was the default before 3.36.0)
|
||||
# HAVE_READLINE: Enable readline support to allow easy editing, history and auto-completion
|
||||
set(SQLITE_DEFINES "-DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_DEFAULT_FOREIGN_KEYS=1 -DSQLITE_DQS=0 -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TEMP_STORE=2 -DSQLITE_DEFAULT_CACHE_SIZE=-8000 -DSQLITE_USE_URI=1 -DSQLITE_OMIT_DESERIALIZE -DHAVE_READLINE")
|
||||
# SQLITE_DEFAULT_CACHE_SIZE=-16384: Allow up to 16 MiB of cache to be used by SQLite3 (default is 2000 kiB)
|
||||
set(SQLITE_DEFINES "-DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_DEFAULT_FOREIGN_KEYS=1 -DSQLITE_DQS=0 -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TEMP_STORE=2 -DSQLITE_DEFAULT_CACHE_SIZE=-8000 -DSQLITE_USE_URI=1 -DSQLITE_OMIT_DESERIALIZE -DHAVE_READLINE_CACHE_SIZE=-16384")
|
||||
|
||||
# Code hardening and debugging improvements
|
||||
# -fstack-protector-strong: The program will be resistant to having its stack overflowed
|
||||
@@ -228,6 +229,24 @@ else()
|
||||
message(STATUS "Building FTL with readline support: NO")
|
||||
endif()
|
||||
|
||||
# Do we want to compile an all-in FTL version?
|
||||
if(DEFINED ENV{CI_ARCH})
|
||||
if($ENV{CI_ARCH} STREQUAL "x86_64_full")
|
||||
add_definitions(-DDNSMASQ_ALL_OPTS)
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
find_package(DBus REQUIRED)
|
||||
# Use results of find_package() call.
|
||||
include_directories(${DBUS_INCLUDE_DIRS})
|
||||
target_link_libraries(pihole-FTL ${DBUS_LIBRARIES})
|
||||
find_library(LIBMNL mnl)
|
||||
find_library(LIBNFTNL nftnl)
|
||||
find_library(LIBNFTABLES nftables)
|
||||
find_library(LIBNFNETLINK nfnetlink)
|
||||
find_library(LIBNETFILTER_CONNTRACK netfilter_conntrack)
|
||||
target_link_libraries(pihole-FTL ${LIBMNL} ${LIBNFTABLES} ${LIBNFTNL} ${LIBNFNETLINK} ${LIBNETFILTER_CONNTRACK})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||
set(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "..." FORCE)
|
||||
endif()
|
||||
@@ -246,6 +265,7 @@ add_subdirectory(ph7)
|
||||
add_subdirectory(database)
|
||||
add_subdirectory(dnsmasq)
|
||||
add_subdirectory(lua)
|
||||
add_subdirectory(lua/scripts)
|
||||
add_subdirectory(tre-regex)
|
||||
add_subdirectory(syscalls)
|
||||
add_subdirectory(config)
|
||||
|
||||
@@ -129,8 +129,8 @@
|
||||
|
||||
// DELAY_STARTUP should only delay the startup of the resolver during a starting up system
|
||||
// This setting control how long after boot we consider a system to be in starting-up mode
|
||||
// Default: 60 [seconds]
|
||||
#define DELAY_UPTIME 60
|
||||
// Default: 180 [seconds]
|
||||
#define DELAY_UPTIME 180
|
||||
|
||||
// DB_QUERY_MAX_ITER defines how many queries we check periodically for updates to be added
|
||||
// to the in-memory database. This value may need to be increased on *very* busy systems.
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# - Try to find DBus
|
||||
# Once done, this will define
|
||||
#
|
||||
# DBUS_FOUND - system has DBus
|
||||
# DBUS_INCLUDE_DIRS - the DBus include directories
|
||||
# DBUS_LIBRARIES - link these to use DBus
|
||||
#
|
||||
# Copyright (C) 2012 Raphael Kubo da Costa <rakuco@webkit.org>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS
|
||||
# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS
|
||||
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
FIND_PACKAGE(PkgConfig)
|
||||
PKG_CHECK_MODULES(PC_DBUS QUIET dbus-1)
|
||||
|
||||
FIND_LIBRARY(DBUS_LIBRARIES
|
||||
NAMES dbus-1
|
||||
HINTS ${PC_DBUS_LIBDIR}
|
||||
${PC_DBUS_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
FIND_PATH(DBUS_INCLUDE_DIR
|
||||
NAMES dbus/dbus.h
|
||||
HINTS ${PC_DBUS_INCLUDEDIR}
|
||||
${PC_DBUS_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
GET_FILENAME_COMPONENT(_DBUS_LIBRARY_DIR ${DBUS_LIBRARIES} PATH)
|
||||
FIND_PATH(DBUS_ARCH_INCLUDE_DIR
|
||||
NAMES dbus/dbus-arch-deps.h
|
||||
HINTS ${PC_DBUS_INCLUDEDIR}
|
||||
${PC_DBUS_INCLUDE_DIRS}
|
||||
${_DBUS_LIBRARY_DIR}
|
||||
${DBUS_INCLUDE_DIR}
|
||||
PATH_SUFFIXES include
|
||||
)
|
||||
|
||||
SET(DBUS_INCLUDE_DIRS ${DBUS_INCLUDE_DIR} ${DBUS_ARCH_INCLUDE_DIR})
|
||||
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(DBUS REQUIRED_VARS DBUS_INCLUDE_DIRS DBUS_LIBRARIES)
|
||||
+225
-134
@@ -13,6 +13,12 @@
|
||||
#include "dnsmasq/dnsmasq.h"
|
||||
#undef __USE_XOPEN
|
||||
|
||||
#include <nettle/bignum.h>
|
||||
#if !defined(NETTLE_VERSION_MAJOR)
|
||||
# define NETTLE_VERSION_MAJOR 2
|
||||
# define NETTLE_VERSION_MINOR 0
|
||||
#endif
|
||||
|
||||
#include "FTL.h"
|
||||
#include "args.h"
|
||||
#include "version.h"
|
||||
@@ -28,8 +34,6 @@
|
||||
#include "lua/ftl_lua.h"
|
||||
// run_dhcp_discover()
|
||||
#include "dhcp-discover.h"
|
||||
// defined in dnsmasq.c
|
||||
extern void print_dnsmasq_version(void);
|
||||
// mg_version()
|
||||
#include "civetweb/civetweb.h"
|
||||
// cJSON_Version()
|
||||
@@ -37,6 +41,9 @@ extern void print_dnsmasq_version(void);
|
||||
// ph7_lib_version()
|
||||
#include "ph7/ph7.h"
|
||||
|
||||
// defined in dnsmasq.c
|
||||
extern void print_dnsmasq_version(const char *yellow, const char *green, const char *bold, const char *normal);
|
||||
|
||||
// defined in database/shell.c
|
||||
extern int sqlite3_shell_main(int argc, char **argv);
|
||||
|
||||
@@ -45,6 +52,86 @@ bool daemonmode = true, cli_mode = false;
|
||||
int argc_dnsmasq = 0;
|
||||
const char** argv_dnsmasq = NULL;
|
||||
|
||||
// Extended SGR sequence:
|
||||
//
|
||||
// "\x1b[%dm"
|
||||
//
|
||||
// where %d is one of the following values for commonly supported colors:
|
||||
//
|
||||
// 0: reset colors/style
|
||||
// 1: bold
|
||||
// 4: underline
|
||||
// 30 - 37: black, red, green, yellow, blue, magenta, cyan, and white text
|
||||
// 40 - 47: black, red, green, yellow, blue, magenta, cyan, and white background
|
||||
//
|
||||
// https://en.wikipedia.org/wiki/ANSI_escape_code#SGR
|
||||
//
|
||||
#define COL_NC "\x1b[0m" // normal font
|
||||
#define COL_BOLD "\x1b[1m" // bold font
|
||||
#define COL_ITALIC "\x1b[3m" // italic font
|
||||
#define COL_ULINE "\x1b[4m" // underline font
|
||||
#define COL_GREEN "\x1b[32m" // normal foreground color
|
||||
#define COL_YELLOW "\x1b[33m" // normal foreground color
|
||||
#define COL_GRAY "\x1b[90m" // bright foreground color
|
||||
#define COL_RED "\x1b[91m" // bright foreground color
|
||||
#define COL_BLUE "\x1b[94m" // bright foreground color
|
||||
#define COL_PURPLE "\x1b[95m" // bright foreground color
|
||||
#define COL_CYAN "\x1b[96m" // bright foreground color
|
||||
|
||||
static inline bool __attribute__ ((pure)) is_term(void)
|
||||
{
|
||||
// test whether STDOUT refers to a terminal
|
||||
return isatty(fileno(stdout)) == 1;
|
||||
}
|
||||
|
||||
// Returns green [✓]
|
||||
const char __attribute__ ((pure)) *cli_tick(void)
|
||||
{
|
||||
return is_term() ? "["COL_GREEN"✓"COL_NC"]" : "[✓]";
|
||||
}
|
||||
|
||||
// Returns red [✗]
|
||||
const char __attribute__ ((pure)) *cli_cross(void)
|
||||
{
|
||||
return is_term() ? "["COL_RED"✗"COL_NC"]" : "[✗]";
|
||||
}
|
||||
|
||||
// Returns [i]
|
||||
const char __attribute__ ((pure)) *cli_info(void)
|
||||
{
|
||||
return is_term() ? COL_BOLD"[i]"COL_NC : "[i]";
|
||||
}
|
||||
|
||||
// Returns [?]
|
||||
const char __attribute__ ((const)) *cli_qst(void)
|
||||
{
|
||||
return "[?]";
|
||||
}
|
||||
|
||||
// Returns green "done!""
|
||||
const char __attribute__ ((pure)) *cli_done(void)
|
||||
{
|
||||
return is_term() ? COL_GREEN"done!"COL_NC : "done!";
|
||||
}
|
||||
|
||||
// Sets font to bold
|
||||
const char __attribute__ ((pure)) *cli_bold(void)
|
||||
{
|
||||
return is_term() ? COL_BOLD : "";
|
||||
}
|
||||
|
||||
// Resets font to normal
|
||||
const char __attribute__ ((pure)) *cli_normal(void)
|
||||
{
|
||||
return is_term() ? COL_NC : "";
|
||||
}
|
||||
|
||||
// Set color if STDOUT is a terminal
|
||||
static const char __attribute__ ((pure)) *cli_color(const char *color)
|
||||
{
|
||||
return is_term() ? color : "";
|
||||
}
|
||||
|
||||
static inline bool strEndsWith(const char *input, const char *end){
|
||||
return strcmp(input + strlen(input) - strlen(end), end) == 0;
|
||||
}
|
||||
@@ -137,8 +224,7 @@ void parse_args(int argc, char* argv[])
|
||||
const char *arg[2];
|
||||
arg[0] = "";
|
||||
arg[1] = "--test";
|
||||
main_dnsmasq(2, arg);
|
||||
ok = true;
|
||||
exit(main_dnsmasq(2, arg));
|
||||
}
|
||||
|
||||
// If we find "--" we collect everything behind that for dnsmasq
|
||||
@@ -147,23 +233,23 @@ void parse_args(int argc, char* argv[])
|
||||
// Remember that the rest is for dnsmasq ...
|
||||
consume_for_dnsmasq = true;
|
||||
|
||||
// Special command interpretation for "pihole-FTL -- --help dhcp"
|
||||
if(argc > 1 && strcmp(argv[argc-2], "--help") == 0 && strcmp(argv[argc-1], "dhcp") == 0)
|
||||
{
|
||||
display_opts();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
// and "pihole-FTL -- --help dhcp6"
|
||||
if(argc > 1 && strcmp(argv[argc-2], "--help") == 0 && strcmp(argv[argc-1], "dhcp6") == 0)
|
||||
{
|
||||
display_opts6();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
// ... and skip the current argument ("--")
|
||||
continue;
|
||||
}
|
||||
|
||||
// List available DHCPv4 config options
|
||||
if(strcmp(argv[i], "--list-dhcp") == 0 || strcmp(argv[i], "--list-dhcp4") == 0)
|
||||
{
|
||||
display_opts();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
// List available DHCPv6 config options
|
||||
if(strcmp(argv[i], "--list-dhcp6") == 0)
|
||||
{
|
||||
display_opts6();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
// If consume_for_dnsmasq is true, we collect all remaining options for
|
||||
// dnsmasq
|
||||
if(consume_for_dnsmasq)
|
||||
@@ -238,21 +324,30 @@ void parse_args(int argc, char* argv[])
|
||||
// Extended version output
|
||||
if(strcmp(argv[i], "-vv") == 0)
|
||||
{
|
||||
const char *bold = cli_bold();
|
||||
const char *normal = cli_normal();
|
||||
const char *green = cli_color(COL_GREEN);
|
||||
const char *yellow = cli_color(COL_YELLOW);
|
||||
|
||||
// Print FTL version
|
||||
printf("****************************** FTL **********************************\n");
|
||||
printf("Version: %s\n", get_FTL_version());
|
||||
printf("Branch: %s\n", GIT_BRANCH);
|
||||
printf("Commit: %s (%s)\n", GIT_HASH, GIT_DATE);
|
||||
printf("Architecture: %s\n", FTL_ARCH);
|
||||
printf("Compiler: %s\n\n", FTL_CC);
|
||||
printf("****************************** %s%sFTL%s **********************************\n",
|
||||
yellow, bold, normal);
|
||||
printf("Version: %s%s%s%s\n",
|
||||
green, bold, get_FTL_version(), normal);
|
||||
printf("Branch: " GIT_BRANCH "\n");
|
||||
printf("Commit: " GIT_HASH " (" GIT_DATE ")\n");
|
||||
printf("Architecture: " FTL_ARCH "\n");
|
||||
printf("Compiler: " FTL_CC "\n\n");
|
||||
|
||||
// Print dnsmasq version and compile time options
|
||||
print_dnsmasq_version();
|
||||
print_dnsmasq_version(yellow, green, bold, normal);
|
||||
|
||||
// Print SQLite3 version and compile time options
|
||||
printf("****************************** SQLite3 ******************************\n");
|
||||
printf("Version: %s\n", sqlite3_libversion());
|
||||
printf("Compile options: ");
|
||||
printf("****************************** %s%sSQLite3%s ******************************\n",
|
||||
yellow, bold, normal);
|
||||
printf("Version: %s%s%s%s\n",
|
||||
green, bold, sqlite3_libversion(), normal);
|
||||
printf("Features: ");
|
||||
unsigned int o = 0;
|
||||
const char *opt = NULL;
|
||||
while((opt = sqlite3_compileoption_get(o++)) != NULL)
|
||||
@@ -262,17 +357,30 @@ void parse_args(int argc, char* argv[])
|
||||
printf("%s", opt);
|
||||
}
|
||||
printf("\n\n");
|
||||
printf("****************************** LUA **********************************\n");
|
||||
printf("Version: %s\n", LUA_COPYRIGHT);
|
||||
printf("******************************** %s%sLUA%s ********************************\n",
|
||||
yellow, bold, normal);
|
||||
printf("Version: %s%s" LUA_VERSION_MAJOR "." LUA_VERSION_MINOR"%s\n",
|
||||
green, bold, normal);
|
||||
printf("Libraries: ");
|
||||
print_embedded_scripts();
|
||||
printf("\n\n");
|
||||
printf("***************************** %s%sLIBNETTLE%s *****************************\n",
|
||||
yellow, bold, normal);
|
||||
printf("Version: %s%s" xstr(NETTLE_VERSION_MAJOR) "." xstr(NETTLE_VERSION_MINOR) "%s\n",
|
||||
green, bold, normal);
|
||||
printf("GMP: %s\n", NETTLE_USE_MINI_GMP ? "Mini" : "Full");
|
||||
printf("\n");
|
||||
printf("****************************** CivetWeb *****************************\n");
|
||||
printf("Version: %s\n", mg_version());
|
||||
printf("****************************** %s%sCivetWeb%s *****************************\n",
|
||||
yellow, bold, normal);
|
||||
printf("Version: %s%s%s%s\n", green, bold, mg_version(), normal);
|
||||
printf("\n");
|
||||
printf("****************************** cJSON ********************************\n");
|
||||
printf("Version: %s\n", cJSON_Version());
|
||||
printf("****************************** %s%scJSON%s ********************************\n",
|
||||
yellow, bold, normal);
|
||||
printf("Version: %s%s%s%s\n", green, bold, cJSON_Version(), normal);
|
||||
printf("\n");
|
||||
printf("****************************** PH7 **********************************\n");
|
||||
printf("Version: %s\n", ph7_lib_version());
|
||||
printf("****************************** %s%sPH7%s **********************************\n",
|
||||
yellow, bold, normal);
|
||||
printf("Version: %s%s%s%s\n", green, bold, ph7_lib_version(), normal);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -290,6 +398,12 @@ void parse_args(int argc, char* argv[])
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
if(strcmp(argv[i], "--hash") == 0)
|
||||
{
|
||||
printf("%s\n",GIT_HASH);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
// Don't go into background
|
||||
if(strcmp(argv[i], "-f") == 0 ||
|
||||
strcmp(argv[i], "no-daemon") == 0)
|
||||
@@ -332,33 +446,84 @@ void parse_args(int argc, char* argv[])
|
||||
// List of implemented arguments
|
||||
if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "help") == 0 || strcmp(argv[i], "--help") == 0)
|
||||
{
|
||||
printf("pihole-FTL - The Pi-hole FTL engine\n\n");
|
||||
printf("Usage: sudo service pihole-FTL <action>\n");
|
||||
printf("where '<action>' is one of start / stop / restart\n\n");
|
||||
printf("Available arguments:\n");
|
||||
printf("\t debug More verbose logging,\n");
|
||||
printf("\t don't go into daemon mode\n");
|
||||
printf("\t test Don't start pihole-FTL but\n");
|
||||
printf("\t instead quit immediately\n");
|
||||
printf("\t-v, version Return FTL version\n");
|
||||
printf("\t-vv Return more version information\n");
|
||||
printf("\t-t, tag Return git tag\n");
|
||||
printf("\t-b, branch Return git branch\n");
|
||||
printf("\t-f, no-daemon Don't go into daemon mode\n");
|
||||
printf("\t-h, help Display this help and exit\n");
|
||||
printf("\tdnsmasq-test Test syntax of dnsmasq's\n");
|
||||
printf("\t config files and exit\n");
|
||||
printf("\tregex-test str Test str against all regular\n");
|
||||
const char *bold = cli_bold();
|
||||
const char *normal = cli_normal();
|
||||
const char *blue = cli_color(COL_BLUE);
|
||||
const char *cyan = cli_color(COL_CYAN);
|
||||
const char *gray = cli_color(COL_GRAY);
|
||||
const char *green = cli_color(COL_GREEN);
|
||||
const char *yellow = cli_color(COL_YELLOW);
|
||||
const char *purple = cli_color(COL_PURPLE);
|
||||
|
||||
printf("%sThe Pi-hole FTL engine - %s%s\n\n", bold, get_FTL_version(), normal);
|
||||
printf("Typically, pihole-FTL runs as a system service and is controlled\n");
|
||||
printf("by %ssudo service pihole-FTL %s<action>%s where %s<action>%s is one out\n", green, purple, normal, purple, normal);
|
||||
printf("of %sstart%s, %sstop%s, or %srestart%s.\n\n", green, normal, green, normal, green, normal);
|
||||
printf("pihole-FTL exposes some features going beyond the standard\n");
|
||||
printf("%sservice pihole-FTL%s command. These are:\n\n", green, normal);
|
||||
|
||||
printf("%sVersion information:%s\n", yellow, normal);
|
||||
printf("\t%s-v%s, %sversion%s Return FTL version\n", green, normal, green, normal);
|
||||
printf("\t%s-vv%s Return verbose version information\n", green, normal);
|
||||
printf("\t%s-t%s, %stag%s Return git tag\n", green, normal, green, normal);
|
||||
printf("\t%s-b%s, %sbranch%s Return git branch\n", green, normal, green, normal);
|
||||
printf("\t%s--hash%s Return git commit hash\n\n", green, normal);
|
||||
|
||||
printf("%sRegular expression testing:%s\n", yellow, normal);
|
||||
printf("\t%sregex-test %sstr%s Test %sstr%s against all regular\n", green, blue, normal, blue, normal);
|
||||
printf("\t expressions in the database\n");
|
||||
printf("\tregex-test str rgx Test str against regular expression\n");
|
||||
printf("\t given by rgx\n");
|
||||
printf("\t--lua, lua FTL's lua interpreter\n");
|
||||
printf("\t--luac, luac FTL's lua compiler\n");
|
||||
printf("\tdhcp-discover Discover DHCP servers in the local\n");
|
||||
printf("\t%sregex-test %sstr %srgx%s Test %sstr%s against regular expression\n", green, blue, cyan, normal, blue, normal);
|
||||
printf("\t given by regular expression %srgx%s\n\n", cyan, normal);
|
||||
|
||||
printf(" Example: %spihole-FTL regex-test %ssomebad.domain %sbad%s\n", green, blue, cyan, normal);
|
||||
printf(" to test %ssomebad.domain%s against %sbad%s\n\n", blue, normal, cyan, normal);
|
||||
printf(" An optional %s-q%s prevents any output (exit code testing):\n", gray, normal);
|
||||
printf(" %spihole-FTL %s-q%s regex-test %ssomebad.domain %sbad%s\n\n", green, gray, green, blue, cyan, normal);
|
||||
|
||||
printf("%sEmbedded Lua engine:%s\n", yellow, normal);
|
||||
printf("\t%s--lua%s, %slua%s FTL's lua interpreter\n", green, normal, green, normal);
|
||||
printf("\t%s--luac%s, %sluac%s FTL's lua compiler\n\n", green, normal, green, normal);
|
||||
|
||||
printf(" Usage: %spihole-FTL lua %s[OPTIONS] [SCRIPT [ARGS]]%s\n\n", green, cyan, normal);
|
||||
printf(" Options:\n\n");
|
||||
printf(" - %s[OPTIONS]%s is an optional set of options. All available\n", cyan, normal);
|
||||
printf(" options can be seen by running %spihole-FTL lua --help%s\n", green, normal);
|
||||
printf(" - %s[SCRIPT]%s is the optional name of a Lua script.\n", cyan, normal);
|
||||
printf(" If this script does not exist, an interactive shell is\n");
|
||||
printf(" started instead.\n");
|
||||
printf(" - %s[SCRIPT [ARGS]]%s can be used to pass optional args to\n", cyan, normal);
|
||||
printf(" the script.\n\n");
|
||||
|
||||
printf("%sEmbedded SQLite3 shell:%s\n", yellow, normal);
|
||||
printf("\t%ssql %s[-h]%s, %ssqlite3 %s[-h]%s FTL's SQLite3 shell\n", green, gray, normal, green, gray, normal);
|
||||
printf("\t%s-h%s starts a special %shuman-readable mode%s\n\n", gray, normal, bold, normal);
|
||||
|
||||
printf(" Usage: %spihole-FTL sqlite3 %s[-h] %s[OPTIONS] [FILENAME] [SQL]%s\n\n", green, gray, cyan, normal);
|
||||
printf(" Options:\n\n");
|
||||
printf(" - %s[OPTIONS]%s is an optional set of options. All available\n", cyan, normal);
|
||||
printf(" options can be found in %spihole-FTL sqlite3 --help%s\n", green, normal);
|
||||
printf(" - %s[FILENAME]%s is the optional name of an SQLite database.\n", cyan, normal);
|
||||
printf(" A new database is created if the file does not previously\n");
|
||||
printf(" exist. If this argument is omitted, SQLite3 will use a\n");
|
||||
printf(" transient in-memory database instead.\n");
|
||||
printf(" - %s[SQL]%s is an optional SQL statement to be executed. If\n", cyan, normal);
|
||||
printf(" omitted, an interactive shell is started instead.\n\n");
|
||||
|
||||
printf("%sEmbedded dnsmasq options:%s\n", yellow, normal);
|
||||
printf("\t%sdnsmasq-test%s Test syntax of dnsmasq's config\n", green, normal);
|
||||
printf("\t%s--list-dhcp4%s List known DHCPv4 config options\n", green, normal);
|
||||
printf("\t%s--list-dhcp6%s List known DHCPv6 config options\n\n", green, normal);
|
||||
|
||||
printf("%sDebugging and special use:%s\n", yellow, normal);
|
||||
printf("\t%sd%s, %sdebug%s Enter debugging mode\n", green, normal, green, normal);
|
||||
printf("\t%stest%s Don't start pihole-FTL but\n", green, normal);
|
||||
printf("\t instead quit immediately\n");
|
||||
printf("\t%s-f%s, %sno-daemon%s Don't go into daemon mode\n\n", green, normal, green, normal);
|
||||
|
||||
printf("%sOther:%s\n", yellow, normal);
|
||||
printf("\t%sdhcp-discover%s Discover DHCP servers in the local\n", green, normal);
|
||||
printf("\t network\n");
|
||||
printf("\tsql, sqlite3 FTL's SQLite3 shell\n");
|
||||
printf("\tsql -h, sqlite3 -h FTL's SQLite3 shell (human-readable mode)\n");
|
||||
printf("\n\nOnline help: https://github.com/pi-hole/FTL\n");
|
||||
printf("\t%s-h%s, %shelp%s Display this help and exit\n\n", green, normal, green, normal);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -391,77 +556,3 @@ void parse_args(int argc, char* argv[])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extended SGR sequence:
|
||||
//
|
||||
// "\x1b[%dm"
|
||||
//
|
||||
// where %d is one of the following values for commonly supported colors:
|
||||
//
|
||||
// 0: reset colors/style
|
||||
// 1: bold
|
||||
// 4: underline
|
||||
// 30 - 37: black, red, green, yellow, blue, magenta, cyan, and white text
|
||||
// 40 - 47: black, red, green, yellow, blue, magenta, cyan, and white background
|
||||
//
|
||||
// https://en.wikipedia.org/wiki/ANSI_escape_code#SGR
|
||||
//
|
||||
#define COL_NC "\x1b[0m" // normal font
|
||||
#define COL_BOLD "\x1b[1m" // bold font
|
||||
#define COL_ITALIC "\x1b[3m" // italic font
|
||||
#define COL_ULINE "\x1b[4m" // underline font
|
||||
#define COL_GREEN "\x1b[32m" // normal foreground color
|
||||
#define COL_YELLOW "\x1b[33m" // normal foreground color
|
||||
#define COL_GRAY "\x1b[90m" // bright foreground color
|
||||
#define COL_RED "\x1b[91m" // bright foreground color
|
||||
#define COL_BLUE "\x1b[94m" // bright foreground color
|
||||
#define COL_PURPLE "\x1b[95m" // bright foreground color
|
||||
#define COL_CYAN "\x1b[96m" // bright foreground color
|
||||
|
||||
static inline bool __attribute__ ((const)) is_term(void)
|
||||
{
|
||||
// test whether STDOUT refers to a terminal
|
||||
return isatty(fileno(stdout)) == 1;
|
||||
}
|
||||
|
||||
// Returns green [✓]
|
||||
const char __attribute__ ((const)) *cli_tick(void)
|
||||
{
|
||||
return is_term() ? "["COL_GREEN"✓"COL_NC"]" : "[✓]";
|
||||
}
|
||||
|
||||
// Returns red [✗]
|
||||
const char __attribute__ ((const)) *cli_cross(void)
|
||||
{
|
||||
return is_term() ? "["COL_RED"✗"COL_NC"]" : "[✗]";
|
||||
}
|
||||
|
||||
// Returns [i]
|
||||
const char __attribute__ ((const)) *cli_info(void)
|
||||
{
|
||||
return is_term() ? COL_BOLD"[i]"COL_NC : "[i]";
|
||||
}
|
||||
|
||||
// Returns [?]
|
||||
const char __attribute__ ((const)) *cli_qst(void)
|
||||
{
|
||||
return "[?]";
|
||||
}
|
||||
|
||||
// Returns green "done!""
|
||||
const char __attribute__ ((const)) *cli_done(void)
|
||||
{
|
||||
return is_term() ? COL_GREEN"done!"COL_NC : "done!";
|
||||
}
|
||||
|
||||
// Sets font to bold
|
||||
const char __attribute__ ((const)) *cli_bold(void)
|
||||
{
|
||||
return is_term() ? COL_BOLD : "";
|
||||
}
|
||||
|
||||
// Resets font to normal
|
||||
const char __attribute__ ((const)) *cli_normal(void)
|
||||
{
|
||||
return is_term() ? COL_NC : "";
|
||||
}
|
||||
|
||||
+6
-6
@@ -16,13 +16,13 @@ extern bool daemonmode, cli_mode, dnsmasq_debug;
|
||||
extern int argc_dnsmasq;
|
||||
extern const char ** argv_dnsmasq;
|
||||
|
||||
const char *cli_tick(void) __attribute__ ((const));
|
||||
const char *cli_cross(void) __attribute__ ((const));
|
||||
const char *cli_info(void) __attribute__ ((const));
|
||||
const char *cli_tick(void) __attribute__ ((pure));
|
||||
const char *cli_cross(void) __attribute__ ((pure));
|
||||
const char *cli_info(void) __attribute__ ((pure));
|
||||
const char *cli_qst(void) __attribute__ ((const));
|
||||
const char *cli_done(void) __attribute__ ((const));
|
||||
const char *cli_bold(void) __attribute__ ((const));
|
||||
const char *cli_normal(void) __attribute__ ((const));
|
||||
const char *cli_done(void) __attribute__ ((pure));
|
||||
const char *cli_bold(void) __attribute__ ((pure));
|
||||
const char *cli_normal(void) __attribute__ ((pure));
|
||||
|
||||
// defined in dnsmasq_interface.c
|
||||
int check_struct_sizes(void);
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "toml_helper.h"
|
||||
#include "../config/config.h"
|
||||
|
||||
FILE *openFTLtoml(const char *mode)
|
||||
FILE * __attribute((malloc)) __attribute((nonnull(1))) openFTLtoml(const char *mode)
|
||||
{
|
||||
FILE *fp;
|
||||
// If reading: first check if there is a local file
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#include "../FTL.h"
|
||||
|
||||
FILE *openFTLtoml(const char *mode);
|
||||
FILE *openFTLtoml(const char *mode) __attribute((malloc)) __attribute((nonnull(1)));
|
||||
void catTOMLsection(FILE *fp, const unsigned int indent, const char *key);
|
||||
void catTOMLextrainfo(FILE *fp, const unsigned int indent, const char *infostr);
|
||||
void catTOMLstring(FILE *fp, const unsigned int indent, const char *key, const char *description, const char *values, const char *val, const char *dptr);
|
||||
|
||||
+7
-17
@@ -28,7 +28,6 @@
|
||||
|
||||
pthread_t threads[THREADS_MAX] = { 0 };
|
||||
pthread_t api_threads[MAX_API_THREADS] = { 0 };
|
||||
pid_t api_tids[MAX_API_THREADS] = { 0 };
|
||||
bool resolver_ready = false;
|
||||
|
||||
void go_daemon(void)
|
||||
@@ -121,17 +120,6 @@ static void removepid(void)
|
||||
return;
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
// We also try to remove the file. We still empty the file above
|
||||
// to ensure it is at least empty when it cannot be removed.
|
||||
// because removing files on Linux is actually unlinking them.
|
||||
// If any processes still have the file open, it will remain
|
||||
// in existence until the last file descriptor referring to
|
||||
// it is closed.
|
||||
if(remove(config.files.pid) != 0)
|
||||
{
|
||||
log_warn("Unable to remove PID file: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
char *getUserName(void)
|
||||
@@ -201,7 +189,12 @@ void delay_startup(void)
|
||||
|
||||
// Sleep if requested by DELAY_STARTUP
|
||||
log_info("Sleeping for %d seconds as requested by configuration ...", config.delay_startup);
|
||||
sleep(config.delay_startup);
|
||||
if(sleep(config.delay_startup) != 0)
|
||||
{
|
||||
log_crit("Sleeping was interrupted by an external signal");
|
||||
cleanup(EXIT_FAILURE);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
log_info("Done sleeping, continuing startup of resolver...");
|
||||
}
|
||||
|
||||
@@ -266,15 +259,12 @@ void cleanup(const int ret)
|
||||
// Do proper cleanup only if FTL started successfully
|
||||
if(resolver_ready)
|
||||
{
|
||||
// Terminate threads
|
||||
terminate_threads();
|
||||
|
||||
// Cancel and join possibly still running API worker threads
|
||||
for(unsigned int tid = 0; tid < MAX_API_THREADS; tid++)
|
||||
{
|
||||
// Skip if this is an unused slot
|
||||
if(api_threads[tid] == 0)
|
||||
continue;
|
||||
|
||||
// Otherwise, cancel and join the thread
|
||||
log_notice("Joining API worker thread %d", tid);
|
||||
pthread_cancel(api_threads[tid]);
|
||||
|
||||
+1
-2
@@ -12,9 +12,8 @@
|
||||
|
||||
#include "enums.h"
|
||||
extern pthread_t threads[THREADS_MAX];
|
||||
#define MAX_API_THREADS 40
|
||||
#define MAX_API_THREADS 5
|
||||
extern pthread_t api_threads[MAX_API_THREADS];
|
||||
extern pid_t api_tids[MAX_API_THREADS];
|
||||
|
||||
void go_daemon(void);
|
||||
void savepid(void);
|
||||
|
||||
@@ -144,8 +144,8 @@ int dbquery(sqlite3* db, const char *format, ...)
|
||||
|
||||
int rc = sqlite3_exec(db, query, NULL, NULL, NULL);
|
||||
if( rc != SQLITE_OK ){
|
||||
log_err("SQL query \"%s\" failed: %s",
|
||||
query, sqlite3_errstr(rc));
|
||||
log_err("ERROR: SQL query \"%s\" failed: %s (%s)",
|
||||
query, sqlite3_errstr(rc), sqlite3ErrName(sqlite3_extended_errcode(db)));
|
||||
sqlite3_free(query);
|
||||
checkFTLDBrc(rc);
|
||||
return rc;
|
||||
|
||||
@@ -54,12 +54,15 @@ const char *get_sqlite3_version(void);
|
||||
extern bool DBdeleteoldqueries;
|
||||
|
||||
// Return if FTL's database is known to be broken
|
||||
// We abort execution of all database-related activitites in this case
|
||||
// We abort execution of all database-related activities in this case
|
||||
bool FTLDBerror(void) __attribute__ ((pure));
|
||||
|
||||
// Check SQLite3 non-success return codes for possible database corruption
|
||||
bool checkFTLDBrc(const int rc);
|
||||
|
||||
// Get human-readable *extended* error codes (defined in sqlite3.c)
|
||||
extern const char *sqlite3ErrName(int rc);
|
||||
|
||||
// Database macros
|
||||
#define SQL_bool(db, ...) {\
|
||||
int ret;\
|
||||
|
||||
+73
-44
@@ -15,13 +15,12 @@
|
||||
#include "../config/config.h"
|
||||
// logging routines
|
||||
#include "../log.h"
|
||||
// match_regex()
|
||||
#include "../regex_r.h"
|
||||
// getstr()
|
||||
#include "../shmem.h"
|
||||
// SQLite3 prepared statement vectors
|
||||
#include "../vector.h"
|
||||
// log_subnet_warning()
|
||||
// logg_inaccessible_adlist
|
||||
#include "message-table.h"
|
||||
// getMACfromIP()
|
||||
#include "network-table.h"
|
||||
@@ -149,17 +148,16 @@ bool gravityDB_open(void)
|
||||
// BUT NOT google.de itself
|
||||
// Example 3: *google.de
|
||||
// matches 'google.de' and all of its subdomains but
|
||||
// also other domains starting in google.de, like
|
||||
// also other domains ending in google.de, like
|
||||
// abcgoogle.de
|
||||
rc = sqlite3_prepare_v3(gravity_db,
|
||||
"SELECT EXISTS("
|
||||
"SELECT domain, "
|
||||
"CASE WHEN substr(domain, 1, 1) = '*' " // Does the database string start in '*' ?
|
||||
"THEN '*' || substr(:input, - length(domain) + 1) " // If so: Crop the input domain and prepend '*'
|
||||
"ELSE :input " // If not: Use input domain directly for comparison
|
||||
"END matcher "
|
||||
"FROM domain_audit WHERE matcher = domain" // Match where (modified) domain equals the database domain
|
||||
");", -1, SQLITE_PREPARE_PERSISTENT, &auditlist_stmt, NULL);
|
||||
"SELECT domain, "
|
||||
"CASE WHEN substr(domain, 1, 1) = '*' " // Does the database string start in '*' ?
|
||||
"THEN '*' || substr(:input, - length(domain) + 1) " // If so: Crop the input domain and prepend '*'
|
||||
"ELSE :input " // If not: Use input domain directly for comparison
|
||||
"END matcher "
|
||||
"FROM domain_audit WHERE matcher = domain" // Match where (modified) domain equals the database domain
|
||||
";", -1, SQLITE_PREPARE_PERSISTENT, &auditlist_stmt, NULL);
|
||||
|
||||
if( rc != SQLITE_OK )
|
||||
{
|
||||
@@ -201,11 +199,11 @@ bool gravityDB_reopen(void)
|
||||
return gravityDB_open();
|
||||
}
|
||||
|
||||
static char* get_client_querystr(const char* table, const char* groups)
|
||||
static char* get_client_querystr(const char *table, const char *column, const char *groups)
|
||||
{
|
||||
// Build query string with group filtering
|
||||
char *querystr = NULL;
|
||||
if(asprintf(&querystr, "SELECT EXISTS(SELECT domain from %s WHERE domain = ? AND group_id IN (%s));", table, groups) < 1)
|
||||
if(asprintf(&querystr, "SELECT %s from %s WHERE domain = ? AND group_id IN (%s);", column, table, groups) < 1)
|
||||
{
|
||||
log_err("get_client_querystr(%s, %s) - asprintf() error", table, groups);
|
||||
return NULL;
|
||||
@@ -833,12 +831,12 @@ bool gravityDB_prepare_client_statements(clientsData *client)
|
||||
// returns true as soon as it sees the first row from the query inside
|
||||
// of EXISTS().
|
||||
log_debug(DEBUG_DATABASE, "gravityDB_open(): Preparing vw_whitelist statement for client %s", clientip);
|
||||
querystr = get_client_querystr("vw_whitelist", getstr(client->groupspos));
|
||||
querystr = get_client_querystr("vw_whitelist", "id", getstr(client->groupspos));
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
int rc = sqlite3_prepare_v3(gravity_db, querystr, -1, SQLITE_PREPARE_PERSISTENT, &stmt, NULL);
|
||||
if( rc != SQLITE_OK )
|
||||
{
|
||||
log_err("gravityDB_open(\"SELECT EXISTS(... vw_whitelist ...)\") - SQL error prepare: %s", sqlite3_errstr(rc));
|
||||
log_err("gravityDB_open(\"SELECT(... vw_whitelist ...)\") - SQL error prepare: %s", sqlite3_errstr(rc));
|
||||
gravityDB_close();
|
||||
return false;
|
||||
}
|
||||
@@ -847,11 +845,11 @@ bool gravityDB_prepare_client_statements(clientsData *client)
|
||||
|
||||
// Prepare gravity statement
|
||||
log_debug(DEBUG_DATABASE, "gravityDB_open(): Preparing vw_gravity statement for client %s", clientip);
|
||||
querystr = get_client_querystr("vw_gravity", getstr(client->groupspos));
|
||||
querystr = get_client_querystr("vw_gravity", "domain", getstr(client->groupspos));
|
||||
rc = sqlite3_prepare_v3(gravity_db, querystr, -1, SQLITE_PREPARE_PERSISTENT, &stmt, NULL);
|
||||
if( rc != SQLITE_OK )
|
||||
{
|
||||
log_err("gravityDB_open(\"SELECT EXISTS(... vw_gravity ...)\") - SQL error prepare: %s", sqlite3_errstr(rc));
|
||||
log_err("gravityDB_open(\"SELECT(... vw_gravity ...)\") - SQL error prepare: %s", sqlite3_errstr(rc));
|
||||
gravityDB_close();
|
||||
return false;
|
||||
}
|
||||
@@ -860,11 +858,11 @@ bool gravityDB_prepare_client_statements(clientsData *client)
|
||||
|
||||
// Prepare blacklist statement
|
||||
log_debug(DEBUG_DATABASE, "gravityDB_open(): Preparing vw_blacklist statement for client %s", clientip);
|
||||
querystr = get_client_querystr("vw_blacklist", getstr(client->groupspos));
|
||||
querystr = get_client_querystr("vw_blacklist", "id", getstr(client->groupspos));
|
||||
rc = sqlite3_prepare_v3(gravity_db, querystr, -1, SQLITE_PREPARE_PERSISTENT, &stmt, NULL);
|
||||
if( rc != SQLITE_OK )
|
||||
{
|
||||
log_err("gravityDB_open(\"SELECT EXISTS(... vw_blacklist ...)\") - SQL error prepare: %s", sqlite3_errstr(rc));
|
||||
log_err("gravityDB_open(\"SELECT(... vw_blacklist ...)\") - SQL error prepare: %s", sqlite3_errstr(rc));
|
||||
gravityDB_close();
|
||||
return false;
|
||||
}
|
||||
@@ -978,10 +976,11 @@ bool gravityDB_getTable(const unsigned char list)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get a single domain from a running SELECT operation This function returns a
|
||||
// pointer to a string as long as there are domains available. Once we reached
|
||||
// the end of the table, it returns NULL. It also returns NULL when it
|
||||
// encounters an error (e.g., on reading errors). Errors are logged to FTL.log
|
||||
// Get a single domain from a running SELECT operation
|
||||
// This function returns a pointer to a string as long as there are domains
|
||||
// available. Once we reached the end of the table, it returns NULL. It also
|
||||
// returns NULL when it encounters an error (e.g., on reading errors). Errors
|
||||
// are logged to FTL.log
|
||||
// This function is performance critical as it might be called millions of times
|
||||
// for large blocking lists
|
||||
inline const char* gravityDB_getDomain(int *rowid)
|
||||
@@ -1112,7 +1111,7 @@ int gravityDB_count(const enum gravity_tables list)
|
||||
return result;
|
||||
}
|
||||
|
||||
static enum db_result domain_in_list(const char *domain, sqlite3_stmt *stmt, const char *listname)
|
||||
static enum db_result domain_in_list(const char *domain, sqlite3_stmt *stmt, const char *listname, int *domain_id)
|
||||
{
|
||||
// Do not try to bind text to statement when database is not available
|
||||
if(!gravityDB_opened && !gravityDB_open())
|
||||
@@ -1148,7 +1147,7 @@ static enum db_result domain_in_list(const char *domain, sqlite3_stmt *stmt, con
|
||||
sqlite3_clear_bindings(stmt);
|
||||
return LIST_NOT_AVAILABLE;
|
||||
}
|
||||
else if(rc != SQLITE_ROW)
|
||||
else if(rc != SQLITE_ROW && rc != SQLITE_DONE)
|
||||
{
|
||||
// Any return code that is neither SQLITE_BUSY not SQLITE_ROW
|
||||
// is a real error we should log
|
||||
@@ -1159,8 +1158,10 @@ static enum db_result domain_in_list(const char *domain, sqlite3_stmt *stmt, con
|
||||
return LIST_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
// Get result of query "SELECT EXISTS(...)"
|
||||
const int result = sqlite3_column_int(stmt, 0);
|
||||
// Get result of query (if available)
|
||||
const int result = (rc == SQLITE_ROW) ? sqlite3_column_int(stmt, 0) : -1;
|
||||
if(domain_id != NULL)
|
||||
*domain_id = result;
|
||||
|
||||
log_debug(DEBUG_DATABASE, "domain_in_list(\"%s\", %p, %s): %d", domain, stmt, listname, result);
|
||||
|
||||
@@ -1176,8 +1177,7 @@ static enum db_result domain_in_list(const char *domain, sqlite3_stmt *stmt, con
|
||||
sqlite3_clear_bindings(stmt);
|
||||
|
||||
// Return if domain was found in current table
|
||||
// SELECT EXISTS(...) either returns 0 (false) or 1 (true).
|
||||
return (result == 1) ? FOUND : NOT_FOUND;
|
||||
return (rc == SQLITE_ROW) ? FOUND : NOT_FOUND;
|
||||
}
|
||||
|
||||
void gravityDB_reload_groups(clientsData* client)
|
||||
@@ -1234,17 +1234,7 @@ enum db_result in_allowlist(const char *domain, DNSCacheData *dns_cache, clients
|
||||
// We have to check both the exact whitelist (using a prepared database statement)
|
||||
// as well the compiled regex whitelist filters to check if the current domain is
|
||||
// whitelisted.
|
||||
enum db_result allowed = domain_in_list(domain, stmt, "allow");
|
||||
|
||||
// For performance reasons, the regex evaluations is executed only if the
|
||||
// exact whitelist lookup does not deliver a positive match. This is an
|
||||
// optimization as the database lookup will most likely hit (a) more domains
|
||||
// and (b) will be faster (given a sufficiently large number of regex
|
||||
// whitelisting filters).
|
||||
if(allowed == NOT_FOUND)
|
||||
allowed = match_regex(domain, dns_cache, client->id, REGEX_ALLOW, false) != -1;
|
||||
|
||||
return allowed;
|
||||
return domain_in_list(domain, stmt, "whitelist", &dns_cache->domainlist_id);
|
||||
}
|
||||
|
||||
enum db_result in_gravity(const char *domain, clientsData *client)
|
||||
@@ -1272,10 +1262,10 @@ enum db_result in_gravity(const char *domain, clientsData *client)
|
||||
if(stmt == NULL)
|
||||
stmt = gravity_stmt->get(gravity_stmt, client->id);
|
||||
|
||||
return domain_in_list(domain, stmt, "gravity");
|
||||
return domain_in_list(domain, stmt, "gravity", NULL);
|
||||
}
|
||||
|
||||
enum db_result in_denylist(const char *domain, clientsData *client)
|
||||
enum db_result in_denylist(const char *domain, DNSCacheData *dns_cache, clientsData *client)
|
||||
{
|
||||
// If list statement is not ready and cannot be initialized (e.g. no
|
||||
// access to the database), we return false to prevent an FTL crash
|
||||
@@ -1300,7 +1290,7 @@ enum db_result in_denylist(const char *domain, clientsData *client)
|
||||
if(stmt == NULL)
|
||||
stmt = blacklist_stmt->get(blacklist_stmt, client->id);
|
||||
|
||||
return domain_in_list(domain, stmt, "deny");
|
||||
return domain_in_list(domain, stmt, "blacklist", &dns_cache->domainlist_id);
|
||||
}
|
||||
|
||||
bool in_auditlist(const char *domain)
|
||||
@@ -1311,7 +1301,7 @@ bool in_auditlist(const char *domain)
|
||||
return false;
|
||||
|
||||
// We check the domain_audit table for the given domain
|
||||
return domain_in_list(domain, auditlist_stmt, "auditlist") == FOUND;
|
||||
return domain_in_list(domain, auditlist_stmt, "auditlist", NULL) == FOUND;
|
||||
}
|
||||
|
||||
bool gravityDB_get_regex_client_groups(clientsData* client, const unsigned int numregex, const regexData *regex,
|
||||
@@ -2240,3 +2230,42 @@ bool gravityDB_edit_groups(const enum gravity_list_type listtype, cJSON *groups,
|
||||
|
||||
return okay;
|
||||
}
|
||||
|
||||
void check_inaccessible_adlists(void)
|
||||
{
|
||||
// Check if any adlist was inaccessible in the last gravity run
|
||||
// If so, gravity stored `status` in the adlist table with
|
||||
// "3": List unavailable, Pi-hole used a local copy
|
||||
// "4": List unavailable, there is no local copy available
|
||||
|
||||
// Do not proceed when database is not available
|
||||
if(!gravityDB_opened && !gravityDB_open())
|
||||
{
|
||||
log_err("check_inaccessible_adlists(): Gravity database not available");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *querystr = "SELECT id, address FROM adlist WHERE status IN (3,4) AND enabled=1";
|
||||
|
||||
// Prepare query
|
||||
sqlite3_stmt *query_stmt;
|
||||
int rc = sqlite3_prepare_v2(gravity_db, querystr, -1, &query_stmt, NULL);
|
||||
if(rc != SQLITE_OK){
|
||||
log_err("check_inaccessible_adlists(): %s - SQL error prepare: %s", querystr, sqlite3_errstr(rc));
|
||||
gravityDB_close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform query
|
||||
while((rc = sqlite3_step(query_stmt)) == SQLITE_ROW)
|
||||
{
|
||||
int id = sqlite3_column_int(query_stmt, 0);
|
||||
const char *address = (const char*)sqlite3_column_text(query_stmt, 1);
|
||||
|
||||
// log to the message table
|
||||
logg_inaccessible_adlist(id, address);
|
||||
}
|
||||
|
||||
// Finalize statement
|
||||
sqlite3_finalize(query_stmt);
|
||||
}
|
||||
|
||||
@@ -50,9 +50,10 @@ const char* gravityDB_getDomain(int *rowid);
|
||||
char* get_client_names_from_ids(const char *group_ids) __attribute__ ((malloc));
|
||||
void gravityDB_finalizeTable(void);
|
||||
int gravityDB_count(const enum gravity_tables list);
|
||||
void check_inaccessible_adlists(void);
|
||||
|
||||
enum db_result in_gravity(const char *domain, clientsData *client);
|
||||
enum db_result in_denylist(const char *domain, clientsData *client);
|
||||
enum db_result in_denylist(const char *domain, DNSCacheData *dns_cache, clientsData *client);
|
||||
enum db_result in_allowlist(const char *domain, DNSCacheData *dns_cache, clientsData *client);
|
||||
bool in_auditlist(const char *domain);
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "../gc.h"
|
||||
|
||||
static const char *message_types[MAX_MESSAGE] =
|
||||
{ "REGEX", "SUBNET", "HOSTNAME", "DNSMASQ_CONFIG", "RATE_LIMIT", "DNSMASQ_WARN", "LOAD", "SHMEM", "DISK" };
|
||||
{ "REGEX", "SUBNET", "HOSTNAME", "DNSMASQ_CONFIG", "RATE_LIMIT", "DNSMASQ_WARN", "LOAD", "SHMEM", "DISK", "ADLIST" };
|
||||
|
||||
static unsigned char message_blob_types[MAX_MESSAGE][5] =
|
||||
{
|
||||
@@ -94,6 +94,13 @@ static unsigned char message_blob_types[MAX_MESSAGE][5] =
|
||||
SQLITE_NULL, // Not used
|
||||
SQLITE_NULL // Not used
|
||||
},
|
||||
{ // INACCESSIBLE_ADLIST_MESSAGE: The message column contains the corresponding adlist URL
|
||||
SQLITE_INTEGER, // database index of the adlist (so the dashboard can show a link)
|
||||
SQLITE_NULL, // not used
|
||||
SQLITE_NULL, // not used
|
||||
SQLITE_NULL, // not used
|
||||
SQLITE_NULL // not used
|
||||
},
|
||||
};
|
||||
// Create message table in the database
|
||||
bool create_message_table(sqlite3 *db)
|
||||
@@ -143,7 +150,7 @@ bool flush_message_table(void)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool add_message(const enum message_type type, const bool unique,
|
||||
static bool add_message(const enum message_type type,
|
||||
const char *message, const int count,...)
|
||||
{
|
||||
bool okay = false;
|
||||
@@ -159,55 +166,52 @@ static bool add_message(const enum message_type type, const bool unique,
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure there are no duplicates when adding host name or rate-limiting messages
|
||||
if(unique)
|
||||
{
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
const char *querystr = "DELETE FROM message WHERE type = ?1 AND message = ?2";
|
||||
int rc = sqlite3_prepare_v2(db, querystr, -1, &stmt, NULL);
|
||||
if( rc != SQLITE_OK ){
|
||||
log_err("add_message(type=%u, message=%s) - SQL error prepare DELETE: %s",
|
||||
type, message, sqlite3_errstr(rc));
|
||||
goto end_of_add_message;
|
||||
}
|
||||
|
||||
// Bind type to prepared statement
|
||||
if((rc = sqlite3_bind_text(stmt, 1, message_types[type], -1, SQLITE_STATIC)) != SQLITE_OK)
|
||||
{
|
||||
log_err("add_message(type=%u, message=%s) - Failed to bind type DELETE: %s",
|
||||
type, message, sqlite3_errstr(rc));
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
goto end_of_add_message;
|
||||
}
|
||||
|
||||
// Bind message to prepared statement
|
||||
if((rc = sqlite3_bind_text(stmt, 2, message, -1, SQLITE_STATIC)) != SQLITE_OK)
|
||||
{
|
||||
log_err("add_message(type=%u, message=%s) - Failed to bind message DELETE: %s",
|
||||
type, message, sqlite3_errstr(rc));
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
goto end_of_add_message;
|
||||
}
|
||||
|
||||
// Execute and finalize
|
||||
if((rc = sqlite3_step(stmt)) != SQLITE_OK && rc != SQLITE_DONE)
|
||||
{
|
||||
log_err("add_message(type=%u, message=%s) - SQL error step DELETE: %s",
|
||||
type, message, sqlite3_errstr(rc));
|
||||
goto end_of_add_message;
|
||||
}
|
||||
sqlite3_clear_bindings(stmt);
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
// Ensure there are no duplicates when adding messages
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
const char *querystr = "DELETE FROM message WHERE type = ?1 AND message = ?2";
|
||||
int rc = sqlite3_prepare_v2(db, querystr, -1, &stmt, NULL);
|
||||
if( rc != SQLITE_OK ){
|
||||
log_err("add_message(type=%u, message=%s) - SQL error prepare DELETE: %s",
|
||||
type, message, sqlite3_errstr(rc));
|
||||
goto end_of_add_message;
|
||||
}
|
||||
|
||||
// Bind type to prepared statement
|
||||
if((rc = sqlite3_bind_text(stmt, 1, message_types[type], -1, SQLITE_STATIC)) != SQLITE_OK)
|
||||
{
|
||||
log_err("add_message(type=%u, message=%s) - Failed to bind type DELETE: %s",
|
||||
type, message, sqlite3_errstr(rc));
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
goto end_of_add_message;
|
||||
}
|
||||
|
||||
// Bind message to prepared statement
|
||||
if((rc = sqlite3_bind_text(stmt, 2, message, -1, SQLITE_STATIC)) != SQLITE_OK)
|
||||
{
|
||||
log_err("add_message(type=%u, message=%s) - Failed to bind message DELETE: %s",
|
||||
type, message, sqlite3_errstr(rc));
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
goto end_of_add_message;
|
||||
}
|
||||
|
||||
// Execute and finalize
|
||||
if((rc = sqlite3_step(stmt)) != SQLITE_OK && rc != SQLITE_DONE)
|
||||
{
|
||||
log_err("add_message(type=%u, message=%s) - SQL error step DELETE: %s",
|
||||
type, message, sqlite3_errstr(rc));
|
||||
goto end_of_add_message;
|
||||
}
|
||||
sqlite3_clear_bindings(stmt);
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
stmt = NULL;
|
||||
|
||||
// Prepare SQLite statement
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
const char *querystr = "INSERT INTO message (timestamp,type,message,blob1,blob2,blob3,blob4,blob5) "
|
||||
"VALUES ((cast(strftime('%s', 'now') as int)),?,?,?,?,?,?,?);";
|
||||
int rc = sqlite3_prepare_v2(db, querystr, -1, &stmt, NULL);
|
||||
querystr = "INSERT INTO message (timestamp,type,message,blob1,blob2,blob3,blob4,blob5) "
|
||||
"VALUES ((cast(strftime('%s', 'now') as int)),?,?,?,?,?,?,?);";
|
||||
rc = sqlite3_prepare_v2(db, querystr, -1, &stmt, NULL);
|
||||
if( rc != SQLITE_OK )
|
||||
{
|
||||
log_err("add_message(type=%u, message=%s) - SQL error prepare: %s",
|
||||
@@ -313,7 +317,7 @@ void logg_regex_warning(const char *type, const char *warning, const int dbindex
|
||||
|
||||
// Log to database only if not in CLI mode
|
||||
if(!cli_mode)
|
||||
add_message(REGEX_MESSAGE, false, warning, 3, type, regex, dbindex);
|
||||
add_message(REGEX_MESSAGE, warning, 3, type, regex, dbindex);
|
||||
}
|
||||
|
||||
void logg_subnet_warning(const char *ip, const int matching_count, const char *matching_ids,
|
||||
@@ -328,7 +332,7 @@ void logg_subnet_warning(const char *ip, const int matching_count, const char *m
|
||||
|
||||
// Log to database
|
||||
char *names = get_client_names_from_ids(matching_ids);
|
||||
add_message(SUBNET_MESSAGE, false, ip, 5, matching_count, names, matching_ids, chosen_match_text, chosen_match_id);
|
||||
add_message(SUBNET_MESSAGE, ip, 5, matching_count, names, matching_ids, chosen_match_text, chosen_match_id);
|
||||
free(names);
|
||||
}
|
||||
|
||||
@@ -339,7 +343,7 @@ void logg_hostname_warning(const char *ip, const char *name, const unsigned int
|
||||
ip, name, pos);
|
||||
|
||||
// Log to database
|
||||
add_message(HOSTNAME_MESSAGE, true, ip, 2, name, (const int)pos);
|
||||
add_message(HOSTNAME_MESSAGE, ip, 2, name, (const int)pos);
|
||||
}
|
||||
|
||||
void logg_fatal_dnsmasq_message(const char *message)
|
||||
@@ -348,7 +352,7 @@ void logg_fatal_dnsmasq_message(const char *message)
|
||||
log_crit("Error in dnsmasq core: %s", message);
|
||||
|
||||
// Log to database
|
||||
add_message(DNSMASQ_CONFIG_MESSAGE, false, message, 0);
|
||||
add_message(DNSMASQ_CONFIG_MESSAGE, message, 0);
|
||||
|
||||
// FTL will dies after this point, so we should make sure to clean up
|
||||
// behind ourselves
|
||||
@@ -364,7 +368,7 @@ void logg_rate_limit_message(const char *clientIP, const unsigned int rate_limit
|
||||
clientIP, turnaround, turnaround == 1 ? "" : "s");
|
||||
|
||||
// Log to database
|
||||
add_message(RATE_LIMIT_MESSAGE, true, clientIP, 2, config.rate_limit.count, config.rate_limit.interval);
|
||||
add_message(RATE_LIMIT_MESSAGE, clientIP, 2, config.rate_limit.count, config.rate_limit.interval);
|
||||
}
|
||||
|
||||
void logg_warn_dnsmasq_message(char *message)
|
||||
@@ -373,7 +377,7 @@ void logg_warn_dnsmasq_message(char *message)
|
||||
log_warn("WARNING in dnsmasq core: %s", message);
|
||||
|
||||
// Log to database
|
||||
add_message(DNSMASQ_WARN_MESSAGE, false, message, 0);
|
||||
add_message(DNSMASQ_WARN_MESSAGE, message, 0);
|
||||
}
|
||||
|
||||
void log_resource_shortage(const double load, const int nprocs, const int shmem, const int disk, const char *path, const char *msg)
|
||||
@@ -381,16 +385,25 @@ void log_resource_shortage(const double load, const int nprocs, const int shmem,
|
||||
if(load > 0.0)
|
||||
{
|
||||
log_warn("Long-term load (15min avg) larger than number of processors: %.1f > %d", load, nprocs);
|
||||
add_message(LOAD_MESSAGE, true, "excessive load", 2, load, nprocs);
|
||||
add_message(LOAD_MESSAGE, "excessive load", 2, load, nprocs);
|
||||
}
|
||||
else if(shmem > -1)
|
||||
{
|
||||
log_warn("RAM shortage (%s) ahead: %d%% is used (%s)", path, shmem, msg);
|
||||
add_message(SHMEM_MESSAGE, true, path, 2, shmem, msg);
|
||||
add_message(SHMEM_MESSAGE, path, 2, shmem, msg);
|
||||
}
|
||||
else if(disk > -1)
|
||||
{
|
||||
log_warn("Disk shortage (%s) ahead: %d%% is used (%s)", path, disk, msg);
|
||||
add_message(DISK_MESSAGE, true, path, 2, disk, msg);
|
||||
add_message(DISK_MESSAGE, path, 2, disk, msg);
|
||||
}
|
||||
}
|
||||
|
||||
void logg_inaccessible_adlist(const int dbindex, const char *address)
|
||||
{
|
||||
// Log to FTL.log
|
||||
log_warn("Adlist with ID %d (%s) was inaccessible during last gravity run", dbindex, address);
|
||||
|
||||
// Log to database
|
||||
add_message(INACCESSIBLE_ADLIST_MESSAGE, address, 1, dbindex);
|
||||
}
|
||||
|
||||
@@ -23,5 +23,6 @@ void logg_fatal_dnsmasq_message(const char *message);
|
||||
void logg_rate_limit_message(const char *clientIP, const unsigned int rate_limit_count);
|
||||
void logg_warn_dnsmasq_message(char *message);
|
||||
void log_resource_shortage(const double load, const int nprocs, const int shmem, const int disk, const char *path, const char *msg);
|
||||
void logg_inaccessible_adlist(const int dbindex, const char *address);
|
||||
|
||||
#endif //MESSAGETABLE_H
|
||||
|
||||
+12
-10
@@ -404,8 +404,7 @@ bool export_queries_to_disk(bool final)
|
||||
last_disk_db_idx, last_mem_db_idx, time);
|
||||
|
||||
// Start database timer
|
||||
if(config.debug & DEBUG_DATABASE)
|
||||
timer_start(DATABASE_WRITE_TIMER);
|
||||
timer_start(DATABASE_WRITE_TIMER);
|
||||
|
||||
// Attach disk database
|
||||
if(!attach_disk_database(NULL))
|
||||
@@ -761,7 +760,7 @@ void DB_read_queries(void)
|
||||
}
|
||||
|
||||
const int status_int = sqlite3_column_int(stmt, 3);
|
||||
if(status_int < QUERY_UNKNOWN || status_int > QUERY_STATUS_MAX)
|
||||
if(status_int < QUERY_UNKNOWN || status_int >= QUERY_STATUS_MAX)
|
||||
{
|
||||
log_warn("Database: STATUS should be within [%i,%i] but is %i",
|
||||
QUERY_UNKNOWN, QUERY_STATUS_MAX-1, status_int);
|
||||
@@ -925,16 +924,18 @@ void DB_read_queries(void)
|
||||
query->CNAME_domainID = CNAMEdomainID;
|
||||
}
|
||||
}
|
||||
else if(status == QUERY_REGEX)
|
||||
else if(sqlite3_column_bytes(stmt, 7) != 0)
|
||||
{
|
||||
// QUERY_REGEX: Set ID regex which was the reason for blocking
|
||||
const int cacheID = findCacheID(query->domainID, query->clientID, query->type);
|
||||
// Set ID of the domainlist entry that was the reason for permitting/blocking this query
|
||||
// We assume the value in this field is said ID when it is not a CNAME-related domain
|
||||
// (checked above) and the value of additional_info is not NULL (0 bytes storage size)
|
||||
const int cacheID = findCacheID(query->domainID, query->clientID, query->type, true);
|
||||
DNSCacheData *cache = getDNSCache(cacheID, true);
|
||||
// Only load if
|
||||
// a) we have a cache entry
|
||||
// b) the value of additional_info is not NULL (0 bytes storage size)
|
||||
if(cache != NULL && sqlite3_column_bytes(stmt, 7) != 0)
|
||||
cache->deny_regex_id = sqlite3_column_int(stmt, 7);
|
||||
cache->domainlist_id = sqlite3_column_int(stmt, 7);
|
||||
}
|
||||
|
||||
// Increment status counters, we first have to add one to the count of
|
||||
@@ -985,6 +986,7 @@ void DB_read_queries(void)
|
||||
break;
|
||||
|
||||
case QUERY_CACHE: // Cached or local config
|
||||
case QUERY_CACHE_STALE:
|
||||
// Nothing to be done here
|
||||
break;
|
||||
|
||||
@@ -1280,16 +1282,16 @@ bool queries_to_database(void)
|
||||
else if(query->status == QUERY_REGEX)
|
||||
{
|
||||
// Restore regex ID if applicable
|
||||
const int cacheID = findCacheID(query->domainID, query->clientID, query->type);
|
||||
const int cacheID = findCacheID(query->domainID, query->clientID, query->type, false);
|
||||
DNSCacheData *cache = getDNSCache(cacheID, true);
|
||||
if(cache != NULL)
|
||||
{
|
||||
sqlite3_bind_int(query_stmt, 9, ADDINFO_REGEX_ID);
|
||||
sqlite3_bind_int(query_stmt, 10, cache->deny_regex_id);
|
||||
sqlite3_bind_int(query_stmt, 10, cache->domainlist_id);
|
||||
|
||||
// Execute prepared addinfo statement and check if successful
|
||||
sqlite3_bind_int(addinfo_stmt, 1, ADDINFO_REGEX_ID);
|
||||
sqlite3_bind_int(addinfo_stmt, 2, cache->deny_regex_id);
|
||||
sqlite3_bind_int(addinfo_stmt, 2, cache->domainlist_id);
|
||||
if(sqlite3_step(addinfo_stmt) != SQLITE_DONE)
|
||||
{
|
||||
log_err("Encountered error while trying to store addinfo");
|
||||
|
||||
+3943
-1158
File diff suppressed because it is too large
Load Diff
+3591
-1636
File diff suppressed because it is too large
Load Diff
+85
-34
@@ -146,9 +146,9 @@ extern "C" {
|
||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||
** [sqlite_version()] and [sqlite_source_id()].
|
||||
*/
|
||||
#define SQLITE_VERSION "3.39.0"
|
||||
#define SQLITE_VERSION_NUMBER 3039000
|
||||
#define SQLITE_SOURCE_ID "2022-06-25 14:57:57 14e166f40dbfa6e055543f8301525f2ca2e96a02a57269818b9e69e162e98918"
|
||||
#define SQLITE_VERSION "3.40.0"
|
||||
#define SQLITE_VERSION_NUMBER 3040000
|
||||
#define SQLITE_SOURCE_ID "2022-11-16 12:10:08 89c459e766ea7e9165d0beeb124708b955a4950d0f4792f457465d71b158d318"
|
||||
|
||||
/*
|
||||
** CAPI3REF: Run-Time Library Version Numbers
|
||||
@@ -670,13 +670,17 @@ SQLITE_API int sqlite3_exec(
|
||||
**
|
||||
** SQLite uses one of these integer values as the second
|
||||
** argument to calls it makes to the xLock() and xUnlock() methods
|
||||
** of an [sqlite3_io_methods] object.
|
||||
** of an [sqlite3_io_methods] object. These values are ordered from
|
||||
** lest restrictive to most restrictive.
|
||||
**
|
||||
** The argument to xLock() is always SHARED or higher. The argument to
|
||||
** xUnlock is either SHARED or NONE.
|
||||
*/
|
||||
#define SQLITE_LOCK_NONE 0
|
||||
#define SQLITE_LOCK_SHARED 1
|
||||
#define SQLITE_LOCK_RESERVED 2
|
||||
#define SQLITE_LOCK_PENDING 3
|
||||
#define SQLITE_LOCK_EXCLUSIVE 4
|
||||
#define SQLITE_LOCK_NONE 0 /* xUnlock() only */
|
||||
#define SQLITE_LOCK_SHARED 1 /* xLock() or xUnlock() */
|
||||
#define SQLITE_LOCK_RESERVED 2 /* xLock() only */
|
||||
#define SQLITE_LOCK_PENDING 3 /* xLock() only */
|
||||
#define SQLITE_LOCK_EXCLUSIVE 4 /* xLock() only */
|
||||
|
||||
/*
|
||||
** CAPI3REF: Synchronization Type Flags
|
||||
@@ -754,7 +758,14 @@ struct sqlite3_file {
|
||||
** <li> [SQLITE_LOCK_PENDING], or
|
||||
** <li> [SQLITE_LOCK_EXCLUSIVE].
|
||||
** </ul>
|
||||
** xLock() increases the lock. xUnlock() decreases the lock.
|
||||
** xLock() upgrades the database file lock. In other words, xLock() moves the
|
||||
** database file lock in the direction NONE toward EXCLUSIVE. The argument to
|
||||
** xLock() is always on of SHARED, RESERVED, PENDING, or EXCLUSIVE, never
|
||||
** SQLITE_LOCK_NONE. If the database file lock is already at or above the
|
||||
** requested lock, then the call to xLock() is a no-op.
|
||||
** xUnlock() downgrades the database file lock to either SHARED or NONE.
|
||||
* If the lock is already at or below the requested lock state, then the call
|
||||
** to xUnlock() is a no-op.
|
||||
** The xCheckReservedLock() method checks whether any database connection,
|
||||
** either in this process or in some other process, is holding a RESERVED,
|
||||
** PENDING, or EXCLUSIVE lock on the file. It returns true
|
||||
@@ -859,9 +870,8 @@ struct sqlite3_io_methods {
|
||||
** opcode causes the xFileControl method to write the current state of
|
||||
** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED],
|
||||
** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE])
|
||||
** into an integer that the pArg argument points to. This capability
|
||||
** is used during testing and is only available when the SQLITE_TEST
|
||||
** compile-time option is used.
|
||||
** into an integer that the pArg argument points to.
|
||||
** This capability is only available if SQLite is compiled with [SQLITE_DEBUG].
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_SIZE_HINT]]
|
||||
** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS
|
||||
@@ -1253,6 +1263,26 @@ typedef struct sqlite3_mutex sqlite3_mutex;
|
||||
*/
|
||||
typedef struct sqlite3_api_routines sqlite3_api_routines;
|
||||
|
||||
/*
|
||||
** CAPI3REF: File Name
|
||||
**
|
||||
** Type [sqlite3_filename] is used by SQLite to pass filenames to the
|
||||
** xOpen method of a [VFS]. It may be cast to (const char*) and treated
|
||||
** as a normal, nul-terminated, UTF-8 buffer containing the filename, but
|
||||
** may also be passed to special APIs such as:
|
||||
**
|
||||
** <ul>
|
||||
** <li> sqlite3_filename_database()
|
||||
** <li> sqlite3_filename_journal()
|
||||
** <li> sqlite3_filename_wal()
|
||||
** <li> sqlite3_uri_parameter()
|
||||
** <li> sqlite3_uri_boolean()
|
||||
** <li> sqlite3_uri_int64()
|
||||
** <li> sqlite3_uri_key()
|
||||
** </ul>
|
||||
*/
|
||||
typedef const char *sqlite3_filename;
|
||||
|
||||
/*
|
||||
** CAPI3REF: OS Interface Object
|
||||
**
|
||||
@@ -1431,7 +1461,7 @@ struct sqlite3_vfs {
|
||||
sqlite3_vfs *pNext; /* Next registered VFS */
|
||||
const char *zName; /* Name of this virtual file system */
|
||||
void *pAppData; /* Pointer to application-specific data */
|
||||
int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*,
|
||||
int (*xOpen)(sqlite3_vfs*, sqlite3_filename zName, sqlite3_file*,
|
||||
int flags, int *pOutFlags);
|
||||
int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir);
|
||||
int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut);
|
||||
@@ -2309,6 +2339,7 @@ struct sqlite3_mem_methods {
|
||||
** <ul>
|
||||
** <li> The [PRAGMA writable_schema=ON] statement.
|
||||
** <li> The [PRAGMA journal_mode=OFF] statement.
|
||||
** <li> The [PRAGMA schema_version=N] statement.
|
||||
** <li> Writes to the [sqlite_dbpage] virtual table.
|
||||
** <li> Direct writes to [shadow tables].
|
||||
** </ul>
|
||||
@@ -3424,6 +3455,9 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
|
||||
** <dd>The database is opened [shared cache] enabled, overriding
|
||||
** the default shared cache setting provided by
|
||||
** [sqlite3_enable_shared_cache()].)^
|
||||
** The [use of shared cache mode is discouraged] and hence shared cache
|
||||
** capabilities may be omitted from many builds of SQLite. In such cases,
|
||||
** this option is a no-op.
|
||||
**
|
||||
** ^(<dt>[SQLITE_OPEN_PRIVATECACHE]</dt>
|
||||
** <dd>The database is opened [shared cache] disabled, overriding
|
||||
@@ -3439,7 +3473,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
|
||||
** to return an extended result code.</dd>
|
||||
**
|
||||
** [[OPEN_NOFOLLOW]] ^(<dt>[SQLITE_OPEN_NOFOLLOW]</dt>
|
||||
** <dd>The database filename is not allowed to be a symbolic link</dd>
|
||||
** <dd>The database filename is not allowed to contain a symbolic link</dd>
|
||||
** </dl>)^
|
||||
**
|
||||
** If the 3rd parameter to sqlite3_open_v2() is not one of the
|
||||
@@ -3698,10 +3732,10 @@ SQLITE_API int sqlite3_open_v2(
|
||||
**
|
||||
** See the [URI filename] documentation for additional information.
|
||||
*/
|
||||
SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam);
|
||||
SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault);
|
||||
SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64);
|
||||
SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N);
|
||||
SQLITE_API const char *sqlite3_uri_parameter(sqlite3_filename z, const char *zParam);
|
||||
SQLITE_API int sqlite3_uri_boolean(sqlite3_filename z, const char *zParam, int bDefault);
|
||||
SQLITE_API sqlite3_int64 sqlite3_uri_int64(sqlite3_filename, const char*, sqlite3_int64);
|
||||
SQLITE_API const char *sqlite3_uri_key(sqlite3_filename z, int N);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Translate filenames
|
||||
@@ -3730,9 +3764,9 @@ SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N);
|
||||
** return value from [sqlite3_db_filename()], then the result is
|
||||
** undefined and is likely a memory access violation.
|
||||
*/
|
||||
SQLITE_API const char *sqlite3_filename_database(const char*);
|
||||
SQLITE_API const char *sqlite3_filename_journal(const char*);
|
||||
SQLITE_API const char *sqlite3_filename_wal(const char*);
|
||||
SQLITE_API const char *sqlite3_filename_database(sqlite3_filename);
|
||||
SQLITE_API const char *sqlite3_filename_journal(sqlite3_filename);
|
||||
SQLITE_API const char *sqlite3_filename_wal(sqlite3_filename);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Database File Corresponding To A Journal
|
||||
@@ -3798,14 +3832,14 @@ SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*);
|
||||
** then the corresponding [sqlite3_module.xClose() method should also be
|
||||
** invoked prior to calling sqlite3_free_filename(Y).
|
||||
*/
|
||||
SQLITE_API char *sqlite3_create_filename(
|
||||
SQLITE_API sqlite3_filename sqlite3_create_filename(
|
||||
const char *zDatabase,
|
||||
const char *zJournal,
|
||||
const char *zWal,
|
||||
int nParam,
|
||||
const char **azParam
|
||||
);
|
||||
SQLITE_API void sqlite3_free_filename(char*);
|
||||
SQLITE_API void sqlite3_free_filename(sqlite3_filename);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Error Codes And Messages
|
||||
@@ -5508,6 +5542,16 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6
|
||||
** then the conversion is performed. Otherwise no conversion occurs.
|
||||
** The [SQLITE_INTEGER | datatype] after conversion is returned.)^
|
||||
**
|
||||
** ^(The sqlite3_value_encoding(X) interface returns one of [SQLITE_UTF8],
|
||||
** [SQLITE_UTF16BE], or [SQLITE_UTF16LE] according to the current encoding
|
||||
** of the value X, assuming that X has type TEXT.)^ If sqlite3_value_type(X)
|
||||
** returns something other than SQLITE_TEXT, then the return value from
|
||||
** sqlite3_value_encoding(X) is meaningless. ^Calls to
|
||||
** sqlite3_value_text(X), sqlite3_value_text16(X), sqlite3_value_text16be(X),
|
||||
** sqlite3_value_text16le(X), sqlite3_value_bytes(X), or
|
||||
** sqlite3_value_bytes16(X) might change the encoding of the value X and
|
||||
** thus change the return from subsequent calls to sqlite3_value_encoding(X).
|
||||
**
|
||||
** ^Within the [xUpdate] method of a [virtual table], the
|
||||
** sqlite3_value_nochange(X) interface returns true if and only if
|
||||
** the column corresponding to X is unchanged by the UPDATE operation
|
||||
@@ -5572,6 +5616,7 @@ SQLITE_API int sqlite3_value_type(sqlite3_value*);
|
||||
SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
|
||||
SQLITE_API int sqlite3_value_nochange(sqlite3_value*);
|
||||
SQLITE_API int sqlite3_value_frombind(sqlite3_value*);
|
||||
SQLITE_API int sqlite3_value_encoding(sqlite3_value*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Finding The Subtype Of SQL Values
|
||||
@@ -5625,7 +5670,7 @@ SQLITE_API void sqlite3_value_free(sqlite3_value*);
|
||||
**
|
||||
** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer
|
||||
** when first called if N is less than or equal to zero or if a memory
|
||||
** allocate error occurs.
|
||||
** allocation error occurs.
|
||||
**
|
||||
** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is
|
||||
** determined by the N parameter on first successful call. Changing the
|
||||
@@ -5830,9 +5875,10 @@ typedef void (*sqlite3_destructor_type)(void*);
|
||||
** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE].
|
||||
** ^SQLite takes the text result from the application from
|
||||
** the 2nd parameter of the sqlite3_result_text* interfaces.
|
||||
** ^If the 3rd parameter to the sqlite3_result_text* interfaces
|
||||
** is negative, then SQLite takes result text from the 2nd parameter
|
||||
** through the first zero character.
|
||||
** ^If the 3rd parameter to any of the sqlite3_result_text* interfaces
|
||||
** other than sqlite3_result_text64() is negative, then SQLite computes
|
||||
** the string length itself by searching the 2nd parameter for the first
|
||||
** zero character.
|
||||
** ^If the 3rd parameter to the sqlite3_result_text* interfaces
|
||||
** is non-negative, then as many bytes (not characters) of the text
|
||||
** pointed to by the 2nd parameter are taken as the application-defined
|
||||
@@ -6282,7 +6328,7 @@ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
|
||||
**
|
||||
** ^The sqlite3_db_name(D,N) interface returns a pointer to the schema name
|
||||
** for the N-th database on database connection D, or a NULL pointer of N is
|
||||
** out of range. An N alue of 0 means the main database file. An N of 1 is
|
||||
** out of range. An N value of 0 means the main database file. An N of 1 is
|
||||
** the "temp" schema. Larger values of N correspond to various ATTACH-ed
|
||||
** databases.
|
||||
**
|
||||
@@ -6328,7 +6374,7 @@ SQLITE_API const char *sqlite3_db_name(sqlite3 *db, int N);
|
||||
** <li> [sqlite3_filename_wal()]
|
||||
** </ul>
|
||||
*/
|
||||
SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName);
|
||||
SQLITE_API sqlite3_filename sqlite3_db_filename(sqlite3 *db, const char *zDbName);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Determine if a database is read-only
|
||||
@@ -6465,7 +6511,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
|
||||
** function C that is invoked prior to each autovacuum of the database
|
||||
** file. ^The callback is passed a copy of the generic data pointer (P),
|
||||
** the schema-name of the attached database that is being autovacuumed,
|
||||
** the the size of the database file in pages, the number of free pages,
|
||||
** the size of the database file in pages, the number of free pages,
|
||||
** and the number of bytes per page, respectively. The callback should
|
||||
** return the number of free pages that should be removed by the
|
||||
** autovacuum. ^If the callback returns zero, then no autovacuum happens.
|
||||
@@ -6586,6 +6632,11 @@ SQLITE_API void *sqlite3_update_hook(
|
||||
** to the same database. Sharing is enabled if the argument is true
|
||||
** and disabled if the argument is false.)^
|
||||
**
|
||||
** This interface is omitted if SQLite is compiled with
|
||||
** [-DSQLITE_OMIT_SHARED_CACHE]. The [-DSQLITE_OMIT_SHARED_CACHE]
|
||||
** compile-time option is recommended because the
|
||||
** [use of shared cache mode is discouraged].
|
||||
**
|
||||
** ^Cache sharing is enabled and disabled for an entire process.
|
||||
** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]).
|
||||
** In prior versions of SQLite,
|
||||
@@ -6684,7 +6735,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*);
|
||||
** ^The soft heap limit may not be greater than the hard heap limit.
|
||||
** ^If the hard heap limit is enabled and if sqlite3_soft_heap_limit(N)
|
||||
** is invoked with a value of N that is greater than the hard heap limit,
|
||||
** the the soft heap limit is set to the value of the hard heap limit.
|
||||
** the soft heap limit is set to the value of the hard heap limit.
|
||||
** ^The soft heap limit is automatically enabled whenever the hard heap
|
||||
** limit is enabled. ^When sqlite3_hard_heap_limit64(N) is invoked and
|
||||
** the soft heap limit is outside the range of 1..N, then the soft heap
|
||||
@@ -8979,7 +9030,7 @@ typedef struct sqlite3_backup sqlite3_backup;
|
||||
** if the application incorrectly accesses the destination [database connection]
|
||||
** and so no error code is reported, but the operations may malfunction
|
||||
** nevertheless. Use of the destination database connection while a
|
||||
** backup is in progress might also also cause a mutex deadlock.
|
||||
** backup is in progress might also cause a mutex deadlock.
|
||||
**
|
||||
** If running in [shared cache mode], the application must
|
||||
** guarantee that the shared cache used by the destination database
|
||||
@@ -9407,7 +9458,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2(
|
||||
*/
|
||||
#define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */
|
||||
#define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */
|
||||
#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for for readers */
|
||||
#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */
|
||||
#define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */
|
||||
|
||||
/*
|
||||
|
||||
+68
-11
@@ -37,7 +37,7 @@ void strtolower(char *str)
|
||||
}
|
||||
|
||||
// creates a simple hash of a string that fits into a uint32_t
|
||||
uint32_t hashStr(const char *s)
|
||||
uint32_t __attribute__ ((pure)) hashStr(const char *s)
|
||||
{
|
||||
uint32_t hash = 0;
|
||||
// Jenkins' One-at-a-Time hash (http://www.burtleburtle.net/bob/hash/doobs.html)
|
||||
@@ -319,13 +319,14 @@ void change_clientcount(clientsData *client, int total, int blocked, int overTim
|
||||
}
|
||||
}
|
||||
|
||||
int findCacheID(int domainID, int clientID, enum query_type query_type)
|
||||
int _findCacheID(const int domainID, const int clientID, const enum query_type query_type,
|
||||
const bool create_new, const char *func, int line, const char *file)
|
||||
{
|
||||
// Compare content of client against known client IP addresses
|
||||
for(int cacheID = 0; cacheID < counters->dns_cache_size; cacheID++)
|
||||
{
|
||||
// Get cache pointer
|
||||
DNSCacheData* dns_cache = getDNSCache(cacheID, true);
|
||||
DNSCacheData* dns_cache = _getDNSCache(cacheID, true, line, func, file);
|
||||
|
||||
// Check if the returned pointer is valid before trying to access it
|
||||
if(dns_cache == NULL)
|
||||
@@ -339,11 +340,14 @@ int findCacheID(int domainID, int clientID, enum query_type query_type)
|
||||
}
|
||||
}
|
||||
|
||||
if(!create_new)
|
||||
return -1;
|
||||
|
||||
// Get ID of new cache entry
|
||||
const int cacheID = counters->dns_cache_size;
|
||||
|
||||
// Get client pointer
|
||||
DNSCacheData* dns_cache = getDNSCache(cacheID, false);
|
||||
DNSCacheData* dns_cache = _getDNSCache(cacheID, false, line, func, file);
|
||||
|
||||
if(dns_cache == NULL)
|
||||
{
|
||||
@@ -358,6 +362,7 @@ int findCacheID(int domainID, int clientID, enum query_type query_type)
|
||||
dns_cache->clientID = clientID;
|
||||
dns_cache->query_type = query_type;
|
||||
dns_cache->force_reply = 0u;
|
||||
dns_cache->domainlist_id = -1; // -1 = not set
|
||||
|
||||
// Increase counter by one
|
||||
counters->dns_cache_size++;
|
||||
@@ -510,6 +515,9 @@ void FTL_reload_all_domainlists(void)
|
||||
// only after having called gravityDB_open()
|
||||
read_regex_from_database();
|
||||
|
||||
// Check for inaccessible adlist URLs
|
||||
check_inaccessible_adlists();
|
||||
|
||||
// Reset FTL's internal DNS cache storing whether a specific domain
|
||||
// has already been validated for a specific user
|
||||
FTL_reset_per_client_domain_data();
|
||||
@@ -607,6 +615,8 @@ const char * __attribute__ ((const)) get_query_status_str(const enum query_statu
|
||||
return "DBBUSY";
|
||||
case QUERY_SPECIAL_DOMAIN:
|
||||
return "SPECIAL_DOMAIN";
|
||||
case QUERY_CACHE_STALE:
|
||||
return "CACHE_STALE";
|
||||
case QUERY_STATUS_MAX:
|
||||
default:
|
||||
return "INVALID";
|
||||
@@ -718,6 +728,7 @@ bool __attribute__ ((const)) is_blocked(const enum query_status status)
|
||||
case QUERY_RETRIED:
|
||||
case QUERY_RETRIED_DNSSEC:
|
||||
case QUERY_IN_PROGRESS:
|
||||
case QUERY_CACHE_STALE:
|
||||
case QUERY_STATUS_MAX:
|
||||
default:
|
||||
return false;
|
||||
@@ -759,22 +770,68 @@ int __attribute__ ((pure)) get_cached_count(void)
|
||||
return counters->status[QUERY_CACHE];
|
||||
}
|
||||
|
||||
void _query_set_status(queriesData *query, const enum query_status new_status, const char *file, const int line)
|
||||
static const char* __attribute__ ((const)) query_status_str(const enum query_status status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case QUERY_UNKNOWN:
|
||||
return "UNKNOWN";
|
||||
case QUERY_GRAVITY:
|
||||
return "GRAVITY";
|
||||
case QUERY_FORWARDED:
|
||||
return "FORWARDED";
|
||||
case QUERY_CACHE:
|
||||
return "CACHE";
|
||||
case QUERY_REGEX:
|
||||
return "REGEX";
|
||||
case QUERY_DENYLIST:
|
||||
return "DENYLIST";
|
||||
case QUERY_EXTERNAL_BLOCKED_IP:
|
||||
return "EXTERNAL_BLOCKED_IP";
|
||||
case QUERY_EXTERNAL_BLOCKED_NULL:
|
||||
return "EXTERNAL_BLOCKED_NULL";
|
||||
case QUERY_EXTERNAL_BLOCKED_NXRA:
|
||||
return "EXTERNAL_BLOCKED_NXRA";
|
||||
case QUERY_GRAVITY_CNAME:
|
||||
return "GRAVITY_CNAME";
|
||||
case QUERY_REGEX_CNAME:
|
||||
return "REGEX_CNAME";
|
||||
case QUERY_DENYLIST_CNAME:
|
||||
return "DENYLIST_CNAME";
|
||||
case QUERY_RETRIED:
|
||||
return "RETRIED";
|
||||
case QUERY_RETRIED_DNSSEC:
|
||||
return "RETRIED_DNSSEC";
|
||||
case QUERY_IN_PROGRESS:
|
||||
return "IN_PROGRESS";
|
||||
case QUERY_DBBUSY:
|
||||
return "DBBUSY";
|
||||
case QUERY_SPECIAL_DOMAIN:
|
||||
return "SPECIAL_DOMAIN";
|
||||
case QUERY_CACHE_STALE:
|
||||
return "CACHE_STALE";
|
||||
case QUERY_STATUS_MAX:
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void _query_set_status(queriesData *query, const enum query_status new_status, const char *func, const int line, const char *file)
|
||||
{
|
||||
// Debug logging
|
||||
if(config.debug & DEBUG_STATUS)
|
||||
{
|
||||
const char *oldstr = get_query_status_str(query->status);
|
||||
const char *oldstr = query->status < QUERY_STATUS_MAX ? query_status_str(query->status) : "INVALID";
|
||||
if(query->status == new_status)
|
||||
{
|
||||
log_debug(DEBUG_STATUS, "Query status unchanged: %s (%d) in %s:%i",
|
||||
oldstr, query->status, short_path(file), line);
|
||||
log_debug(DEBUG_STATUS, "Query %i: status unchanged: %s (%d) in %s() (%s:%i)",
|
||||
query->id, oldstr, query->status, func, short_path(file), line);
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *newstr = get_query_status_str(new_status);
|
||||
log_debug(DEBUG_STATUS, "Query status changed: %s (%d) -> %s (%d) in %s:%i",
|
||||
oldstr, query->status, newstr, new_status, short_path(file), line);
|
||||
const char *newstr = new_status < QUERY_STATUS_MAX ? query_status_str(new_status) : "INVALID";
|
||||
log_debug(DEBUG_STATUS, "Query %i: status changed: %s (%d) -> %s (%d) in %s() (%s:%i)",
|
||||
query->id, oldstr, query->status, newstr, new_status, func, short_path(file), line);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-11
@@ -113,16 +113,17 @@ typedef struct {
|
||||
enum query_type query_type;
|
||||
int domainID;
|
||||
int clientID;
|
||||
int deny_regex_id;
|
||||
int domainlist_id;
|
||||
} DNSCacheData;
|
||||
|
||||
void strtolower(char *str);
|
||||
uint32_t hashStr(const char *s) __attribute__((const));
|
||||
uint32_t hashStr(const char *s) __attribute__((pure));
|
||||
int findQueryID(const int id);
|
||||
int findUpstreamID(const char * upstream, const in_port_t port);
|
||||
int findDomainID(const char *domain, const bool count);
|
||||
int findClientID(const char *client, const bool count, const bool aliasclient);
|
||||
int findCacheID(int domainID, int clientID, enum query_type query_type);
|
||||
#define findCacheID(domainID, clientID, query_type, create_new) _findCacheID(domainID, clientID, query_type, create_new, __FUNCTION__, __LINE__, __FILE__)
|
||||
int _findCacheID(const int domainID, const int clientID, const enum query_type query_type, const bool create_new, const char *func, const int line, const char *file);
|
||||
bool isValidIPv4(const char *addr);
|
||||
bool isValidIPv6(const char *addr);
|
||||
|
||||
@@ -130,9 +131,8 @@ bool is_blocked(const enum query_status status) __attribute__ ((const));
|
||||
int get_blocked_count(void) __attribute__ ((pure));
|
||||
int get_forwarded_count(void) __attribute__ ((pure));
|
||||
int get_cached_count(void) __attribute__ ((pure));
|
||||
void query_set_status(queriesData *query, const enum query_status new_status);
|
||||
#define query_set_status(query, new_status) _query_set_status(query, new_status, __FILE__, __LINE__)
|
||||
void _query_set_status(queriesData *query, const enum query_status new_status, const char *file, const int line);
|
||||
#define query_set_status(query, new_status) _query_set_status(query, new_status, __FUNCTION__, __LINE__, __FILE__)
|
||||
void _query_set_status(queriesData *query, const enum query_status new_status, const char *func, const int line, const char *file);
|
||||
|
||||
void FTL_reload_all_domainlists(void);
|
||||
void FTL_reset_per_client_domain_data(void);
|
||||
@@ -152,14 +152,14 @@ const char *get_blocking_mode_str(const enum blocking_mode mode) __attribute__ (
|
||||
|
||||
// Pointer getter functions
|
||||
#define getQuery(queryID, checkMagic) _getQuery(queryID, checkMagic, __LINE__, __FUNCTION__, __FILE__)
|
||||
queriesData* _getQuery(int queryID, bool checkMagic, int line, const char * function, const char * file);
|
||||
queriesData* _getQuery(int queryID, bool checkMagic, int line, const char *func, const char *file);
|
||||
#define getClient(clientID, checkMagic) _getClient(clientID, checkMagic, __LINE__, __FUNCTION__, __FILE__)
|
||||
clientsData* _getClient(int clientID, bool checkMagic, int line, const char * function, const char * file);
|
||||
clientsData* _getClient(int clientID, bool checkMagic, int line, const char *func, const char *file);
|
||||
#define getDomain(domainID, checkMagic) _getDomain(domainID, checkMagic, __LINE__, __FUNCTION__, __FILE__)
|
||||
domainsData* _getDomain(int domainID, bool checkMagic, int line, const char * function, const char * file);
|
||||
domainsData* _getDomain(int domainID, bool checkMagic, int line, const char *func, const char *file);
|
||||
#define getUpstream(upstreamID, checkMagic) _getUpstream(upstreamID, checkMagic, __LINE__, __FUNCTION__, __FILE__)
|
||||
upstreamsData* _getUpstream(int upstreamID, bool checkMagic, int line, const char * function, const char * file);
|
||||
upstreamsData* _getUpstream(int upstreamID, bool checkMagic, int line, const char *func, const char *file);
|
||||
#define getDNSCache(cacheID, checkMagic) _getDNSCache(cacheID, checkMagic, __LINE__, __FUNCTION__, __FILE__)
|
||||
DNSCacheData* _getDNSCache(int cacheID, bool checkMagic, int line, const char * function, const char * file);
|
||||
DNSCacheData* _getDNSCache(int cacheID, bool checkMagic, int line, const char *func, const char *file);
|
||||
|
||||
#endif //DATASTRUCTURE_H
|
||||
|
||||
+84
-25
@@ -190,7 +190,7 @@ void rehash(int size)
|
||||
else if (new_size <= hash_size || !(new = whine_malloc(new_size * sizeof(struct crec *))))
|
||||
return;
|
||||
|
||||
for(i = 0; i < new_size; i++)
|
||||
for (i = 0; i < new_size; i++)
|
||||
new[i] = NULL;
|
||||
|
||||
old = hash_table;
|
||||
@@ -234,7 +234,8 @@ static void cache_hash(struct crec *crecp)
|
||||
immortal entries are at the end of the hash-chain.
|
||||
This allows reverse searches and garbage collection to be optimised */
|
||||
|
||||
struct crec **up = hash_bucket(cache_get_name(crecp));
|
||||
char *name = cache_get_name(crecp);
|
||||
struct crec **up = hash_bucket(name);
|
||||
|
||||
if (!(crecp->flags & F_REVERSE))
|
||||
{
|
||||
@@ -245,6 +246,11 @@ static void cache_hash(struct crec *crecp)
|
||||
while (*up && !((*up)->flags & F_IMMORTAL))
|
||||
up = &((*up)->hash_next);
|
||||
}
|
||||
|
||||
/* Preserve order when inserting the same name multiple times. */
|
||||
while (*up && hostname_isequal(cache_get_name(*up), name))
|
||||
up = &((*up)->hash_next);
|
||||
|
||||
crecp->hash_next = *up;
|
||||
*up = crecp;
|
||||
}
|
||||
@@ -375,6 +381,19 @@ static int is_outdated_cname_pointer(struct crec *crecp)
|
||||
|
||||
static int is_expired(time_t now, struct crec *crecp)
|
||||
{
|
||||
/* Don't dump expired entries if they are within the accepted timeout range.
|
||||
The cache becomes approx. LRU. Never use expired DS or DNSKEY entries.
|
||||
Possible values for daemon->cache_max_expiry:
|
||||
-1 == serve cached content regardless how long ago it expired
|
||||
0 == the option is disabled, expired content isn't served
|
||||
<n> == serve cached content only if it expire less than <n> seconds
|
||||
ago (where n is a positive integer) */
|
||||
if (daemon->cache_max_expiry != 0 &&
|
||||
(daemon->cache_max_expiry == -1 ||
|
||||
difftime(now, crecp->ttd) < daemon->cache_max_expiry) &&
|
||||
!(crecp->flags & (F_DS | F_DNSKEY)))
|
||||
return 0;
|
||||
|
||||
if (crecp->flags & F_IMMORTAL)
|
||||
return 0;
|
||||
|
||||
@@ -384,6 +403,27 @@ static int is_expired(time_t now, struct crec *crecp)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Remove entries with a given UID from the cache */
|
||||
unsigned int cache_remove_uid(const unsigned int uid)
|
||||
{
|
||||
int i;
|
||||
unsigned int removed = 0;
|
||||
struct crec *crecp, **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;
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned short class, time_t now,
|
||||
unsigned int flags, struct crec **target_crec, unsigned int *target_uid)
|
||||
{
|
||||
@@ -554,7 +594,7 @@ static struct crec *really_insert(char *name, union all_addr *addr, unsigned sho
|
||||
{
|
||||
struct crec *new, *target_crec = NULL;
|
||||
union bigname *big_name = NULL;
|
||||
int freed_all = flags & F_REVERSE;
|
||||
int freed_all = (flags & F_REVERSE);
|
||||
int free_avail = 0;
|
||||
unsigned int target_uid;
|
||||
|
||||
@@ -629,8 +669,12 @@ static struct crec *really_insert(char *name, union all_addr *addr, unsigned sho
|
||||
{
|
||||
/* For DNSSEC records, uid holds class. */
|
||||
free_avail = 1; /* Must be free space now. */
|
||||
|
||||
/* condition valid when stale-caching */
|
||||
if (difftime(now, new->ttd) < 0)
|
||||
daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED]++;
|
||||
|
||||
cache_scan_free(cache_get_name(new), &new->addr, new->uid, now, new->flags, NULL, NULL);
|
||||
daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -693,7 +737,7 @@ static struct crec *really_insert(char *name, union all_addr *addr, unsigned sho
|
||||
new->ttd = now + (time_t)ttl;
|
||||
new->next = new_chain;
|
||||
new_chain = new;
|
||||
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
@@ -871,7 +915,7 @@ int cache_find_non_terminal(char *name, time_t now)
|
||||
struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned int prot)
|
||||
{
|
||||
struct crec *ans;
|
||||
int no_rr = prot & F_NO_RR;
|
||||
int no_rr = (prot & F_NO_RR) || option_bool(OPT_NORR);
|
||||
|
||||
prot &= ~F_NO_RR;
|
||||
|
||||
@@ -1019,16 +1063,17 @@ struct crec *cache_find_by_addr(struct crec *crecp, union all_addr *addr,
|
||||
void add_hosts_entry(struct crec *cache, union all_addr *addr, int addrlen,
|
||||
unsigned int index, struct crec **rhash, int hashsz)
|
||||
{
|
||||
struct crec *lookup = cache_find_by_name(NULL, cache_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6));
|
||||
int i;
|
||||
unsigned int j;
|
||||
struct crec *lookup = NULL;
|
||||
|
||||
/* Remove duplicates in hosts files. */
|
||||
if (lookup && (lookup->flags & F_HOSTS) && memcmp(&lookup->addr, addr, addrlen) == 0)
|
||||
{
|
||||
free(cache);
|
||||
return;
|
||||
}
|
||||
while ((lookup = cache_find_by_name(lookup, cache_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6))))
|
||||
if ((lookup->flags & F_HOSTS) && memcmp(&lookup->addr, addr, addrlen) == 0)
|
||||
{
|
||||
free(cache);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure there is only one address -> name mapping (first one trumps)
|
||||
We do this by steam here, The entries are kept in hash chains, linked
|
||||
@@ -1133,7 +1178,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
|
||||
{
|
||||
FILE *f = fopen(filename, "r");
|
||||
char *token = daemon->namebuff, *domain_suffix = NULL;
|
||||
int addr_count = 0, name_count = cache_size, lineno = 1;
|
||||
int names_done = 0, name_count = cache_size, lineno = 1;
|
||||
unsigned int flags = 0;
|
||||
union all_addr addr;
|
||||
int atnl, addrlen = 0;
|
||||
@@ -1169,8 +1214,6 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
|
||||
continue;
|
||||
}
|
||||
|
||||
addr_count++;
|
||||
|
||||
/* rehash every 1000 names. */
|
||||
if (rhash && ((name_count - cache_size) > 1000))
|
||||
{
|
||||
@@ -1202,6 +1245,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
|
||||
cache->ttd = daemon->local_ttl;
|
||||
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
|
||||
name_count++;
|
||||
names_done++;
|
||||
}
|
||||
if ((cache = whine_malloc(SIZEOF_BARE_CREC + strlen(canon) + 1)))
|
||||
{
|
||||
@@ -1210,6 +1254,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
|
||||
cache->ttd = daemon->local_ttl;
|
||||
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
|
||||
name_count++;
|
||||
names_done++;
|
||||
}
|
||||
free(canon);
|
||||
|
||||
@@ -1226,7 +1271,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
|
||||
if (rhash)
|
||||
rehash(name_count);
|
||||
|
||||
my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
|
||||
my_syslog(LOG_INFO, _("read %s - %d names"), filename, names_done);
|
||||
|
||||
return name_count;
|
||||
}
|
||||
@@ -1683,10 +1728,8 @@ int cache_make_stat(struct txt_record *t)
|
||||
{
|
||||
/* expand buffer if necessary */
|
||||
newlen = bytes_needed + 1 + bufflen - bytes_avail;
|
||||
if (!(new = whine_malloc(newlen)))
|
||||
if (!(new = whine_realloc(buff, newlen)))
|
||||
return 0;
|
||||
memcpy(new, buff, bufflen);
|
||||
free(buff);
|
||||
p = new + (p - buff);
|
||||
lenp = p - 1;
|
||||
buff = new;
|
||||
@@ -1770,6 +1813,8 @@ void dump_cache(time_t now)
|
||||
daemon->cachesize, daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED], daemon->metrics[METRIC_DNS_CACHE_INSERTED]);
|
||||
my_syslog(LOG_INFO, _("queries forwarded %u, queries answered locally %u"),
|
||||
daemon->metrics[METRIC_DNS_QUERIES_FORWARDED], daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]);
|
||||
if (daemon->cache_max_expiry != 0)
|
||||
my_syslog(LOG_INFO, _("queries answered from stale cache %u"), daemon->metrics[METRIC_DNS_STALE_ANSWERED]);
|
||||
#ifdef HAVE_AUTH
|
||||
my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->metrics[METRIC_DNS_AUTH_ANSWERED]);
|
||||
#endif
|
||||
@@ -1784,16 +1829,23 @@ void dump_cache(time_t now)
|
||||
if (!(serv->flags & SERV_MARK))
|
||||
{
|
||||
int port;
|
||||
unsigned int queries = 0, failed_queries = 0;
|
||||
unsigned int queries = 0, failed_queries = 0, nxdomain_replies = 0, retrys = 0;
|
||||
unsigned int sigma_latency = 0, count_latency = 0;
|
||||
|
||||
for (serv1 = serv; serv1; serv1 = serv1->next)
|
||||
if (!(serv1->flags & SERV_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr))
|
||||
{
|
||||
serv1->flags |= SERV_MARK;
|
||||
queries += serv1->queries;
|
||||
failed_queries += serv1->failed_queries;
|
||||
nxdomain_replies += serv1->nxdomain_replies;
|
||||
retrys += serv1->retrys;
|
||||
sigma_latency += serv1->query_latency;
|
||||
count_latency++;
|
||||
}
|
||||
port = prettyprint_addr(&serv->addr, daemon->addrbuff);
|
||||
my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), daemon->addrbuff, port, queries, failed_queries);
|
||||
my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried %u, failed %u, nxdomain replies %u, avg. latency %ums"),
|
||||
daemon->addrbuff, port, queries, retrys, failed_queries, nxdomain_replies, sigma_latency/count_latency);
|
||||
}
|
||||
|
||||
if (option_bool(OPT_DEBUG) || option_bool(OPT_LOG))
|
||||
@@ -1888,7 +1940,10 @@ void dump_cache(time_t now)
|
||||
char *record_source(unsigned int index)
|
||||
{
|
||||
struct hostsfile *ah;
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
struct dyndir *dd;
|
||||
#endif
|
||||
|
||||
if (index == SRC_CONFIG)
|
||||
return "config";
|
||||
else if (index == SRC_HOSTS)
|
||||
@@ -1899,9 +1954,11 @@ char *record_source(unsigned int index)
|
||||
return ah->fname;
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
|
||||
if (ah->index == index)
|
||||
return ah->fname;
|
||||
/* Dynamic directories contain multiple files */
|
||||
for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
|
||||
for (ah = dd->files; ah; ah = ah->next)
|
||||
if (ah->index == index)
|
||||
return ah->fname;
|
||||
#endif
|
||||
|
||||
return "<unknown>";
|
||||
@@ -2129,6 +2186,8 @@ void _log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
|
||||
name = arg;
|
||||
verb = daemon->addrbuff;
|
||||
}
|
||||
else if (flags & F_STALE)
|
||||
source = "cached-stale";
|
||||
else
|
||||
source = "cached";
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#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 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 */
|
||||
#define FORWARD_TIME 600 /* or 10 minutes */
|
||||
#define UDP_TEST_TIME 60 /* How often to reset our idea of max packet size. */
|
||||
@@ -60,6 +61,8 @@
|
||||
#define SOA_EXPIRY 1209600 /* SOA expiry default */
|
||||
#define LOOP_TEST_DOMAIN "test" /* domain for loop testing, "test" is reserved by RFC 2606 and won't therefore clash */
|
||||
#define LOOP_TEST_TYPE T_TXT
|
||||
#define DEFAULT_FAST_RETRY 1000 /* ms, default delay before fast retry */
|
||||
#define STALE_CACHE_EXPIRY 86400 /* 1 day in secs, default maximum expiry time for stale cache data */
|
||||
|
||||
/* compile-time options: uncomment below to enable or do eg.
|
||||
make COPTS=-DHAVE_BROKEN_RTC
|
||||
@@ -200,6 +203,17 @@ RESOLVFILE
|
||||
/* #define HAVE_DNSSEC */
|
||||
/* #define HAVE_NFTSET */
|
||||
|
||||
/* Pi-hole definitions */
|
||||
#define HAVE_LUASCRIPT
|
||||
#define HAVE_IDN
|
||||
#define HAVE_DNSSEC
|
||||
#ifdef DNSMASQ_ALL_OPTS
|
||||
#define HAVE_DBUS
|
||||
#define HAVE_CONNTRACK
|
||||
#define HAVE_NFTSET
|
||||
#endif
|
||||
/***********************/
|
||||
|
||||
/* Default locations for important system files. */
|
||||
|
||||
#ifndef LEASEFILE
|
||||
|
||||
+21
-8
@@ -309,14 +309,14 @@ static int dnsmasq_gostdsa_verify(struct blockdata *key_data, unsigned int key_l
|
||||
mpz_init(y);
|
||||
}
|
||||
|
||||
mpz_import(x, 32 , 1, 1, 0, 0, p);
|
||||
mpz_import(y, 32 , 1, 1, 0, 0, p + 32);
|
||||
mpz_import(x, 32, -1, 1, 0, 0, p);
|
||||
mpz_import(y, 32, -1, 1, 0, 0, p + 32);
|
||||
|
||||
if (!ecc_point_set(gost_key, x, y))
|
||||
return 0;
|
||||
return 0;
|
||||
|
||||
mpz_import(sig_struct->r, 32, 1, 1, 0, 0, sig);
|
||||
mpz_import(sig_struct->s, 32, 1, 1, 0, 0, sig + 32);
|
||||
mpz_import(sig_struct->s, 32, 1, 1, 0, 0, sig);
|
||||
mpz_import(sig_struct->r, 32, 1, 1, 0, 0, sig + 32);
|
||||
|
||||
return nettle_gostdsa_verify(gost_key, digest_len, digest, sig_struct);
|
||||
}
|
||||
@@ -390,7 +390,12 @@ static int (*verify_func(int algo))(struct blockdata *key_data, unsigned int key
|
||||
return dnsmasq_ecdsa_verify;
|
||||
|
||||
#if MIN_VERSION(3, 1)
|
||||
case 15: case 16:
|
||||
case 15:
|
||||
return dnsmasq_eddsa_verify;
|
||||
#endif
|
||||
|
||||
#if MIN_VERSION(3, 6)
|
||||
case 16:
|
||||
return dnsmasq_eddsa_verify;
|
||||
#endif
|
||||
}
|
||||
@@ -425,7 +430,9 @@ char *ds_digest_name(int digest)
|
||||
{
|
||||
case 1: return "sha1";
|
||||
case 2: return "sha256";
|
||||
case 3: return "gosthash94";
|
||||
#if MIN_VERSION(3, 6)
|
||||
case 3: return "gosthash94cp";
|
||||
#endif
|
||||
case 4: return "sha384";
|
||||
default: return NULL;
|
||||
}
|
||||
@@ -444,11 +451,17 @@ char *algo_digest_name(int algo)
|
||||
case 7: return "sha1"; /* RSASHA1-NSEC3-SHA1 */
|
||||
case 8: return "sha256"; /* RSA/SHA-256 */
|
||||
case 10: return "sha512"; /* RSA/SHA-512 */
|
||||
case 12: return "gosthash94"; /* ECC-GOST */
|
||||
#if MIN_VERSION(3, 6)
|
||||
case 12: return "gosthash94cp"; /* ECC-GOST */
|
||||
#endif
|
||||
case 13: return "sha256"; /* ECDSAP256SHA256 */
|
||||
case 14: return "sha384"; /* ECDSAP384SHA384 */
|
||||
#if MIN_VERSION(3, 1)
|
||||
case 15: return "null_hash"; /* ED25519 */
|
||||
# if MIN_VERSION(3, 6)
|
||||
case 16: return "null_hash"; /* ED448 */
|
||||
# endif
|
||||
#endif
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
+150
-19
@@ -91,6 +91,11 @@ const char* introspection_xml_template =
|
||||
" <method name=\"GetMetrics\">\n"
|
||||
" <arg name=\"metrics\" direction=\"out\" type=\"a{su}\"/>\n"
|
||||
" </method>\n"
|
||||
" <method name=\"GetServerMetrics\">\n"
|
||||
" <arg name=\"metrics\" direction=\"out\" type=\"a{ss}\"/>\n"
|
||||
" </method>\n"
|
||||
" <method name=\"ClearMetrics\">\n"
|
||||
" </method>\n"
|
||||
" </interface>\n"
|
||||
"</node>\n";
|
||||
|
||||
@@ -287,6 +292,11 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
u16 flags = 0;
|
||||
char interface[IF_NAMESIZE];
|
||||
char *str_addr, *str_domain = NULL;
|
||||
struct server_details sdetails = { 0 };
|
||||
sdetails.addr = &addr;
|
||||
sdetails.source_addr = &source_addr;
|
||||
sdetails.interface = interface;
|
||||
sdetails.flags = &flags;
|
||||
|
||||
if (strings)
|
||||
{
|
||||
@@ -369,20 +379,6 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
strcpy(str_addr, str);
|
||||
}
|
||||
|
||||
/* parse the IP address */
|
||||
if ((addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, &flags)))
|
||||
{
|
||||
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"Invalid IP address '%s': %s",
|
||||
str, addr_err);
|
||||
break;
|
||||
}
|
||||
|
||||
/* 0.0.0.0 for server address == NULL, for Dbus */
|
||||
if (addr.in.sin_family == AF_INET &&
|
||||
addr.in.sin_addr.s_addr == 0)
|
||||
flags |= SERV_LITERAL_ADDRESS;
|
||||
|
||||
if (strings)
|
||||
{
|
||||
char *p;
|
||||
@@ -396,7 +392,31 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
else
|
||||
p = NULL;
|
||||
|
||||
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL);
|
||||
if (strings && strlen(str_addr) == 0)
|
||||
add_update_server(SERV_LITERAL_ADDRESS | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL);
|
||||
else
|
||||
{
|
||||
if ((addr_err = parse_server(str_addr, &sdetails)))
|
||||
{
|
||||
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"Invalid IP address '%s': %s",
|
||||
str, addr_err);
|
||||
break;
|
||||
}
|
||||
|
||||
while (parse_server_next(&sdetails))
|
||||
{
|
||||
if ((addr_err = parse_server_addr(&sdetails)))
|
||||
{
|
||||
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"Invalid IP address '%s': %s",
|
||||
str, addr_err);
|
||||
break;
|
||||
}
|
||||
|
||||
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL);
|
||||
}
|
||||
}
|
||||
} while ((str_domain = p));
|
||||
}
|
||||
else
|
||||
@@ -410,11 +430,40 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
if (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING)
|
||||
dbus_message_iter_get_basic(&string_iter, &str);
|
||||
dbus_message_iter_next (&string_iter);
|
||||
|
||||
if ((addr_err = parse_server(str_addr, &sdetails)))
|
||||
{
|
||||
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"Invalid IP address '%s': %s",
|
||||
str, addr_err);
|
||||
break;
|
||||
}
|
||||
|
||||
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str, NULL);
|
||||
while (parse_server_next(&sdetails))
|
||||
{
|
||||
if ((addr_err = parse_server_addr(&sdetails)))
|
||||
{
|
||||
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"Invalid IP address '%s': %s",
|
||||
str, addr_err);
|
||||
break;
|
||||
}
|
||||
|
||||
/* 0.0.0.0 for server address == NULL, for Dbus */
|
||||
if (addr.in.sin_family == AF_INET &&
|
||||
addr.in.sin_addr.s_addr == 0)
|
||||
flags |= SERV_LITERAL_ADDRESS;
|
||||
else
|
||||
flags &= ~SERV_LITERAL_ADDRESS;
|
||||
|
||||
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str, NULL);
|
||||
}
|
||||
} while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING);
|
||||
}
|
||||
|
||||
|
||||
if (sdetails.orig_hostinfo)
|
||||
freeaddrinfo(sdetails.orig_hostinfo);
|
||||
|
||||
/* jump to next element in outer array */
|
||||
dbus_message_iter_next(&array_iter);
|
||||
}
|
||||
@@ -644,6 +693,77 @@ static DBusMessage *dbus_get_metrics(DBusMessage* message)
|
||||
return reply;
|
||||
}
|
||||
|
||||
static void add_dict_entry(DBusMessageIter *container, const char *key, const char *val)
|
||||
{
|
||||
DBusMessageIter dict;
|
||||
|
||||
dbus_message_iter_open_container(container, DBUS_TYPE_DICT_ENTRY, NULL, &dict);
|
||||
dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &key);
|
||||
dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &val);
|
||||
dbus_message_iter_close_container(container, &dict);
|
||||
}
|
||||
|
||||
static void add_dict_int(DBusMessageIter *container, const char *key, const unsigned int val)
|
||||
{
|
||||
snprintf(daemon->namebuff, MAXDNAME, "%u", val);
|
||||
|
||||
add_dict_entry(container, key, daemon->namebuff);
|
||||
}
|
||||
|
||||
static DBusMessage *dbus_get_server_metrics(DBusMessage* message)
|
||||
{
|
||||
DBusMessage *reply = dbus_message_new_method_return(message);
|
||||
DBusMessageIter server_array, dict_array, server_iter;
|
||||
struct server *serv;
|
||||
|
||||
dbus_message_iter_init_append(reply, &server_iter);
|
||||
dbus_message_iter_open_container(&server_iter, DBUS_TYPE_ARRAY, "a{ss}", &server_array);
|
||||
|
||||
/* sum counts from different records for same server */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
serv->flags &= ~SERV_MARK;
|
||||
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if (!(serv->flags & SERV_MARK))
|
||||
{
|
||||
unsigned int port;
|
||||
unsigned int queries = 0, failed_queries = 0, nxdomain_replies = 0, retrys = 0;
|
||||
unsigned int sigma_latency = 0, count_latency = 0;
|
||||
|
||||
struct server *serv1;
|
||||
|
||||
for (serv1 = serv; serv1; serv1 = serv1->next)
|
||||
if (!(serv1->flags & SERV_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr))
|
||||
{
|
||||
serv1->flags |= SERV_MARK;
|
||||
queries += serv1->queries;
|
||||
failed_queries += serv1->failed_queries;
|
||||
nxdomain_replies += serv1->nxdomain_replies;
|
||||
retrys += serv1->retrys;
|
||||
sigma_latency += serv1->query_latency;
|
||||
count_latency++;
|
||||
}
|
||||
|
||||
dbus_message_iter_open_container(&server_array, DBUS_TYPE_ARRAY, "{ss}", &dict_array);
|
||||
|
||||
port = prettyprint_addr(&serv->addr, daemon->namebuff);
|
||||
add_dict_entry(&dict_array, "address", daemon->namebuff);
|
||||
|
||||
add_dict_int(&dict_array, "port", port);
|
||||
add_dict_int(&dict_array, "queries", serv->queries);
|
||||
add_dict_int(&dict_array, "failed_queries", serv->failed_queries);
|
||||
add_dict_int(&dict_array, "nxdomain", serv->nxdomain_replies);
|
||||
add_dict_int(&dict_array, "retries", serv->retrys);
|
||||
add_dict_int(&dict_array, "latency", sigma_latency/count_latency);
|
||||
|
||||
dbus_message_iter_close_container(&server_array, &dict_array);
|
||||
}
|
||||
|
||||
dbus_message_iter_close_container(&server_iter, &server_array);
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
DBusHandlerResult message_handler(DBusConnection *connection,
|
||||
DBusMessage *message,
|
||||
void *user_data)
|
||||
@@ -719,6 +839,14 @@ DBusHandlerResult message_handler(DBusConnection *connection,
|
||||
{
|
||||
reply = dbus_get_metrics(message);
|
||||
}
|
||||
else if (strcmp(method, "GetServerMetrics") == 0)
|
||||
{
|
||||
reply = dbus_get_server_metrics(message);
|
||||
}
|
||||
else if (strcmp(method, "ClearMetrics") == 0)
|
||||
{
|
||||
clear_metrics();
|
||||
}
|
||||
else if (strcmp(method, "ClearCache") == 0)
|
||||
clear_cache = 1;
|
||||
else
|
||||
@@ -761,8 +889,11 @@ char *dbus_init(void)
|
||||
|
||||
dbus_error_init (&dbus_error);
|
||||
if (!(connection = dbus_bus_get (DBUS_BUS_SYSTEM, &dbus_error)))
|
||||
return NULL;
|
||||
|
||||
{
|
||||
dbus_error_free(&dbus_error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dbus_connection_set_exit_on_disconnect(connection, FALSE);
|
||||
dbus_connection_set_watch_functions(connection, add_watch, remove_watch,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
@@ -685,6 +685,7 @@ const struct opttab_t {
|
||||
{ "client-machine-id", 97, 0 },
|
||||
{ "posix-timezone", 100, OT_NAME }, /* RFC 4833, Sec. 2 */
|
||||
{ "tzdb-timezone", 101, OT_NAME }, /* RFC 4833, Sec. 2 */
|
||||
{ "ipv6-only", 108, 4 | OT_DEC }, /* RFC 8925 */
|
||||
{ "subnet-select", 118, OT_INTERNAL },
|
||||
{ "domain-search", 119, OT_RFC1035_NAME },
|
||||
{ "sip-server", 120, 0 },
|
||||
@@ -721,6 +722,8 @@ static const struct opttab_t opttab6[] = {
|
||||
{ "sntp-server", 31, OT_ADDR_LIST },
|
||||
{ "information-refresh-time", 32, OT_TIME },
|
||||
{ "FQDN", 39, OT_INTERNAL | OT_RFC1035_NAME },
|
||||
{ "posix-timezone", 41, OT_NAME }, /* RFC 4833, Sec. 3 */
|
||||
{ "tzdb-timezone", 42, OT_NAME }, /* RFC 4833, Sec. 3 */
|
||||
{ "ntp-server", 56, 0 /* OT_ADDR_LIST | OT_RFC1035_NAME */ },
|
||||
{ "bootfile-url", 59, OT_NAME },
|
||||
{ "bootfile-param", 60, OT_CSTRING },
|
||||
@@ -1017,7 +1020,10 @@ void log_relay(int family, struct dhcp_relay *relay)
|
||||
{
|
||||
int broadcast = relay->server.addr4.s_addr == 0;
|
||||
inet_ntop(family, &relay->local, daemon->addrbuff, ADDRSTRLEN);
|
||||
inet_ntop(family, &relay->server, daemon->namebuff, ADDRSTRLEN);
|
||||
inet_ntop(family, &relay->server, daemon->namebuff, ADDRSTRLEN);
|
||||
|
||||
if (family == AF_INET && relay->port != DHCP_SERVER_PORT)
|
||||
sprintf(daemon->namebuff + strlen(daemon->namebuff), "#%u", relay->port);
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
struct in6_addr multicast;
|
||||
@@ -1025,7 +1031,11 @@ void log_relay(int family, struct dhcp_relay *relay)
|
||||
inet_pton(AF_INET6, ALL_SERVERS, &multicast);
|
||||
|
||||
if (family == AF_INET6)
|
||||
broadcast = IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast);
|
||||
{
|
||||
broadcast = IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast);
|
||||
if (relay->port != DHCPV6_SERVER_PORT)
|
||||
sprintf(daemon->namebuff + strlen(daemon->namebuff), "#%u", relay->port);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
#define OPTION_SIP_SERVER 120
|
||||
#define OPTION_VENDOR_IDENT 124
|
||||
#define OPTION_VENDOR_IDENT_OPT 125
|
||||
#define OPTION_MUD_URL_V4 161
|
||||
#define OPTION_END 255
|
||||
|
||||
#define SUBOPT_CIRCUIT_ID 1
|
||||
|
||||
+8
-9
@@ -177,8 +177,7 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
return;
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_DHCP, (void *)daemon->dhcp_packet.iov_base, sz, (union mysockaddr *)&dest, NULL,
|
||||
pxe_fd ? PXE_PORT : daemon->dhcp_server_port);
|
||||
dump_packet_udp(DUMP_DHCP, (void *)daemon->dhcp_packet.iov_base, sz, (union mysockaddr *)&dest, NULL, fd);
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_LINUX_NETWORK)
|
||||
@@ -464,8 +463,8 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
dest.sin_addr = mess->yiaddr;
|
||||
dest.sin_port = htons(daemon->dhcp_client_port);
|
||||
|
||||
dump_packet(DUMP_DHCP, (void *)iov.iov_base, iov.iov_len, NULL,
|
||||
(union mysockaddr *)&dest, daemon->dhcp_server_port);
|
||||
dump_packet_udp(DUMP_DHCP, (void *)iov.iov_base, iov.iov_len, NULL,
|
||||
(union mysockaddr *)&dest, fd);
|
||||
#endif
|
||||
|
||||
send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
|
||||
@@ -478,8 +477,8 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_DHCP, (void *)iov.iov_base, iov.iov_len, NULL,
|
||||
(union mysockaddr *)&dest, daemon->dhcp_server_port);
|
||||
dump_packet_udp(DUMP_DHCP, (void *)iov.iov_base, iov.iov_len, NULL,
|
||||
(union mysockaddr *)&dest, fd);
|
||||
#endif
|
||||
|
||||
while(retry_send(sendmsg(fd, &msg, 0)));
|
||||
@@ -1121,7 +1120,7 @@ static int relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz)
|
||||
|
||||
to.sa.sa_family = AF_INET;
|
||||
to.in.sin_addr = relay->server.addr4;
|
||||
to.in.sin_port = htons(daemon->dhcp_server_port);
|
||||
to.in.sin_port = htons(relay->port);
|
||||
|
||||
/* Broadcasting to server. */
|
||||
if (relay->server.addr4.s_addr == 0)
|
||||
@@ -1147,8 +1146,8 @@ static int relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz)
|
||||
fromsock.in.sin_port = htons(daemon->dhcp_server_port);
|
||||
fromsock.in.sin_addr = from.addr4;
|
||||
fromsock.sa.sa_family = AF_INET;
|
||||
|
||||
dump_packet(DUMP_DHCP, (void *)mess, sz, &fromsock, &to, 0);
|
||||
|
||||
dump_packet_udp(DUMP_DHCP, (void *)mess, sz, &fromsock, &to, -1);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
#define OPTION6_FQDN 39
|
||||
#define OPTION6_NTP_SERVER 56
|
||||
#define OPTION6_CLIENT_MAC 79
|
||||
#define OPTION6_MUD_URL 112
|
||||
|
||||
#define NTP_SUBOPTION_SRV_ADDR 1
|
||||
#define NTP_SUBOPTION_MC_ADDR 2
|
||||
|
||||
+6
-6
@@ -119,8 +119,8 @@ void dhcp6_packet(time_t now)
|
||||
return;
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_DHCPV6, (void *)daemon->dhcp_packet.iov_base, sz,
|
||||
(union mysockaddr *)&from, NULL, DHCPV6_SERVER_PORT);
|
||||
dump_packet_udp(DUMP_DHCPV6, (void *)daemon->dhcp_packet.iov_base, sz,
|
||||
(union mysockaddr *)&from, NULL, daemon->dhcp6fd);
|
||||
#endif
|
||||
|
||||
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
|
||||
@@ -142,8 +142,8 @@ void dhcp6_packet(time_t now)
|
||||
if (relay_reply6(&from, sz, ifr.ifr_name))
|
||||
{
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_DHCPV6, (void *)daemon->outpacket.iov_base, save_counter(-1), NULL,
|
||||
(union mysockaddr *)&from, DHCPV6_SERVER_PORT);
|
||||
dump_packet_udp(DUMP_DHCPV6, (void *)daemon->outpacket.iov_base, save_counter(-1), NULL,
|
||||
(union mysockaddr *)&from, daemon->dhcp6fd);
|
||||
#endif
|
||||
|
||||
while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base,
|
||||
@@ -254,8 +254,8 @@ void dhcp6_packet(time_t now)
|
||||
from.sin6_port = htons(port);
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_DHCPV6, (void *)daemon->outpacket.iov_base, save_counter(-1),
|
||||
NULL, (union mysockaddr *)&from, DHCPV6_SERVER_PORT);
|
||||
dump_packet_udp(DUMP_DHCPV6, (void *)daemon->outpacket.iov_base, save_counter(-1),
|
||||
NULL, (union mysockaddr *)&from, daemon->dhcp6fd);
|
||||
#endif
|
||||
|
||||
while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base,
|
||||
|
||||
+18
-14
@@ -272,6 +272,10 @@ int main_dnsmasq (int argc, char **argv)
|
||||
|
||||
if (daemon->max_port < daemon->min_port)
|
||||
die(_("max_port cannot be smaller than min_port"), NULL, EC_BADCONF);
|
||||
|
||||
if (daemon->max_port != 0 &&
|
||||
daemon->max_port - daemon->min_port + 1 < daemon->randport_limit)
|
||||
die(_("port_limit must not be larger than available port range"), NULL, EC_BADCONF);
|
||||
|
||||
now = dnsmasq_time();
|
||||
|
||||
@@ -1068,19 +1072,20 @@ int main_dnsmasq (int argc, char **argv)
|
||||
|
||||
while (!terminate)
|
||||
{
|
||||
int timeout = -1;
|
||||
int timeout = fast_retry(now);
|
||||
|
||||
poll_reset();
|
||||
|
||||
/* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */
|
||||
if (daemon->tftp_trans ||
|
||||
(option_bool(OPT_DBUS) && !daemon->dbus))
|
||||
if ((daemon->tftp_trans || (option_bool(OPT_DBUS) && !daemon->dbus)) &&
|
||||
(timeout == -1 || timeout > 250))
|
||||
timeout = 250;
|
||||
|
||||
|
||||
/* Wake every second whilst waiting for DAD to complete */
|
||||
else if (is_dad_listeners())
|
||||
else if (is_dad_listeners() &&
|
||||
(timeout == -1 || timeout > 1000))
|
||||
timeout = 1000;
|
||||
|
||||
|
||||
set_dns_listeners();
|
||||
|
||||
#ifdef HAVE_DBUS
|
||||
@@ -1689,9 +1694,10 @@ static void poll_resolv(int force, int do_reload, time_t now)
|
||||
else
|
||||
{
|
||||
res->logged = 0;
|
||||
if (force || (statbuf.st_mtime != res->mtime))
|
||||
if (force || (statbuf.st_mtime != res->mtime || statbuf.st_ino != res->ino))
|
||||
{
|
||||
res->mtime = statbuf.st_mtime;
|
||||
res->ino = statbuf.st_ino;
|
||||
if (difftime(statbuf.st_mtime, last_change) > 0.0)
|
||||
{
|
||||
last_change = statbuf.st_mtime;
|
||||
@@ -2050,9 +2056,6 @@ static void check_dns_listeners(time_t now)
|
||||
FTL_TCP_worker_terminating(true);
|
||||
/**********************************************/
|
||||
|
||||
shutdown(confd, SHUT_RDWR);
|
||||
close(confd);
|
||||
|
||||
if (buff)
|
||||
free(buff);
|
||||
|
||||
@@ -2228,10 +2231,11 @@ int delay_dhcp(time_t start, int sec, int fd, uint32_t addr, unsigned short id)
|
||||
#endif /* HAVE_DHCP */
|
||||
|
||||
/******************************** Pi-hole modification ********************************/
|
||||
void print_dnsmasq_version(void)
|
||||
void print_dnsmasq_version(const char *yellow, const char *green, const char *bold, const char *normal)
|
||||
{
|
||||
printf("****************************** dnsmasq ******************************\n");
|
||||
printf(_("Version: %s\n"), VERSION);
|
||||
printf(_("Compile options: %s\n\n"), compile_opts);
|
||||
printf("****************************** %s%sdnsmasq%s ******************************\n",
|
||||
bold, yellow, normal);
|
||||
printf(_("Version: %s%s%s%s\n"), bold, green, VERSION, normal);
|
||||
printf(_("Features: %s\n\n"), compile_opts);
|
||||
}
|
||||
/**************************************************************************************/
|
||||
|
||||
+50
-18
@@ -14,13 +14,6 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Pi-hole definitions */
|
||||
#define HAVE_DNSSEC
|
||||
#define HAVE_DNSSEC_STATIC
|
||||
#define HAVE_IDN
|
||||
#define HAVE_LUASCRIPT
|
||||
/***********************/
|
||||
|
||||
#define COPYRIGHT "Copyright (c) 2000-2022 Simon Kelley"
|
||||
|
||||
/* We do defines that influence behavior of stdio.h, so complain
|
||||
@@ -140,6 +133,7 @@ typedef unsigned long long u64;
|
||||
#include <sys/uio.h>
|
||||
#include <syslog.h>
|
||||
#include <dirent.h>
|
||||
#include <netdb.h>
|
||||
#ifndef HAVE_LINUX_NETWORK
|
||||
# include <net/if_dl.h>
|
||||
#endif
|
||||
@@ -286,7 +280,8 @@ struct event_desc {
|
||||
#define OPT_FILTER_AAAA 68
|
||||
#define OPT_STRIP_ECS 69
|
||||
#define OPT_STRIP_MAC 70
|
||||
#define OPT_LAST 71
|
||||
#define OPT_NORR 71
|
||||
#define OPT_LAST 72
|
||||
|
||||
#define OPTION_BITS (sizeof(unsigned int)*8)
|
||||
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
|
||||
@@ -520,6 +515,7 @@ struct crec {
|
||||
#define F_DOMAINSRV (1u<<28)
|
||||
#define F_RCODE (1u<<29)
|
||||
#define F_SRV (1u<<30)
|
||||
#define F_STALE (1u<<31)
|
||||
|
||||
#define UID_NONE 0
|
||||
/* Values of uid in crecs with F_CONFIG bit set. */
|
||||
@@ -593,7 +589,8 @@ struct server {
|
||||
struct serverfd *sfd;
|
||||
int tcpfd, edns_pktsz;
|
||||
time_t pktsz_reduced;
|
||||
unsigned int queries, failed_queries;
|
||||
unsigned int queries, failed_queries, nxdomain_replies, retrys;
|
||||
unsigned int query_latency, mma_latency;
|
||||
time_t forwardtime;
|
||||
int forwardcount;
|
||||
#ifdef HAVE_LOOP
|
||||
@@ -677,6 +674,7 @@ struct resolvc {
|
||||
struct resolvc *next;
|
||||
int is_default, logged;
|
||||
time_t mtime;
|
||||
ino_t ino;
|
||||
char *name;
|
||||
#ifdef HAVE_INOTIFY
|
||||
int wd; /* inotify watch descriptor */
|
||||
@@ -695,10 +693,17 @@ struct hostsfile {
|
||||
struct hostsfile *next;
|
||||
int flags;
|
||||
char *fname;
|
||||
unsigned int index; /* matches to cache entries for logging */
|
||||
};
|
||||
|
||||
struct dyndir {
|
||||
struct dyndir *next;
|
||||
struct hostsfile *files;
|
||||
int flags;
|
||||
char *dname;
|
||||
#ifdef HAVE_INOTIFY
|
||||
int wd; /* inotify watch descriptor */
|
||||
#endif
|
||||
unsigned int index; /* matches to cache entries for logging */
|
||||
};
|
||||
|
||||
/* packet-dump flags */
|
||||
@@ -766,11 +771,13 @@ struct frec {
|
||||
unsigned short new_id;
|
||||
int forwardall, flags;
|
||||
time_t time;
|
||||
u32 forward_timestamp;
|
||||
int forward_delay;
|
||||
unsigned char *hash[HASH_SIZE];
|
||||
#ifdef HAVE_DNSSEC
|
||||
int class, work_counter;
|
||||
struct blockdata *stash; /* Saved reply, whilst we validate */
|
||||
size_t stash_len;
|
||||
#ifdef HAVE_DNSSEC
|
||||
int class, work_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. */
|
||||
@@ -987,6 +994,8 @@ struct dhcp_bridge {
|
||||
|
||||
struct cond_domain {
|
||||
char *domain, *prefix; /* prefix is text-prefix on domain name */
|
||||
char *interface; /* These two set when domain comes from interface. */
|
||||
struct addrlist *al;
|
||||
struct in_addr start, end;
|
||||
struct in6_addr start6, end6;
|
||||
int is6, indexed, prefixlen;
|
||||
@@ -1094,6 +1103,7 @@ struct dhcp_relay {
|
||||
union all_addr local, server;
|
||||
char *interface; /* Allowable interface for replies from server, and dest for IPv6 multicast */
|
||||
int iface_index; /* working - interface in which requests arrived, for return */
|
||||
int port; /* Port of relay we forward to. */
|
||||
#ifdef HAVE_SCRIPT
|
||||
struct snoop_record {
|
||||
struct in6_addr client, prefix;
|
||||
@@ -1147,6 +1157,7 @@ extern struct daemon {
|
||||
int log_fac; /* log facility */
|
||||
char *log_file; /* optional log file */
|
||||
int max_logs; /* queue limit */
|
||||
int randport_limit; /* Maximum number of source ports for query. */
|
||||
int cachesize, ftabsize;
|
||||
int port, query_port, min_port, max_port;
|
||||
unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl, max_cache_ttl, auth_ttl, dhcp_ttl, use_dhcp_ttl;
|
||||
@@ -1154,6 +1165,7 @@ extern struct daemon {
|
||||
u32 umbrella_org;
|
||||
u32 umbrella_asset;
|
||||
u8 umbrella_device[8];
|
||||
int host_index;
|
||||
struct hostsfile *addn_hosts;
|
||||
struct dhcp_context *dhcp, *dhcp6;
|
||||
struct ra_interface *ra_interfaces;
|
||||
@@ -1174,7 +1186,8 @@ extern struct daemon {
|
||||
int doing_ra, doing_dhcp6;
|
||||
struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names;
|
||||
struct dhcp_netid_list *force_broadcast, *bootp_dynamic;
|
||||
struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *dynamic_dirs;
|
||||
struct hostsfile *dhcp_hosts_file, *dhcp_opts_file;
|
||||
struct dyndir *dynamic_dirs;
|
||||
int dhcp_max, tftp_max, tftp_mtu;
|
||||
int dhcp_server_port, dhcp_client_port;
|
||||
int start_tftp_port, end_tftp_port;
|
||||
@@ -1191,6 +1204,8 @@ extern struct daemon {
|
||||
int dump_mask;
|
||||
unsigned long soa_sn, soa_refresh, soa_retry, soa_expiry;
|
||||
u32 metrics[__METRIC_MAX];
|
||||
int fast_retry_time, fast_retry_timeout;
|
||||
int cache_max_expiry;
|
||||
#ifdef HAVE_DNSSEC
|
||||
struct ds_config *ds;
|
||||
char *timestamp_file;
|
||||
@@ -1283,6 +1298,14 @@ extern struct daemon {
|
||||
#endif
|
||||
} *daemon;
|
||||
|
||||
struct server_details {
|
||||
union mysockaddr *addr, *source_addr;
|
||||
struct addrinfo *hostinfo, *orig_hostinfo;
|
||||
char *interface, *source, *scope_id, *interface_opt;
|
||||
int serv_port, source_port, addr_type, scope_index, valid;
|
||||
u16 *flags;
|
||||
};
|
||||
|
||||
/* cache.c */
|
||||
void cache_init(void);
|
||||
void next_uid(struct crec *crecp);
|
||||
@@ -1300,6 +1323,7 @@ struct crec *cache_find_by_name(struct crec *crecp,
|
||||
char *name, time_t now, unsigned int prot);
|
||||
void cache_end_insert(void);
|
||||
void cache_start_insert(void);
|
||||
unsigned int cache_remove_uid(const unsigned int uid);
|
||||
int cache_recv_insert(time_t now, int fd);
|
||||
struct crec *cache_insert(char *name, union all_addr *addr, unsigned short class,
|
||||
time_t now, unsigned long ttl, unsigned int flags);
|
||||
@@ -1349,7 +1373,8 @@ void report_addresses(struct dns_header *header, size_t len, u32 mark);
|
||||
#endif
|
||||
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
struct in_addr local_addr, struct in_addr local_netmask,
|
||||
time_t now, int ad_reqd, int do_bit, int have_pseudoheader);
|
||||
time_t now, int ad_reqd, int do_bit, int have_pseudoheader,
|
||||
int *stale);
|
||||
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
|
||||
time_t now);
|
||||
int check_for_ignored_address(struct dns_header *header, size_t qlen);
|
||||
@@ -1408,12 +1433,15 @@ void *safe_malloc(size_t size);
|
||||
void safe_strncpy(char *dest, const char *src, size_t size);
|
||||
void safe_pipe(int *fd, int read_noblock);
|
||||
void *whine_malloc(size_t size);
|
||||
void *whine_realloc(void *ptr, size_t size);
|
||||
int sa_len(union mysockaddr *addr);
|
||||
int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2);
|
||||
int sockaddr_isnull(const union mysockaddr *s);
|
||||
int hostname_order(const char *a, const char *b);
|
||||
int hostname_isequal(const char *a, const char *b);
|
||||
int hostname_issubdomain(char *a, char *b);
|
||||
time_t dnsmasq_time(void);
|
||||
u32 dnsmasq_milliseconds(void);
|
||||
int netmask_length(struct in_addr mask);
|
||||
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
|
||||
int is_same_net_prefix(struct in_addr a, struct in_addr b, int prefix);
|
||||
@@ -1457,8 +1485,9 @@ void read_servers_file(void);
|
||||
void set_option_bool(unsigned int opt);
|
||||
void reset_option_bool(unsigned int opt);
|
||||
struct hostsfile *expand_filelist(struct hostsfile *list);
|
||||
char *parse_server(char *arg, union mysockaddr *addr,
|
||||
union mysockaddr *source_addr, char *interface, u16 *flags);
|
||||
char *parse_server(char *arg, struct server_details *sdetails);
|
||||
char *parse_server_addr(struct server_details *sdetails);
|
||||
int parse_server_next(struct server_details *sdetails);
|
||||
int option_read_dynfile(char *file, int flags);
|
||||
|
||||
/* forward.c */
|
||||
@@ -1473,6 +1502,7 @@ int send_from(int fd, int nowild, char *packet, size_t len,
|
||||
void resend_query(void);
|
||||
int allocate_rfd(struct randfd_list **fdlp, struct server *serv);
|
||||
void free_rfds(struct randfd_list **fdlp);
|
||||
int fast_retry(time_t now);
|
||||
|
||||
/* network.c */
|
||||
int indextoname(int fd, int index, char *name);
|
||||
@@ -1825,8 +1855,10 @@ int do_arp_script_run(void);
|
||||
/* dump.c */
|
||||
#ifdef HAVE_DUMPFILE
|
||||
void dump_init(void);
|
||||
void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src,
|
||||
union mysockaddr *dst, int port);
|
||||
void dump_packet_udp(int mask, void *packet, size_t len, union mysockaddr *src,
|
||||
union mysockaddr *dst, int fd);
|
||||
void dump_packet_icmp(int mask, void *packet, size_t len, union mysockaddr *src,
|
||||
union mysockaddr *dst);
|
||||
#endif
|
||||
|
||||
/* domain-match.c */
|
||||
|
||||
+38
-23
@@ -979,10 +979,13 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
}
|
||||
|
||||
/* The DNS packet is expected to contain the answer to a DS query
|
||||
Put all DSs in the answer which are valid into the cache.
|
||||
Put all DSs in the answer which are valid and have hash and signature algos
|
||||
we support into the cache.
|
||||
Also handles replies which prove that there's no DS at this location,
|
||||
either because the zone is unsigned or this isn't a zone cut. These are
|
||||
cached too.
|
||||
If none of the DS's are for supported algos, treat the answer as if
|
||||
it's a proof of no DS at this location. RFC4035 para 5.2.
|
||||
return codes:
|
||||
STAT_OK At least one valid DS found and in cache.
|
||||
STAT_BOGUS no DS in reply or not signed, fails validation, bad packet.
|
||||
@@ -993,8 +996,8 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)(header+1);
|
||||
int qtype, qclass, rc, i, neganswer, nons, neg_ttl = 0;
|
||||
int aclass, atype, rdlen;
|
||||
int qtype, qclass, rc, i, neganswer, nons, neg_ttl = 0, found_supported = 0;
|
||||
int aclass, atype, rdlen, flags;
|
||||
unsigned long ttl;
|
||||
union all_addr a;
|
||||
|
||||
@@ -1065,14 +1068,22 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
algo = *p++;
|
||||
digest = *p++;
|
||||
|
||||
if ((key = blockdata_alloc((char*)p, rdlen - 4)))
|
||||
if (!ds_digest_name(digest) || !algo_digest_name(algo))
|
||||
{
|
||||
a.log.keytag = keytag;
|
||||
a.log.algo = algo;
|
||||
a.log.digest = digest;
|
||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu (not supported)", 0);
|
||||
neg_ttl = ttl;
|
||||
}
|
||||
else if ((key = blockdata_alloc((char*)p, rdlen - 4)))
|
||||
{
|
||||
a.ds.digest = digest;
|
||||
a.ds.keydata = key;
|
||||
a.ds.algo = algo;
|
||||
a.ds.keytag = keytag;
|
||||
a.ds.keylen = rdlen - 4;
|
||||
|
||||
|
||||
if (!cache_insert(name, &a, class, now, ttl, F_FORWARD | F_DS | F_DNSSECOK))
|
||||
{
|
||||
blockdata_free(key);
|
||||
@@ -1083,26 +1094,29 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
a.log.keytag = keytag;
|
||||
a.log.algo = algo;
|
||||
a.log.digest = digest;
|
||||
if (ds_digest_name(digest) && algo_digest_name(algo))
|
||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu", 0);
|
||||
else
|
||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu (not supported)", 0);
|
||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu", 0);
|
||||
found_supported = 1;
|
||||
}
|
||||
}
|
||||
|
||||
p = psave;
|
||||
}
|
||||
|
||||
if (!ADD_RDLEN(header, p, plen, rdlen))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
}
|
||||
|
||||
cache_end_insert();
|
||||
|
||||
/* Fall through if no supported algo DS found. */
|
||||
if (found_supported)
|
||||
return STAT_OK;
|
||||
}
|
||||
else
|
||||
|
||||
flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
|
||||
|
||||
if (neganswer)
|
||||
{
|
||||
int flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
|
||||
|
||||
if (RCODE(header) == NXDOMAIN)
|
||||
flags |= F_NXDOMAIN;
|
||||
|
||||
@@ -1110,17 +1124,18 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
to store presence/absence of NS. */
|
||||
if (nons)
|
||||
flags &= ~F_DNSSECOK;
|
||||
|
||||
cache_start_insert();
|
||||
|
||||
/* Use TTL from NSEC for negative cache entries */
|
||||
if (!cache_insert(name, NULL, class, now, neg_ttl, flags))
|
||||
return STAT_BOGUS;
|
||||
|
||||
cache_end_insert();
|
||||
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no DS/cut" : "no DS", 0);
|
||||
}
|
||||
|
||||
cache_start_insert();
|
||||
|
||||
/* Use TTL from NSEC for negative cache entries */
|
||||
if (!cache_insert(name, NULL, class, now, neg_ttl, flags))
|
||||
return STAT_BOGUS;
|
||||
|
||||
cache_end_insert();
|
||||
|
||||
if (neganswer)
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no DS/cut" : "no DS", 0);
|
||||
|
||||
return STAT_OK;
|
||||
}
|
||||
@@ -1851,7 +1866,7 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
|
||||
STAT_NEED_DS need DS to complete validation (name is returned in keyname)
|
||||
|
||||
daemon->rr_status points to a char array which corressponds to the RRs in the
|
||||
answer and auth sections. This is set to 1 for each RR which is validated, and 0 for any which aren't.
|
||||
answer and auth sections. This is set to >1 for each RR which is validated, and 0 for any which aren't.
|
||||
|
||||
When validating replies to DS records, we're only interested in the NSEC{3} RRs in the auth section.
|
||||
Other RRs in that section missing sigs will not cause am INSECURE reply. We determine this mode
|
||||
|
||||
+49
-26
@@ -213,9 +213,13 @@ int lookup_domain(char *domain, int flags, int *lowout, int *highout)
|
||||
to continue generalising */
|
||||
{
|
||||
/* We've matched a setting which says to use servers without a domain.
|
||||
Continue the search with empty query */
|
||||
Continue the search with empty query. We set the F_SERVER flag
|
||||
so that --address=/#/... doesn't match. */
|
||||
if (daemon->serverarray[nlow]->flags & SERV_USE_RESOLV)
|
||||
crop_query = qlen;
|
||||
{
|
||||
crop_query = qlen;
|
||||
flags |= F_SERVER;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
@@ -299,7 +303,7 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
|
||||
|
||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_6ADDR); i++);
|
||||
|
||||
if (i != nlow && (flags & F_IPV6))
|
||||
if (!(flags & F_SERVER) && i != nlow && (flags & F_IPV6))
|
||||
nhigh = i;
|
||||
else
|
||||
{
|
||||
@@ -307,7 +311,7 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
|
||||
|
||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_4ADDR); i++);
|
||||
|
||||
if (i != nlow && (flags & F_IPV4))
|
||||
if (!(flags & F_SERVER) && i != nlow && (flags & F_IPV4))
|
||||
nhigh = i;
|
||||
else
|
||||
{
|
||||
@@ -315,7 +319,7 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
|
||||
|
||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_ALL_ZEROS); i++);
|
||||
|
||||
if (i != nlow && (flags & (F_IPV4 | F_IPV6)))
|
||||
if (!(flags & F_SERVER) && i != nlow && (flags & (F_IPV4 | F_IPV6)))
|
||||
nhigh = i;
|
||||
else
|
||||
{
|
||||
@@ -542,11 +546,23 @@ static int order_qsort(const void *a, const void *b)
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* When loading large numbers of server=.... lines during startup,
|
||||
there's no possibility that there will be server records that can be reused, but
|
||||
searching a long list for each server added grows as O(n^2) and slows things down.
|
||||
This flag is set only if is known there may be free server records that can be reused.
|
||||
There's a call to mark_servers(0) in read_opts() to reset the flag before
|
||||
main config read. */
|
||||
|
||||
static int maybe_free_servers = 0;
|
||||
|
||||
/* Must be called before add_update_server() to set daemon->servers_tail */
|
||||
void mark_servers(int flag)
|
||||
{
|
||||
struct server *serv, **up;
|
||||
struct server *serv, *next, **up;
|
||||
|
||||
maybe_free_servers = !!flag;
|
||||
|
||||
daemon->servers_tail = NULL;
|
||||
|
||||
/* mark everything with argument flag */
|
||||
@@ -564,11 +580,13 @@ void mark_servers(int flag)
|
||||
1) numerous and 2) not reloaded often. We just delete
|
||||
and recreate. */
|
||||
if (flag)
|
||||
for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = serv->next)
|
||||
for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = next)
|
||||
{
|
||||
next = serv->next;
|
||||
|
||||
if (serv->flags & flag)
|
||||
{
|
||||
*up = serv->next;
|
||||
*up = next;
|
||||
free(serv->domain);
|
||||
free(serv);
|
||||
}
|
||||
@@ -663,25 +681,30 @@ int add_update_server(int flags,
|
||||
and move to the end of the list, for order. The entry found may already
|
||||
be at the end. */
|
||||
struct server **up, *tmp;
|
||||
|
||||
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
|
||||
{
|
||||
tmp = serv->next;
|
||||
if ((serv->flags & SERV_MARK) &&
|
||||
hostname_isequal(alloc_domain, serv->domain))
|
||||
{
|
||||
/* Need to move down? */
|
||||
if (serv->next)
|
||||
{
|
||||
*up = serv->next;
|
||||
daemon->servers_tail->next = serv;
|
||||
daemon->servers_tail = serv;
|
||||
serv->next = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
serv = NULL;
|
||||
|
||||
if (maybe_free_servers)
|
||||
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
|
||||
{
|
||||
tmp = serv->next;
|
||||
if ((serv->flags & SERV_MARK) &&
|
||||
hostname_isequal(alloc_domain, serv->domain))
|
||||
{
|
||||
/* Need to move down? */
|
||||
if (serv->next)
|
||||
{
|
||||
*up = serv->next;
|
||||
daemon->servers_tail->next = serv;
|
||||
daemon->servers_tail = serv;
|
||||
serv->next = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
up = &serv->next;
|
||||
}
|
||||
|
||||
if (serv)
|
||||
{
|
||||
free(alloc_domain);
|
||||
|
||||
+24
-7
@@ -230,9 +230,17 @@ int is_rev_synth(int flag, union all_addr *addr, char *name)
|
||||
|
||||
static int match_domain(struct in_addr addr, struct cond_domain *c)
|
||||
{
|
||||
if (!c->is6 &&
|
||||
ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
|
||||
ntohl(addr.s_addr) <= ntohl(c->end.s_addr))
|
||||
if (c->interface)
|
||||
{
|
||||
struct addrlist *al;
|
||||
for (al = c->al; al; al = al->next)
|
||||
if (!(al->flags & ADDRLIST_IPV6) &&
|
||||
is_same_net_prefix(addr, al->addr.addr4, al->prefixlen))
|
||||
return 1;
|
||||
}
|
||||
else if (!c->is6 &&
|
||||
ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
|
||||
ntohl(addr.s_addr) <= ntohl(c->end.s_addr))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
@@ -259,12 +267,21 @@ char *get_domain(struct in_addr addr)
|
||||
|
||||
static int match_domain6(struct in6_addr *addr, struct cond_domain *c)
|
||||
{
|
||||
u64 addrpart = addr6part(addr);
|
||||
|
||||
if (c->is6)
|
||||
|
||||
/* subnet from interface address. */
|
||||
if (c->interface)
|
||||
{
|
||||
struct addrlist *al;
|
||||
for (al = c->al; al; al = al->next)
|
||||
if (al->flags & ADDRLIST_IPV6 &&
|
||||
is_same_net6(addr, &al->addr.addr6, al->prefixlen))
|
||||
return 1;
|
||||
}
|
||||
else if (c->is6)
|
||||
{
|
||||
if (c->prefixlen >= 64)
|
||||
{
|
||||
u64 addrpart = addr6part(addr);
|
||||
if (is_same_net6(addr, &c->start6, 64) &&
|
||||
addrpart >= addr6part(&c->start6) &&
|
||||
addrpart <= addr6part(&c->end6))
|
||||
@@ -273,7 +290,7 @@ static int match_domain6(struct in6_addr *addr, struct cond_domain *c)
|
||||
else if (is_same_net6(addr, &c->start6, c->prefixlen))
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+80
-45
@@ -21,6 +21,8 @@
|
||||
#include <netinet/icmp6.h>
|
||||
|
||||
static u32 packet_count;
|
||||
static void do_dump_packet(int mask, void *packet, size_t len,
|
||||
union mysockaddr *src, union mysockaddr *dst, int port, int proto);
|
||||
|
||||
/* https://wiki.wireshark.org/Development/LibpcapFileFormat */
|
||||
struct pcap_hdr_s {
|
||||
@@ -81,9 +83,45 @@ void dump_init(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* port == -1 ->ICMPv6 */
|
||||
void dump_packet(int mask, void *packet, size_t len,
|
||||
union mysockaddr *src, union mysockaddr *dst, int port)
|
||||
void dump_packet_udp(int mask, void *packet, size_t len,
|
||||
union mysockaddr *src, union mysockaddr *dst, int fd)
|
||||
{
|
||||
union mysockaddr fd_addr;
|
||||
socklen_t addr_len = sizeof(fd_addr);
|
||||
|
||||
if (daemon->dumpfd != -1 && (mask & daemon->dump_mask))
|
||||
{
|
||||
/* if fd is negative it carries a port number (negated)
|
||||
which we use as a source or destination when not otherwise
|
||||
specified so wireshark can ID the packet.
|
||||
If both src and dst are specified, set this to -1 to avoid
|
||||
a spurious getsockname() call. */
|
||||
int port = (fd < 0) ? -fd : -1;
|
||||
|
||||
/* fd >= 0 is a file descriptor and the address of that file descriptor is used
|
||||
in place of a NULL src or dst. */
|
||||
if (fd >= 0 && getsockname(fd, (struct sockaddr *)&fd_addr, &addr_len) != -1)
|
||||
{
|
||||
if (!src)
|
||||
src = &fd_addr;
|
||||
|
||||
if (!dst)
|
||||
dst = &fd_addr;
|
||||
}
|
||||
|
||||
do_dump_packet(mask, packet, len, src, dst, port, IPPROTO_UDP);
|
||||
}
|
||||
}
|
||||
|
||||
void dump_packet_icmp(int mask, void *packet, size_t len,
|
||||
union mysockaddr *src, union mysockaddr *dst)
|
||||
{
|
||||
if (daemon->dumpfd != -1 && (mask & daemon->dump_mask))
|
||||
do_dump_packet(mask, packet, len, src, dst, -1, IPPROTO_ICMP);
|
||||
}
|
||||
|
||||
static void do_dump_packet(int mask, void *packet, size_t len,
|
||||
union mysockaddr *src, union mysockaddr *dst, int port, int proto)
|
||||
{
|
||||
struct ip ip;
|
||||
struct ip6_hdr ip6;
|
||||
@@ -100,13 +138,14 @@ void dump_packet(int mask, void *packet, size_t len,
|
||||
void *iphdr;
|
||||
size_t ipsz;
|
||||
int rc;
|
||||
|
||||
/* if port != -1 it carries a port number
|
||||
which we use as a source or destination when not otherwise
|
||||
specified so wireshark can ID the packet.
|
||||
If both src and dst are specified, set this to -1 to avoid
|
||||
a spurious getsockname() call. */
|
||||
udp.uh_sport = udp.uh_dport = htons(port < 0 ? 0 : port);
|
||||
|
||||
if (daemon->dumpfd == -1 || !(mask & daemon->dump_mask))
|
||||
return;
|
||||
|
||||
/* So wireshark can Id the packet. */
|
||||
udp.uh_sport = udp.uh_dport = htons(port);
|
||||
|
||||
if (src)
|
||||
family = src->sa.sa_family;
|
||||
else
|
||||
@@ -121,15 +160,12 @@ void dump_packet(int mask, void *packet, size_t len,
|
||||
ip6.ip6_vfc = 6 << 4;
|
||||
ip6.ip6_hops = 64;
|
||||
|
||||
if (port == -1)
|
||||
{
|
||||
ip6.ip6_plen = htons(len);
|
||||
ip6.ip6_nxt = IPPROTO_ICMPV6;
|
||||
}
|
||||
if ((ip6.ip6_nxt = proto) == IPPROTO_UDP)
|
||||
ip6.ip6_plen = htons(sizeof(struct udphdr) + len);
|
||||
else
|
||||
{
|
||||
ip6.ip6_plen = htons(sizeof(struct udphdr) + len);
|
||||
ip6.ip6_nxt = IPPROTO_UDP;
|
||||
proto = ip6.ip6_nxt = IPPROTO_ICMPV6;
|
||||
ip6.ip6_plen = htons(len);
|
||||
}
|
||||
|
||||
if (src)
|
||||
@@ -161,15 +197,12 @@ void dump_packet(int mask, void *packet, size_t len,
|
||||
ip.ip_hl = sizeof(struct ip) / 4;
|
||||
ip.ip_ttl = IPDEFTTL;
|
||||
|
||||
if (port == -1)
|
||||
{
|
||||
ip.ip_len = htons(sizeof(struct ip) + len);
|
||||
ip.ip_p = IPPROTO_ICMP;
|
||||
}
|
||||
if ((ip.ip_p = proto) == IPPROTO_UDP)
|
||||
ip.ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + len);
|
||||
else
|
||||
{
|
||||
ip.ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + len);
|
||||
ip.ip_p = IPPROTO_UDP;
|
||||
ip.ip_len = htons(sizeof(struct ip) + len);
|
||||
proto = ip.ip_p = IPPROTO_ICMP;
|
||||
}
|
||||
|
||||
if (src)
|
||||
@@ -191,7 +224,7 @@ void dump_packet(int mask, void *packet, size_t len,
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
|
||||
|
||||
/* start UDP checksum */
|
||||
/* start UDP/ICMP checksum */
|
||||
sum = ip.ip_src.s_addr & 0xffff;
|
||||
sum += (ip.ip_src.s_addr >> 16) & 0xffff;
|
||||
sum += ip.ip_dst.s_addr & 0xffff;
|
||||
@@ -201,25 +234,7 @@ void dump_packet(int mask, void *packet, size_t len,
|
||||
if (len & 1)
|
||||
((unsigned char *)packet)[len] = 0; /* for checksum, in case length is odd. */
|
||||
|
||||
if (port == -1)
|
||||
{
|
||||
/* ICMP - ICMPv6 packet is a superset of ICMP */
|
||||
struct icmp6_hdr *icmp = packet;
|
||||
|
||||
/* See comment in UDP code below. */
|
||||
sum += htons((family == AF_INET6) ? IPPROTO_ICMPV6 : IPPROTO_ICMP);
|
||||
sum += htons(len);
|
||||
|
||||
icmp->icmp6_cksum = 0;
|
||||
for (i = 0; i < (len + 1) / 2; i++)
|
||||
sum += ((u16 *)packet)[i];
|
||||
while (sum >> 16)
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
icmp->icmp6_cksum = (sum == 0xffff) ? sum : ~sum;
|
||||
|
||||
pcap_header.incl_len = pcap_header.orig_len = ipsz + len;
|
||||
}
|
||||
else
|
||||
if (proto == IPPROTO_UDP)
|
||||
{
|
||||
/* Add Remaining part of the pseudoheader. Note that though the
|
||||
IPv6 pseudoheader is very different to the IPv4 one, the
|
||||
@@ -241,7 +256,25 @@ void dump_packet(int mask, void *packet, size_t len,
|
||||
|
||||
pcap_header.incl_len = pcap_header.orig_len = ipsz + sizeof(udp) + len;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
/* ICMP - ICMPv6 packet is a superset of ICMP */
|
||||
struct icmp6_hdr *icmp = packet;
|
||||
|
||||
/* See comment in UDP code above. */
|
||||
sum += htons(proto);
|
||||
sum += htons(len);
|
||||
|
||||
icmp->icmp6_cksum = 0;
|
||||
for (i = 0; i < (len + 1) / 2; i++)
|
||||
sum += ((u16 *)packet)[i];
|
||||
while (sum >> 16)
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
icmp->icmp6_cksum = (sum == 0xffff) ? sum : ~sum;
|
||||
|
||||
pcap_header.incl_len = pcap_header.orig_len = ipsz + len;
|
||||
}
|
||||
|
||||
rc = gettimeofday(&time, NULL);
|
||||
pcap_header.ts_sec = time.tv_sec;
|
||||
pcap_header.ts_usec = time.tv_usec;
|
||||
@@ -249,9 +282,11 @@ void dump_packet(int mask, void *packet, size_t len,
|
||||
if (rc == -1 ||
|
||||
!read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), 0) ||
|
||||
!read_write(daemon->dumpfd, iphdr, ipsz, 0) ||
|
||||
(port != -1 && !read_write(daemon->dumpfd, (void *)&udp, sizeof(udp), 0)) ||
|
||||
(proto == IPPROTO_UDP && !read_write(daemon->dumpfd, (void *)&udp, sizeof(udp), 0)) ||
|
||||
!read_write(daemon->dumpfd, (void *)packet, len, 0))
|
||||
my_syslog(LOG_ERR, _("failed to write packet dump"));
|
||||
else if (option_bool(OPT_EXTRALOG))
|
||||
my_syslog(LOG_INFO, _("%u dumping packet %u mask 0x%04x"), daemon->log_display_id, ++packet_count, mask);
|
||||
else
|
||||
my_syslog(LOG_INFO, _("dumping packet %u mask 0x%04x"), ++packet_count, mask);
|
||||
|
||||
|
||||
+489
-159
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,7 @@
|
||||
|
||||
The hash used is SHA-256. If we're building with DNSSEC support,
|
||||
we use the Nettle cypto library. If not, we prefer not to
|
||||
add a dependency on Nettle, and use a stand-alone implementaion.
|
||||
add a dependency on Nettle, and use a stand-alone implementation.
|
||||
*/
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
+12
-3
@@ -430,6 +430,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
|
||||
end = extradata + data.ed_len;
|
||||
buf = extradata;
|
||||
|
||||
lua_pushnumber(lua, data.ed_len == 0 ? 1 : 0);
|
||||
lua_setfield(lua, -2, "data_missing");
|
||||
|
||||
if (!is6)
|
||||
buf = grab_extradata_lua(buf, end, "vendor_class");
|
||||
@@ -457,7 +460,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
buf = grab_extradata_lua(buf, end, "subscriber_id");
|
||||
buf = grab_extradata_lua(buf, end, "remote_id");
|
||||
}
|
||||
|
||||
|
||||
buf = grab_extradata_lua(buf, end, "requested_options");
|
||||
buf = grab_extradata_lua(buf, end, "mud_url");
|
||||
buf = grab_extradata_lua(buf, end, "tags");
|
||||
|
||||
if (is6)
|
||||
@@ -608,6 +613,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
|
||||
end = extradata + data.ed_len;
|
||||
buf = extradata;
|
||||
|
||||
if (data.ed_len == 0)
|
||||
my_setenv("DNSMASQ_DATA_MISSING", "1", &err);
|
||||
|
||||
if (!is6)
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
|
||||
@@ -636,11 +644,12 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_CIRCUIT_ID", &err);
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_SUBSCRIBER_ID", &err);
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_REMOTE_ID", &err);
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_REQUESTED_OPTIONS", &err);
|
||||
}
|
||||
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_REQUESTED_OPTIONS", &err);
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_MUD_URL", &err);
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
|
||||
|
||||
|
||||
if (is6)
|
||||
buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
|
||||
else
|
||||
|
||||
+92
-45
@@ -133,81 +133,112 @@ void inotify_dnsmasq_init()
|
||||
}
|
||||
}
|
||||
|
||||
static struct hostsfile *dyndir_addhosts(struct dyndir *dd, char *path)
|
||||
{
|
||||
/* Check if this file is already known in dd->files */
|
||||
struct hostsfile *ah = NULL;
|
||||
for(ah = dd->files; ah; ah = ah->next)
|
||||
if(ah && ah->fname && strcmp(path, ah->fname) == 0)
|
||||
return ah;
|
||||
|
||||
/* Not known, create new hostsfile record for this dyndir */
|
||||
struct hostsfile *newah = NULL;
|
||||
if(!(newah = whine_malloc(sizeof(struct hostsfile))))
|
||||
return NULL;
|
||||
|
||||
/* Add this file to the tip of the linked list */
|
||||
newah->next = dd->files;
|
||||
dd->files = newah;
|
||||
|
||||
/* Copy flags, set index and the full file path */
|
||||
newah->flags = dd->flags;
|
||||
newah->index = daemon->host_index++;
|
||||
newah->fname = path;
|
||||
|
||||
return newah;
|
||||
}
|
||||
|
||||
|
||||
/* initialisation for dynamic-dir. Set inotify watch for each directory, and read pre-existing files */
|
||||
void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz)
|
||||
{
|
||||
struct hostsfile *ah;
|
||||
|
||||
for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
|
||||
struct dyndir *dd;
|
||||
|
||||
for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
|
||||
{
|
||||
DIR *dir_stream = NULL;
|
||||
struct dirent *ent;
|
||||
struct stat buf;
|
||||
|
||||
if (!(ah->flags & flag))
|
||||
|
||||
if (!(dd->flags & flag))
|
||||
continue;
|
||||
|
||||
if (stat(ah->fname, &buf) == -1)
|
||||
|
||||
if (stat(dd->dname, &buf) == -1)
|
||||
{
|
||||
my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
|
||||
ah->fname, strerror(errno));
|
||||
dd->dname, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(S_ISDIR(buf.st_mode)))
|
||||
{
|
||||
my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
|
||||
ah->fname, _("not a directory"));
|
||||
dd->dname, _("not a directory"));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(ah->flags & AH_WD_DONE))
|
||||
|
||||
if (!(dd->flags & AH_WD_DONE))
|
||||
{
|
||||
ah->wd = inotify_add_watch(daemon->inotifyfd, ah->fname, IN_CLOSE_WRITE | IN_MOVED_TO);
|
||||
ah->flags |= AH_WD_DONE;
|
||||
dd->wd = inotify_add_watch(daemon->inotifyfd, dd->dname, IN_CLOSE_WRITE | IN_MOVED_TO | IN_DELETE);
|
||||
dd->flags |= AH_WD_DONE;
|
||||
}
|
||||
|
||||
/* Read contents of dir _after_ calling add_watch, in the hope of avoiding
|
||||
a race which misses files being added as we start */
|
||||
if (ah->wd == -1 || !(dir_stream = opendir(ah->fname)))
|
||||
if (dd->wd == -1 || !(dir_stream = opendir(dd->dname)))
|
||||
{
|
||||
my_syslog(LOG_ERR, _("failed to create inotify for %s: %s"),
|
||||
ah->fname, strerror(errno));
|
||||
dd->dname, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
while ((ent = readdir(dir_stream)))
|
||||
{
|
||||
size_t lendir = strlen(ah->fname);
|
||||
size_t lendir = strlen(dd->dname);
|
||||
size_t lenfile = strlen(ent->d_name);
|
||||
char *path;
|
||||
|
||||
|
||||
/* ignore emacs backups and dotfiles */
|
||||
if (lenfile == 0 ||
|
||||
ent->d_name[lenfile - 1] == '~' ||
|
||||
(ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
|
||||
ent->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
|
||||
if ((path = whine_malloc(lendir + lenfile + 2)))
|
||||
{
|
||||
strcpy(path, ah->fname);
|
||||
struct hostsfile *ah;
|
||||
|
||||
strcpy(path, dd->dname);
|
||||
strcat(path, "/");
|
||||
strcat(path, ent->d_name);
|
||||
|
||||
if (!(ah = dyndir_addhosts(dd, path)))
|
||||
{
|
||||
free(path);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ignore non-regular files */
|
||||
if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
|
||||
{
|
||||
if (ah->flags & AH_HOSTS)
|
||||
if (dd->flags & AH_HOSTS)
|
||||
total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz);
|
||||
#ifdef HAVE_DHCP
|
||||
else if (ah->flags & (AH_DHCP_HST | AH_DHCP_OPT))
|
||||
option_read_dynfile(path, ah->flags);
|
||||
else if (dd->flags & (AH_DHCP_HST | AH_DHCP_OPT))
|
||||
option_read_dynfile(path, dd->flags);
|
||||
#endif
|
||||
}
|
||||
|
||||
free(path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +249,7 @@ void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revh
|
||||
int inotify_check(time_t now)
|
||||
{
|
||||
int hit = 0;
|
||||
struct hostsfile *ah;
|
||||
struct dyndir *dd;
|
||||
|
||||
while (1)
|
||||
{
|
||||
@@ -249,36 +280,51 @@ int inotify_check(time_t now)
|
||||
if (res->wd == in->wd && strcmp(res->file, in->name) == 0)
|
||||
hit = 1;
|
||||
|
||||
for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
|
||||
if (ah->wd == in->wd)
|
||||
for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
|
||||
if (dd->wd == in->wd)
|
||||
{
|
||||
size_t lendir = strlen(ah->fname);
|
||||
size_t lendir = strlen(dd->dname);
|
||||
char *path;
|
||||
|
||||
|
||||
if ((path = whine_malloc(lendir + in->len + 2)))
|
||||
{
|
||||
strcpy(path, ah->fname);
|
||||
struct hostsfile *ah = NULL;
|
||||
|
||||
strcpy(path, dd->dname);
|
||||
strcat(path, "/");
|
||||
strcat(path, in->name);
|
||||
|
||||
my_syslog(LOG_INFO, _("inotify, new or changed file %s"), path);
|
||||
|
||||
if (ah->flags & AH_HOSTS)
|
||||
/* Is this is a deletion event? */
|
||||
if (in->mask & IN_DELETE)
|
||||
my_syslog(LOG_INFO, _("inotify: %s removed"), path);
|
||||
else
|
||||
my_syslog(LOG_INFO, _("inotify: %s new or modified"), path);
|
||||
|
||||
if (dd->flags & AH_HOSTS)
|
||||
{
|
||||
read_hostsfile(path, ah->index, 0, NULL, 0);
|
||||
#ifdef HAVE_DHCP
|
||||
if (daemon->dhcp || daemon->doing_dhcp6)
|
||||
if ((ah = dyndir_addhosts(dd, path)))
|
||||
{
|
||||
/* Propagate the consequences of loading a new dhcp-host */
|
||||
dhcp_update_configs(daemon->dhcp_conf);
|
||||
lease_update_from_configs();
|
||||
lease_update_file(now);
|
||||
lease_update_dns(1);
|
||||
}
|
||||
const unsigned int removed = cache_remove_uid(ah->index);
|
||||
if (removed > 0)
|
||||
my_syslog(LOG_INFO, _("inotify: flushed %u names read from %s"), removed, path);
|
||||
|
||||
/* (Re-)load hostsfile only if this event isn't triggered by deletion */
|
||||
if (!(in->mask & IN_DELETE))
|
||||
read_hostsfile(path, ah->index, 0, NULL, 0);
|
||||
#ifdef HAVE_DHCP
|
||||
if (daemon->dhcp || daemon->doing_dhcp6)
|
||||
{
|
||||
/* Propagate the consequences of loading a new dhcp-host */
|
||||
dhcp_update_configs(daemon->dhcp_conf);
|
||||
lease_update_from_configs();
|
||||
lease_update_file(now);
|
||||
lease_update_dns(1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#ifdef HAVE_DHCP
|
||||
else if (ah->flags & AH_DHCP_HST)
|
||||
else if (dd->flags & AH_DHCP_HST)
|
||||
{
|
||||
if (option_read_dynfile(path, AH_DHCP_HST))
|
||||
{
|
||||
@@ -289,11 +335,12 @@ int inotify_check(time_t now)
|
||||
lease_update_dns(1);
|
||||
}
|
||||
}
|
||||
else if (ah->flags & AH_DHCP_OPT)
|
||||
else if (dd->flags & AH_DHCP_OPT)
|
||||
option_read_dynfile(path, AH_DHCP_OPT);
|
||||
#endif
|
||||
|
||||
free(path);
|
||||
if (!ah)
|
||||
free(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-7
@@ -1180,17 +1180,11 @@ void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data, unsigned
|
||||
if ((lease->extradata_size - lease->extradata_len) < (len + 1))
|
||||
{
|
||||
size_t newsz = lease->extradata_len + len + 100;
|
||||
unsigned char *new = whine_malloc(newsz);
|
||||
unsigned char *new = whine_realloc(lease->extradata, newsz);
|
||||
|
||||
if (!new)
|
||||
return;
|
||||
|
||||
if (lease->extradata)
|
||||
{
|
||||
memcpy(new, lease->extradata, lease->extradata_len);
|
||||
free(lease->extradata);
|
||||
}
|
||||
|
||||
lease->extradata = new;
|
||||
lease->extradata_size = newsz;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ const char * metric_names[] = {
|
||||
"dns_queries_forwarded",
|
||||
"dns_auth_answered",
|
||||
"dns_local_answered",
|
||||
"dns_stale_answered",
|
||||
"dns_unanswered",
|
||||
"bootp",
|
||||
"pxe",
|
||||
"dhcp_ack",
|
||||
@@ -42,3 +44,23 @@ const char * metric_names[] = {
|
||||
const char* get_metric_name(int i) {
|
||||
return metric_names[i];
|
||||
}
|
||||
|
||||
void clear_metrics(void)
|
||||
{
|
||||
int i;
|
||||
struct server *serv;
|
||||
|
||||
for (i = 0; i < __METRIC_MAX; i++)
|
||||
daemon->metrics[i] = 0;
|
||||
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
{
|
||||
serv->queries = 0;
|
||||
serv->failed_queries = 0;
|
||||
serv->failed_queries = 0;
|
||||
serv->retrys = 0;
|
||||
serv->nxdomain_replies = 0;
|
||||
serv->query_latency = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ enum {
|
||||
METRIC_DNS_QUERIES_FORWARDED,
|
||||
METRIC_DNS_AUTH_ANSWERED,
|
||||
METRIC_DNS_LOCAL_ANSWERED,
|
||||
METRIC_DNS_STALE_ANSWERED,
|
||||
METRIC_DNS_UNANSWERED_QUERY,
|
||||
METRIC_BOOTP,
|
||||
METRIC_PXE,
|
||||
METRIC_DHCPACK,
|
||||
@@ -41,3 +43,4 @@ enum {
|
||||
};
|
||||
|
||||
const char* get_metric_name(int);
|
||||
void clear_metrics(void);
|
||||
|
||||
+10
-1
@@ -258,7 +258,16 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
|
||||
while (RTA_OK(rta, len1))
|
||||
{
|
||||
if (rta->rta_type == IFA_ADDRESS)
|
||||
/*
|
||||
* Important comment: (from if_addr.h)
|
||||
* IFA_ADDRESS is prefix address, rather than local interface address.
|
||||
* It makes no difference for normally configured broadcast interfaces,
|
||||
* but for point-to-point IFA_ADDRESS is DESTINATION address,
|
||||
* local address is supplied in IFA_LOCAL attribute.
|
||||
*/
|
||||
if (rta->rta_type == IFA_LOCAL)
|
||||
addrp = ((struct in6_addr *)(rta+1));
|
||||
else if (rta->rta_type == IFA_ADDRESS && !addrp)
|
||||
addrp = ((struct in6_addr *)(rta+1));
|
||||
else if (rta->rta_type == IFA_CACHEINFO)
|
||||
{
|
||||
|
||||
+58
-4
@@ -233,6 +233,7 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
|
||||
union mysockaddr *addr, struct in_addr netmask, int prefixlen, int iface_flags)
|
||||
{
|
||||
struct irec *iface;
|
||||
struct cond_domain *cond;
|
||||
int loopback;
|
||||
struct ifreq ifr;
|
||||
int tftp_ok = !!option_bool(OPT_TFTP);
|
||||
@@ -361,7 +362,7 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
|
||||
|
||||
if (int_name->flags & INP4)
|
||||
{
|
||||
if (netmask.s_addr == 0xffff)
|
||||
if (netmask.s_addr == 0xffffffff)
|
||||
continue;
|
||||
|
||||
newaddr.s_addr = (addr->in.sin_addr.s_addr & netmask.s_addr) |
|
||||
@@ -455,7 +456,37 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Update addresses for domain=<domain>,<interface> */
|
||||
for (cond = daemon->cond_domain; cond; cond = cond->next)
|
||||
if (cond->interface && strncmp(label, cond->interface, IF_NAMESIZE) == 0)
|
||||
{
|
||||
struct addrlist *al;
|
||||
|
||||
if (param->spare)
|
||||
{
|
||||
al = param->spare;
|
||||
param->spare = al->next;
|
||||
}
|
||||
else
|
||||
al = whine_malloc(sizeof(struct addrlist));
|
||||
|
||||
if (addr->sa.sa_family == AF_INET)
|
||||
{
|
||||
al->addr.addr4 = addr->in.sin_addr;
|
||||
al->flags = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
al->addr.addr6 = addr->in6.sin6_addr;
|
||||
al->flags = ADDRLIST_IPV6;
|
||||
}
|
||||
|
||||
al->prefixlen = prefixlen;
|
||||
al->next = cond->al;
|
||||
cond->al = al;
|
||||
}
|
||||
|
||||
/* check whether the interface IP has been added already
|
||||
we call this routine multiple times. */
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
@@ -700,6 +731,7 @@ int enumerate_interfaces(int reset)
|
||||
int errsave, ret = 1;
|
||||
struct addrlist *addr, *tmp;
|
||||
struct interface_name *intname;
|
||||
struct cond_domain *cond;
|
||||
struct irec *iface;
|
||||
#ifdef HAVE_AUTH
|
||||
struct auth_zone *zone;
|
||||
@@ -759,6 +791,19 @@ again:
|
||||
intname->addr = NULL;
|
||||
}
|
||||
|
||||
/* remove addresses stored against cond-domains. */
|
||||
for (cond = daemon->cond_domain; cond; cond = cond->next)
|
||||
{
|
||||
for (addr = cond->al; addr; addr = tmp)
|
||||
{
|
||||
tmp = addr->next;
|
||||
addr->next = spare;
|
||||
spare = addr;
|
||||
}
|
||||
|
||||
cond->al = NULL;
|
||||
}
|
||||
|
||||
/* Remove list of addresses of local interfaces */
|
||||
for (addr = daemon->interface_addrs; addr; addr = tmp)
|
||||
{
|
||||
@@ -1346,7 +1391,7 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind
|
||||
or both are set. Otherwise use the OS's random ephemeral port allocation by
|
||||
leaving port == 0 and tries == 1 */
|
||||
ports_avail = daemon->max_port - daemon->min_port + 1;
|
||||
tries = ports_avail < 30 ? 3 * ports_avail : 100;
|
||||
tries = (ports_avail < SMALL_PORT_RANGE) ? ports_avail : 100;
|
||||
port = htons(daemon->min_port + (rand16() % ports_avail));
|
||||
}
|
||||
|
||||
@@ -1375,7 +1420,16 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind
|
||||
if (--tries == 0)
|
||||
return 0;
|
||||
|
||||
port = htons(daemon->min_port + (rand16() % ports_avail));
|
||||
/* For small ranges, do a systematic search, not a random one. */
|
||||
if (ports_avail < SMALL_PORT_RANGE)
|
||||
{
|
||||
unsigned short hport = ntohs(port);
|
||||
if (hport++ == daemon->max_port)
|
||||
hport = daemon->min_port;
|
||||
port = htons(hport);
|
||||
}
|
||||
else
|
||||
port = htons(daemon->min_port + (rand16() % ports_avail));
|
||||
}
|
||||
|
||||
if (!is_tcp && ifindex > 0)
|
||||
|
||||
+437
-162
@@ -185,6 +185,10 @@ struct myoption {
|
||||
#define LOPT_STRIP_MAC 372
|
||||
#define LOPT_CONF_OPT 373
|
||||
#define LOPT_CONF_SCRIPT 374
|
||||
#define LOPT_RANDPORT_LIM 375
|
||||
#define LOPT_FAST_RETRY 376
|
||||
#define LOPT_STALE_CACHE 377
|
||||
#define LOPT_NORR 378
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
static const struct option opts[] =
|
||||
@@ -237,6 +241,7 @@ static const struct myoption opts[] =
|
||||
{ "localmx", 0, 0, 'L' },
|
||||
{ "local-ttl", 1, 0, 'T' },
|
||||
{ "no-negcache", 0, 0, 'N' },
|
||||
{ "no-round-robin", 0, 0, LOPT_NORR },
|
||||
{ "addn-hosts", 1, 0, 'H' },
|
||||
{ "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
|
||||
{ "query-port", 1, 0, 'Q' },
|
||||
@@ -370,6 +375,9 @@ static const struct myoption opts[] =
|
||||
{ "log-debug", 0, 0, LOPT_LOG_DEBUG },
|
||||
{ "umbrella", 2, 0, LOPT_UMBRELLA },
|
||||
{ "quiet-tftp", 0, 0, LOPT_QUIET_TFTP },
|
||||
{ "port-limit", 1, 0, LOPT_RANDPORT_LIM },
|
||||
{ "fast-dns-retry", 2, 0, LOPT_FAST_RETRY },
|
||||
{ "use-stale-cache", 2, 0 , LOPT_STALE_CACHE },
|
||||
{ NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
@@ -427,6 +435,7 @@ static struct {
|
||||
{ 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
|
||||
{ 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
|
||||
{ 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
|
||||
{ LOPT_STALE_CACHE, ARG_ONE, "[=<max_expired>]", gettext_noop("Use expired cache data for faster reply."), NULL },
|
||||
{ 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
|
||||
{ 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
|
||||
{ LOPT_FORCE, ARG_DUP, "<optspec>", gettext_noop("DHCP option sent even if the client does not request it."), NULL},
|
||||
@@ -434,6 +443,7 @@ static struct {
|
||||
{ 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
|
||||
{ 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
|
||||
{ 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
|
||||
{ LOPT_RANDPORT_LIM, ARG_ONE, "#ports", gettext_noop("Set maximum number of random originating ports for a query."), NULL },
|
||||
{ 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
|
||||
{ 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
|
||||
{ LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
|
||||
@@ -447,6 +457,7 @@ static struct {
|
||||
{ LOPT_MAXTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for maximum TTL to send to clients."), NULL },
|
||||
{ LOPT_MAXCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live ceiling for cache."), NULL },
|
||||
{ LOPT_MINCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live floor for cache."), NULL },
|
||||
{ LOPT_FAST_RETRY, ARG_ONE, "<milliseconds>", gettext_noop("Retry DNS queries after this many milliseconds."), NULL},
|
||||
{ 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
|
||||
{ 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
|
||||
{ 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
|
||||
@@ -562,6 +573,7 @@ static struct {
|
||||
{ LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL },
|
||||
{ LOPT_UMBRELLA, ARG_ONE, "[=<optspec>]", gettext_noop("Send Cisco Umbrella identifiers including remote IP."), NULL },
|
||||
{ LOPT_QUIET_TFTP, OPT_QUIET_TFTP, NULL, gettext_noop("Do not log routine TFTP."), NULL },
|
||||
{ LOPT_NORR, OPT_NORR, NULL, gettext_noop("Suppress round-robin ordering of DNS records."), NULL },
|
||||
{ 0, 0, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
@@ -847,117 +859,241 @@ static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, u16 *flags)
|
||||
char *parse_server(char *arg, struct server_details *sdetails)
|
||||
{
|
||||
int source_port = 0, serv_port = NAMESERVER_PORT;
|
||||
char *portno, *source;
|
||||
char *interface_opt = NULL;
|
||||
int scope_index = 0;
|
||||
char *scope_id;
|
||||
|
||||
*interface = 0;
|
||||
sdetails->serv_port = NAMESERVER_PORT;
|
||||
char *portno;
|
||||
int ecode = 0;
|
||||
struct addrinfo hints;
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
|
||||
*sdetails->interface = 0;
|
||||
sdetails->addr_type = AF_UNSPEC;
|
||||
|
||||
if (strcmp(arg, "#") == 0)
|
||||
{
|
||||
if (flags)
|
||||
*flags |= SERV_USE_RESOLV;
|
||||
if (sdetails->flags)
|
||||
*sdetails->flags |= SERV_USE_RESOLV;
|
||||
sdetails->addr_type = AF_LOCAL;
|
||||
sdetails->valid = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((source = split_chr(arg, '@')) && /* is there a source. */
|
||||
(portno = split_chr(source, '#')) &&
|
||||
!atoi_check16(portno, &source_port))
|
||||
if ((sdetails->source = split_chr(arg, '@')) && /* is there a source. */
|
||||
(portno = split_chr(sdetails->source, '#')) &&
|
||||
!atoi_check16(portno, &sdetails->source_port))
|
||||
return _("bad port");
|
||||
|
||||
if ((portno = split_chr(arg, '#')) && /* is there a port no. */
|
||||
!atoi_check16(portno, &serv_port))
|
||||
!atoi_check16(portno, &sdetails->serv_port))
|
||||
return _("bad port");
|
||||
|
||||
scope_id = split_chr(arg, '%');
|
||||
sdetails->scope_id = split_chr(arg, '%');
|
||||
|
||||
if (source) {
|
||||
interface_opt = split_chr(source, '@');
|
||||
if (sdetails->source) {
|
||||
sdetails->interface_opt = split_chr(sdetails->source, '@');
|
||||
|
||||
if (interface_opt)
|
||||
if (sdetails->interface_opt)
|
||||
{
|
||||
#if defined(SO_BINDTODEVICE)
|
||||
safe_strncpy(interface, source, IF_NAMESIZE);
|
||||
source = interface_opt;
|
||||
safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE);
|
||||
sdetails->source = sdetails->interface_opt;
|
||||
#else
|
||||
return _("interface binding not supported");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
|
||||
if (inet_pton(AF_INET, arg, &sdetails->addr->in.sin_addr) > 0)
|
||||
sdetails->addr_type = AF_INET;
|
||||
else if (inet_pton(AF_INET6, arg, &sdetails->addr->in6.sin6_addr) > 0)
|
||||
sdetails->addr_type = AF_INET6;
|
||||
else
|
||||
{
|
||||
addr->in.sin_port = htons(serv_port);
|
||||
addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
|
||||
/* if the argument is neither an IPv4 not an IPv6 address, it might be a
|
||||
hostname and we should try to resolve it to a suitable address. */
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
/* The AI_ADDRCONFIG flag ensures that then IPv4 addresses are returned in
|
||||
the result only if the local system has at least one IPv4 address
|
||||
configured, and IPv6 addresses are returned only if the local system
|
||||
has at least one IPv6 address configured. The loopback address is not
|
||||
considered for this case as valid as a configured address. This flag is
|
||||
useful on, for example, IPv4-only systems, to ensure that getaddrinfo()
|
||||
does not return IPv6 socket addresses that would always fail in
|
||||
subsequent connect() or bind() attempts. */
|
||||
hints.ai_flags = AI_ADDRCONFIG;
|
||||
#if defined(HAVE_IDN) && defined(AI_IDN)
|
||||
/* If the AI_IDN flag is specified and we have glibc 2.3.4 or newer, then
|
||||
the node name given in node is converted to IDN format if necessary.
|
||||
The source encoding is that of the current locale. */
|
||||
hints.ai_flags |= AI_IDN;
|
||||
#endif
|
||||
source_addr->in.sin_addr.s_addr = INADDR_ANY;
|
||||
source_addr->in.sin_port = htons(daemon->query_port);
|
||||
|
||||
if (source)
|
||||
{
|
||||
if (flags)
|
||||
*flags |= SERV_HAS_SOURCE;
|
||||
source_addr->in.sin_port = htons(source_port);
|
||||
if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))
|
||||
{
|
||||
#if defined(SO_BINDTODEVICE)
|
||||
if (interface_opt)
|
||||
return _("interface can only be specified once");
|
||||
|
||||
source_addr->in.sin_addr.s_addr = INADDR_ANY;
|
||||
safe_strncpy(interface, source, IF_NAMESIZE);
|
||||
#else
|
||||
return _("interface binding not supported");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
|
||||
{
|
||||
if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
|
||||
return _("bad interface name");
|
||||
|
||||
addr->in6.sin6_port = htons(serv_port);
|
||||
addr->in6.sin6_scope_id = scope_index;
|
||||
source_addr->in6.sin6_addr = in6addr_any;
|
||||
source_addr->in6.sin6_port = htons(daemon->query_port);
|
||||
source_addr->in6.sin6_scope_id = 0;
|
||||
addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
|
||||
addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
|
||||
#endif
|
||||
if (source)
|
||||
{
|
||||
if (flags)
|
||||
*flags |= SERV_HAS_SOURCE;
|
||||
source_addr->in6.sin6_port = htons(source_port);
|
||||
if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
|
||||
{
|
||||
#if defined(SO_BINDTODEVICE)
|
||||
if (interface_opt)
|
||||
return _("interface can only be specified once");
|
||||
|
||||
source_addr->in6.sin6_addr = in6addr_any;
|
||||
safe_strncpy(interface, source, IF_NAMESIZE);
|
||||
#else
|
||||
return _("interface binding not supported");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
return _("bad address");
|
||||
/* The value AF_UNSPEC indicates that getaddrinfo() should return socket
|
||||
addresses for any address family (either IPv4 or IPv6, for example)
|
||||
that can be used with node <arg> and service "domain". */
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
|
||||
/* Get addresses suitable for sending datagrams. We assume that we can use the
|
||||
same addresses for TCP connections. Settting this to zero gets each address
|
||||
threes times, for SOCK_STREAM, SOCK_RAW and SOCK_DGRAM, which is not useful. */
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
|
||||
/* Get address associated with this hostname */
|
||||
ecode = getaddrinfo(arg, NULL, &hints, &sdetails->hostinfo);
|
||||
if (ecode == 0)
|
||||
{
|
||||
/* The getaddrinfo() function allocated and initialized a linked list of
|
||||
addrinfo structures, one for each network address that matches node
|
||||
and service, subject to the restrictions imposed by our <hints>
|
||||
above, and returns a pointer to the start of the list in <hostinfo>.
|
||||
The items in the linked list are linked by the <ai_next> field. */
|
||||
sdetails->valid = 1;
|
||||
sdetails->orig_hostinfo = sdetails->hostinfo;
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Lookup failed, return human readable error string */
|
||||
if (ecode == EAI_AGAIN)
|
||||
return _("Cannot resolve server name");
|
||||
else
|
||||
return _((char*)gai_strerror(ecode));
|
||||
}
|
||||
}
|
||||
|
||||
sdetails->valid = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *parse_server_addr(struct server_details *sdetails)
|
||||
{
|
||||
if (sdetails->addr_type == AF_INET)
|
||||
{
|
||||
sdetails->addr->in.sin_port = htons(sdetails->serv_port);
|
||||
sdetails->addr->sa.sa_family = sdetails->source_addr->sa.sa_family = AF_INET;
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
sdetails->source_addr->in.sin_len = sdetails->addr->in.sin_len = sizeof(struct sockaddr_in);
|
||||
#endif
|
||||
sdetails->source_addr->in.sin_addr.s_addr = INADDR_ANY;
|
||||
sdetails->source_addr->in.sin_port = htons(daemon->query_port);
|
||||
|
||||
if (sdetails->source)
|
||||
{
|
||||
if (sdetails->flags)
|
||||
*sdetails->flags |= SERV_HAS_SOURCE;
|
||||
sdetails->source_addr->in.sin_port = htons(sdetails->source_port);
|
||||
if (inet_pton(AF_INET, sdetails->source, &sdetails->source_addr->in.sin_addr) == 0)
|
||||
{
|
||||
if (inet_pton(AF_INET6, sdetails->source, &sdetails->source_addr->in6.sin6_addr) == 1)
|
||||
{
|
||||
sdetails->source_addr->sa.sa_family = AF_INET6;
|
||||
/* When resolving a server IP by hostname, we can simply skip mismatching
|
||||
server / source IP pairs. Otherwise, when an IP address is given directly,
|
||||
this is a fatal error. */
|
||||
if (!sdetails->orig_hostinfo)
|
||||
return _("cannot use IPv4 server address with IPv6 source address");
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(SO_BINDTODEVICE)
|
||||
if (sdetails->interface_opt)
|
||||
return _("interface can only be specified once");
|
||||
|
||||
sdetails->source_addr->in.sin_addr.s_addr = INADDR_ANY;
|
||||
safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE);
|
||||
#else
|
||||
return _("interface binding not supported");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sdetails->addr_type == AF_INET6)
|
||||
{
|
||||
if (sdetails->scope_id && (sdetails->scope_index = if_nametoindex(sdetails->scope_id)) == 0)
|
||||
return _("bad interface name");
|
||||
|
||||
sdetails->addr->in6.sin6_port = htons(sdetails->serv_port);
|
||||
sdetails->addr->in6.sin6_scope_id = sdetails->scope_index;
|
||||
sdetails->source_addr->in6.sin6_addr = in6addr_any;
|
||||
sdetails->source_addr->in6.sin6_port = htons(daemon->query_port);
|
||||
sdetails->source_addr->in6.sin6_scope_id = 0;
|
||||
sdetails->addr->sa.sa_family = sdetails->source_addr->sa.sa_family = AF_INET6;
|
||||
sdetails->addr->in6.sin6_flowinfo = sdetails->source_addr->in6.sin6_flowinfo = 0;
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
sdetails->addr->in6.sin6_len = sdetails->source_addr->in6.sin6_len = sizeof(sdetails->addr->in6);
|
||||
#endif
|
||||
if (sdetails->source)
|
||||
{
|
||||
if (sdetails->flags)
|
||||
*sdetails->flags |= SERV_HAS_SOURCE;
|
||||
sdetails->source_addr->in6.sin6_port = htons(sdetails->source_port);
|
||||
if (inet_pton(AF_INET6, sdetails->source, &sdetails->source_addr->in6.sin6_addr) == 0)
|
||||
{
|
||||
if (inet_pton(AF_INET, sdetails->source, &sdetails->source_addr->in.sin_addr) == 1)
|
||||
{
|
||||
sdetails->source_addr->sa.sa_family = AF_INET;
|
||||
/* When resolving a server IP by hostname, we can simply skip mismatching
|
||||
server / source IP pairs. Otherwise, when an IP address is given directly,
|
||||
this is a fatal error. */
|
||||
if(!sdetails->orig_hostinfo)
|
||||
return _("cannot use IPv6 server address with IPv4 source address");
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(SO_BINDTODEVICE)
|
||||
if (sdetails->interface_opt)
|
||||
return _("interface can only be specified once");
|
||||
|
||||
sdetails->source_addr->in6.sin6_addr = in6addr_any;
|
||||
safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE);
|
||||
#else
|
||||
return _("interface binding not supported");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sdetails->addr_type != AF_LOCAL)
|
||||
return _("bad address");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int parse_server_next(struct server_details *sdetails)
|
||||
{
|
||||
/* Looping over resolved addresses? */
|
||||
if (sdetails->hostinfo)
|
||||
{
|
||||
/* Get address type */
|
||||
sdetails->addr_type = sdetails->hostinfo->ai_family;
|
||||
|
||||
/* Get address */
|
||||
if (sdetails->addr_type == AF_INET)
|
||||
memcpy(&sdetails->addr->in.sin_addr,
|
||||
&((struct sockaddr_in *) sdetails->hostinfo->ai_addr)->sin_addr,
|
||||
sizeof(sdetails->addr->in.sin_addr));
|
||||
else if (sdetails->addr_type == AF_INET6)
|
||||
memcpy(&sdetails->addr->in6.sin6_addr,
|
||||
&((struct sockaddr_in6 *) sdetails->hostinfo->ai_addr)->sin6_addr,
|
||||
sizeof(sdetails->addr->in6.sin6_addr));
|
||||
|
||||
/* Iterate to the next available address */
|
||||
sdetails->valid = sdetails->hostinfo->ai_next != NULL;
|
||||
sdetails->hostinfo = sdetails->hostinfo->ai_next;
|
||||
return 1;
|
||||
}
|
||||
else if (sdetails->valid)
|
||||
{
|
||||
/* When using an IP address, we return the address only once */
|
||||
sdetails->valid = 0;
|
||||
return 1;
|
||||
}
|
||||
/* Stop iterating here, we used all available addresses */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int size)
|
||||
{
|
||||
int i, j;
|
||||
@@ -968,22 +1104,29 @@ static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int
|
||||
union mysockaddr serv_addr, source_addr;
|
||||
char interface[IF_NAMESIZE+1];
|
||||
int count = 1, rem, addrbytes, addrbits;
|
||||
|
||||
struct server_details sdetails;
|
||||
|
||||
memset(&sdetails, 0, sizeof(struct server_details));
|
||||
sdetails.addr = &serv_addr;
|
||||
sdetails.source_addr = &source_addr;
|
||||
sdetails.interface = interface;
|
||||
sdetails.flags = &flags;
|
||||
|
||||
if (!server)
|
||||
flags = SERV_LITERAL_ADDRESS;
|
||||
else if ((string = parse_server(server, &serv_addr, &source_addr, interface, &flags)))
|
||||
else if ((string = parse_server(server, &sdetails)))
|
||||
return string;
|
||||
|
||||
|
||||
if (from_file)
|
||||
flags |= SERV_FROM_FILE;
|
||||
|
||||
|
||||
rem = size & 0x7;
|
||||
addrbytes = (32 - size) >> 3;
|
||||
addrbits = (32 - size) & 7;
|
||||
|
||||
if (size > 32 || size < 1)
|
||||
return _("bad IPv4 prefix length");
|
||||
|
||||
|
||||
/* Zero out last address bits according to CIDR mask */
|
||||
((u8 *)addr4)[3-addrbytes] &= ~((1 << addrbits)-1);
|
||||
|
||||
@@ -997,23 +1140,40 @@ static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int
|
||||
*domain = 0;
|
||||
string = domain;
|
||||
msize = size/8;
|
||||
|
||||
|
||||
for (j = (rem == 0) ? msize-1 : msize; j >= 0; j--)
|
||||
{
|
||||
int dig = ((unsigned char *)addr4)[j];
|
||||
|
||||
|
||||
if (j == msize)
|
||||
dig += i;
|
||||
|
||||
|
||||
string += sprintf(string, "%d.", dig);
|
||||
}
|
||||
|
||||
|
||||
sprintf(string, "in-addr.arpa");
|
||||
|
||||
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
|
||||
return _("error");
|
||||
}
|
||||
if (flags & SERV_LITERAL_ADDRESS)
|
||||
{
|
||||
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
|
||||
return _("error");
|
||||
}
|
||||
else
|
||||
{
|
||||
while (parse_server_next(&sdetails))
|
||||
{
|
||||
if ((string = parse_server_addr(&sdetails)))
|
||||
return string;
|
||||
|
||||
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
|
||||
return _("error");
|
||||
}
|
||||
|
||||
if (sdetails.orig_hostinfo)
|
||||
freeaddrinfo(sdetails.orig_hostinfo);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1027,10 +1187,17 @@ static char *domain_rev6(int from_file, char *server, struct in6_addr *addr6, in
|
||||
union mysockaddr serv_addr, source_addr;
|
||||
char interface[IF_NAMESIZE+1];
|
||||
int count = 1, rem, addrbytes, addrbits;
|
||||
|
||||
struct server_details sdetails;
|
||||
|
||||
memset(&sdetails, 0, sizeof(struct server_details));
|
||||
sdetails.addr = &serv_addr;
|
||||
sdetails.source_addr = &source_addr;
|
||||
sdetails.interface = interface;
|
||||
sdetails.flags = &flags;
|
||||
|
||||
if (!server)
|
||||
flags = SERV_LITERAL_ADDRESS;
|
||||
else if ((string = parse_server(server, &serv_addr, &source_addr, interface, &flags)))
|
||||
else if ((string = parse_server(server, &sdetails)))
|
||||
return string;
|
||||
|
||||
if (from_file)
|
||||
@@ -1071,10 +1238,27 @@ static char *domain_rev6(int from_file, char *server, struct in6_addr *addr6, in
|
||||
|
||||
sprintf(string, "ip6.arpa");
|
||||
|
||||
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
|
||||
return _("error");
|
||||
}
|
||||
if (flags & SERV_LITERAL_ADDRESS)
|
||||
{
|
||||
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
|
||||
return _("error");
|
||||
}
|
||||
else
|
||||
{
|
||||
while (parse_server_next(&sdetails))
|
||||
{
|
||||
if ((string = parse_server_addr(&sdetails)))
|
||||
return string;
|
||||
|
||||
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
|
||||
return _("error");
|
||||
}
|
||||
|
||||
if (sdetails.orig_hostinfo)
|
||||
freeaddrinfo(sdetails.orig_hostinfo);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -2157,15 +2341,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
|
||||
case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
|
||||
case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
|
||||
case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
|
||||
case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
|
||||
case LOPT_HOST_INOTIFY: /* --hostsdir */
|
||||
case 'H': /* --addn-hosts */
|
||||
{
|
||||
struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
|
||||
static unsigned int hosts_index = SRC_AH;
|
||||
new->fname = opt_string_alloc(arg);
|
||||
new->index = hosts_index++;
|
||||
new->index = daemon->host_index++;
|
||||
new->flags = 0;
|
||||
if (option == 'H')
|
||||
{
|
||||
@@ -2181,21 +2361,29 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
{
|
||||
new->next = daemon->dhcp_opts_file;
|
||||
daemon->dhcp_opts_file = new;
|
||||
}
|
||||
else
|
||||
{
|
||||
new->next = daemon->dynamic_dirs;
|
||||
daemon->dynamic_dirs = new;
|
||||
if (option == LOPT_DHCP_INOTIFY)
|
||||
new->flags |= AH_DHCP_HST;
|
||||
else if (option == LOPT_DHOPT_INOTIFY)
|
||||
new->flags |= AH_DHCP_OPT;
|
||||
else if (option == LOPT_HOST_INOTIFY)
|
||||
new->flags |= AH_HOSTS;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
|
||||
case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
|
||||
case LOPT_HOST_INOTIFY: /* --hostsdir */
|
||||
{
|
||||
struct dyndir *new = opt_malloc(sizeof(struct dyndir));
|
||||
new->dname = opt_string_alloc(arg);
|
||||
new->flags = 0;
|
||||
new->next = daemon->dynamic_dirs;
|
||||
daemon->dynamic_dirs = new;
|
||||
if (option == LOPT_DHCP_INOTIFY)
|
||||
new->flags |= AH_DHCP_HST;
|
||||
else if (option == LOPT_DHOPT_INOTIFY)
|
||||
new->flags |= AH_DHCP_OPT;
|
||||
else if (option == LOPT_HOST_INOTIFY)
|
||||
new->flags |= AH_HOSTS;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case LOPT_AUTHSERV: /* --auth-server */
|
||||
comma = split(arg);
|
||||
@@ -2496,9 +2684,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
else if (!inet_pton(AF_INET6, arg, &new->end6))
|
||||
ret_err_free(gen_err, new);
|
||||
}
|
||||
else
|
||||
else if (option == 's')
|
||||
{
|
||||
/* subnet from interface. */
|
||||
new->interface = opt_string_alloc(comma);
|
||||
new->al = NULL;
|
||||
}
|
||||
else
|
||||
ret_err_free(gen_err, new);
|
||||
|
||||
|
||||
if (option != 's' && prefstr)
|
||||
{
|
||||
if (!(new->prefix = canonicalise_opt(prefstr)) ||
|
||||
@@ -2775,13 +2969,20 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
case LOPT_LOCAL: /* --local */
|
||||
case 'A': /* --address */
|
||||
{
|
||||
char *lastdomain = NULL, *domain = "";
|
||||
char *lastdomain = NULL, *domain = "", *cur_domain;
|
||||
u16 flags = 0;
|
||||
char *err;
|
||||
union all_addr addr;
|
||||
union mysockaddr serv_addr, source_addr;
|
||||
char interface[IF_NAMESIZE+1];
|
||||
struct server_details sdetails;
|
||||
|
||||
memset(&sdetails, 0, sizeof(struct server_details));
|
||||
sdetails.addr = &serv_addr;
|
||||
sdetails.source_addr = &source_addr;
|
||||
sdetails.interface = interface;
|
||||
sdetails.flags = &flags;
|
||||
|
||||
unhide_metas(arg);
|
||||
|
||||
/* split the domain args, if any and skip to the end of them. */
|
||||
@@ -2814,36 +3015,55 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((err = parse_server(arg, &serv_addr, &source_addr, interface, &flags)))
|
||||
if ((err = parse_server(arg, &sdetails)))
|
||||
ret_err(err);
|
||||
}
|
||||
|
||||
if (servers_only && option == 'S')
|
||||
flags |= SERV_FROM_FILE;
|
||||
|
||||
while (1)
|
||||
{
|
||||
/* server=//1.2.3.4 is special. */
|
||||
if (lastdomain)
|
||||
{
|
||||
if (strlen(domain) == 0)
|
||||
flags |= SERV_FOR_NODOTS;
|
||||
else
|
||||
flags &= ~SERV_FOR_NODOTS;
|
||||
|
||||
/* address=/#/ matches the same as without domain */
|
||||
if (option == 'A' && domain[0] == '#' && domain[1] == 0)
|
||||
domain[0] = 0;
|
||||
cur_domain = domain;
|
||||
while ((flags & SERV_LITERAL_ADDRESS) || parse_server_next(&sdetails))
|
||||
{
|
||||
cur_domain = domain;
|
||||
|
||||
if (!(flags & SERV_LITERAL_ADDRESS) && (err = parse_server_addr(&sdetails)))
|
||||
ret_err(err);
|
||||
|
||||
/* When source is set only use DNS records of the same type and skip all others */
|
||||
if (flags & SERV_HAS_SOURCE && sdetails.addr_type != sdetails.source_addr->sa.sa_family)
|
||||
continue;
|
||||
|
||||
while (1)
|
||||
{
|
||||
/* server=//1.2.3.4 is special. */
|
||||
if (lastdomain)
|
||||
{
|
||||
if (strlen(cur_domain) == 0)
|
||||
flags |= SERV_FOR_NODOTS;
|
||||
else
|
||||
flags &= ~SERV_FOR_NODOTS;
|
||||
|
||||
/* address=/#/ matches the same as without domain */
|
||||
if (option == 'A' && cur_domain[0] == '#' && cur_domain[1] == 0)
|
||||
cur_domain[0] = 0;
|
||||
}
|
||||
|
||||
if (!add_update_server(flags, sdetails.addr, sdetails.source_addr, sdetails.interface, cur_domain, &addr))
|
||||
ret_err(gen_err);
|
||||
|
||||
if (!lastdomain || cur_domain == lastdomain)
|
||||
break;
|
||||
|
||||
cur_domain += strlen(cur_domain) + 1;
|
||||
}
|
||||
|
||||
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, &addr))
|
||||
ret_err(gen_err);
|
||||
|
||||
if (!lastdomain || domain == lastdomain)
|
||||
|
||||
if (flags & SERV_LITERAL_ADDRESS)
|
||||
break;
|
||||
|
||||
domain += strlen(domain) + 1;
|
||||
}
|
||||
|
||||
if (sdetails.orig_hostinfo)
|
||||
freeaddrinfo(sdetails.orig_hostinfo);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -3177,6 +3397,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
if (daemon->query_port == 0)
|
||||
daemon->osport = 1;
|
||||
break;
|
||||
|
||||
case LOPT_RANDPORT_LIM: /* --port-limit */
|
||||
if (!atoi_check(arg, &daemon->randport_limit) || (daemon->randport_limit < 1))
|
||||
ret_err(gen_err);
|
||||
break;
|
||||
|
||||
case 'T': /* --local-ttl */
|
||||
case LOPT_NEGTTL: /* --neg-ttl */
|
||||
@@ -3212,7 +3437,30 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
daemon->local_ttl = (unsigned long)ttl;
|
||||
break;
|
||||
}
|
||||
|
||||
case LOPT_FAST_RETRY:
|
||||
daemon->fast_retry_timeout = TIMEOUT;
|
||||
|
||||
if (!arg)
|
||||
daemon->fast_retry_time = DEFAULT_FAST_RETRY;
|
||||
else
|
||||
{
|
||||
int retry;
|
||||
|
||||
comma = split(arg);
|
||||
if (!atoi_check(arg, &retry) || retry < 50)
|
||||
ret_err(gen_err);
|
||||
daemon->fast_retry_time = retry;
|
||||
|
||||
if (comma)
|
||||
{
|
||||
if (!atoi_check(comma, &retry))
|
||||
ret_err(gen_err);
|
||||
daemon->fast_retry_timeout = retry/1000;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
case 'X': /* --dhcp-lease-max */
|
||||
if (!atoi_check(arg, &daemon->dhcp_max))
|
||||
@@ -4331,6 +4579,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
{
|
||||
if (inet_pton(AF_INET, arg, &new->local))
|
||||
{
|
||||
char *hash = split_chr(two, '#');
|
||||
|
||||
if (!hash || !atoi_check16(hash, &new->port))
|
||||
new->port = DHCP_SERVER_PORT;
|
||||
|
||||
if (!inet_pton(AF_INET, two, &new->server))
|
||||
{
|
||||
new->server.addr4.s_addr = 0;
|
||||
@@ -4349,6 +4602,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
#ifdef HAVE_DHCP6
|
||||
else if (inet_pton(AF_INET6, arg, &new->local))
|
||||
{
|
||||
char *hash = split_chr(two, '#');
|
||||
|
||||
if (!hash || !atoi_check16(hash, &new->port))
|
||||
new->port = DHCPV6_SERVER_PORT;
|
||||
|
||||
if (!inet_pton(AF_INET6, two, &new->server))
|
||||
{
|
||||
inet_pton(AF_INET6, ALL_SERVERS, &new->server.addr6);
|
||||
@@ -4897,6 +5155,24 @@ err:
|
||||
break;
|
||||
}
|
||||
|
||||
case LOPT_STALE_CACHE:
|
||||
{
|
||||
int max_expiry = STALE_CACHE_EXPIRY;
|
||||
if (arg)
|
||||
{
|
||||
/* Don't accept negative TTLs here, they'd have the counter-intuitive
|
||||
side-effect of evicting cache records before they expire */
|
||||
if (!atoi_check(arg, &max_expiry) || max_expiry < 0)
|
||||
ret_err(gen_err);
|
||||
/* Store "serve expired forever" as -1 internally, the option isn't
|
||||
active for daemon->cache_max_expiry == 0 */
|
||||
if (max_expiry == 0)
|
||||
max_expiry = -1;
|
||||
}
|
||||
daemon->cache_max_expiry = max_expiry;
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
|
||||
daemon->timestamp_file = opt_string_alloc(arg);
|
||||
@@ -4978,26 +5254,20 @@ err:
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void read_file(char *file, FILE *f, int hard_opt)
|
||||
static void read_file(char *file, FILE *f, int hard_opt, int from_script)
|
||||
{
|
||||
volatile int lineno = 0;
|
||||
char *buff = daemon->namebuff;
|
||||
|
||||
while (fgets(buff, MAXDNAME, f))
|
||||
{
|
||||
int white, i, script = 0;
|
||||
int white, i;
|
||||
volatile int option;
|
||||
char *errmess, *p, *arg, *start;
|
||||
size_t len;
|
||||
|
||||
if (hard_opt == LOPT_CONF_SCRIPT)
|
||||
{
|
||||
hard_opt = 0;
|
||||
script = 1;
|
||||
}
|
||||
|
||||
option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
|
||||
|
||||
|
||||
/* Memory allocation failure longjmps here if mem_recover == 1 */
|
||||
if (option != 0 || hard_opt == LOPT_REV_SERV)
|
||||
{
|
||||
@@ -5005,7 +5275,7 @@ static void read_file(char *file, FILE *f, int hard_opt)
|
||||
continue;
|
||||
mem_recover = 1;
|
||||
}
|
||||
|
||||
|
||||
arg = NULL;
|
||||
lineno++;
|
||||
errmess = NULL;
|
||||
@@ -5111,7 +5381,7 @@ static void read_file(char *file, FILE *f, int hard_opt)
|
||||
|
||||
if (errmess || !one_opt(option, arg, daemon->namebuff, _("error"), 0, hard_opt == LOPT_REV_SERV))
|
||||
{
|
||||
if (script)
|
||||
if (from_script)
|
||||
sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" in output from %s"), file);
|
||||
else
|
||||
sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
|
||||
@@ -5157,8 +5427,14 @@ static int one_file(char *file, int hard_opt)
|
||||
hard_opt = 0;
|
||||
nofile_ok = 1;
|
||||
}
|
||||
|
||||
if (hard_opt == 0 && strcmp(file, "-") == 0)
|
||||
|
||||
if (hard_opt == LOPT_CONF_SCRIPT)
|
||||
{
|
||||
hard_opt = 0;
|
||||
do_popen = 1;
|
||||
}
|
||||
|
||||
if (hard_opt == 0 && !do_popen && strcmp(file, "-") == 0)
|
||||
{
|
||||
if (read_stdin == 1)
|
||||
return 1;
|
||||
@@ -5171,12 +5447,6 @@ static int one_file(char *file, int hard_opt)
|
||||
/* ignore repeated files. */
|
||||
struct stat statbuf;
|
||||
|
||||
if (hard_opt == LOPT_CONF_SCRIPT)
|
||||
{
|
||||
hard_opt = 0;
|
||||
do_popen = 1;
|
||||
}
|
||||
|
||||
if (hard_opt == 0 && stat(file, &statbuf) == 0)
|
||||
{
|
||||
struct fileread *r;
|
||||
@@ -5215,7 +5485,7 @@ static int one_file(char *file, int hard_opt)
|
||||
}
|
||||
}
|
||||
|
||||
read_file(file, f, do_popen ? LOPT_CONF_SCRIPT : hard_opt);
|
||||
read_file(file, f, hard_opt, do_popen);
|
||||
|
||||
if (do_popen)
|
||||
{
|
||||
@@ -5369,7 +5639,7 @@ void read_servers_file(void)
|
||||
}
|
||||
|
||||
mark_servers(SERV_FROM_FILE);
|
||||
read_file(daemon->servers_file, f, LOPT_REV_SERV);
|
||||
read_file(daemon->servers_file, f, LOPT_REV_SERV, 0);
|
||||
fclose(f);
|
||||
cleanup_servers();
|
||||
check_servers(0);
|
||||
@@ -5488,6 +5758,8 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
daemon->soa_refresh = SOA_REFRESH;
|
||||
daemon->soa_retry = SOA_RETRY;
|
||||
daemon->soa_expiry = SOA_EXPIRY;
|
||||
daemon->randport_limit = 1;
|
||||
daemon->host_index = SRC_AH;
|
||||
|
||||
#ifndef NO_ID
|
||||
add_txt("version.bind", "dnsmasq-" VERSION, 0 );
|
||||
@@ -5509,7 +5781,10 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
/******** Pi-hole modification ********/
|
||||
add_txt("version.FTL", (char*)get_FTL_version(), 0 );
|
||||
/**************************************/
|
||||
|
||||
|
||||
/* See comment above make_servers(). Optimises server-read code. */
|
||||
mark_servers(0);
|
||||
|
||||
while (1)
|
||||
{
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
|
||||
+5
-12
@@ -96,28 +96,21 @@ void poll_listen(int fd, short event)
|
||||
pollfds[i].events |= event;
|
||||
else
|
||||
{
|
||||
if (arrsize != nfds)
|
||||
memmove(&pollfds[i+1], &pollfds[i], (nfds - i) * sizeof(struct pollfd));
|
||||
else
|
||||
if (arrsize == nfds)
|
||||
{
|
||||
/* Array too small, extend. */
|
||||
struct pollfd *new;
|
||||
|
||||
arrsize = (arrsize == 0) ? 64 : arrsize * 2;
|
||||
|
||||
if (!(new = whine_malloc(arrsize * sizeof(struct pollfd))))
|
||||
if (!(new = whine_realloc(pollfds, arrsize * sizeof(struct pollfd))))
|
||||
return;
|
||||
|
||||
if (pollfds)
|
||||
{
|
||||
memcpy(new, pollfds, i * sizeof(struct pollfd));
|
||||
memcpy(&new[i+1], &pollfds[i], (nfds - i) * sizeof(struct pollfd));
|
||||
free(pollfds);
|
||||
}
|
||||
|
||||
pollfds = new;
|
||||
}
|
||||
|
||||
|
||||
memmove(&pollfds[i+1], &pollfds[i], (nfds - i) * sizeof(struct pollfd));
|
||||
|
||||
pollfds[i].fd = fd;
|
||||
pollfds[i].events = event;
|
||||
nfds++;
|
||||
|
||||
+8
-2
@@ -203,7 +203,7 @@ void icmp6_packet(time_t now)
|
||||
int opt_sz;
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_RA, (void *)packet, sz, (union mysockaddr *)&from, NULL, -1);
|
||||
dump_packet_icmp(DUMP_RA, (void *)packet, sz, (union mysockaddr *)&from, NULL);
|
||||
#endif
|
||||
|
||||
/* look for link-layer address option for logging */
|
||||
@@ -560,7 +560,13 @@ static void send_ra_alias(time_t now, int iface, char *iface_name, struct in6_ad
|
||||
}
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_RA, (void *)daemon->outpacket.iov_base, save_counter(-1), NULL, (union mysockaddr *)&addr, -1);
|
||||
{
|
||||
struct sockaddr_in6 src;
|
||||
src.sin6_family = AF_INET6;
|
||||
src.sin6_addr = parm.link_local;
|
||||
|
||||
dump_packet_icmp(DUMP_RA, (void *)daemon->outpacket.iov_base, save_counter(-1), (union mysockaddr *)&src, (union mysockaddr *)&addr);
|
||||
}
|
||||
#endif
|
||||
|
||||
while (retry_send(sendto(daemon->icmp6fd, daemon->outpacket.iov_base,
|
||||
|
||||
+106
-48
@@ -539,8 +539,10 @@ static int print_txt(struct dns_header *header, const size_t qlen, char *name,
|
||||
/* Note that the following code can create CNAME chains that don't point to a real record,
|
||||
either because of lack of memory, or lack of SOA records. These are treated by the cache code as
|
||||
expired and cleaned out that way.
|
||||
Return 1 if we reject an address because it look like part of dns-rebinding attack. */
|
||||
// Pi-hole: Return 2 if we reject a part of a CNAME chain
|
||||
Return 1 if we reject an address because it look like part of dns-rebinding attack.
|
||||
Return 2 if the packet is malformed.
|
||||
Return 99 if we reject parts of a CNAME chain (*** Pi-hole modification ***)
|
||||
*/
|
||||
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)
|
||||
@@ -591,7 +593,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
namep = p = (unsigned char *)(header+1);
|
||||
|
||||
if (ntohs(header->qdcount) != 1 || !extract_name(header, qlen, &p, name, 1, 4))
|
||||
return 0; /* bad packet */
|
||||
return 2; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
GETSHORT(qclass, p);
|
||||
@@ -609,13 +611,13 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
{
|
||||
cname_loop:
|
||||
if (!(p1 = skip_questions(header, qlen)))
|
||||
return 0;
|
||||
return 2;
|
||||
|
||||
for (j = 0; j < ntohs(header->ancount); j++)
|
||||
{
|
||||
int secflag = 0;
|
||||
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
|
||||
return 0; /* bad packet */
|
||||
return 2; /* bad packet */
|
||||
|
||||
GETSHORT(aqtype, p1);
|
||||
GETSHORT(aqclass, p1);
|
||||
@@ -652,7 +654,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL, 0);
|
||||
|
||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||
return 0;
|
||||
return 2;
|
||||
|
||||
if (aqtype == T_CNAME)
|
||||
{
|
||||
@@ -678,7 +680,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
|
||||
p1 = endrr;
|
||||
if (!CHECK_LEN(header, p1, qlen, 0))
|
||||
return 0; /* bad packet */
|
||||
return 2; /* bad packet */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -723,14 +725,14 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
|
||||
cname_loop1:
|
||||
if (!(p1 = skip_questions(header, qlen)))
|
||||
return 0;
|
||||
return 2;
|
||||
|
||||
for (j = 0; j < ntohs(header->ancount); j++)
|
||||
{
|
||||
int secflag = 0;
|
||||
|
||||
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
|
||||
return 0; /* bad packet */
|
||||
return 2; /* bad packet */
|
||||
|
||||
GETSHORT(aqtype, p1);
|
||||
GETSHORT(aqclass, p1);
|
||||
@@ -748,7 +750,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
{
|
||||
p1 = endrr;
|
||||
if (!CHECK_LEN(header, p1, qlen, 0))
|
||||
return 0; /* bad packet */
|
||||
return 2; /* bad packet */
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -763,16 +765,6 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
}
|
||||
#endif
|
||||
|
||||
// ****************************** Pi-hole modification ******************************
|
||||
if(FTL_CNAME(name, cpp, daemon->log_display_id))
|
||||
{
|
||||
// Found while processing a reply from upstream. We prevent cache insertion here
|
||||
// This query is to be blocked as we found a blocked
|
||||
// domain while walking the CNAME path. Log to pihole.log here
|
||||
log_query(F_UPSTREAM, name, NULL, "blocked during CNAME inspection", 0);
|
||||
return 2;
|
||||
}
|
||||
// **********************************************************************************
|
||||
|
||||
if (aqtype == T_CNAME)
|
||||
{
|
||||
@@ -802,8 +794,19 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
|
||||
namep = p1;
|
||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||
return 0;
|
||||
return 2;
|
||||
|
||||
// ****************************** Pi-hole modification ******************************
|
||||
const char *src = cpp != NULL ? cpp->flags & F_BIGNAME ? cpp->name.bname->name : cpp->name.sname : NULL;
|
||||
if(FTL_CNAME(name, src, daemon->log_display_id))
|
||||
{
|
||||
// Found while processing a reply from upstream. We prevent cache insertion here
|
||||
// This query is to be blocked as we found a blocked
|
||||
// domain while walking the CNAME path. Log to pihole.log here
|
||||
log_query(F_UPSTREAM, name, NULL, "blocked during CNAME inspection", 0);
|
||||
return 99;
|
||||
}
|
||||
// **********************************************************************************
|
||||
if (qtype != T_CNAME)
|
||||
goto cname_loop1;
|
||||
|
||||
@@ -825,25 +828,25 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
unsigned char *tmp = namep;
|
||||
|
||||
if (!CHECK_LEN(header, p1, qlen, 6))
|
||||
return 0; /* bad packet */
|
||||
return 2; /* bad packet */
|
||||
GETSHORT(addr.srv.priority, p1);
|
||||
GETSHORT(addr.srv.weight, p1);
|
||||
GETSHORT(addr.srv.srvport, p1);
|
||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||
return 0;
|
||||
return 2;
|
||||
addr.srv.targetlen = strlen(name) + 1; /* include terminating zero */
|
||||
if (!(addr.srv.target = blockdata_alloc(name, addr.srv.targetlen)))
|
||||
return 0;
|
||||
|
||||
/* we overwrote the original name, so get it back here. */
|
||||
if (!extract_name(header, qlen, &tmp, name, 1, 0))
|
||||
return 0;
|
||||
return 2;
|
||||
}
|
||||
else if (flags & (F_IPV4 | F_IPV6))
|
||||
{
|
||||
/* copy address into aligned storage */
|
||||
if (!CHECK_LEN(header, p1, qlen, addrlen))
|
||||
return 0; /* bad packet */
|
||||
return 2; /* bad packet */
|
||||
memcpy(&addr, p1, addrlen);
|
||||
|
||||
/* check for returned address in private space */
|
||||
@@ -887,7 +890,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
if (aqtype == T_TXT)
|
||||
{
|
||||
if (!print_txt(header, qlen, name, p1, ardlen, secflag))
|
||||
return 0;
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
log_query(flags | F_FORWARD | secflag | F_UPSTREAM, name, &addr, NULL, aqtype);
|
||||
@@ -895,7 +898,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
|
||||
p1 = endrr;
|
||||
if (!CHECK_LEN(header, p1, qlen, 0))
|
||||
return 0; /* bad packet */
|
||||
return 2; /* bad packet */
|
||||
}
|
||||
|
||||
if (!found && (qtype != T_ANY || (flags & F_NXDOMAIN)))
|
||||
@@ -1372,8 +1375,15 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
|
||||
#undef CHECK_LIMIT
|
||||
}
|
||||
|
||||
static int crec_isstale(struct crec *crecp, time_t now)
|
||||
{
|
||||
return (!(crecp->flags & F_IMMORTAL)) && difftime(crecp->ttd, now) < 0;
|
||||
}
|
||||
|
||||
static unsigned long crec_ttl(struct crec *crecp, time_t now)
|
||||
{
|
||||
signed long ttl = difftime(crecp->ttd, now);
|
||||
|
||||
/* Return 0 ttl for DHCP entries, which might change
|
||||
before the lease expires, unless configured otherwise. */
|
||||
|
||||
@@ -1382,8 +1392,8 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
|
||||
int conf_ttl = daemon->use_dhcp_ttl ? daemon->dhcp_ttl : daemon->local_ttl;
|
||||
|
||||
/* Apply ceiling of actual lease length to configured TTL. */
|
||||
if (!(crecp->flags & F_IMMORTAL) && (crecp->ttd - now) < conf_ttl)
|
||||
return crecp->ttd - now;
|
||||
if (!(crecp->flags & F_IMMORTAL) && ttl < conf_ttl)
|
||||
return ttl;
|
||||
|
||||
return conf_ttl;
|
||||
}
|
||||
@@ -1392,9 +1402,13 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
|
||||
if (crecp->flags & F_IMMORTAL)
|
||||
return crecp->ttd;
|
||||
|
||||
/* Stale cache entries. */
|
||||
if (ttl < 0)
|
||||
return 0;
|
||||
|
||||
/* Return the Max TTL value if it is lower than the actual TTL */
|
||||
if (daemon->max_ttl == 0 || ((unsigned)(crecp->ttd - now) < daemon->max_ttl))
|
||||
return crecp->ttd - now;
|
||||
if (daemon->max_ttl == 0 || ((unsigned)ttl < daemon->max_ttl))
|
||||
return ttl;
|
||||
else
|
||||
return daemon->max_ttl;
|
||||
}
|
||||
@@ -1407,7 +1421,8 @@ static int cache_validated(const struct crec *crecp)
|
||||
/* return zero if we can't answer from cache, or packet size if we can */
|
||||
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
struct in_addr local_addr, struct in_addr local_netmask,
|
||||
time_t now, int ad_reqd, int do_bit, int have_pseudoheader)
|
||||
time_t now, int ad_reqd, int do_bit, int have_pseudoheader,
|
||||
int *stale)
|
||||
{
|
||||
char *name = daemon->namebuff;
|
||||
unsigned char *p, *ansp;
|
||||
@@ -1423,6 +1438,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
size_t len;
|
||||
int rd_bit = (header->hb3 & HB3_RD);
|
||||
|
||||
if (stale)
|
||||
*stale = 0;
|
||||
|
||||
/* never answer queries with RD unset, to avoid cache snooping. */
|
||||
if (ntohs(header->ancount) != 0 ||
|
||||
ntohs(header->nscount) != 0 ||
|
||||
@@ -1471,13 +1489,22 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
while (--count != 0 && (crecp = cache_find_by_name(NULL, name, now, F_CNAME | F_NXDOMAIN)))
|
||||
{
|
||||
char *cname_target;
|
||||
|
||||
int stale_flag = 0;
|
||||
|
||||
if (crec_isstale(crecp, now))
|
||||
{
|
||||
if (stale)
|
||||
*stale = 1;
|
||||
|
||||
stale_flag = F_STALE;
|
||||
}
|
||||
|
||||
if (crecp->flags & F_NXDOMAIN)
|
||||
{
|
||||
if (qtype == T_CNAME)
|
||||
{
|
||||
if (!dryrun)
|
||||
log_query(crecp->flags, name, NULL, record_source(crecp->uid), 0);
|
||||
log_query(stale_flag | crecp->flags, name, NULL, record_source(crecp->uid), 0);
|
||||
auth = 0;
|
||||
nxdomain = 1;
|
||||
ans = 1;
|
||||
@@ -1499,7 +1526,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
|
||||
if (!dryrun)
|
||||
{
|
||||
log_query(crecp->flags, name, NULL, record_source(crecp->uid), 0);
|
||||
log_query(stale_flag | crecp->flags, name, NULL, record_source(crecp->uid), 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
crec_ttl(crecp, now), &nameoffset,
|
||||
T_CNAME, C_IN, "d", cname_target))
|
||||
@@ -1609,7 +1636,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
|
||||
if (addrlist)
|
||||
break;
|
||||
else
|
||||
else if (!(intr->flags & INP4))
|
||||
while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
|
||||
intr = intr->next;
|
||||
}
|
||||
@@ -1624,7 +1651,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
|
||||
if (addrlist)
|
||||
break;
|
||||
else
|
||||
else if (!(intr->flags & INP6))
|
||||
while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
|
||||
intr = intr->next;
|
||||
}
|
||||
@@ -1668,22 +1695,33 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
{
|
||||
do
|
||||
{
|
||||
int stale_flag = 0;
|
||||
|
||||
if (crec_isstale(crecp, now))
|
||||
{
|
||||
if (stale)
|
||||
*stale = 1;
|
||||
|
||||
stale_flag = F_STALE;
|
||||
}
|
||||
|
||||
/* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */
|
||||
if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||
continue;
|
||||
|
||||
|
||||
if (!(crecp->flags & F_DNSSECOK))
|
||||
sec_data = 0;
|
||||
|
||||
|
||||
ans = 1;
|
||||
|
||||
|
||||
if (crecp->flags & F_NEG)
|
||||
{
|
||||
auth = 0;
|
||||
if (crecp->flags & F_NXDOMAIN)
|
||||
nxdomain = 1;
|
||||
if (!dryrun)
|
||||
log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL, 0);
|
||||
log_query(stale_flag | (crecp->flags & ~F_FORWARD), name, &addr, NULL, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1691,9 +1729,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
auth = 0;
|
||||
if (!dryrun)
|
||||
{
|
||||
log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
|
||||
log_query(stale_flag | (crecp->flags & ~F_FORWARD), cache_get_name(crecp), &addr,
|
||||
record_source(crecp->uid), 0);
|
||||
|
||||
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
crec_ttl(crecp, now), NULL,
|
||||
T_PTR, C_IN, "d", cache_get_name(crecp)))
|
||||
@@ -1800,7 +1838,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
if ((crecp = cache_find_by_name(NULL, name, now, flag | F_NXDOMAIN | (dryrun ? F_NO_RR : 0))))
|
||||
{
|
||||
int localise = 0;
|
||||
|
||||
|
||||
/* See if a putative address is on the network from which we received
|
||||
the query, is so we'll filter other answers. */
|
||||
if (local_addr.s_addr != 0 && option_bool(OPT_LOCALISE) && flag == F_IPV4)
|
||||
@@ -1822,6 +1860,16 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
(rd_bit && (!do_bit || cache_validated(crecp)) ))
|
||||
do
|
||||
{
|
||||
int stale_flag = 0;
|
||||
|
||||
if (crec_isstale(crecp, now))
|
||||
{
|
||||
if (stale)
|
||||
*stale = 1;
|
||||
|
||||
stale_flag = F_STALE;
|
||||
}
|
||||
|
||||
/* don't answer wildcard queries with data not from /etc/hosts
|
||||
or DHCP leases */
|
||||
if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
|
||||
@@ -1839,7 +1887,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
if (!dryrun)
|
||||
// Pi-hole modification: Added record_source(crecp->uid) such that the subroutines know
|
||||
// where the reply came from (e.g. gravity.list)
|
||||
log_query(crecp->flags, name, NULL, record_source(crecp->uid), 0);
|
||||
log_query(stale_flag | crecp->flags, name, NULL, record_source(crecp->uid), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1856,10 +1904,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
ans = 1;
|
||||
if (!dryrun)
|
||||
{
|
||||
log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr,
|
||||
log_query(stale_flag | (crecp->flags & ~F_REVERSE), name, &crecp->addr,
|
||||
record_source(crecp->uid), 0);
|
||||
// ****************************** Pi-hole modification ******************************
|
||||
if(FTL_CNAME(name, crecp, daemon->log_display_id))
|
||||
const char *src = crecp != NULL ? crecp->flags & F_BIGNAME ? crecp->name.bname->name : crecp->name.sname : NULL;
|
||||
if(FTL_CNAME(name, src, daemon->log_display_id))
|
||||
{
|
||||
// Served from cache. This can happen if a domain hidden in the CNAME path
|
||||
// is only blocked for some but not all clients. In this case, the entire
|
||||
@@ -1979,6 +2028,15 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
rd_bit && (!do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))))
|
||||
do
|
||||
{
|
||||
int stale_flag = 0;
|
||||
|
||||
if (crec_isstale(crecp, now))
|
||||
{
|
||||
if (stale)
|
||||
*stale = 1;
|
||||
|
||||
stale_flag = F_STALE;
|
||||
}
|
||||
/* don't answer wildcard queries with data not from /etc/hosts or dhcp leases, except for NXDOMAIN */
|
||||
if (qtype == T_ANY && !(crecp->flags & (F_NXDOMAIN)))
|
||||
break;
|
||||
@@ -1994,12 +2052,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
if (crecp->flags & F_NXDOMAIN)
|
||||
nxdomain = 1;
|
||||
if (!dryrun)
|
||||
log_query(crecp->flags, name, NULL, NULL, 0);
|
||||
log_query(stale_flag | crecp->flags, name, NULL, NULL, 0);
|
||||
}
|
||||
else if (!dryrun)
|
||||
{
|
||||
char *target = blockdata_retrieve(crecp->addr.srv.target, crecp->addr.srv.targetlen, NULL);
|
||||
log_query(crecp->flags, name, NULL, NULL, 0);
|
||||
log_query(stale_flag | crecp->flags, name, NULL, NULL, 0);
|
||||
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
crec_ttl(crecp, now), NULL, T_SRV, C_IN, "sssd",
|
||||
|
||||
+16
-12
@@ -1153,15 +1153,22 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
tagif_netid = run_tag_if(&context->netid);
|
||||
}
|
||||
|
||||
log_tags(tagif_netid, ntohl(mess->xid));
|
||||
apply_delay(mess->xid, recvtime, tagif_netid);
|
||||
|
||||
if (option_bool(OPT_RAPID_COMMIT) && option_find(mess, sz, OPTION_RAPID_COMMIT, 0))
|
||||
{
|
||||
rapid_commit = 1;
|
||||
/* If a lease exists for this host and another address, squash it. */
|
||||
if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
|
||||
{
|
||||
lease_prune(lease, now);
|
||||
lease = NULL;
|
||||
}
|
||||
goto rapid_commit;
|
||||
}
|
||||
|
||||
log_tags(tagif_netid, ntohl(mess->xid));
|
||||
|
||||
daemon->metrics[METRIC_DHCPOFFER]++;
|
||||
log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
|
||||
|
||||
@@ -1420,21 +1427,18 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
/* DNSMASQ_REQUESTED_OPTIONS */
|
||||
if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 1)))
|
||||
{
|
||||
int len = option_len(opt);
|
||||
int i, len = option_len(opt);
|
||||
unsigned char *rop = option_ptr(opt, 0);
|
||||
char *q = daemon->namebuff;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
q += snprintf(q, MAXDNAME - (q - daemon->namebuff), "%d%s", rop[i], i + 1 == len ? "" : ",");
|
||||
}
|
||||
lease_add_extradata(lease, (unsigned char *)daemon->namebuff, (q - daemon->namebuff), 0);
|
||||
lease_add_extradata(lease, (unsigned char *)daemon->namebuff,
|
||||
sprintf(daemon->namebuff, "%u", rop[i]), (i + 1) == len ? 0 : ',');
|
||||
}
|
||||
else
|
||||
{
|
||||
add_extradata_opt(lease, NULL);
|
||||
}
|
||||
|
||||
lease_add_extradata(lease, NULL, 0, 0);
|
||||
|
||||
add_extradata_opt(lease, option_find(mess, sz, OPTION_MUD_URL_V4, 1));
|
||||
|
||||
/* space-concat tag set */
|
||||
if (!tagif_netid)
|
||||
add_extradata_opt(lease, NULL);
|
||||
|
||||
+61
-46
@@ -33,9 +33,9 @@ struct state {
|
||||
unsigned int mac_len, mac_type;
|
||||
};
|
||||
|
||||
static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz,
|
||||
static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t sz,
|
||||
struct in6_addr *client_addr, int is_unicast, time_t now);
|
||||
static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_t sz, int is_unicast, time_t now);
|
||||
static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbuff, size_t sz, int is_unicast, time_t now);
|
||||
static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_opts);
|
||||
static void log6_packet(struct state *state, char *type, struct in6_addr *addr, char *string);
|
||||
static void log6_quiet(struct state *state, char *type, struct in6_addr *addr, char *string);
|
||||
@@ -104,12 +104,12 @@ unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *if
|
||||
}
|
||||
|
||||
/* This cost me blood to write, it will probably cost you blood to understand - srk. */
|
||||
static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz,
|
||||
static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t sz,
|
||||
struct in6_addr *client_addr, int is_unicast, time_t now)
|
||||
{
|
||||
void *end = inbuff + sz;
|
||||
void *opts = inbuff + 34;
|
||||
int msg_type = *((unsigned char *)inbuff);
|
||||
int msg_type = *inbuff;
|
||||
unsigned char *outmsgtypep;
|
||||
void *opt;
|
||||
struct dhcp_vendor *vendor;
|
||||
@@ -259,15 +259,15 @@ static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_t sz, int is_unicast, time_t now)
|
||||
static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbuff, size_t sz, int is_unicast, time_t now)
|
||||
{
|
||||
void *opt;
|
||||
int i, o, o1, start_opts;
|
||||
int i, o, o1, start_opts, start_msg;
|
||||
struct dhcp_opt *opt_cfg;
|
||||
struct dhcp_netid *tagif;
|
||||
struct dhcp_config *config = NULL;
|
||||
struct dhcp_netid known_id, iface_id, v6_id;
|
||||
unsigned char *outmsgtypep;
|
||||
unsigned char outmsgtype;
|
||||
struct dhcp_vendor *vendor;
|
||||
struct dhcp_context *context_tmp;
|
||||
struct dhcp_mac *mac_opt;
|
||||
@@ -296,12 +296,13 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
v6_id.next = state->tags;
|
||||
state->tags = &v6_id;
|
||||
|
||||
/* copy over transaction-id, and save pointer to message type */
|
||||
if (!(outmsgtypep = put_opt6(inbuff, 4)))
|
||||
start_msg = save_counter(-1);
|
||||
/* copy over transaction-id */
|
||||
if (!put_opt6(inbuff, 4))
|
||||
return 0;
|
||||
start_opts = save_counter(-1);
|
||||
state->xid = outmsgtypep[3] | outmsgtypep[2] << 8 | outmsgtypep[1] << 16;
|
||||
|
||||
state->xid = inbuff[3] | inbuff[2] << 8 | inbuff[1] << 16;
|
||||
|
||||
/* We're going to be linking tags from all context we use.
|
||||
mark them as unused so we don't link one twice and break the list */
|
||||
for (context_tmp = state->context; context_tmp; context_tmp = context_tmp->current)
|
||||
@@ -347,7 +348,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
(msg_type == DHCP6REQUEST || msg_type == DHCP6RENEW || msg_type == DHCP6RELEASE || msg_type == DHCP6DECLINE))
|
||||
|
||||
{
|
||||
*outmsgtypep = DHCP6REPLY;
|
||||
outmsgtype = DHCP6REPLY;
|
||||
o1 = new_opt6(OPTION6_STATUS_CODE);
|
||||
put_opt6_short(DHCP6USEMULTI);
|
||||
put_opt6_string("Use multicast");
|
||||
@@ -619,11 +620,11 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
struct dhcp_netid *solicit_tags;
|
||||
struct dhcp_context *c;
|
||||
|
||||
*outmsgtypep = DHCP6ADVERTISE;
|
||||
outmsgtype = DHCP6ADVERTISE;
|
||||
|
||||
if (opt6_find(state->packet_options, state->end, OPTION6_RAPID_COMMIT, 0))
|
||||
{
|
||||
*outmsgtypep = DHCP6REPLY;
|
||||
outmsgtype = DHCP6REPLY;
|
||||
state->lease_allocate = 1;
|
||||
o = new_opt6(OPTION6_RAPID_COMMIT);
|
||||
end_opt6(o);
|
||||
@@ -809,7 +810,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
int start = save_counter(-1);
|
||||
|
||||
/* set reply message type */
|
||||
*outmsgtypep = DHCP6REPLY;
|
||||
outmsgtype = DHCP6REPLY;
|
||||
state->lease_allocate = 1;
|
||||
|
||||
log6_quiet(state, "DHCPREQUEST", NULL, ignore ? _("ignored") : NULL);
|
||||
@@ -924,7 +925,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
int address_assigned = 0;
|
||||
|
||||
/* set reply message type */
|
||||
*outmsgtypep = DHCP6REPLY;
|
||||
outmsgtype = DHCP6REPLY;
|
||||
|
||||
log6_quiet(state, msg_type == DHCP6RENEW ? "DHCPRENEW" : "DHCPREBIND", NULL, NULL);
|
||||
|
||||
@@ -1057,7 +1058,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
int good_addr = 0;
|
||||
|
||||
/* set reply message type */
|
||||
*outmsgtypep = DHCP6REPLY;
|
||||
outmsgtype = DHCP6REPLY;
|
||||
|
||||
log6_quiet(state, "DHCPCONFIRM", NULL, NULL);
|
||||
|
||||
@@ -1121,7 +1122,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
log6_quiet(state, "DHCPINFORMATION-REQUEST", NULL, ignore ? _("ignored") : state->hostname);
|
||||
if (ignore)
|
||||
return 0;
|
||||
*outmsgtypep = DHCP6REPLY;
|
||||
outmsgtype = DHCP6REPLY;
|
||||
tagif = add_options(state, 1);
|
||||
break;
|
||||
}
|
||||
@@ -1130,7 +1131,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
case DHCP6RELEASE:
|
||||
{
|
||||
/* set reply message type */
|
||||
*outmsgtypep = DHCP6REPLY;
|
||||
outmsgtype = DHCP6REPLY;
|
||||
|
||||
log6_quiet(state, "DHCPRELEASE", NULL, NULL);
|
||||
|
||||
@@ -1195,7 +1196,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
case DHCP6DECLINE:
|
||||
{
|
||||
/* set reply message type */
|
||||
*outmsgtypep = DHCP6REPLY;
|
||||
outmsgtype = DHCP6REPLY;
|
||||
|
||||
log6_quiet(state, "DHCPDECLINE", NULL, NULL);
|
||||
|
||||
@@ -1275,7 +1276,12 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Fill in the message type. Note that we store the offset,
|
||||
not a direct pointer, since the packet memory may have been
|
||||
reallocated. */
|
||||
((unsigned char *)(daemon->outpacket.iov_base))[start_msg] = outmsgtype;
|
||||
|
||||
log_tags(tagif, state->xid);
|
||||
log6_opts(0, state->xid, daemon->outpacket.iov_base + start_opts, daemon->outpacket.iov_base + save_counter(-1));
|
||||
|
||||
@@ -1873,23 +1879,24 @@ static void update_leases(struct state *state, struct dhcp_context *context, str
|
||||
#ifdef HAVE_SCRIPT
|
||||
if (daemon->lease_change_command)
|
||||
{
|
||||
void *class_opt;
|
||||
void *opt;
|
||||
|
||||
lease->flags |= LEASE_CHANGED;
|
||||
free(lease->extradata);
|
||||
lease->extradata = NULL;
|
||||
lease->extradata_size = lease->extradata_len = 0;
|
||||
lease->vendorclass_count = 0;
|
||||
|
||||
if ((class_opt = opt6_find(state->packet_options, state->end, OPTION6_VENDOR_CLASS, 4)))
|
||||
if ((opt = opt6_find(state->packet_options, state->end, OPTION6_VENDOR_CLASS, 4)))
|
||||
{
|
||||
void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt));
|
||||
void *enc_opt, *enc_end = opt6_ptr(opt, opt6_len(opt));
|
||||
lease->vendorclass_count++;
|
||||
/* send enterprise number first */
|
||||
sprintf(daemon->dhcp_buff2, "%u", opt6_uint(class_opt, 0, 4));
|
||||
sprintf(daemon->dhcp_buff2, "%u", opt6_uint(opt, 0, 4));
|
||||
lease_add_extradata(lease, (unsigned char *)daemon->dhcp_buff2, strlen(daemon->dhcp_buff2), 0);
|
||||
|
||||
if (opt6_len(class_opt) >= 6)
|
||||
for (enc_opt = opt6_ptr(class_opt, 4); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
|
||||
if (opt6_len(opt) >= 6)
|
||||
for (enc_opt = opt6_ptr(opt, 4); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
|
||||
{
|
||||
lease->vendorclass_count++;
|
||||
lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0);
|
||||
@@ -1899,6 +1906,24 @@ static void update_leases(struct state *state, struct dhcp_context *context, str
|
||||
lease_add_extradata(lease, (unsigned char *)state->client_hostname,
|
||||
state->client_hostname ? strlen(state->client_hostname) : 0, 0);
|
||||
|
||||
/* DNSMASQ_REQUESTED_OPTIONS */
|
||||
if ((opt = opt6_find(state->packet_options, state->end, OPTION6_ORO, 2)))
|
||||
{
|
||||
int i, len = opt6_len(opt)/2;
|
||||
u16 *rop = opt6_ptr(opt, 0);
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
lease_add_extradata(lease, (unsigned char *)daemon->namebuff,
|
||||
sprintf(daemon->namebuff, "%u", ntohs(rop[i])), (i + 1) == len ? 0 : ',');
|
||||
}
|
||||
else
|
||||
lease_add_extradata(lease, NULL, 0, 0);
|
||||
|
||||
if ((opt = opt6_find(state->packet_options, state->end, OPTION6_MUD_URL, 1)))
|
||||
lease_add_extradata(lease, opt6_ptr(opt, 0), opt6_len(opt), 0);
|
||||
else
|
||||
lease_add_extradata(lease, NULL, 0, 0);
|
||||
|
||||
/* space-concat tag set */
|
||||
if (!tagif && !context->netid.net)
|
||||
lease_add_extradata(lease, NULL, 0, 0);
|
||||
@@ -1928,10 +1953,10 @@ static void update_leases(struct state *state, struct dhcp_context *context, str
|
||||
|
||||
lease_add_extradata(lease, (unsigned char *)daemon->addrbuff, state->link_address ? strlen(daemon->addrbuff) : 0, 0);
|
||||
|
||||
if ((class_opt = opt6_find(state->packet_options, state->end, OPTION6_USER_CLASS, 2)))
|
||||
if ((opt = opt6_find(state->packet_options, state->end, OPTION6_USER_CLASS, 2)))
|
||||
{
|
||||
void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt));
|
||||
for (enc_opt = opt6_ptr(class_opt, 0); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
|
||||
void *enc_opt, *enc_end = opt6_ptr(opt, opt6_len(opt));
|
||||
for (enc_opt = opt6_ptr(opt, 0); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
|
||||
lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0);
|
||||
}
|
||||
}
|
||||
@@ -2156,15 +2181,12 @@ int relay_upstream6(int iface_index, ssize_t sz,
|
||||
if (relay->iface_index != 0 && relay->iface_index == iface_index)
|
||||
{
|
||||
union mysockaddr to;
|
||||
union all_addr from;
|
||||
|
||||
/* source address == relay address */
|
||||
from.addr6 = relay->local.addr6;
|
||||
|
||||
memcpy(&header[2], &relay->local.addr6, IN6ADDRSZ);
|
||||
|
||||
to.sa.sa_family = AF_INET6;
|
||||
to.in6.sin6_addr = relay->server.addr6;
|
||||
to.in6.sin6_port = htons(DHCPV6_SERVER_PORT);
|
||||
to.in6.sin6_port = htons(relay->port);
|
||||
to.in6.sin6_flowinfo = 0;
|
||||
to.in6.sin6_scope_id = 0;
|
||||
|
||||
@@ -2181,18 +2203,11 @@ int relay_upstream6(int iface_index, ssize_t sz,
|
||||
}
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
{
|
||||
union mysockaddr fromsock;
|
||||
fromsock.in6.sin6_port = htons(DHCPV6_SERVER_PORT);
|
||||
fromsock.in6.sin6_addr = from.addr6;
|
||||
fromsock.sa.sa_family = AF_INET6;
|
||||
fromsock.in6.sin6_flowinfo = 0;
|
||||
fromsock.in6.sin6_scope_id = 0;
|
||||
|
||||
dump_packet(DUMP_DHCPV6, (void *)daemon->outpacket.iov_base, save_counter(-1), &fromsock, &to, 0);
|
||||
}
|
||||
dump_packet_udp(DUMP_DHCPV6, (void *)daemon->outpacket.iov_base, save_counter(-1), NULL, &to, daemon->dhcp6fd);
|
||||
#endif
|
||||
send_from(daemon->dhcp6fd, 0, daemon->outpacket.iov_base, save_counter(-1), &to, &from, 0);
|
||||
|
||||
while (retry_send(sendto(daemon->dhcp6fd, (void *)daemon->outpacket.iov_base, save_counter(-1),
|
||||
0, (struct sockaddr *)&to, sa_len(&to))));
|
||||
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
{
|
||||
|
||||
+6
-10
@@ -159,7 +159,7 @@ static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, i
|
||||
/* mode may be remove EDNS0 or DNSSEC RRs or remove A or AAAA from answer section. */
|
||||
size_t rrfilter(struct dns_header *header, size_t plen, int mode)
|
||||
{
|
||||
static unsigned char **rrs;
|
||||
static unsigned char **rrs = NULL;
|
||||
static int rr_sz = 0;
|
||||
|
||||
unsigned char *p = (unsigned char *)(header+1);
|
||||
@@ -339,15 +339,11 @@ int expand_workspace(unsigned char ***wkspc, int *szp, int new)
|
||||
return 0;
|
||||
|
||||
new += 5;
|
||||
|
||||
if (!(p = whine_malloc(new * sizeof(unsigned char *))))
|
||||
return 0;
|
||||
|
||||
if (old != 0 && *wkspc)
|
||||
{
|
||||
memcpy(p, *wkspc, old * sizeof(unsigned char *));
|
||||
free(*wkspc);
|
||||
}
|
||||
|
||||
if (!(p = whine_realloc(*wkspc, new * sizeof(unsigned char *))))
|
||||
return 0;
|
||||
|
||||
memset(p+old, 0, new-old);
|
||||
|
||||
*wkspc = p;
|
||||
*szp = new;
|
||||
|
||||
+4
-4
@@ -97,7 +97,7 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
return;
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_TFTP, (void *)packet, len, (union mysockaddr *)&peer, NULL, TFTP_PORT);
|
||||
dump_packet_udp(DUMP_TFTP, (void *)packet, len, (union mysockaddr *)&peer, NULL, listen->tftpfd);
|
||||
#endif
|
||||
|
||||
/* Can always get recvd interface for IPv6 */
|
||||
@@ -488,7 +488,7 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), packet, len, &peer, &addra, if_index);
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_TFTP, (void *)packet, len, NULL, (union mysockaddr *)&peer, TFTP_PORT);
|
||||
dump_packet_udp(DUMP_TFTP, (void *)packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd);
|
||||
#endif
|
||||
|
||||
if (is_err)
|
||||
@@ -610,7 +610,7 @@ void check_tftp_listeners(time_t now)
|
||||
while(retry_send(sendto(transfer->sockfd, daemon->packet, len, 0, &peer.sa, sa_len(&peer))));
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&peer, TFTP_PORT);
|
||||
dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -650,7 +650,7 @@ void check_tftp_listeners(time_t now)
|
||||
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len,
|
||||
&transfer->peer, &transfer->source, transfer->if_index);
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&transfer->peer, TFTP_PORT);
|
||||
dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&transfer->peer, transfer->sockfd);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -336,6 +336,16 @@ void *whine_malloc(size_t size)
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *whine_realloc(void *ptr, size_t size)
|
||||
{
|
||||
void *ret = realloc(ptr, size);
|
||||
|
||||
if (!ret)
|
||||
my_syslog(LOG_ERR, _("failed to reallocate %d bytes"), (int) size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2)
|
||||
{
|
||||
if (s1->sa.sa_family == s2->sa.sa_family)
|
||||
@@ -354,6 +364,19 @@ int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sockaddr_isnull(const union mysockaddr *s)
|
||||
{
|
||||
if (s->sa.sa_family == AF_INET &&
|
||||
s->in.sin_addr.s_addr == 0)
|
||||
return 1;
|
||||
|
||||
if (s->sa.sa_family == AF_INET6 &&
|
||||
IN6_IS_ADDR_UNSPECIFIED(&s->in6.sin6_addr))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sa_len(union mysockaddr *addr)
|
||||
{
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
@@ -447,6 +470,15 @@ time_t dnsmasq_time(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
u32 dnsmasq_milliseconds(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
return (tv.tv_sec) * 1000 + (tv.tv_usec / 1000);
|
||||
}
|
||||
|
||||
int netmask_length(struct in_addr mask)
|
||||
{
|
||||
int zero_count = 0;
|
||||
|
||||
+81
-47
@@ -77,6 +77,7 @@ static void check_pihole_PTR(char *domain);
|
||||
#define query_set_dnssec(query, dnssec) _query_set_dnssec(query, dnssec, __FILE__, __LINE__)
|
||||
static void _query_set_dnssec(queriesData *query, const enum dnssec_status dnssec, const char *file, const int line);
|
||||
static char *get_ptrname(struct in_addr *addr);
|
||||
static const char *check_dnsmasq_name(const char *name);
|
||||
|
||||
// Static blocking metadata
|
||||
static const char *blockingreason = "";
|
||||
@@ -98,22 +99,17 @@ static struct {
|
||||
static union mysockaddr last_server = {{ 0 }};
|
||||
|
||||
unsigned char* pihole_privacylevel = &config.privacylevel;
|
||||
const char *flagnames[] = {"F_IMMORTAL ", "F_NAMEP ", "F_REVERSE ", "F_FORWARD ", "F_DHCP ", "F_NEG ", "F_HOSTS ", "F_IPV4 ", "F_IPV6 ", "F_BIGNAME ", "F_NXDOMAIN ", "F_CNAME ", "F_DNSKEY ", "F_CONFIG ", "F_DS ", "F_DNSSECOK ", "F_UPSTREAM ", "F_RRNAME ", "F_SERVER ", "F_QUERY ", "F_NOERR ", "F_AUTH ", "F_DNSSEC ", "F_KEYTAG ", "F_SECSTAT ", "F_NO_RR ", "F_IPSET ", "F_NOEXTRA ", "F_SERVFAIL", "F_RCODE"};
|
||||
const char *flagnames[] = {"F_IMMORTAL ", "F_NAMEP ", "F_REVERSE ", "F_FORWARD ", "F_DHCP ", "F_NEG ", "F_HOSTS ", "F_IPV4 ", "F_IPV6 ", "F_BIGNAME ", "F_NXDOMAIN ", "F_CNAME ", "F_DNSKEY ", "F_CONFIG ", "F_DS ", "F_DNSSECOK ", "F_UPSTREAM ", "F_RRNAME ", "F_SERVER ", "F_QUERY ", "F_NOERR ", "F_AUTH ", "F_DNSSEC ", "F_KEYTAG ", "F_SECSTAT ", "F_NO_RR ", "F_IPSET ", "F_NOEXTRA ", "F_SERVFAIL", "F_RCODE", "F_SRV", "F_STALE" };
|
||||
|
||||
void FTL_hook(unsigned int flags, char *name, union all_addr *addr, char *arg, int id, unsigned short type, const char* file, const int line)
|
||||
void FTL_hook(unsigned int flags, const char *name, union all_addr *addr, char *arg, int id, unsigned short type, const char* file, const int line)
|
||||
{
|
||||
// Extract filename from path
|
||||
const char *path = short_path(file);
|
||||
log_debug(DEBUG_FLAGS, "Processing FTL hook from %s:%d...", path, line);
|
||||
log_debug(DEBUG_FLAGS, "Processing FTL hook from %s:%d (name: \"%s\")...", path, line, name);
|
||||
print_flags(flags);
|
||||
|
||||
// Special domain name handling
|
||||
// 1. Substitute "(NULL)" if no name is available (should not happen)
|
||||
// 2. Substitute "." if we are querying the root domain (e.g. DNSKEY)
|
||||
if(!name)
|
||||
name = (char*)"(NULL)";
|
||||
else if(!name[0])
|
||||
name = (char*)".";
|
||||
// Check domain name received from dnsmasq
|
||||
name = check_dnsmasq_name(name);
|
||||
|
||||
// Note: The order matters here!
|
||||
if((flags & F_QUERY) && (flags & F_FORWARD))
|
||||
@@ -139,10 +135,29 @@ void FTL_hook(unsigned int flags, char *name, union all_addr *addr, char *arg, i
|
||||
// derive the real query type from the arg string
|
||||
unsigned short qtype = type;
|
||||
if(strcmp(arg, "dnssec-query[DNSKEY]") == 0)
|
||||
{
|
||||
qtype = T_DNSKEY;
|
||||
arg = (char*)"dnssec-query";
|
||||
}
|
||||
else if(strcmp(arg, "dnssec-query[DS]") == 0)
|
||||
{
|
||||
qtype = T_DS;
|
||||
arg = (char*)"dnssec-query";
|
||||
arg = (char*)"dnssec-query";
|
||||
}
|
||||
else if(strcmp(arg, "dnssec-retry[DNSKEY]") == 0)
|
||||
{
|
||||
qtype = T_DNSKEY;
|
||||
arg = (char*)"dnssec-retry";
|
||||
}
|
||||
else if(strcmp(arg, "dnssec-retry[DS]") == 0)
|
||||
{
|
||||
qtype = T_DS;
|
||||
arg = (char*)"dnssec-retry";
|
||||
}
|
||||
else
|
||||
{
|
||||
arg = (char*)"dnssec-unknown";
|
||||
}
|
||||
|
||||
_FTL_new_query(flags, name, NULL, arg, qtype, id, &edns, INTERNAL, file, line);
|
||||
// forwarded upstream (type is used to store the upstream port)
|
||||
@@ -159,6 +174,7 @@ void FTL_hook(unsigned int flags, char *name, union all_addr *addr, char *arg, i
|
||||
// This is inspired by make_local_answer()
|
||||
size_t _FTL_make_answer(struct dns_header *header, char *limit, const size_t len, int *ede, const char *file, const int line)
|
||||
{
|
||||
log_debug(DEBUG_FLAGS, "FTL_make_answer() called from %s:%d", short_path(file), line);
|
||||
// Exit early if there are no questions in this query
|
||||
if(ntohs(header->qdcount) == 0)
|
||||
return 0;
|
||||
@@ -501,6 +517,9 @@ bool _FTL_new_query(const unsigned int flags, const char *name,
|
||||
break;
|
||||
}
|
||||
|
||||
// Check domain name received from dnsmasq
|
||||
name = check_dnsmasq_name(name);
|
||||
|
||||
// If domain is "pi.hole" or the local hostname we skip analyzing this query
|
||||
// and, instead, immediately reply with the IP address - these queries are not further analyzed
|
||||
if(is_pihole_domain(name))
|
||||
@@ -1074,7 +1093,7 @@ static bool check_domain_blocked(const char *domain, const int clientID,
|
||||
return false;
|
||||
|
||||
// Check domains against exact blacklist
|
||||
enum db_result blacklist = in_denylist(domain, client);
|
||||
enum db_result blacklist = in_denylist(domain, dns_cache, client);
|
||||
if(blacklist == FOUND)
|
||||
{
|
||||
// Set new status
|
||||
@@ -1140,10 +1159,9 @@ static bool check_domain_blocked(const char *domain, const int clientID,
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check domain against deny regex filters
|
||||
// Skipped when the domain is whitelisted or blocked by exact denylist or gravity
|
||||
int regex_idx = 0;
|
||||
if((regex_idx = match_regex(domain, dns_cache, client->id, REGEX_DENY, false)) > -1)
|
||||
// Check domain against blacklist regex filters
|
||||
// Skipped when the domain is whitelisted or blocked by exact blacklist or gravity
|
||||
if(in_regex(domain, dns_cache, client-> id, REGEX_DENY))
|
||||
{
|
||||
// Set new status
|
||||
*new_status = QUERY_REGEX;
|
||||
@@ -1151,14 +1169,13 @@ static bool check_domain_blocked(const char *domain, const int clientID,
|
||||
|
||||
// Mark domain as regex matched for this client
|
||||
set_dnscache_blockingstatus(dns_cache, client, REGEX_BLOCKED, domain);
|
||||
dns_cache->deny_regex_id = regex_idx;
|
||||
|
||||
// Regex may be overwriting reply type for this domain
|
||||
if(dns_cache->force_reply != REPLY_UNKNOWN)
|
||||
force_next_DNS_reply = dns_cache->force_reply;
|
||||
|
||||
// Store ID of this regex (fork-private)
|
||||
last_regex_idx = regex_idx;
|
||||
last_regex_idx = dns_cache->domainlist_id;
|
||||
|
||||
// We block this domain
|
||||
return true;
|
||||
@@ -1235,7 +1252,7 @@ static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const c
|
||||
}
|
||||
|
||||
// Get cache pointer
|
||||
unsigned int cacheID = findCacheID(domainID, clientID, query->type);
|
||||
unsigned int cacheID = findCacheID(domainID, clientID, query->type, true);
|
||||
DNSCacheData *dns_cache = getDNSCache(cacheID, true);
|
||||
if(dns_cache == NULL)
|
||||
{
|
||||
@@ -1302,7 +1319,7 @@ static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const c
|
||||
if(!query->flags.allowed)
|
||||
{
|
||||
force_next_DNS_reply = dns_cache->force_reply;
|
||||
last_regex_idx = dns_cache->deny_regex_id;
|
||||
last_regex_idx = dns_cache->domainlist_id;
|
||||
query_blocked(query, domain, client, QUERY_REGEX);
|
||||
return true;
|
||||
}
|
||||
@@ -1327,7 +1344,7 @@ static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const c
|
||||
log_debug(DEBUG_QUERIES, "%s is known as special domain", domainstr);
|
||||
|
||||
force_next_DNS_reply = dns_cache->force_reply;
|
||||
query_blocked(query, domain, client, QUERY_CACHE);
|
||||
query_blocked(query, domain, client, QUERY_SPECIAL_DOMAIN);
|
||||
return true;
|
||||
break;
|
||||
|
||||
@@ -1370,9 +1387,13 @@ static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const c
|
||||
domainstr = strdup(domainstr);
|
||||
const char *blockedDomain = domainstr;
|
||||
|
||||
// Check whitelist (exact + regex) for match
|
||||
// Check exact whitelist for match
|
||||
query->flags.allowed = in_allowlist(domainstr, dns_cache, client) == FOUND;
|
||||
|
||||
// If not found: Check regex whitelist for match
|
||||
if(!query->flags.allowed)
|
||||
query->flags.allowed = in_regex(domainstr, dns_cache, client->id, REGEX_ALLOW);
|
||||
|
||||
// Check blacklist (exact + regex) and gravity for queried domain
|
||||
unsigned char new_status = QUERY_UNKNOWN;
|
||||
bool db_okay = true;
|
||||
@@ -1433,12 +1454,9 @@ static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const c
|
||||
}
|
||||
|
||||
|
||||
bool _FTL_CNAME(const char *domain, const struct crec *cpp, const int id, const char* file, const int line)
|
||||
bool _FTL_CNAME(const char *dst, const char *src, const int id, const char* file, const int line)
|
||||
{
|
||||
// Get CNAME destination and source (if applicable)
|
||||
const char *dst = domain;
|
||||
const char *src = cpp != NULL ? cpp->flags & F_BIGNAME ? cpp->name.bname->name : cpp->name.sname : NULL;
|
||||
log_debug(DEBUG_QUERIES, "FTL_CNAME called with: src = %s, dst = %s, id = %d", src, domain, id);
|
||||
log_debug(DEBUG_QUERIES, "FTL_CNAME called with: src = %s, dst = %s, id = %d", src, dst, id);
|
||||
|
||||
// Does the user want to skip deep CNAME inspection?
|
||||
if(!config.cname_deep_inspection)
|
||||
@@ -1485,7 +1503,7 @@ bool _FTL_CNAME(const char *domain, const struct crec *cpp, const int id, const
|
||||
|
||||
// child_domain = Intermediate domain in CNAME path
|
||||
// This is the domain which was queried later in this chain
|
||||
char *child_domain = strdup(domain);
|
||||
char *child_domain = strdup(dst);
|
||||
// Convert to lowercase for matching
|
||||
strtolower(child_domain);
|
||||
const int child_domainID = findDomainID(child_domain, false);
|
||||
@@ -1529,8 +1547,8 @@ bool _FTL_CNAME(const char *domain, const struct crec *cpp, const int id, const
|
||||
else if(query->status == QUERY_REGEX)
|
||||
{
|
||||
// Get parent and child DNS cache entries
|
||||
const int parent_cacheID = findCacheID(parent_domainID, clientID, query->type);
|
||||
const int child_cacheID = findCacheID(child_domainID, clientID, query->type);
|
||||
const int parent_cacheID = findCacheID(parent_domainID, clientID, query->type, false);
|
||||
const int child_cacheID = findCacheID(child_domainID, clientID, query->type, false);
|
||||
|
||||
// Get cache pointers
|
||||
DNSCacheData *parent_cache = getDNSCache(parent_cacheID, true);
|
||||
@@ -1538,9 +1556,7 @@ bool _FTL_CNAME(const char *domain, const struct crec *cpp, const int id, const
|
||||
|
||||
// Propagate ID of responsible regex up from the child to the parent domain
|
||||
if(parent_cache != NULL && child_cache != NULL)
|
||||
{
|
||||
child_cache->deny_regex_id = parent_cache->deny_regex_id;
|
||||
}
|
||||
child_cache->domainlist_id = parent_cache->domainlist_id;
|
||||
|
||||
// Set status
|
||||
query_set_status(query, QUERY_REGEX_CNAME);
|
||||
@@ -1553,10 +1569,7 @@ bool _FTL_CNAME(const char *domain, const struct crec *cpp, const int id, const
|
||||
}
|
||||
|
||||
// Debug logging for deep CNAME inspection (if enabled)
|
||||
if(src == NULL)
|
||||
log_debug(DEBUG_QUERIES, "Query %d: CNAME %s", id, dst);
|
||||
else
|
||||
log_debug(DEBUG_QUERIES, "Query %d: CNAME %s ---> %s", id, src, dst);
|
||||
log_debug(DEBUG_QUERIES, "Query %d: CNAME %s ---> %s", id, src, dst);
|
||||
|
||||
// Mark query for updating in the database
|
||||
query->flags.database.changed = true;
|
||||
@@ -1728,6 +1741,7 @@ void FTL_dnsmasq_reload(void)
|
||||
// Gravity database updates
|
||||
// - (Re-)open gravity database connection
|
||||
// - Get number of blocked domains
|
||||
// - check adlist table for inaccessible adlists
|
||||
// - Read and compile regex filters (incl. per-client)
|
||||
// - Flush FTL's DNS cache
|
||||
set_event(RELOAD_GRAVITY);
|
||||
@@ -1858,6 +1872,9 @@ static void FTL_reply(const unsigned int flags, const char *name, const union al
|
||||
log_debug(DEBUG_FLAGS, "***** Unknown cache query");
|
||||
}
|
||||
|
||||
// Is this a stale reply?
|
||||
const bool stale = flags & F_STALE;
|
||||
|
||||
// Possible debugging output
|
||||
if(config.debug & DEBUG_QUERIES)
|
||||
{
|
||||
@@ -1909,16 +1926,18 @@ static void FTL_reply(const unsigned int flags, const char *name, const union al
|
||||
|
||||
if(cached || last_server.sa.sa_family == 0)
|
||||
// Log cache or upstream reply from unknown source
|
||||
log_debug(DEBUG_QUERIES, "**** got %s reply: %s is %s (ID %i, %s:%i)",
|
||||
cached ? "cache" : "upstream", dispname, answer, id, file, line);
|
||||
log_debug(DEBUG_QUERIES, "**** got %s%s reply: %s is %s (ID %i, %s:%i)",
|
||||
stale ? "stale ": "", cached ? "cache" : "upstream",
|
||||
dispname, answer, id, file, line);
|
||||
else
|
||||
{
|
||||
char ip[ADDRSTRLEN+1] = { 0 };
|
||||
in_port_t port = 0;
|
||||
mysockaddr_extract_ip_port(&last_server, ip, &port);
|
||||
// Log server which replied to our request
|
||||
log_debug(DEBUG_QUERIES, "**** got %s reply from %s#%d: %s is %s (ID %i, %s:%i)",
|
||||
cached ? "cache" : "upstream", ip, port, dispname, answer, id, file, line);
|
||||
log_debug(DEBUG_QUERIES, "**** got %s%s reply from %s#%d: %s is %s (ID %i, %s:%i)",
|
||||
stale ? "stale ": "", cached ? "cache" : "upstream",
|
||||
ip, port, dispname, answer, id, file, line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1967,13 +1986,16 @@ static void FTL_reply(const unsigned int flags, const char *name, const union al
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine query status (live or stale data?)
|
||||
const enum query_status qs = stale ? QUERY_CACHE_STALE : QUERY_CACHE;
|
||||
|
||||
// This is either a reply served from cache or a blocked query (which appear
|
||||
// to be from cache because of flags containing F_HOSTS)
|
||||
if(cached)
|
||||
{
|
||||
// Set status of this query only if this is not a blocked query
|
||||
if(!is_blocked(query->status))
|
||||
query_set_status(query, QUERY_CACHE);
|
||||
query_set_status(query, qs);
|
||||
|
||||
// Detect if returned IP indicates that this query was blocked
|
||||
const enum query_status new_status = detect_blocked_IP(flags, addr, query, domain);
|
||||
@@ -2017,7 +2039,7 @@ static void FTL_reply(const unsigned int flags, const char *name, const union al
|
||||
// Answered from a custom (user provided) cache file or because
|
||||
// we're the authoritative DNS server (e.g. DHCP server and this
|
||||
// is our own domain)
|
||||
query_set_status(query, QUERY_CACHE);
|
||||
query_set_status(query, qs);
|
||||
|
||||
// Save reply type and update individual reply counters
|
||||
query_set_reply(flags, 0, addr, query, response);
|
||||
@@ -2597,8 +2619,7 @@ static void _query_set_reply(const unsigned int flags, const enum reply_type rep
|
||||
if(config.debug & DEBUG_QUERIES)
|
||||
{
|
||||
const char *path = short_path(file);
|
||||
log_debug(DEBUG_QUERIES, "Set reply to %s (%d) in %s:%d", get_query_reply_str(query->reply), query->reply,
|
||||
path, line);
|
||||
log_debug(DEBUG_QUERIES, "Set reply to %s (%d) in %s:%d", get_query_reply_str(new_reply), new_reply, path, line);
|
||||
if(query->reply != REPLY_UNKNOWN && query->reply != new_reply)
|
||||
log_debug(DEBUG_QUERIES, "Reply of query %i was %s now changing to %s", query->id,
|
||||
get_query_reply_str(query->reply), get_query_reply_str(new_reply));
|
||||
@@ -3278,8 +3299,8 @@ int check_struct_sizes(void)
|
||||
result += check_one_struct("overTimeData", sizeof(overTimeData), 32, 24);
|
||||
result += check_one_struct("regexData", sizeof(regexData), 56, 44);
|
||||
result += check_one_struct("SharedMemory", sizeof(SharedMemory), 24, 12);
|
||||
result += check_one_struct("ShmSettings", sizeof(ShmSettings), 12, 12);
|
||||
result += check_one_struct("countersStruct", sizeof(countersStruct), 292, 292);
|
||||
result += check_one_struct("ShmSettings", sizeof(ShmSettings), 16, 16);
|
||||
result += check_one_struct("countersStruct", sizeof(countersStruct), 248, 248);
|
||||
result += check_one_struct("sqlite3_stmt_vec", sizeof(sqlite3_stmt_vec), 32, 16);
|
||||
|
||||
if(result == 0)
|
||||
@@ -3287,3 +3308,16 @@ int check_struct_sizes(void)
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static const char *check_dnsmasq_name(const char *name)
|
||||
{
|
||||
// Special domain name handling
|
||||
if(!name)
|
||||
// 1. Substitute "(NULL)" if no name is available (should not happen)
|
||||
return "(NULL)";
|
||||
else if(!name[0])
|
||||
// 2. Substitute "." if we are querying the root domain (e.g. DNSKEY)
|
||||
return ".";
|
||||
// else
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -16,12 +16,10 @@
|
||||
#include "edns0.h"
|
||||
#include "cache_info.h"
|
||||
|
||||
extern int socketfd, telnetfd4, telnetfd6;
|
||||
extern unsigned char *pihole_privacylevel;
|
||||
extern unsigned char* pihole_privacylevel;
|
||||
enum protocol { TCP, UDP, INTERNAL };
|
||||
|
||||
enum protocol { TCP, UDP, INTERNAL } __attribute__ ((packed));
|
||||
|
||||
void FTL_hook(unsigned int flags, char *name, union all_addr *addr, char *arg, int id, unsigned short type, const char* file, const int line);
|
||||
void FTL_hook(unsigned int flags, const char *name, union all_addr *addr, char *arg, int id, unsigned short type, const char* file, const int line);
|
||||
|
||||
#define FTL_iface(iface, addr, addrfamily) _FTL_iface(iface, addr, addrfamily, __FILE__, __LINE__)
|
||||
void _FTL_iface(struct irec *recviface, const union all_addr *addr, const sa_family_t addrfamily, const char* file, const int line);
|
||||
@@ -37,8 +35,8 @@ void FTL_forwarding_retried(const struct server *server, const int oldID, const
|
||||
#define FTL_make_answer(header, limit, len, ede) _FTL_make_answer(header, limit, len, ede, __FILE__, __LINE__)
|
||||
size_t _FTL_make_answer(struct dns_header *header, char *limit, const size_t len, int *ede, const char* file, const int line);
|
||||
|
||||
#define FTL_CNAME(domain, cpp, id) _FTL_CNAME(domain, cpp, id, __FILE__, __LINE__)
|
||||
bool _FTL_CNAME(const char *domain, const struct crec *cpp, const int id, const char* file, const int line);
|
||||
#define FTL_CNAME(dst, src, id) _FTL_CNAME(dst, src, id, __FILE__, __LINE__)
|
||||
bool _FTL_CNAME(const char *dst, const char *src, const int id, const char* file, const int line);
|
||||
|
||||
unsigned int FTL_extract_question_flags(struct dns_header *header, const size_t qlen);
|
||||
void FTL_query_in_progress(const int id);
|
||||
|
||||
+10
-1
@@ -47,6 +47,7 @@ enum query_status {
|
||||
QUERY_IN_PROGRESS,
|
||||
QUERY_DBBUSY,
|
||||
QUERY_SPECIAL_DOMAIN,
|
||||
QUERY_CACHE_STALE,
|
||||
QUERY_STATUS_MAX
|
||||
} __attribute__ ((packed));
|
||||
|
||||
@@ -249,6 +250,13 @@ enum thread_types {
|
||||
THREADS_MAX
|
||||
} __attribute__ ((packed));
|
||||
|
||||
enum telnet_type {
|
||||
TELNETv4,
|
||||
TELNETv6,
|
||||
TELNET_SOCK,
|
||||
TELNET_MAX
|
||||
} __attribute__ ((packed));
|
||||
|
||||
enum message_type {
|
||||
REGEX_MESSAGE,
|
||||
SUBNET_MESSAGE,
|
||||
@@ -259,7 +267,8 @@ enum message_type {
|
||||
LOAD_MESSAGE,
|
||||
SHMEM_MESSAGE,
|
||||
DISK_MESSAGE,
|
||||
MAX_MESSAGE
|
||||
INACCESSIBLE_ADLIST_MESSAGE,
|
||||
MAX_MESSAGE,
|
||||
} __attribute__ ((packed));
|
||||
|
||||
enum ptr_type {
|
||||
|
||||
@@ -77,17 +77,21 @@ time_t get_rate_limit_turnaround(const unsigned int rate_limit_count)
|
||||
return (time_t)config.rate_limit.interval*how_often - (time(NULL) - lastRateLimitCleaner);
|
||||
}
|
||||
|
||||
static void check_space(const char *file)
|
||||
static int check_space(const char *file, int LastUsage)
|
||||
{
|
||||
if(config.check.disk == 0)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
int perc = 0;
|
||||
char buffer[64] = { 0 };
|
||||
// Warn if space usage at the device holding the corresponding file
|
||||
// exceeds the configured threshold
|
||||
if((perc = get_filepath_usage(file, buffer)) > config.check.disk)
|
||||
// exceeds the configured threshold and current usage is higher than
|
||||
// usage in the last run (to prevent log spam)
|
||||
perc = get_filepath_usage(file, buffer);
|
||||
if(perc > config.check.disk && perc > LastUsage )
|
||||
log_resource_shortage(-1.0, 0, -1, perc, file, buffer);
|
||||
|
||||
return perc;
|
||||
}
|
||||
|
||||
static void check_load(void)
|
||||
@@ -120,6 +124,10 @@ void *GC_thread(void *val)
|
||||
lastRateLimitCleaner = time(NULL);
|
||||
time_t lastResourceCheck = 0;
|
||||
|
||||
// Remember disk usage
|
||||
int LastLogStorageUsage = 0;
|
||||
int LastDBStorageUsage = 0;
|
||||
|
||||
// Run as long as this thread is not canceled
|
||||
while(!killed)
|
||||
{
|
||||
@@ -141,8 +149,8 @@ void *GC_thread(void *val)
|
||||
if(now - lastResourceCheck >= RCinterval)
|
||||
{
|
||||
check_load();
|
||||
check_space(config.files.database);
|
||||
check_space(config.files.log);
|
||||
LastDBStorageUsage = check_space(config.files.database, LastDBStorageUsage);
|
||||
LastLogStorageUsage = check_space(config.files.log, LastLogStorageUsage);
|
||||
lastResourceCheck = now;
|
||||
}
|
||||
|
||||
@@ -210,6 +218,7 @@ void *GC_thread(void *val)
|
||||
// Adjusting counters is done below in moveOverTimeMemory()
|
||||
break;
|
||||
case QUERY_CACHE:
|
||||
case QUERY_CACHE_STALE:
|
||||
// Answered from local cache _or_ local config
|
||||
break;
|
||||
case QUERY_GRAVITY: // Blocked by Pi-hole's blocking lists (fall through)
|
||||
|
||||
@@ -24,7 +24,7 @@ if(DEFINED ENV{GIT_HASH})
|
||||
set(GIT_HASH "$ENV{GIT_HASH}")
|
||||
else()
|
||||
execute_process(
|
||||
COMMAND git --no-pager describe --always --dirty
|
||||
COMMAND git --no-pager describe --always --abbrev=8 --dirty
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_HASH
|
||||
ERROR_QUIET
|
||||
@@ -36,7 +36,7 @@ if(DEFINED ENV{GIT_VERSION})
|
||||
set(GIT_VERSION "$ENV{GIT_VERSION}")
|
||||
else()
|
||||
execute_process(
|
||||
COMMAND git --no-pager describe --tags --always --dirty
|
||||
COMMAND git --no-pager describe --tags --always --abbrev=8 --dirty
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_VERSION
|
||||
ERROR_QUIET
|
||||
|
||||
+48
-3
@@ -16,6 +16,7 @@
|
||||
#include "../log.h"
|
||||
#include <readline/history.h>
|
||||
#include <wordexp.h>
|
||||
#include "scripts/scripts.h"
|
||||
|
||||
int run_lua_interpreter(const int argc, char **argv, bool dnsmasq_debug)
|
||||
{
|
||||
@@ -98,9 +99,53 @@ LUAMOD_API int luaopen_pihole(lua_State *L) {
|
||||
return LUA_YIELD;
|
||||
}
|
||||
|
||||
// Load bundled libraries and make the available globally
|
||||
static bool ftl_lua_load_embedded_script(lua_State *L, const char *name, const char *script, const size_t script_len, const bool make_global)
|
||||
{
|
||||
// Explanation:
|
||||
// luaL_dostring(L, script) expands to (luaL_loadstring(L, script) || lua_pcall(L, 0, LUA_MULTRET, 0))
|
||||
// luaL_loadstring(L, script) calls luaL_loadbuffer(L, s, strlen(s), s)
|
||||
if (luaL_loadbufferx(L, script, script_len, name, NULL) || lua_pcall(L, 0, LUA_MULTRET, 0) != 0)
|
||||
{
|
||||
const char *lua_err = lua_tostring(L, -1);
|
||||
printf("LUA error while trying to import %s.lua: %s\n", name, lua_err);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(make_global)
|
||||
{
|
||||
/* Set global[name] = luaL_dostring return */
|
||||
lua_setglobal(L, name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct {
|
||||
const char *name;
|
||||
const char *content;
|
||||
const size_t contentlen;
|
||||
const bool global;
|
||||
} scripts[] =
|
||||
{
|
||||
{"inspect", inspect_lua, sizeof(inspect_lua), true},
|
||||
};
|
||||
|
||||
// Loop over bundled LUA libraries and print their names on the console
|
||||
void print_embedded_scripts(void)
|
||||
{
|
||||
for(unsigned int i = 0; i < sizeof(scripts)/sizeof(scripts[0]); i++)
|
||||
{
|
||||
char prefix[2] = { 0 };
|
||||
double formatted = 0.0;
|
||||
format_memory_size(prefix, scripts[i].contentlen, &formatted);
|
||||
|
||||
printf("%s.lua (%.2f %sB) ", scripts[i].name, formatted, prefix);
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over bundled LUA libraries and load them
|
||||
void ftl_lua_init(lua_State *L)
|
||||
{
|
||||
if(dolibrary(L, "inspect") != LUA_OK)
|
||||
printf("Failed to load library inspect.lua\n");
|
||||
for(unsigned int i = 0; i < sizeof(scripts)/sizeof(scripts[0]); i++)
|
||||
ftl_lua_load_embedded_script(L, scripts[i].name, scripts[i].content, scripts[i].contentlen, scripts[i].global);
|
||||
}
|
||||
|
||||
+2
-1
@@ -21,8 +21,9 @@ int run_luac(const int argc, char **argv);
|
||||
int lua_main (int argc, char **argv);
|
||||
int luac_main (int argc, char **argv);
|
||||
|
||||
extern int dolibrary (lua_State *L, const char *name);
|
||||
extern int dolibrary (lua_State *L, char *name);
|
||||
|
||||
void print_embedded_scripts(void);
|
||||
void ftl_lua_init(lua_State *L);
|
||||
|
||||
#endif //FTL_LUA_H
|
||||
+84
-42
@@ -39,7 +39,7 @@ const char lua_ident[] =
|
||||
|
||||
|
||||
/*
|
||||
** Test for a valid index.
|
||||
** Test for a valid index (one that is not the 'nilvalue').
|
||||
** '!ttisnil(o)' implies 'o != &G(L)->nilvalue', so it is not needed.
|
||||
** However, it covers the most common cases in a faster way.
|
||||
*/
|
||||
@@ -53,6 +53,10 @@ const char lua_ident[] =
|
||||
#define isupvalue(i) ((i) < LUA_REGISTRYINDEX)
|
||||
|
||||
|
||||
/*
|
||||
** Convert an acceptable index to a pointer to its respective value.
|
||||
** Non-valid indices return the special nil value 'G(L)->nilvalue'.
|
||||
*/
|
||||
static TValue *index2value (lua_State *L, int idx) {
|
||||
CallInfo *ci = L->ci;
|
||||
if (idx > 0) {
|
||||
@@ -70,21 +74,28 @@ static TValue *index2value (lua_State *L, int idx) {
|
||||
else { /* upvalues */
|
||||
idx = LUA_REGISTRYINDEX - idx;
|
||||
api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large");
|
||||
if (ttislcf(s2v(ci->func))) /* light C function? */
|
||||
return &G(L)->nilvalue; /* it has no upvalues */
|
||||
else {
|
||||
if (ttisCclosure(s2v(ci->func))) { /* C closure? */
|
||||
CClosure *func = clCvalue(s2v(ci->func));
|
||||
return (idx <= func->nupvalues) ? &func->upvalue[idx-1] : &G(L)->nilvalue;
|
||||
return (idx <= func->nupvalues) ? &func->upvalue[idx-1]
|
||||
: &G(L)->nilvalue;
|
||||
}
|
||||
else { /* light C function or Lua function (through a hook)?) */
|
||||
api_check(L, ttislcf(s2v(ci->func)), "caller not a C function");
|
||||
return &G(L)->nilvalue; /* no upvalues */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static StkId index2stack (lua_State *L, int idx) {
|
||||
|
||||
/*
|
||||
** Convert a valid actual index (not a pseudo-index) to its address.
|
||||
*/
|
||||
l_sinline StkId index2stack (lua_State *L, int idx) {
|
||||
CallInfo *ci = L->ci;
|
||||
if (idx > 0) {
|
||||
StkId o = ci->func + idx;
|
||||
api_check(L, o < L->top, "unacceptable index");
|
||||
api_check(L, o < L->top, "invalid index");
|
||||
return o;
|
||||
}
|
||||
else { /* non-positive index */
|
||||
@@ -172,7 +183,7 @@ LUA_API int lua_gettop (lua_State *L) {
|
||||
|
||||
LUA_API void lua_settop (lua_State *L, int idx) {
|
||||
CallInfo *ci;
|
||||
StkId func;
|
||||
StkId func, newtop;
|
||||
ptrdiff_t diff; /* difference for new top */
|
||||
lua_lock(L);
|
||||
ci = L->ci;
|
||||
@@ -187,9 +198,26 @@ LUA_API void lua_settop (lua_State *L, int idx) {
|
||||
api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top");
|
||||
diff = idx + 1; /* will "subtract" index (as it is negative) */
|
||||
}
|
||||
if (diff < 0 && hastocloseCfunc(ci->nresults))
|
||||
luaF_close(L, L->top + diff, LUA_OK);
|
||||
L->top += diff; /* correct top only after closing any upvalue */
|
||||
api_check(L, L->tbclist < L->top, "previous pop of an unclosed slot");
|
||||
newtop = L->top + diff;
|
||||
if (diff < 0 && L->tbclist >= newtop) {
|
||||
lua_assert(hastocloseCfunc(ci->nresults));
|
||||
luaF_close(L, newtop, CLOSEKTOP, 0);
|
||||
}
|
||||
L->top = newtop; /* correct top only after closing any upvalue */
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
||||
|
||||
LUA_API void lua_closeslot (lua_State *L, int idx) {
|
||||
StkId level;
|
||||
lua_lock(L);
|
||||
level = index2stack(L, idx);
|
||||
api_check(L, hastocloseCfunc(L->ci->nresults) && L->tbclist == level,
|
||||
"no variable to close at given level");
|
||||
luaF_close(L, level, CLOSEKTOP, 0);
|
||||
level = index2stack(L, idx); /* stack may be moved */
|
||||
setnilvalue(s2v(level));
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
||||
@@ -200,7 +228,7 @@ LUA_API void lua_settop (lua_State *L, int idx) {
|
||||
** Note that we move(copy) only the value inside the stack.
|
||||
** (We do not move additional fields that may exist.)
|
||||
*/
|
||||
static void reverse (lua_State *L, StkId from, StkId to) {
|
||||
l_sinline void reverse (lua_State *L, StkId from, StkId to) {
|
||||
for (; from < to; from++, to--) {
|
||||
TValue temp;
|
||||
setobj(L, &temp, s2v(from));
|
||||
@@ -420,7 +448,7 @@ LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) {
|
||||
}
|
||||
|
||||
|
||||
static void *touserdata (const TValue *o) {
|
||||
l_sinline void *touserdata (const TValue *o) {
|
||||
switch (ttype(o)) {
|
||||
case LUA_TUSERDATA: return getudatamem(uvalue(o));
|
||||
case LUA_TLIGHTUSERDATA: return pvalue(o);
|
||||
@@ -612,7 +640,7 @@ LUA_API int lua_pushthread (lua_State *L) {
|
||||
*/
|
||||
|
||||
|
||||
static int auxgetstr (lua_State *L, const TValue *t, const char *k) {
|
||||
l_sinline int auxgetstr (lua_State *L, const TValue *t, const char *k) {
|
||||
const TValue *slot;
|
||||
TString *str = luaS_new(L, k);
|
||||
if (luaV_fastget(L, t, str, slot, luaH_getstr)) {
|
||||
@@ -629,11 +657,21 @@ static int auxgetstr (lua_State *L, const TValue *t, const char *k) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Get the global table in the registry. Since all predefined
|
||||
** indices in the registry were inserted right when the registry
|
||||
** was created and never removed, they must always be in the array
|
||||
** part of the registry.
|
||||
*/
|
||||
#define getGtable(L) \
|
||||
(&hvalue(&G(L)->l_registry)->array[LUA_RIDX_GLOBALS - 1])
|
||||
|
||||
|
||||
LUA_API int lua_getglobal (lua_State *L, const char *name) {
|
||||
Table *reg;
|
||||
const TValue *G;
|
||||
lua_lock(L);
|
||||
reg = hvalue(&G(L)->l_registry);
|
||||
return auxgetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name);
|
||||
G = getGtable(L);
|
||||
return auxgetstr(L, G, name);
|
||||
}
|
||||
|
||||
|
||||
@@ -677,7 +715,7 @@ LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) {
|
||||
}
|
||||
|
||||
|
||||
static int finishrawget (lua_State *L, const TValue *val) {
|
||||
l_sinline int finishrawget (lua_State *L, const TValue *val) {
|
||||
if (isempty(val)) /* avoid copying empty items to the stack */
|
||||
setnilvalue(s2v(L->top));
|
||||
else
|
||||
@@ -811,10 +849,10 @@ static void auxsetstr (lua_State *L, const TValue *t, const char *k) {
|
||||
|
||||
|
||||
LUA_API void lua_setglobal (lua_State *L, const char *name) {
|
||||
Table *reg;
|
||||
const TValue *G;
|
||||
lua_lock(L); /* unlock done in 'auxsetstr' */
|
||||
reg = hvalue(&G(L)->l_registry);
|
||||
auxsetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name);
|
||||
G = getGtable(L);
|
||||
auxsetstr(L, G, name);
|
||||
}
|
||||
|
||||
|
||||
@@ -861,12 +899,10 @@ LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) {
|
||||
|
||||
static void aux_rawset (lua_State *L, int idx, TValue *key, int n) {
|
||||
Table *t;
|
||||
TValue *slot;
|
||||
lua_lock(L);
|
||||
api_checknelems(L, n);
|
||||
t = gettable(L, idx);
|
||||
slot = luaH_set(L, t, key);
|
||||
setobj2t(L, slot, s2v(L->top - 1));
|
||||
luaH_set(L, t, key, s2v(L->top - 1));
|
||||
invalidateTMcache(t);
|
||||
luaC_barrierback(L, obj2gco(t), s2v(L->top - 1));
|
||||
L->top -= n;
|
||||
@@ -1063,8 +1099,7 @@ LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
|
||||
LClosure *f = clLvalue(s2v(L->top - 1)); /* get newly created function */
|
||||
if (f->nupvalues >= 1) { /* does it have an upvalue? */
|
||||
/* get global table from registry */
|
||||
Table *reg = hvalue(&G(L)->l_registry);
|
||||
const TValue *gt = luaH_getint(reg, LUA_RIDX_GLOBALS);
|
||||
const TValue *gt = getGtable(L);
|
||||
/* set global table as 1st upvalue of 'f' (may be LUA_ENV) */
|
||||
setobj(L, f->upvals[0]->v, gt);
|
||||
luaC_barrier(L, f->upvals[0], gt);
|
||||
@@ -1101,18 +1136,19 @@ LUA_API int lua_status (lua_State *L) {
|
||||
LUA_API int lua_gc (lua_State *L, int what, ...) {
|
||||
va_list argp;
|
||||
int res = 0;
|
||||
global_State *g;
|
||||
global_State *g = G(L);
|
||||
if (g->gcstp & GCSTPGC) /* internal stop? */
|
||||
return -1; /* all options are invalid when stopped */
|
||||
lua_lock(L);
|
||||
g = G(L);
|
||||
va_start(argp, what);
|
||||
switch (what) {
|
||||
case LUA_GCSTOP: {
|
||||
g->gcrunning = 0;
|
||||
g->gcstp = GCSTPUSR; /* stopped by the user */
|
||||
break;
|
||||
}
|
||||
case LUA_GCRESTART: {
|
||||
luaE_setdebt(g, 0);
|
||||
g->gcrunning = 1;
|
||||
g->gcstp = 0; /* (GCSTPGC must be already zero here) */
|
||||
break;
|
||||
}
|
||||
case LUA_GCCOLLECT: {
|
||||
@@ -1131,8 +1167,8 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
|
||||
case LUA_GCSTEP: {
|
||||
int data = va_arg(argp, int);
|
||||
l_mem debt = 1; /* =1 to signal that it did an actual step */
|
||||
lu_byte oldrunning = g->gcrunning;
|
||||
g->gcrunning = 1; /* allow GC to run */
|
||||
lu_byte oldstp = g->gcstp;
|
||||
g->gcstp = 0; /* allow GC to run (GCSTPGC must be zero here) */
|
||||
if (data == 0) {
|
||||
luaE_setdebt(g, 0); /* do a basic step */
|
||||
luaC_step(L);
|
||||
@@ -1142,7 +1178,7 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
|
||||
luaE_setdebt(g, debt);
|
||||
luaC_checkGC(L);
|
||||
}
|
||||
g->gcrunning = oldrunning; /* restore previous state */
|
||||
g->gcstp = oldstp; /* restore previous state */
|
||||
if (debt > 0 && g->gcstate == GCSpause) /* end of cycle? */
|
||||
res = 1; /* signal it */
|
||||
break;
|
||||
@@ -1160,7 +1196,7 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
|
||||
break;
|
||||
}
|
||||
case LUA_GCISRUNNING: {
|
||||
res = g->gcrunning;
|
||||
res = gcrunning(g);
|
||||
break;
|
||||
}
|
||||
case LUA_GCGEN: {
|
||||
@@ -1240,8 +1276,7 @@ LUA_API void lua_toclose (lua_State *L, int idx) {
|
||||
lua_lock(L);
|
||||
o = index2stack(L, idx);
|
||||
nresults = L->ci->nresults;
|
||||
api_check(L, L->openupval == NULL || uplevel(L->openupval) <= o,
|
||||
"marked index below or equal new one");
|
||||
api_check(L, L->tbclist < o, "given index below or equal a marked one");
|
||||
luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */
|
||||
if (!hastocloseCfunc(nresults)) /* function not marked yet? */
|
||||
L->ci->nresults = codeNresults(nresults); /* mark it */
|
||||
@@ -1383,13 +1418,16 @@ LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {
|
||||
|
||||
|
||||
static UpVal **getupvalref (lua_State *L, int fidx, int n, LClosure **pf) {
|
||||
static const UpVal *const nullup = NULL;
|
||||
LClosure *f;
|
||||
TValue *fi = index2value(L, fidx);
|
||||
api_check(L, ttisLclosure(fi), "Lua function expected");
|
||||
f = clLvalue(fi);
|
||||
api_check(L, (1 <= n && n <= f->p->sizeupvalues), "invalid upvalue index");
|
||||
if (pf) *pf = f;
|
||||
return &f->upvals[n - 1]; /* get its upvalue pointer */
|
||||
if (1 <= n && n <= f->p->sizeupvalues)
|
||||
return &f->upvals[n - 1]; /* get its upvalue pointer */
|
||||
else
|
||||
return (UpVal**)&nullup;
|
||||
}
|
||||
|
||||
|
||||
@@ -1401,11 +1439,14 @@ LUA_API void *lua_upvalueid (lua_State *L, int fidx, int n) {
|
||||
}
|
||||
case LUA_VCCL: { /* C closure */
|
||||
CClosure *f = clCvalue(fi);
|
||||
api_check(L, 1 <= n && n <= f->nupvalues, "invalid upvalue index");
|
||||
return &f->upvalue[n - 1];
|
||||
}
|
||||
if (1 <= n && n <= f->nupvalues)
|
||||
return &f->upvalue[n - 1];
|
||||
/* else */
|
||||
} /* FALLTHROUGH */
|
||||
case LUA_VLCF:
|
||||
return NULL; /* light C functions have no upvalues */
|
||||
default: {
|
||||
api_check(L, 0, "closure expected");
|
||||
api_check(L, 0, "function expected");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -1417,6 +1458,7 @@ LUA_API void lua_upvaluejoin (lua_State *L, int fidx1, int n1,
|
||||
LClosure *f1;
|
||||
UpVal **up1 = getupvalref(L, fidx1, n1, &f1);
|
||||
UpVal **up2 = getupvalref(L, fidx2, n2, NULL);
|
||||
api_check(L, *up1 != NULL && *up2 != NULL, "invalid upvalue index");
|
||||
*up1 = *up2;
|
||||
luaC_objbarrier(L, f1, *up1);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@
|
||||
|
||||
#define hastocloseCfunc(n) ((n) < LUA_MULTRET)
|
||||
|
||||
/* Map [-1, inf) (range of 'nresults') into (-inf, -2] */
|
||||
#define codeNresults(n) (-(n) - 3)
|
||||
#define decodeNresults(n) (-(n) - 3)
|
||||
|
||||
#endif
|
||||
|
||||
+97
-50
@@ -190,7 +190,7 @@ LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) {
|
||||
}
|
||||
|
||||
|
||||
int luaL_typeerror (lua_State *L, int arg, const char *tname) {
|
||||
LUALIB_API int luaL_typeerror (lua_State *L, int arg, const char *tname) {
|
||||
const char *msg;
|
||||
const char *typearg; /* name for the type of the actual argument */
|
||||
if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING)
|
||||
@@ -283,10 +283,10 @@ LUALIB_API int luaL_fileresult (lua_State *L, int stat, const char *fname) {
|
||||
|
||||
|
||||
LUALIB_API int luaL_execresult (lua_State *L, int stat) {
|
||||
const char *what = "exit"; /* type of termination */
|
||||
if (stat != 0 && errno != 0) /* error with an 'errno'? */
|
||||
return luaL_fileresult(L, 0, NULL);
|
||||
else {
|
||||
const char *what = "exit"; /* type of termination */
|
||||
l_inspectstat(stat, what); /* interpret result */
|
||||
if (*what == 'e' && stat == 0) /* successful termination? */
|
||||
lua_pushboolean(L, 1);
|
||||
@@ -378,7 +378,7 @@ LUALIB_API int luaL_checkoption (lua_State *L, int arg, const char *def,
|
||||
** but without 'msg'.)
|
||||
*/
|
||||
LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg) {
|
||||
if (!lua_checkstack(L, space)) {
|
||||
if (l_unlikely(!lua_checkstack(L, space))) {
|
||||
if (msg)
|
||||
luaL_error(L, "stack overflow (%s)", msg);
|
||||
else
|
||||
@@ -388,20 +388,20 @@ LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg) {
|
||||
|
||||
|
||||
LUALIB_API void luaL_checktype (lua_State *L, int arg, int t) {
|
||||
if (lua_type(L, arg) != t)
|
||||
if (l_unlikely(lua_type(L, arg) != t))
|
||||
tag_error(L, arg, t);
|
||||
}
|
||||
|
||||
|
||||
LUALIB_API void luaL_checkany (lua_State *L, int arg) {
|
||||
if (lua_type(L, arg) == LUA_TNONE)
|
||||
if (l_unlikely(lua_type(L, arg) == LUA_TNONE))
|
||||
luaL_argerror(L, arg, "value expected");
|
||||
}
|
||||
|
||||
|
||||
LUALIB_API const char *luaL_checklstring (lua_State *L, int arg, size_t *len) {
|
||||
const char *s = lua_tolstring(L, arg, len);
|
||||
if (!s) tag_error(L, arg, LUA_TSTRING);
|
||||
if (l_unlikely(!s)) tag_error(L, arg, LUA_TSTRING);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -420,7 +420,7 @@ LUALIB_API const char *luaL_optlstring (lua_State *L, int arg,
|
||||
LUALIB_API lua_Number luaL_checknumber (lua_State *L, int arg) {
|
||||
int isnum;
|
||||
lua_Number d = lua_tonumberx(L, arg, &isnum);
|
||||
if (!isnum)
|
||||
if (l_unlikely(!isnum))
|
||||
tag_error(L, arg, LUA_TNUMBER);
|
||||
return d;
|
||||
}
|
||||
@@ -442,7 +442,7 @@ static void interror (lua_State *L, int arg) {
|
||||
LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int arg) {
|
||||
int isnum;
|
||||
lua_Integer d = lua_tointegerx(L, arg, &isnum);
|
||||
if (!isnum) {
|
||||
if (l_unlikely(!isnum)) {
|
||||
interror(L, arg);
|
||||
}
|
||||
return d;
|
||||
@@ -475,7 +475,7 @@ static void *resizebox (lua_State *L, int idx, size_t newsize) {
|
||||
lua_Alloc allocf = lua_getallocf(L, &ud);
|
||||
UBox *box = (UBox *)lua_touserdata(L, idx);
|
||||
void *temp = allocf(ud, box->box, box->bsize, newsize);
|
||||
if (temp == NULL && newsize > 0) { /* allocation error? */
|
||||
if (l_unlikely(temp == NULL && newsize > 0)) { /* allocation error? */
|
||||
lua_pushliteral(L, "not enough memory");
|
||||
lua_error(L); /* raise a memory error */
|
||||
}
|
||||
@@ -515,13 +515,22 @@ static void newbox (lua_State *L) {
|
||||
#define buffonstack(B) ((B)->b != (B)->init.b)
|
||||
|
||||
|
||||
/*
|
||||
** Whenever buffer is accessed, slot 'idx' must either be a box (which
|
||||
** cannot be NULL) or it is a placeholder for the buffer.
|
||||
*/
|
||||
#define checkbufferlevel(B,idx) \
|
||||
lua_assert(buffonstack(B) ? lua_touserdata(B->L, idx) != NULL \
|
||||
: lua_touserdata(B->L, idx) == (void*)B)
|
||||
|
||||
|
||||
/*
|
||||
** Compute new size for buffer 'B', enough to accommodate extra 'sz'
|
||||
** bytes.
|
||||
*/
|
||||
static size_t newbuffsize (luaL_Buffer *B, size_t sz) {
|
||||
size_t newsize = B->size * 2; /* double buffer size */
|
||||
if (MAX_SIZET - sz < B->n) /* overflow in (B->n + sz)? */
|
||||
if (l_unlikely(MAX_SIZET - sz < B->n)) /* overflow in (B->n + sz)? */
|
||||
return luaL_error(B->L, "buffer too large");
|
||||
if (newsize < B->n + sz) /* double is not big enough? */
|
||||
newsize = B->n + sz;
|
||||
@@ -531,10 +540,11 @@ static size_t newbuffsize (luaL_Buffer *B, size_t sz) {
|
||||
|
||||
/*
|
||||
** Returns a pointer to a free area with at least 'sz' bytes in buffer
|
||||
** 'B'. 'boxidx' is the relative position in the stack where the
|
||||
** buffer's box is or should be.
|
||||
** 'B'. 'boxidx' is the relative position in the stack where is the
|
||||
** buffer's box or its placeholder.
|
||||
*/
|
||||
static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) {
|
||||
checkbufferlevel(B, boxidx);
|
||||
if (B->size - B->n >= sz) /* enough space? */
|
||||
return B->b + B->n;
|
||||
else {
|
||||
@@ -545,10 +555,9 @@ static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) {
|
||||
if (buffonstack(B)) /* buffer already has a box? */
|
||||
newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */
|
||||
else { /* no box yet */
|
||||
lua_pushnil(L); /* reserve slot for final result */
|
||||
lua_remove(L, boxidx); /* remove placeholder */
|
||||
newbox(L); /* create a new box */
|
||||
/* move box (and slot) to its intended position */
|
||||
lua_rotate(L, boxidx - 1, 2);
|
||||
lua_insert(L, boxidx); /* move box to its intended position */
|
||||
lua_toclose(L, boxidx);
|
||||
newbuff = (char *)resizebox(L, boxidx, newsize);
|
||||
memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */
|
||||
@@ -583,11 +592,11 @@ LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) {
|
||||
|
||||
LUALIB_API void luaL_pushresult (luaL_Buffer *B) {
|
||||
lua_State *L = B->L;
|
||||
checkbufferlevel(B, -1);
|
||||
lua_pushlstring(L, B->b, B->n);
|
||||
if (buffonstack(B)) {
|
||||
lua_copy(L, -1, -3); /* move string to reserved slot */
|
||||
lua_pop(L, 2); /* pop string and box (closing the box) */
|
||||
}
|
||||
if (buffonstack(B))
|
||||
lua_closeslot(L, -2); /* close the box */
|
||||
lua_remove(L, -2); /* remove box or placeholder from the stack */
|
||||
}
|
||||
|
||||
|
||||
@@ -622,6 +631,7 @@ LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) {
|
||||
B->b = B->init.b;
|
||||
B->n = 0;
|
||||
B->size = LUAL_BUFFERSIZE;
|
||||
lua_pushlightuserdata(L, (void*)B); /* push placeholder */
|
||||
}
|
||||
|
||||
|
||||
@@ -639,10 +649,14 @@ LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) {
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
/* index of free-list header */
|
||||
#define freelist 0
|
||||
|
||||
/* index of free-list header (after the predefined values) */
|
||||
#define freelist (LUA_RIDX_LAST + 1)
|
||||
|
||||
/*
|
||||
** The previously freed references form a linked list:
|
||||
** t[freelist] is the index of a first free index, or zero if list is
|
||||
** empty; t[t[freelist]] is the index of the second element; etc.
|
||||
*/
|
||||
LUALIB_API int luaL_ref (lua_State *L, int t) {
|
||||
int ref;
|
||||
if (lua_isnil(L, -1)) {
|
||||
@@ -650,9 +664,16 @@ LUALIB_API int luaL_ref (lua_State *L, int t) {
|
||||
return LUA_REFNIL; /* 'nil' has a unique fixed reference */
|
||||
}
|
||||
t = lua_absindex(L, t);
|
||||
lua_rawgeti(L, t, freelist); /* get first free element */
|
||||
ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */
|
||||
lua_pop(L, 1); /* remove it from stack */
|
||||
if (lua_rawgeti(L, t, freelist) == LUA_TNIL) { /* first access? */
|
||||
ref = 0; /* list is empty */
|
||||
lua_pushinteger(L, 0); /* initialize as an empty list */
|
||||
lua_rawseti(L, t, freelist); /* ref = t[freelist] = 0 */
|
||||
}
|
||||
else { /* already initialized */
|
||||
lua_assert(lua_isinteger(L, -1));
|
||||
ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */
|
||||
}
|
||||
lua_pop(L, 1); /* remove element from stack */
|
||||
if (ref != 0) { /* any free element? */
|
||||
lua_rawgeti(L, t, ref); /* remove it from list */
|
||||
lua_rawseti(L, t, freelist); /* (t[freelist] = t[ref]) */
|
||||
@@ -668,6 +689,7 @@ LUALIB_API void luaL_unref (lua_State *L, int t, int ref) {
|
||||
if (ref >= 0) {
|
||||
t = lua_absindex(L, t);
|
||||
lua_rawgeti(L, t, freelist);
|
||||
lua_assert(lua_isinteger(L, -1));
|
||||
lua_rawseti(L, t, ref); /* t[ref] = t[freelist] */
|
||||
lua_pushinteger(L, ref);
|
||||
lua_rawseti(L, t, freelist); /* t[freelist] = ref */
|
||||
@@ -851,7 +873,7 @@ LUALIB_API lua_Integer luaL_len (lua_State *L, int idx) {
|
||||
int isnum;
|
||||
lua_len(L, idx);
|
||||
l = lua_tointegerx(L, -1, &isnum);
|
||||
if (!isnum)
|
||||
if (l_unlikely(!isnum))
|
||||
luaL_error(L, "object length is not an integer");
|
||||
lua_pop(L, 1); /* remove object */
|
||||
return l;
|
||||
@@ -859,6 +881,7 @@ LUALIB_API lua_Integer luaL_len (lua_State *L, int idx) {
|
||||
|
||||
|
||||
LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) {
|
||||
idx = lua_absindex(L,idx);
|
||||
if (luaL_callmeta(L, idx, "__tostring")) { /* metafield? */
|
||||
if (!lua_isstring(L, -1))
|
||||
luaL_error(L, "'__tostring' must return a string");
|
||||
@@ -1006,43 +1029,67 @@ static int panic (lua_State *L) {
|
||||
|
||||
|
||||
/*
|
||||
** Emit a warning. '*warnstate' means:
|
||||
** 0 - warning system is off;
|
||||
** 1 - ready to start a new message;
|
||||
** 2 - previous message is to be continued.
|
||||
** Warning functions:
|
||||
** warnfoff: warning system is off
|
||||
** warnfon: ready to start a new message
|
||||
** warnfcont: previous message is to be continued
|
||||
*/
|
||||
static void warnf (void *ud, const char *message, int tocont) {
|
||||
int *warnstate = (int *)ud;
|
||||
if (*warnstate != 2 && !tocont && *message == '@') { /* control message? */
|
||||
if (strcmp(message, "@off") == 0)
|
||||
*warnstate = 0;
|
||||
else if (strcmp(message, "@on") == 0)
|
||||
*warnstate = 1;
|
||||
return;
|
||||
static void warnfoff (void *ud, const char *message, int tocont);
|
||||
static void warnfon (void *ud, const char *message, int tocont);
|
||||
static void warnfcont (void *ud, const char *message, int tocont);
|
||||
|
||||
|
||||
/*
|
||||
** Check whether message is a control message. If so, execute the
|
||||
** control or ignore it if unknown.
|
||||
*/
|
||||
static int checkcontrol (lua_State *L, const char *message, int tocont) {
|
||||
if (tocont || *(message++) != '@') /* not a control message? */
|
||||
return 0;
|
||||
else {
|
||||
if (strcmp(message, "off") == 0)
|
||||
lua_setwarnf(L, warnfoff, L); /* turn warnings off */
|
||||
else if (strcmp(message, "on") == 0)
|
||||
lua_setwarnf(L, warnfon, L); /* turn warnings on */
|
||||
return 1; /* it was a control message */
|
||||
}
|
||||
else if (*warnstate == 0) /* warnings off? */
|
||||
return;
|
||||
if (*warnstate == 1) /* previous message was the last? */
|
||||
lua_writestringerror("%s", "Lua warning: "); /* start a new warning */
|
||||
}
|
||||
|
||||
|
||||
static void warnfoff (void *ud, const char *message, int tocont) {
|
||||
checkcontrol((lua_State *)ud, message, tocont);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Writes the message and handle 'tocont', finishing the message
|
||||
** if needed and setting the next warn function.
|
||||
*/
|
||||
static void warnfcont (void *ud, const char *message, int tocont) {
|
||||
lua_State *L = (lua_State *)ud;
|
||||
lua_writestringerror("%s", message); /* write message */
|
||||
if (tocont) /* not the last part? */
|
||||
*warnstate = 2; /* to be continued */
|
||||
lua_setwarnf(L, warnfcont, L); /* to be continued */
|
||||
else { /* last part */
|
||||
lua_writestringerror("%s", "\n"); /* finish message with end-of-line */
|
||||
*warnstate = 1; /* ready to start a new message */
|
||||
lua_setwarnf(L, warnfon, L); /* next call is a new message */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void warnfon (void *ud, const char *message, int tocont) {
|
||||
if (checkcontrol((lua_State *)ud, message, tocont)) /* control message? */
|
||||
return; /* nothing else to be done */
|
||||
lua_writestringerror("%s", "Lua warning: "); /* start a new warning */
|
||||
warnfcont(ud, message, tocont); /* finish processing */
|
||||
}
|
||||
|
||||
|
||||
LUALIB_API lua_State *luaL_newstate (void) {
|
||||
lua_State *L = lua_newstate(l_alloc, NULL);
|
||||
if (L) {
|
||||
int *warnstate; /* space for warning state */
|
||||
if (l_likely(L)) {
|
||||
lua_atpanic(L, &panic);
|
||||
warnstate = (int *)lua_newuserdatauv(L, sizeof(int), 0);
|
||||
luaL_ref(L, LUA_REGISTRYINDEX); /* make sure it won't be collected */
|
||||
*warnstate = 0; /* default is warnings off */
|
||||
lua_setwarnf(L, warnf, warnstate);
|
||||
lua_setwarnf(L, warnfoff, L); /* default is warnings off */
|
||||
}
|
||||
return L;
|
||||
}
|
||||
|
||||
+28
-3
@@ -12,6 +12,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "luaconf.h"
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
@@ -101,7 +102,7 @@ LUALIB_API lua_State *(luaL_newstate) (void);
|
||||
|
||||
LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx);
|
||||
|
||||
LUALIB_API void luaL_addgsub (luaL_Buffer *b, const char *s,
|
||||
LUALIB_API void (luaL_addgsub) (luaL_Buffer *b, const char *s,
|
||||
const char *p, const char *r);
|
||||
LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s,
|
||||
const char *p, const char *r);
|
||||
@@ -130,10 +131,10 @@ LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
|
||||
(luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
|
||||
|
||||
#define luaL_argcheck(L, cond,arg,extramsg) \
|
||||
((void)((cond) || luaL_argerror(L, (arg), (extramsg))))
|
||||
((void)(luai_likely(cond) || luaL_argerror(L, (arg), (extramsg))))
|
||||
|
||||
#define luaL_argexpected(L,cond,arg,tname) \
|
||||
((void)((cond) || luaL_typeerror(L, (arg), (tname))))
|
||||
((void)(luai_likely(cond) || luaL_typeerror(L, (arg), (tname))))
|
||||
|
||||
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
|
||||
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
|
||||
@@ -153,10 +154,34 @@ LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
|
||||
#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL)
|
||||
|
||||
|
||||
/*
|
||||
** Perform arithmetic operations on lua_Integer values with wrap-around
|
||||
** semantics, as the Lua core does.
|
||||
*/
|
||||
#define luaL_intop(op,v1,v2) \
|
||||
((lua_Integer)((lua_Unsigned)(v1) op (lua_Unsigned)(v2)))
|
||||
|
||||
|
||||
/* push the value used to represent failure/error */
|
||||
#define luaL_pushfail(L) lua_pushnil(L)
|
||||
|
||||
|
||||
/*
|
||||
** Internal assertions for in-house debugging
|
||||
*/
|
||||
#if !defined(lua_assert)
|
||||
|
||||
#if defined LUAI_ASSERT
|
||||
#include <assert.h>
|
||||
#define lua_assert(c) assert(c)
|
||||
#else
|
||||
#define lua_assert(c) ((void)0)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Generic Buffer manipulation
|
||||
|
||||
+31
-9
@@ -138,7 +138,7 @@ static int luaB_setmetatable (lua_State *L) {
|
||||
int t = lua_type(L, 2);
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table");
|
||||
if (luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL)
|
||||
if (l_unlikely(luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL))
|
||||
return luaL_error(L, "cannot change a protected metatable");
|
||||
lua_settop(L, 2);
|
||||
lua_setmetatable(L, 1);
|
||||
@@ -182,11 +182,20 @@ static int luaB_rawset (lua_State *L) {
|
||||
|
||||
|
||||
static int pushmode (lua_State *L, int oldmode) {
|
||||
lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental" : "generational");
|
||||
if (oldmode == -1)
|
||||
luaL_pushfail(L); /* invalid call to 'lua_gc' */
|
||||
else
|
||||
lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental"
|
||||
: "generational");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** check whether call to 'lua_gc' was valid (not inside a finalizer)
|
||||
*/
|
||||
#define checkvalres(res) { if (res == -1) break; }
|
||||
|
||||
static int luaB_collectgarbage (lua_State *L) {
|
||||
static const char *const opts[] = {"stop", "restart", "collect",
|
||||
"count", "step", "setpause", "setstepmul",
|
||||
@@ -199,12 +208,14 @@ static int luaB_collectgarbage (lua_State *L) {
|
||||
case LUA_GCCOUNT: {
|
||||
int k = lua_gc(L, o);
|
||||
int b = lua_gc(L, LUA_GCCOUNTB);
|
||||
checkvalres(k);
|
||||
lua_pushnumber(L, (lua_Number)k + ((lua_Number)b/1024));
|
||||
return 1;
|
||||
}
|
||||
case LUA_GCSTEP: {
|
||||
int step = (int)luaL_optinteger(L, 2, 0);
|
||||
int res = lua_gc(L, o, step);
|
||||
checkvalres(res);
|
||||
lua_pushboolean(L, res);
|
||||
return 1;
|
||||
}
|
||||
@@ -212,11 +223,13 @@ static int luaB_collectgarbage (lua_State *L) {
|
||||
case LUA_GCSETSTEPMUL: {
|
||||
int p = (int)luaL_optinteger(L, 2, 0);
|
||||
int previous = lua_gc(L, o, p);
|
||||
checkvalres(previous);
|
||||
lua_pushinteger(L, previous);
|
||||
return 1;
|
||||
}
|
||||
case LUA_GCISRUNNING: {
|
||||
int res = lua_gc(L, o);
|
||||
checkvalres(res);
|
||||
lua_pushboolean(L, res);
|
||||
return 1;
|
||||
}
|
||||
@@ -233,10 +246,13 @@ static int luaB_collectgarbage (lua_State *L) {
|
||||
}
|
||||
default: {
|
||||
int res = lua_gc(L, o);
|
||||
checkvalres(res);
|
||||
lua_pushinteger(L, res);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
luaL_pushfail(L); /* invalid call (inside a finalizer) */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -260,6 +276,11 @@ static int luaB_next (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
static int pairscont (lua_State *L, int status, lua_KContext k) {
|
||||
(void)L; (void)status; (void)k; /* unused */
|
||||
return 3;
|
||||
}
|
||||
|
||||
static int luaB_pairs (lua_State *L) {
|
||||
luaL_checkany(L, 1);
|
||||
if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */
|
||||
@@ -269,7 +290,7 @@ static int luaB_pairs (lua_State *L) {
|
||||
}
|
||||
else {
|
||||
lua_pushvalue(L, 1); /* argument 'self' to metamethod */
|
||||
lua_call(L, 1, 3); /* get 3 values from metamethod */
|
||||
lua_callk(L, 1, 3, 0, pairscont); /* get 3 values from metamethod */
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
@@ -279,7 +300,8 @@ static int luaB_pairs (lua_State *L) {
|
||||
** Traversal function for 'ipairs'
|
||||
*/
|
||||
static int ipairsaux (lua_State *L) {
|
||||
lua_Integer i = luaL_checkinteger(L, 2) + 1;
|
||||
lua_Integer i = luaL_checkinteger(L, 2);
|
||||
i = luaL_intop(+, i, 1);
|
||||
lua_pushinteger(L, i);
|
||||
return (lua_geti(L, 1, i) == LUA_TNIL) ? 1 : 2;
|
||||
}
|
||||
@@ -299,7 +321,7 @@ static int luaB_ipairs (lua_State *L) {
|
||||
|
||||
|
||||
static int load_aux (lua_State *L, int status, int envidx) {
|
||||
if (status == LUA_OK) {
|
||||
if (l_likely(status == LUA_OK)) {
|
||||
if (envidx != 0) { /* 'env' parameter? */
|
||||
lua_pushvalue(L, envidx); /* environment for loaded function */
|
||||
if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */
|
||||
@@ -355,7 +377,7 @@ static const char *generic_reader (lua_State *L, void *ud, size_t *size) {
|
||||
*size = 0;
|
||||
return NULL;
|
||||
}
|
||||
else if (!lua_isstring(L, -1))
|
||||
else if (l_unlikely(!lua_isstring(L, -1)))
|
||||
luaL_error(L, "reader function must return a string");
|
||||
lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */
|
||||
return lua_tolstring(L, RESERVEDSLOT, size);
|
||||
@@ -393,7 +415,7 @@ static int dofilecont (lua_State *L, int d1, lua_KContext d2) {
|
||||
static int luaB_dofile (lua_State *L) {
|
||||
const char *fname = luaL_optstring(L, 1, NULL);
|
||||
lua_settop(L, 1);
|
||||
if (luaL_loadfile(L, fname) != LUA_OK)
|
||||
if (l_unlikely(luaL_loadfile(L, fname) != LUA_OK))
|
||||
return lua_error(L);
|
||||
lua_callk(L, 0, LUA_MULTRET, 0, dofilecont);
|
||||
return dofilecont(L, 0, 0);
|
||||
@@ -401,7 +423,7 @@ static int luaB_dofile (lua_State *L) {
|
||||
|
||||
|
||||
static int luaB_assert (lua_State *L) {
|
||||
if (lua_toboolean(L, 1)) /* condition is true? */
|
||||
if (l_likely(lua_toboolean(L, 1))) /* condition is true? */
|
||||
return lua_gettop(L); /* return all arguments */
|
||||
else { /* error */
|
||||
luaL_checkany(L, 1); /* there must be a condition */
|
||||
@@ -437,7 +459,7 @@ static int luaB_select (lua_State *L) {
|
||||
** ignored).
|
||||
*/
|
||||
static int finishpcall (lua_State *L, int status, lua_KContext extra) {
|
||||
if (status != LUA_OK && status != LUA_YIELD) { /* error? */
|
||||
if (l_unlikely(status != LUA_OK && status != LUA_YIELD)) { /* error? */
|
||||
lua_pushboolean(L, 0); /* first result (false) */
|
||||
lua_pushvalue(L, -2); /* error message */
|
||||
return 2; /* return false, msg */
|
||||
|
||||
+48
-30
@@ -10,6 +10,7 @@
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <float.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
@@ -314,15 +315,6 @@ void luaK_patchtohere (FuncState *fs, int list) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** MAXimum number of successive Instructions WiTHout ABSolute line
|
||||
** information.
|
||||
*/
|
||||
#if !defined(MAXIWTHABS)
|
||||
#define MAXIWTHABS 120
|
||||
#endif
|
||||
|
||||
|
||||
/* limit for difference between lines in relative line info. */
|
||||
#define LIMLINEDIFF 0x80
|
||||
|
||||
@@ -337,13 +329,13 @@ void luaK_patchtohere (FuncState *fs, int list) {
|
||||
static void savelineinfo (FuncState *fs, Proto *f, int line) {
|
||||
int linedif = line - fs->previousline;
|
||||
int pc = fs->pc - 1; /* last instruction coded */
|
||||
if (abs(linedif) >= LIMLINEDIFF || fs->iwthabs++ > MAXIWTHABS) {
|
||||
if (abs(linedif) >= LIMLINEDIFF || fs->iwthabs++ >= MAXIWTHABS) {
|
||||
luaM_growvector(fs->ls->L, f->abslineinfo, fs->nabslineinfo,
|
||||
f->sizeabslineinfo, AbsLineInfo, MAX_INT, "lines");
|
||||
f->abslineinfo[fs->nabslineinfo].pc = pc;
|
||||
f->abslineinfo[fs->nabslineinfo++].line = line;
|
||||
linedif = ABSLINEINFO; /* signal that there is absolute information */
|
||||
fs->iwthabs = 0; /* restart counter */
|
||||
fs->iwthabs = 1; /* restart counter */
|
||||
}
|
||||
luaM_growvector(fs->ls->L, f->lineinfo, pc, f->sizelineinfo, ls_byte,
|
||||
MAX_INT, "opcodes");
|
||||
@@ -545,11 +537,14 @@ static void freeexps (FuncState *fs, expdesc *e1, expdesc *e2) {
|
||||
** and try to reuse constants. Because some values should not be used
|
||||
** as keys (nil cannot be a key, integer keys can collapse with float
|
||||
** keys), the caller must provide a useful 'key' for indexing the cache.
|
||||
** Note that all functions share the same table, so entering or exiting
|
||||
** a function can make some indices wrong.
|
||||
*/
|
||||
static int addk (FuncState *fs, TValue *key, TValue *v) {
|
||||
TValue val;
|
||||
lua_State *L = fs->ls->L;
|
||||
Proto *f = fs->f;
|
||||
TValue *idx = luaH_set(L, fs->ls->h, key); /* index scanner table */
|
||||
const TValue *idx = luaH_get(fs->ls->h, key); /* query scanner table */
|
||||
int k, oldsize;
|
||||
if (ttisinteger(idx)) { /* is there an index there? */
|
||||
k = cast_int(ivalue(idx));
|
||||
@@ -563,7 +558,8 @@ static int addk (FuncState *fs, TValue *key, TValue *v) {
|
||||
k = fs->nk;
|
||||
/* numerical value does not need GC barrier;
|
||||
table has no metatable, so it does not need to invalidate cache */
|
||||
setivalue(idx, k);
|
||||
setivalue(&val, k);
|
||||
luaH_finishset(L, fs->ls->h, key, idx, &val);
|
||||
luaM_growvector(L, f->k, k, f->sizek, TValue, MAXARG_Ax, "constants");
|
||||
while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);
|
||||
setobj(L, &f->k[k], v);
|
||||
@@ -585,24 +581,41 @@ static int stringK (FuncState *fs, TString *s) {
|
||||
|
||||
/*
|
||||
** Add an integer to list of constants and return its index.
|
||||
** Integers use userdata as keys to avoid collision with floats with
|
||||
** same value; conversion to 'void*' is used only for hashing, so there
|
||||
** are no "precision" problems.
|
||||
*/
|
||||
static int luaK_intK (FuncState *fs, lua_Integer n) {
|
||||
TValue k, o;
|
||||
setpvalue(&k, cast_voidp(cast_sizet(n)));
|
||||
TValue o;
|
||||
setivalue(&o, n);
|
||||
return addk(fs, &k, &o);
|
||||
return addk(fs, &o, &o); /* use integer itself as key */
|
||||
}
|
||||
|
||||
/*
|
||||
** Add a float to list of constants and return its index.
|
||||
** Add a float to list of constants and return its index. Floats
|
||||
** with integral values need a different key, to avoid collision
|
||||
** with actual integers. To that, we add to the number its smaller
|
||||
** power-of-two fraction that is still significant in its scale.
|
||||
** For doubles, that would be 1/2^52.
|
||||
** (This method is not bulletproof: there may be another float
|
||||
** with that value, and for floats larger than 2^53 the result is
|
||||
** still an integer. At worst, this only wastes an entry with
|
||||
** a duplicate.)
|
||||
*/
|
||||
static int luaK_numberK (FuncState *fs, lua_Number r) {
|
||||
TValue o;
|
||||
lua_Integer ik;
|
||||
setfltvalue(&o, r);
|
||||
return addk(fs, &o, &o); /* use number itself as key */
|
||||
if (!luaV_flttointeger(r, &ik, F2Ieq)) /* not an integral value? */
|
||||
return addk(fs, &o, &o); /* use number itself as key */
|
||||
else { /* must build an alternative key */
|
||||
const int nbm = l_floatatt(MANT_DIG);
|
||||
const lua_Number q = l_mathop(ldexp)(l_mathop(1.0), -nbm + 1);
|
||||
const lua_Number k = (ik == 0) ? q : r + r*q; /* new key */
|
||||
TValue kv;
|
||||
setfltvalue(&kv, k);
|
||||
/* result is not an integral value, unless value is too large */
|
||||
lua_assert(!luaV_flttointeger(k, &ik, F2Ieq) ||
|
||||
l_mathop(fabs)(r) >= l_mathop(1e6));
|
||||
return addk(fs, &kv, &o);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -753,7 +766,7 @@ void luaK_setoneret (FuncState *fs, expdesc *e) {
|
||||
|
||||
|
||||
/*
|
||||
** Ensure that expression 'e' is not a variable (nor a constant).
|
||||
** Ensure that expression 'e' is not a variable (nor a <const>).
|
||||
** (Expression still may have jump lists.)
|
||||
*/
|
||||
void luaK_dischargevars (FuncState *fs, expdesc *e) {
|
||||
@@ -763,7 +776,7 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) {
|
||||
break;
|
||||
}
|
||||
case VLOCAL: { /* already in a register */
|
||||
e->u.info = e->u.var.sidx;
|
||||
e->u.info = e->u.var.ridx;
|
||||
e->k = VNONRELOC; /* becomes a non-relocatable value */
|
||||
break;
|
||||
}
|
||||
@@ -805,8 +818,8 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) {
|
||||
|
||||
|
||||
/*
|
||||
** Ensures expression value is in register 'reg' (and therefore
|
||||
** 'e' will become a non-relocatable expression).
|
||||
** Ensure expression value is in register 'reg', making 'e' a
|
||||
** non-relocatable expression.
|
||||
** (Expression still may have jump lists.)
|
||||
*/
|
||||
static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
|
||||
@@ -860,7 +873,8 @@ static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
|
||||
|
||||
|
||||
/*
|
||||
** Ensures expression value is in any register.
|
||||
** Ensure expression value is in a register, making 'e' a
|
||||
** non-relocatable expression.
|
||||
** (Expression still may have jump lists.)
|
||||
*/
|
||||
static void discharge2anyreg (FuncState *fs, expdesc *e) {
|
||||
@@ -946,8 +960,11 @@ int luaK_exp2anyreg (FuncState *fs, expdesc *e) {
|
||||
exp2reg(fs, e, e->u.info); /* put final result in it */
|
||||
return e->u.info;
|
||||
}
|
||||
/* else expression has jumps and cannot change its register
|
||||
to hold the jump values, because it is a local variable.
|
||||
Go through to the default case. */
|
||||
}
|
||||
luaK_exp2nextreg(fs, e); /* otherwise, use next available register */
|
||||
luaK_exp2nextreg(fs, e); /* default: use next available register */
|
||||
return e->u.info;
|
||||
}
|
||||
|
||||
@@ -1032,7 +1049,7 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
|
||||
switch (var->k) {
|
||||
case VLOCAL: {
|
||||
freeexp(fs, ex);
|
||||
exp2reg(fs, ex, var->u.var.sidx); /* compute 'ex' into proper place */
|
||||
exp2reg(fs, ex, var->u.var.ridx); /* compute 'ex' into proper place */
|
||||
return;
|
||||
}
|
||||
case VUPVAL: {
|
||||
@@ -1272,7 +1289,7 @@ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
|
||||
}
|
||||
else {
|
||||
/* register index of the table */
|
||||
t->u.ind.t = (t->k == VLOCAL) ? t->u.var.sidx: t->u.info;
|
||||
t->u.ind.t = (t->k == VLOCAL) ? t->u.var.ridx: t->u.info;
|
||||
if (isKstr(fs, k)) {
|
||||
t->u.ind.idx = k->u.info; /* literal string */
|
||||
t->k = VINDEXSTR;
|
||||
@@ -1299,7 +1316,8 @@ static int validop (int op, TValue *v1, TValue *v2) {
|
||||
case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR:
|
||||
case LUA_OPSHL: case LUA_OPSHR: case LUA_OPBNOT: { /* conversion errors */
|
||||
lua_Integer i;
|
||||
return (tointegerns(v1, &i) && tointegerns(v2, &i));
|
||||
return (luaV_tointegerns(v1, &i, LUA_FLOORN2I) &&
|
||||
luaV_tointegerns(v2, &i, LUA_FLOORN2I));
|
||||
}
|
||||
case LUA_OPDIV: case LUA_OPIDIV: case LUA_OPMOD: /* division by 0 */
|
||||
return (nvalue(v2) != 0);
|
||||
|
||||
+11
-8
@@ -31,14 +31,14 @@ static lua_State *getco (lua_State *L) {
|
||||
*/
|
||||
static int auxresume (lua_State *L, lua_State *co, int narg) {
|
||||
int status, nres;
|
||||
if (!lua_checkstack(co, narg)) {
|
||||
if (l_unlikely(!lua_checkstack(co, narg))) {
|
||||
lua_pushliteral(L, "too many arguments to resume");
|
||||
return -1; /* error flag */
|
||||
}
|
||||
lua_xmove(L, co, narg);
|
||||
status = lua_resume(co, L, narg, &nres);
|
||||
if (status == LUA_OK || status == LUA_YIELD) {
|
||||
if (!lua_checkstack(L, nres + 1)) {
|
||||
if (l_likely(status == LUA_OK || status == LUA_YIELD)) {
|
||||
if (l_unlikely(!lua_checkstack(L, nres + 1))) {
|
||||
lua_pop(co, nres); /* remove results anyway */
|
||||
lua_pushliteral(L, "too many results to resume");
|
||||
return -1; /* error flag */
|
||||
@@ -57,7 +57,7 @@ static int luaB_coresume (lua_State *L) {
|
||||
lua_State *co = getco(L);
|
||||
int r;
|
||||
r = auxresume(L, co, lua_gettop(L) - 1);
|
||||
if (r < 0) {
|
||||
if (l_unlikely(r < 0)) {
|
||||
lua_pushboolean(L, 0);
|
||||
lua_insert(L, -2);
|
||||
return 2; /* return false + error message */
|
||||
@@ -73,10 +73,13 @@ static int luaB_coresume (lua_State *L) {
|
||||
static int luaB_auxwrap (lua_State *L) {
|
||||
lua_State *co = lua_tothread(L, lua_upvalueindex(1));
|
||||
int r = auxresume(L, co, lua_gettop(L));
|
||||
if (r < 0) { /* error? */
|
||||
if (l_unlikely(r < 0)) { /* error? */
|
||||
int stat = lua_status(co);
|
||||
if (stat != LUA_OK && stat != LUA_YIELD) /* error in the coroutine? */
|
||||
lua_resetthread(co); /* close its tbc variables */
|
||||
if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */
|
||||
stat = lua_resetthread(co); /* close its tbc variables */
|
||||
lua_assert(stat != LUA_OK);
|
||||
lua_xmove(co, L, 1); /* move error message to the caller */
|
||||
}
|
||||
if (stat != LUA_ERRMEM && /* not a memory error and ... */
|
||||
lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */
|
||||
luaL_where(L, 1); /* add extra info, if available */
|
||||
@@ -176,7 +179,7 @@ static int luaB_close (lua_State *L) {
|
||||
}
|
||||
else {
|
||||
lua_pushboolean(L, 0);
|
||||
lua_xmove(co, L, 1); /* copy error message */
|
||||
lua_xmove(co, L, 1); /* move error message */
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
+23
-17
@@ -33,7 +33,7 @@ static const char *const HOOKKEY = "_HOOKKEY";
|
||||
** checked.
|
||||
*/
|
||||
static void checkstack (lua_State *L, lua_State *L1, int n) {
|
||||
if (L != L1 && !lua_checkstack(L1, n))
|
||||
if (l_unlikely(L != L1 && !lua_checkstack(L1, n)))
|
||||
luaL_error(L, "stack overflow");
|
||||
}
|
||||
|
||||
@@ -152,6 +152,7 @@ static int db_getinfo (lua_State *L) {
|
||||
lua_State *L1 = getthread(L, &arg);
|
||||
const char *options = luaL_optstring(L, arg+2, "flnSrtu");
|
||||
checkstack(L, L1, 3);
|
||||
luaL_argcheck(L, options[0] != '>', arg + 2, "invalid option '>'");
|
||||
if (lua_isfunction(L, arg + 1)) { /* info about a function? */
|
||||
options = lua_pushfstring(L, ">%s", options); /* add '>' to 'options' */
|
||||
lua_pushvalue(L, arg + 1); /* move function to 'L1' stack */
|
||||
@@ -212,7 +213,7 @@ static int db_getlocal (lua_State *L) {
|
||||
lua_Debug ar;
|
||||
const char *name;
|
||||
int level = (int)luaL_checkinteger(L, arg + 1);
|
||||
if (!lua_getstack(L1, level, &ar)) /* out of range? */
|
||||
if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */
|
||||
return luaL_argerror(L, arg+1, "level out of range");
|
||||
checkstack(L, L1, 1);
|
||||
name = lua_getlocal(L1, &ar, nvar);
|
||||
@@ -237,7 +238,7 @@ static int db_setlocal (lua_State *L) {
|
||||
lua_Debug ar;
|
||||
int level = (int)luaL_checkinteger(L, arg + 1);
|
||||
int nvar = (int)luaL_checkinteger(L, arg + 2);
|
||||
if (!lua_getstack(L1, level, &ar)) /* out of range? */
|
||||
if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */
|
||||
return luaL_argerror(L, arg+1, "level out of range");
|
||||
luaL_checkany(L, arg+3);
|
||||
lua_settop(L, arg+3);
|
||||
@@ -281,25 +282,33 @@ static int db_setupvalue (lua_State *L) {
|
||||
** Check whether a given upvalue from a given closure exists and
|
||||
** returns its index
|
||||
*/
|
||||
static int checkupval (lua_State *L, int argf, int argnup) {
|
||||
static void *checkupval (lua_State *L, int argf, int argnup, int *pnup) {
|
||||
void *id;
|
||||
int nup = (int)luaL_checkinteger(L, argnup); /* upvalue index */
|
||||
luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */
|
||||
luaL_argcheck(L, (lua_getupvalue(L, argf, nup) != NULL), argnup,
|
||||
"invalid upvalue index");
|
||||
return nup;
|
||||
id = lua_upvalueid(L, argf, nup);
|
||||
if (pnup) {
|
||||
luaL_argcheck(L, id != NULL, argnup, "invalid upvalue index");
|
||||
*pnup = nup;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
static int db_upvalueid (lua_State *L) {
|
||||
int n = checkupval(L, 1, 2);
|
||||
lua_pushlightuserdata(L, lua_upvalueid(L, 1, n));
|
||||
void *id = checkupval(L, 1, 2, NULL);
|
||||
if (id != NULL)
|
||||
lua_pushlightuserdata(L, id);
|
||||
else
|
||||
luaL_pushfail(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int db_upvaluejoin (lua_State *L) {
|
||||
int n1 = checkupval(L, 1, 2);
|
||||
int n2 = checkupval(L, 3, 4);
|
||||
int n1, n2;
|
||||
checkupval(L, 1, 2, &n1);
|
||||
checkupval(L, 3, 4, &n2);
|
||||
luaL_argcheck(L, !lua_iscfunction(L, 1), 1, "Lua function expected");
|
||||
luaL_argcheck(L, !lua_iscfunction(L, 3), 3, "Lua function expected");
|
||||
lua_upvaluejoin(L, 1, n1, 3, n2);
|
||||
@@ -369,7 +378,7 @@ static int db_sethook (lua_State *L) {
|
||||
}
|
||||
if (!luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)) {
|
||||
/* table just created; initialize it */
|
||||
lua_pushstring(L, "k");
|
||||
lua_pushliteral(L, "k");
|
||||
lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setmetatable(L, -2); /* metatable(hooktable) = hooktable */
|
||||
@@ -412,7 +421,7 @@ static int db_debug (lua_State *L) {
|
||||
for (;;) {
|
||||
char buffer[250];
|
||||
lua_writestringerror("%s", "lua_debug> ");
|
||||
if (fgets(buffer, sizeof(buffer), stdin) == 0 ||
|
||||
if (fgets(buffer, sizeof(buffer), stdin) == NULL ||
|
||||
strcmp(buffer, "cont\n") == 0)
|
||||
return 0;
|
||||
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
|
||||
@@ -440,10 +449,7 @@ static int db_traceback (lua_State *L) {
|
||||
static int db_setcstacklimit (lua_State *L) {
|
||||
int limit = (int)luaL_checkinteger(L, 1);
|
||||
int res = lua_setcstacklimit(L, limit);
|
||||
if (res == 0)
|
||||
lua_pushboolean(L, 0);
|
||||
else
|
||||
lua_pushinteger(L, res);
|
||||
lua_pushinteger(L, res);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
+142
-76
@@ -33,11 +33,9 @@
|
||||
|
||||
#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL)
|
||||
|
||||
/* inverse of 'pcRel' */
|
||||
#define invpcRel(pc, p) ((p)->code + (pc) + 1)
|
||||
|
||||
static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
|
||||
const char **name);
|
||||
static const char *funcnamefromcall (lua_State *L, CallInfo *ci,
|
||||
const char **name);
|
||||
|
||||
|
||||
static int currentpc (CallInfo *ci) {
|
||||
@@ -48,10 +46,16 @@ static int currentpc (CallInfo *ci) {
|
||||
|
||||
/*
|
||||
** Get a "base line" to find the line corresponding to an instruction.
|
||||
** For that, search the array of absolute line info for the largest saved
|
||||
** instruction smaller or equal to the wanted instruction. A special
|
||||
** case is when there is no absolute info or the instruction is before
|
||||
** the first absolute one.
|
||||
** Base lines are regularly placed at MAXIWTHABS intervals, so usually
|
||||
** an integer division gets the right place. When the source file has
|
||||
** large sequences of empty/comment lines, it may need extra entries,
|
||||
** so the original estimate needs a correction.
|
||||
** If the original estimate is -1, the initial 'if' ensures that the
|
||||
** 'while' will run at least once.
|
||||
** The assertion that the estimate is a lower bound for the correct base
|
||||
** is valid as long as the debug info has been generated with the same
|
||||
** value for MAXIWTHABS or smaller. (Previous releases use a little
|
||||
** smaller value.)
|
||||
*/
|
||||
static int getbaseline (const Proto *f, int pc, int *basepc) {
|
||||
if (f->sizeabslineinfo == 0 || pc < f->abslineinfo[0].pc) {
|
||||
@@ -59,20 +63,12 @@ static int getbaseline (const Proto *f, int pc, int *basepc) {
|
||||
return f->linedefined;
|
||||
}
|
||||
else {
|
||||
unsigned int i;
|
||||
if (pc >= f->abslineinfo[f->sizeabslineinfo - 1].pc)
|
||||
i = f->sizeabslineinfo - 1; /* instruction is after last saved one */
|
||||
else { /* binary search */
|
||||
unsigned int j = f->sizeabslineinfo - 1; /* pc < anchorlines[j] */
|
||||
i = 0; /* abslineinfo[i] <= pc */
|
||||
while (i < j - 1) {
|
||||
unsigned int m = (j + i) / 2;
|
||||
if (pc >= f->abslineinfo[m].pc)
|
||||
i = m;
|
||||
else
|
||||
j = m;
|
||||
}
|
||||
}
|
||||
int i = cast_uint(pc) / MAXIWTHABS - 1; /* get an estimate */
|
||||
/* estimate must be a lower bound of the correct base */
|
||||
lua_assert(i < 0 ||
|
||||
(i < f->sizeabslineinfo && f->abslineinfo[i].pc <= pc));
|
||||
while (i + 1 < f->sizeabslineinfo && pc >= f->abslineinfo[i + 1].pc)
|
||||
i++; /* low estimate; adjust it */
|
||||
*basepc = f->abslineinfo[i].pc;
|
||||
return f->abslineinfo[i].line;
|
||||
}
|
||||
@@ -305,8 +301,15 @@ static void collectvalidlines (lua_State *L, Closure *f) {
|
||||
sethvalue2s(L, L->top, t); /* push it on stack */
|
||||
api_incr_top(L);
|
||||
setbtvalue(&v); /* boolean 'true' to be the value of all indices */
|
||||
for (i = 0; i < p->sizelineinfo; i++) { /* for all lines with code */
|
||||
currentline = nextline(p, currentline, i);
|
||||
if (!p->is_vararg) /* regular function? */
|
||||
i = 0; /* consider all instructions */
|
||||
else { /* vararg function */
|
||||
lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP);
|
||||
currentline = nextline(p, currentline, 0);
|
||||
i = 1; /* skip first instruction (OP_VARARGPREP) */
|
||||
}
|
||||
for (; i < p->sizelineinfo; i++) { /* for each instruction */
|
||||
currentline = nextline(p, currentline, i); /* get its line */
|
||||
luaH_setint(L, t, currentline, &v); /* table[line] = true */
|
||||
}
|
||||
}
|
||||
@@ -314,15 +317,9 @@ static void collectvalidlines (lua_State *L, Closure *f) {
|
||||
|
||||
|
||||
static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) {
|
||||
if (ci == NULL) /* no 'ci'? */
|
||||
return NULL; /* no info */
|
||||
else if (ci->callstatus & CIST_FIN) { /* is this a finalizer? */
|
||||
*name = "__gc";
|
||||
return "metamethod"; /* report it as such */
|
||||
}
|
||||
/* calling function is a known Lua function? */
|
||||
else if (!(ci->callstatus & CIST_TAIL) && isLua(ci->previous))
|
||||
return funcnamefromcode(L, ci->previous, name);
|
||||
/* calling function is a known function? */
|
||||
if (ci != NULL && !(ci->callstatus & CIST_TAIL))
|
||||
return funcnamefromcall(L, ci->previous, name);
|
||||
else return NULL; /* no way to find a name */
|
||||
}
|
||||
|
||||
@@ -594,16 +591,10 @@ static const char *getobjname (const Proto *p, int lastpc, int reg,
|
||||
** Returns what the name is (e.g., "for iterator", "method",
|
||||
** "metamethod") and sets '*name' to point to the name.
|
||||
*/
|
||||
static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
|
||||
const char **name) {
|
||||
static const char *funcnamefromcode (lua_State *L, const Proto *p,
|
||||
int pc, const char **name) {
|
||||
TMS tm = (TMS)0; /* (initial value avoids warnings) */
|
||||
const Proto *p = ci_func(ci)->p; /* calling function */
|
||||
int pc = currentpc(ci); /* calling instruction index */
|
||||
Instruction i = p->code[pc]; /* calling instruction */
|
||||
if (ci->callstatus & CIST_HOOKED) { /* was it called inside a hook? */
|
||||
*name = "?";
|
||||
return "hook";
|
||||
}
|
||||
switch (GET_OPCODE(i)) {
|
||||
case OP_CALL:
|
||||
case OP_TAILCALL:
|
||||
@@ -629,12 +620,10 @@ static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
|
||||
case OP_LEN: tm = TM_LEN; break;
|
||||
case OP_CONCAT: tm = TM_CONCAT; break;
|
||||
case OP_EQ: tm = TM_EQ; break;
|
||||
case OP_LT: case OP_LE: case OP_LTI: case OP_LEI:
|
||||
*name = "order"; /* '<=' can call '__lt', etc. */
|
||||
return "metamethod";
|
||||
case OP_CLOSE: case OP_RETURN:
|
||||
*name = "close";
|
||||
return "metamethod";
|
||||
/* no cases for OP_EQI and OP_EQK, as they don't call metamethods */
|
||||
case OP_LT: case OP_LTI: case OP_GTI: tm = TM_LT; break;
|
||||
case OP_LE: case OP_LEI: case OP_GEI: tm = TM_LE; break;
|
||||
case OP_CLOSE: case OP_RETURN: tm = TM_CLOSE; break;
|
||||
default:
|
||||
return NULL; /* cannot find a reasonable name */
|
||||
}
|
||||
@@ -642,19 +631,43 @@ static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
|
||||
return "metamethod";
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Try to find a name for a function based on how it was called.
|
||||
*/
|
||||
static const char *funcnamefromcall (lua_State *L, CallInfo *ci,
|
||||
const char **name) {
|
||||
if (ci->callstatus & CIST_HOOKED) { /* was it called inside a hook? */
|
||||
*name = "?";
|
||||
return "hook";
|
||||
}
|
||||
else if (ci->callstatus & CIST_FIN) { /* was it called as a finalizer? */
|
||||
*name = "__gc";
|
||||
return "metamethod"; /* report it as such */
|
||||
}
|
||||
else if (isLua(ci))
|
||||
return funcnamefromcode(L, ci_func(ci)->p, currentpc(ci), name);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** The subtraction of two potentially unrelated pointers is
|
||||
** not ISO C, but it should not crash a program; the subsequent
|
||||
** checks are ISO C and ensure a correct result.
|
||||
** Check whether pointer 'o' points to some value in the stack
|
||||
** frame of the current function. Because 'o' may not point to a
|
||||
** value in this stack, we cannot compare it with the region
|
||||
** boundaries (undefined behaviour in ISO C).
|
||||
*/
|
||||
static int isinstack (CallInfo *ci, const TValue *o) {
|
||||
StkId base = ci->func + 1;
|
||||
ptrdiff_t i = cast(StkId, o) - base;
|
||||
return (0 <= i && i < (ci->top - base) && s2v(base + i) == o);
|
||||
StkId pos;
|
||||
for (pos = ci->func + 1; pos < ci->top; pos++) {
|
||||
if (o == s2v(pos))
|
||||
return 1;
|
||||
}
|
||||
return 0; /* not found */
|
||||
}
|
||||
|
||||
|
||||
@@ -677,9 +690,21 @@ static const char *getupvalname (CallInfo *ci, const TValue *o,
|
||||
}
|
||||
|
||||
|
||||
static const char *formatvarinfo (lua_State *L, const char *kind,
|
||||
const char *name) {
|
||||
if (kind == NULL)
|
||||
return ""; /* no information */
|
||||
else
|
||||
return luaO_pushfstring(L, " (%s '%s')", kind, name);
|
||||
}
|
||||
|
||||
/*
|
||||
** Build a string with a "description" for the value 'o', such as
|
||||
** "variable 'x'" or "upvalue 'y'".
|
||||
*/
|
||||
static const char *varinfo (lua_State *L, const TValue *o) {
|
||||
const char *name = NULL; /* to avoid warnings */
|
||||
CallInfo *ci = L->ci;
|
||||
const char *name = NULL; /* to avoid warnings */
|
||||
const char *kind = NULL;
|
||||
if (isLua(ci)) {
|
||||
kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */
|
||||
@@ -687,13 +712,40 @@ static const char *varinfo (lua_State *L, const TValue *o) {
|
||||
kind = getobjname(ci_func(ci)->p, currentpc(ci),
|
||||
cast_int(cast(StkId, o) - (ci->func + 1)), &name);
|
||||
}
|
||||
return (kind) ? luaO_pushfstring(L, " (%s '%s')", kind, name) : "";
|
||||
return formatvarinfo(L, kind, name);
|
||||
}
|
||||
|
||||
|
||||
l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
|
||||
/*
|
||||
** Raise a type error
|
||||
*/
|
||||
static l_noret typeerror (lua_State *L, const TValue *o, const char *op,
|
||||
const char *extra) {
|
||||
const char *t = luaT_objtypename(L, o);
|
||||
luaG_runerror(L, "attempt to %s a %s value%s", op, t, varinfo(L, o));
|
||||
luaG_runerror(L, "attempt to %s a %s value%s", op, t, extra);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Raise a type error with "standard" information about the faulty
|
||||
** object 'o' (using 'varinfo').
|
||||
*/
|
||||
l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
|
||||
typeerror(L, o, op, varinfo(L, o));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Raise an error for calling a non-callable object. Try to find a name
|
||||
** for the object based on how it was called ('funcnamefromcall'); if it
|
||||
** cannot get a name there, try 'varinfo'.
|
||||
*/
|
||||
l_noret luaG_callerror (lua_State *L, const TValue *o) {
|
||||
CallInfo *ci = L->ci;
|
||||
const char *name = NULL; /* to avoid warnings */
|
||||
const char *kind = funcnamefromcall(L, ci, &name);
|
||||
const char *extra = kind ? formatvarinfo(L, kind, name) : varinfo(L, o);
|
||||
typeerror(L, o, "call", extra);
|
||||
}
|
||||
|
||||
|
||||
@@ -722,7 +774,7 @@ l_noret luaG_opinterror (lua_State *L, const TValue *p1,
|
||||
*/
|
||||
l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2) {
|
||||
lua_Integer temp;
|
||||
if (!tointegerns(p1, &temp))
|
||||
if (!luaV_tointegerns(p1, &temp, LUA_FLOORN2I))
|
||||
p2 = p1;
|
||||
luaG_runerror(L, "number%s has no integer representation", varinfo(L, p2));
|
||||
}
|
||||
@@ -780,16 +832,30 @@ l_noret luaG_runerror (lua_State *L, const char *fmt, ...) {
|
||||
|
||||
/*
|
||||
** Check whether new instruction 'newpc' is in a different line from
|
||||
** previous instruction 'oldpc'.
|
||||
** previous instruction 'oldpc'. More often than not, 'newpc' is only
|
||||
** one or a few instructions after 'oldpc' (it must be after, see
|
||||
** caller), so try to avoid calling 'luaG_getfuncline'. If they are
|
||||
** too far apart, there is a good chance of a ABSLINEINFO in the way,
|
||||
** so it goes directly to 'luaG_getfuncline'.
|
||||
*/
|
||||
static int changedline (const Proto *p, int oldpc, int newpc) {
|
||||
if (p->lineinfo == NULL) /* no debug information? */
|
||||
return 0;
|
||||
while (oldpc++ < newpc) {
|
||||
if (p->lineinfo[oldpc] != 0)
|
||||
return (luaG_getfuncline(p, oldpc - 1) != luaG_getfuncline(p, newpc));
|
||||
if (newpc - oldpc < MAXIWTHABS / 2) { /* not too far apart? */
|
||||
int delta = 0; /* line diference */
|
||||
int pc = oldpc;
|
||||
for (;;) {
|
||||
int lineinfo = p->lineinfo[++pc];
|
||||
if (lineinfo == ABSLINEINFO)
|
||||
break; /* cannot compute delta; fall through */
|
||||
delta += lineinfo;
|
||||
if (pc == newpc)
|
||||
return (delta != 0); /* delta computed successfully */
|
||||
}
|
||||
}
|
||||
return 0; /* no line changes between positions */
|
||||
/* either instructions are too far apart or there is an absolute line
|
||||
info in the way; compute line difference explicitly */
|
||||
return (luaG_getfuncline(p, oldpc) != luaG_getfuncline(p, newpc));
|
||||
}
|
||||
|
||||
|
||||
@@ -797,20 +863,19 @@ static int changedline (const Proto *p, int oldpc, int newpc) {
|
||||
** Traces the execution of a Lua function. Called before the execution
|
||||
** of each opcode, when debug is on. 'L->oldpc' stores the last
|
||||
** instruction traced, to detect line changes. When entering a new
|
||||
** function, 'npci' will be zero and will test as a new line without
|
||||
** the need for 'oldpc'; so, 'oldpc' does not need to be initialized
|
||||
** before. Some exceptional conditions may return to a function without
|
||||
** updating 'oldpc'. In that case, 'oldpc' may be invalid; if so, it is
|
||||
** reset to zero. (A wrong but valid 'oldpc' at most causes an extra
|
||||
** call to a line hook.)
|
||||
** function, 'npci' will be zero and will test as a new line whatever
|
||||
** the value of 'oldpc'. Some exceptional conditions may return to
|
||||
** a function without setting 'oldpc'. In that case, 'oldpc' may be
|
||||
** invalid; if so, use zero as a valid value. (A wrong but valid 'oldpc'
|
||||
** at most causes an extra call to a line hook.)
|
||||
** This function is not "Protected" when called, so it should correct
|
||||
** 'L->top' before calling anything that can run the GC.
|
||||
*/
|
||||
int luaG_traceexec (lua_State *L, const Instruction *pc) {
|
||||
CallInfo *ci = L->ci;
|
||||
lu_byte mask = L->hookmask;
|
||||
const Proto *p = ci_func(ci)->p;
|
||||
int counthook;
|
||||
/* 'L->oldpc' may be invalid; reset it in this case */
|
||||
int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0;
|
||||
if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */
|
||||
ci->u.l.trap = 0; /* don't need to stop again */
|
||||
return 0; /* turn off 'trap' */
|
||||
@@ -826,15 +891,16 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
|
||||
ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */
|
||||
return 1; /* do not call hook again (VM yielded, so it did not move) */
|
||||
}
|
||||
if (!isIT(*(ci->u.l.savedpc - 1)))
|
||||
L->top = ci->top; /* prepare top */
|
||||
if (!isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */
|
||||
L->top = ci->top; /* correct top */
|
||||
if (counthook)
|
||||
luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */
|
||||
if (mask & LUA_MASKLINE) {
|
||||
/* 'L->oldpc' may be invalid; use zero in this case */
|
||||
int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0;
|
||||
int npci = pcRel(pc, p);
|
||||
if (npci == 0 || /* call linehook when enter a new function, */
|
||||
pc <= invpcRel(oldpc, p) || /* when jump back (loop), or when */
|
||||
changedline(p, oldpc, npci)) { /* enter new line */
|
||||
if (npci <= oldpc || /* call hook when jump back (loop), */
|
||||
changedline(p, oldpc, npci)) { /* or when enter new line */
|
||||
int newline = luaG_getfuncline(p, npci);
|
||||
luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */
|
||||
}
|
||||
|
||||
@@ -26,11 +26,22 @@
|
||||
*/
|
||||
#define ABSLINEINFO (-0x80)
|
||||
|
||||
|
||||
/*
|
||||
** MAXimum number of successive Instructions WiTHout ABSolute line
|
||||
** information. (A power of two allows fast divisions.)
|
||||
*/
|
||||
#if !defined(MAXIWTHABS)
|
||||
#define MAXIWTHABS 128
|
||||
#endif
|
||||
|
||||
|
||||
LUAI_FUNC int luaG_getfuncline (const Proto *f, int pc);
|
||||
LUAI_FUNC const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n,
|
||||
StkId *pos);
|
||||
LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o,
|
||||
const char *opname);
|
||||
LUAI_FUNC l_noret luaG_callerror (lua_State *L, const TValue *o);
|
||||
LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o,
|
||||
const char *what);
|
||||
LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1,
|
||||
|
||||
+392
-217
@@ -98,11 +98,12 @@ void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
|
||||
setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
|
||||
break;
|
||||
}
|
||||
case CLOSEPROTECT: {
|
||||
case LUA_OK: { /* special case only for closing upvalues */
|
||||
setnilvalue(s2v(oldtop)); /* no error message */
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
lua_assert(errorstatus(errcode)); /* real error */
|
||||
setobjs2s(L, oldtop, L->top - 1); /* error message on current top */
|
||||
break;
|
||||
}
|
||||
@@ -118,17 +119,13 @@ l_noret luaD_throw (lua_State *L, int errcode) {
|
||||
}
|
||||
else { /* thread has no error handler */
|
||||
global_State *g = G(L);
|
||||
errcode = luaF_close(L, L->stack, errcode); /* close all upvalues */
|
||||
L->status = cast_byte(errcode); /* mark it as dead */
|
||||
errcode = luaE_resetthread(L, errcode); /* close all upvalues */
|
||||
if (g->mainthread->errorJmp) { /* main thread has a handler? */
|
||||
setobjs2s(L, g->mainthread->top++, L->top - 1); /* copy error obj. */
|
||||
luaD_throw(g->mainthread, errcode); /* re-throw in main thread */
|
||||
}
|
||||
else { /* no handler at all; abort */
|
||||
if (g->panic) { /* panic function? */
|
||||
luaD_seterrorobj(L, errcode, L->top); /* assume EXTRA_STACK */
|
||||
if (L->ci->top < L->top)
|
||||
L->ci->top = L->top; /* pushing msg. can break this invariant */
|
||||
lua_unlock(L);
|
||||
g->panic(L); /* call panic function (last chance to jump out) */
|
||||
}
|
||||
@@ -139,8 +136,7 @@ l_noret luaD_throw (lua_State *L, int errcode) {
|
||||
|
||||
|
||||
int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
|
||||
global_State *g = G(L);
|
||||
l_uint32 oldnCcalls = g->Cstacklimit - (L->nCcalls + L->nci);
|
||||
l_uint32 oldnCcalls = L->nCcalls;
|
||||
struct lua_longjmp lj;
|
||||
lj.status = LUA_OK;
|
||||
lj.previous = L->errorJmp; /* chain new error handler */
|
||||
@@ -149,7 +145,7 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
|
||||
(*f)(L, ud);
|
||||
);
|
||||
L->errorJmp = lj.previous; /* restore old error handler */
|
||||
L->nCcalls = g->Cstacklimit - oldnCcalls - L->nci;
|
||||
L->nCcalls = oldnCcalls;
|
||||
return lj.status;
|
||||
}
|
||||
|
||||
@@ -164,9 +160,8 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
|
||||
static void correctstack (lua_State *L, StkId oldstack, StkId newstack) {
|
||||
CallInfo *ci;
|
||||
UpVal *up;
|
||||
if (oldstack == newstack)
|
||||
return; /* stack address did not change */
|
||||
L->top = (L->top - oldstack) + newstack;
|
||||
L->tbclist = (L->tbclist - oldstack) + newstack;
|
||||
for (up = L->openupval; up != NULL; up = up->u.open.next)
|
||||
up->v = s2v((uplevel(up) - oldstack) + newstack);
|
||||
for (ci = L->ci; ci != NULL; ci = ci->previous) {
|
||||
@@ -182,22 +177,37 @@ static void correctstack (lua_State *L, StkId oldstack, StkId newstack) {
|
||||
#define ERRORSTACKSIZE (LUAI_MAXSTACK + 200)
|
||||
|
||||
|
||||
/*
|
||||
** Reallocate the stack to a new size, correcting all pointers into
|
||||
** it. (There are pointers to a stack from its upvalues, from its list
|
||||
** of call infos, plus a few individual pointers.) The reallocation is
|
||||
** done in two steps (allocation + free) because the correction must be
|
||||
** done while both addresses (the old stack and the new one) are valid.
|
||||
** (In ISO C, any pointer use after the pointer has been deallocated is
|
||||
** undefined behavior.)
|
||||
** In case of allocation error, raise an error or return false according
|
||||
** to 'raiseerror'.
|
||||
*/
|
||||
int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) {
|
||||
int lim = L->stacksize;
|
||||
StkId newstack = luaM_reallocvector(L, L->stack, lim, newsize, StackValue);
|
||||
int oldsize = stacksize(L);
|
||||
int i;
|
||||
StkId newstack = luaM_reallocvector(L, NULL, 0,
|
||||
newsize + EXTRA_STACK, StackValue);
|
||||
lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE);
|
||||
lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK);
|
||||
if (unlikely(newstack == NULL)) { /* reallocation failed? */
|
||||
if (l_unlikely(newstack == NULL)) { /* reallocation failed? */
|
||||
if (raiseerror)
|
||||
luaM_error(L);
|
||||
else return 0; /* do not raise an error */
|
||||
}
|
||||
for (; lim < newsize; lim++)
|
||||
setnilvalue(s2v(newstack + lim)); /* erase new segment */
|
||||
/* number of elements to be copied to the new stack */
|
||||
i = ((oldsize <= newsize) ? oldsize : newsize) + EXTRA_STACK;
|
||||
memcpy(newstack, L->stack, i * sizeof(StackValue));
|
||||
for (; i < newsize + EXTRA_STACK; i++)
|
||||
setnilvalue(s2v(newstack + i)); /* erase new segment */
|
||||
correctstack(L, L->stack, newstack);
|
||||
luaM_freearray(L, L->stack, oldsize + EXTRA_STACK);
|
||||
L->stack = newstack;
|
||||
L->stacksize = newsize;
|
||||
L->stack_last = L->stack + newsize - EXTRA_STACK;
|
||||
L->stack_last = L->stack + newsize;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -207,51 +217,73 @@ int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) {
|
||||
** is true, raises any error; otherwise, return 0 in case of errors.
|
||||
*/
|
||||
int luaD_growstack (lua_State *L, int n, int raiseerror) {
|
||||
int size = L->stacksize;
|
||||
int newsize = 2 * size; /* tentative new size */
|
||||
if (unlikely(size > LUAI_MAXSTACK)) { /* need more space after extra size? */
|
||||
int size = stacksize(L);
|
||||
if (l_unlikely(size > LUAI_MAXSTACK)) {
|
||||
/* if stack is larger than maximum, thread is already using the
|
||||
extra space reserved for errors, that is, thread is handling
|
||||
a stack error; cannot grow further than that. */
|
||||
lua_assert(stacksize(L) == ERRORSTACKSIZE);
|
||||
if (raiseerror)
|
||||
luaD_throw(L, LUA_ERRERR); /* error inside message handler */
|
||||
else return 0;
|
||||
return 0; /* if not 'raiseerror', just signal it */
|
||||
}
|
||||
else {
|
||||
int needed = cast_int(L->top - L->stack) + n + EXTRA_STACK;
|
||||
int newsize = 2 * size; /* tentative new size */
|
||||
int needed = cast_int(L->top - L->stack) + n;
|
||||
if (newsize > LUAI_MAXSTACK) /* cannot cross the limit */
|
||||
newsize = LUAI_MAXSTACK;
|
||||
if (newsize < needed) /* but must respect what was asked for */
|
||||
newsize = needed;
|
||||
if (unlikely(newsize > LUAI_MAXSTACK)) { /* stack overflow? */
|
||||
if (l_likely(newsize <= LUAI_MAXSTACK))
|
||||
return luaD_reallocstack(L, newsize, raiseerror);
|
||||
else { /* stack overflow */
|
||||
/* add extra size to be able to handle the error message */
|
||||
luaD_reallocstack(L, ERRORSTACKSIZE, raiseerror);
|
||||
if (raiseerror)
|
||||
luaG_runerror(L, "stack overflow");
|
||||
else return 0;
|
||||
return 0;
|
||||
}
|
||||
} /* else no errors */
|
||||
return luaD_reallocstack(L, newsize, raiseerror);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int stackinuse (lua_State *L) {
|
||||
CallInfo *ci;
|
||||
int res;
|
||||
StkId lim = L->top;
|
||||
for (ci = L->ci; ci != NULL; ci = ci->previous) {
|
||||
if (lim < ci->top) lim = ci->top;
|
||||
}
|
||||
lua_assert(lim <= L->stack_last);
|
||||
return cast_int(lim - L->stack) + 1; /* part of stack in use */
|
||||
res = cast_int(lim - L->stack) + 1; /* part of stack in use */
|
||||
if (res < LUA_MINSTACK)
|
||||
res = LUA_MINSTACK; /* ensure a minimum size */
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** If stack size is more than 3 times the current use, reduce that size
|
||||
** to twice the current use. (So, the final stack size is at most 2/3 the
|
||||
** previous size, and half of its entries are empty.)
|
||||
** As a particular case, if stack was handling a stack overflow and now
|
||||
** it is not, 'max' (limited by LUAI_MAXSTACK) will be smaller than
|
||||
** stacksize (equal to ERRORSTACKSIZE in this case), and so the stack
|
||||
** will be reduced to a "regular" size.
|
||||
*/
|
||||
void luaD_shrinkstack (lua_State *L) {
|
||||
int inuse = stackinuse(L);
|
||||
int goodsize = inuse + BASIC_STACK_SIZE;
|
||||
if (goodsize > LUAI_MAXSTACK)
|
||||
goodsize = LUAI_MAXSTACK; /* respect stack limit */
|
||||
int nsize = inuse * 2; /* proposed new size */
|
||||
int max = inuse * 3; /* maximum "reasonable" size */
|
||||
if (max > LUAI_MAXSTACK) {
|
||||
max = LUAI_MAXSTACK; /* respect stack limit */
|
||||
if (nsize > LUAI_MAXSTACK)
|
||||
nsize = LUAI_MAXSTACK;
|
||||
}
|
||||
/* if thread is currently not handling a stack overflow and its
|
||||
good size is smaller than current size, shrink its stack */
|
||||
if (inuse <= (LUAI_MAXSTACK - EXTRA_STACK) && goodsize < L->stacksize)
|
||||
luaD_reallocstack(L, goodsize, 0); /* ok if that fails */
|
||||
size is larger than maximum "reasonable" size, shrink it */
|
||||
if (inuse <= LUAI_MAXSTACK && stacksize(L) > max)
|
||||
luaD_reallocstack(L, nsize, 0); /* ok if that fails */
|
||||
else /* don't change stack */
|
||||
condmovestack(L,{},{}); /* (change only for debugging) */
|
||||
luaE_shrinkCI(L); /* shrink CI list */
|
||||
@@ -277,8 +309,8 @@ void luaD_hook (lua_State *L, int event, int line,
|
||||
if (hook && L->allowhook) { /* make sure there is a hook */
|
||||
int mask = CIST_HOOKED;
|
||||
CallInfo *ci = L->ci;
|
||||
ptrdiff_t top = savestack(L, L->top);
|
||||
ptrdiff_t ci_top = savestack(L, ci->top);
|
||||
ptrdiff_t top = savestack(L, L->top); /* preserve original 'top' */
|
||||
ptrdiff_t ci_top = savestack(L, ci->top); /* idem for 'ci->top' */
|
||||
lua_Debug ar;
|
||||
ar.event = event;
|
||||
ar.currentline = line;
|
||||
@@ -288,8 +320,10 @@ void luaD_hook (lua_State *L, int event, int line,
|
||||
ci->u2.transferinfo.ftransfer = ftransfer;
|
||||
ci->u2.transferinfo.ntransfer = ntransfer;
|
||||
}
|
||||
if (isLua(ci) && L->top < ci->top)
|
||||
L->top = ci->top; /* protect entire activation register */
|
||||
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
|
||||
if (L->top + LUA_MINSTACK > ci->top)
|
||||
if (ci->top < L->top + LUA_MINSTACK)
|
||||
ci->top = L->top + LUA_MINSTACK;
|
||||
L->allowhook = 0; /* cannot call hooks inside a hook */
|
||||
ci->callstatus |= mask;
|
||||
@@ -311,55 +345,60 @@ void luaD_hook (lua_State *L, int event, int line,
|
||||
** active.
|
||||
*/
|
||||
void luaD_hookcall (lua_State *L, CallInfo *ci) {
|
||||
int hook = (ci->callstatus & CIST_TAIL) ? LUA_HOOKTAILCALL : LUA_HOOKCALL;
|
||||
Proto *p;
|
||||
if (!(L->hookmask & LUA_MASKCALL)) /* some other hook? */
|
||||
return; /* don't call hook */
|
||||
p = clLvalue(s2v(ci->func))->p;
|
||||
L->top = ci->top; /* prepare top */
|
||||
ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */
|
||||
luaD_hook(L, hook, -1, 1, p->numparams);
|
||||
ci->u.l.savedpc--; /* correct 'pc' */
|
||||
L->oldpc = 0; /* set 'oldpc' for new function */
|
||||
if (L->hookmask & LUA_MASKCALL) { /* is call hook on? */
|
||||
int event = (ci->callstatus & CIST_TAIL) ? LUA_HOOKTAILCALL
|
||||
: LUA_HOOKCALL;
|
||||
Proto *p = ci_func(ci)->p;
|
||||
ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */
|
||||
luaD_hook(L, event, -1, 1, p->numparams);
|
||||
ci->u.l.savedpc--; /* correct 'pc' */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
|
||||
ptrdiff_t oldtop = savestack(L, L->top); /* hook may change top */
|
||||
int delta = 0;
|
||||
if (isLuacode(ci)) {
|
||||
Proto *p = ci_func(ci)->p;
|
||||
if (p->is_vararg)
|
||||
delta = ci->u.l.nextraargs + p->numparams + 1;
|
||||
if (L->top < ci->top)
|
||||
L->top = ci->top; /* correct top to run hook */
|
||||
}
|
||||
/*
|
||||
** Executes a return hook for Lua and C functions and sets/corrects
|
||||
** 'oldpc'. (Note that this correction is needed by the line hook, so it
|
||||
** is done even when return hooks are off.)
|
||||
*/
|
||||
static void rethook (lua_State *L, CallInfo *ci, int nres) {
|
||||
if (L->hookmask & LUA_MASKRET) { /* is return hook on? */
|
||||
StkId firstres = L->top - nres; /* index of first result */
|
||||
int delta = 0; /* correction for vararg functions */
|
||||
int ftransfer;
|
||||
if (isLua(ci)) {
|
||||
Proto *p = ci_func(ci)->p;
|
||||
if (p->is_vararg)
|
||||
delta = ci->u.l.nextraargs + p->numparams + 1;
|
||||
}
|
||||
ci->func += delta; /* if vararg, back to virtual 'func' */
|
||||
ftransfer = cast(unsigned short, firstres - ci->func);
|
||||
luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */
|
||||
ci->func -= delta;
|
||||
}
|
||||
if (isLua(ci = ci->previous))
|
||||
L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* update 'oldpc' */
|
||||
return restorestack(L, oldtop);
|
||||
L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* set 'oldpc' */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check whether 'func' has a '__call' metafield. If so, put it in the
|
||||
** stack, below original 'func', so that 'luaD_call' can call it. Raise
|
||||
** stack, below original 'func', so that 'luaD_precall' can call it. Raise
|
||||
** an error if there is no '__call' metafield.
|
||||
*/
|
||||
void luaD_tryfuncTM (lua_State *L, StkId func) {
|
||||
const TValue *tm = luaT_gettmbyobj(L, s2v(func), TM_CALL);
|
||||
StkId luaD_tryfuncTM (lua_State *L, StkId func) {
|
||||
const TValue *tm;
|
||||
StkId p;
|
||||
if (unlikely(ttisnil(tm)))
|
||||
luaG_typeerror(L, s2v(func), "call"); /* nothing to call */
|
||||
checkstackGCp(L, 1, func); /* space for metamethod */
|
||||
tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); /* (after previous GC) */
|
||||
if (l_unlikely(ttisnil(tm)))
|
||||
luaG_callerror(L, s2v(func)); /* nothing to call */
|
||||
for (p = L->top; p > func; p--) /* open space for metamethod */
|
||||
setobjs2s(L, p, p-1);
|
||||
L->top++; /* stack space pre-allocated by the caller */
|
||||
setobj2s(L, func, tm); /* metamethod is the new function to be called */
|
||||
return func;
|
||||
}
|
||||
|
||||
|
||||
@@ -369,7 +408,7 @@ void luaD_tryfuncTM (lua_State *L, StkId func) {
|
||||
** expressions, multiple results for tail calls/single parameters)
|
||||
** separated.
|
||||
*/
|
||||
static void moveresults (lua_State *L, StkId res, int nres, int wanted) {
|
||||
l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) {
|
||||
StkId firstresult;
|
||||
int i;
|
||||
switch (wanted) { /* handle typical cases separately */
|
||||
@@ -379,27 +418,34 @@ static void moveresults (lua_State *L, StkId res, int nres, int wanted) {
|
||||
case 1: /* one value needed */
|
||||
if (nres == 0) /* no results? */
|
||||
setnilvalue(s2v(res)); /* adjust with nil */
|
||||
else
|
||||
else /* at least one result */
|
||||
setobjs2s(L, res, L->top - nres); /* move it to proper place */
|
||||
L->top = res + 1;
|
||||
return;
|
||||
case LUA_MULTRET:
|
||||
wanted = nres; /* we want all results */
|
||||
break;
|
||||
default: /* multiple results (or to-be-closed variables) */
|
||||
default: /* two/more results and/or to-be-closed variables */
|
||||
if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */
|
||||
ptrdiff_t savedres = savestack(L, res);
|
||||
luaF_close(L, res, LUA_OK); /* may change the stack */
|
||||
res = restorestack(L, savedres);
|
||||
wanted = codeNresults(wanted); /* correct value */
|
||||
L->ci->callstatus |= CIST_CLSRET; /* in case of yields */
|
||||
L->ci->u2.nres = nres;
|
||||
luaF_close(L, res, CLOSEKTOP, 1);
|
||||
L->ci->callstatus &= ~CIST_CLSRET;
|
||||
if (L->hookmask) /* if needed, call hook after '__close's */
|
||||
rethook(L, L->ci, nres);
|
||||
res = restorestack(L, savedres); /* close and hook can move stack */
|
||||
wanted = decodeNresults(wanted);
|
||||
if (wanted == LUA_MULTRET)
|
||||
wanted = nres;
|
||||
wanted = nres; /* we want all results */
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* generic case */
|
||||
firstresult = L->top - nres; /* index of first result */
|
||||
/* move all results to correct place */
|
||||
for (i = 0; i < nres && i < wanted; i++)
|
||||
if (nres > wanted) /* extra results? */
|
||||
nres = wanted; /* don't need them */
|
||||
for (i = 0; i < nres; i++) /* move all results to correct place */
|
||||
setobjs2s(L, res + i, firstresult + i);
|
||||
for (; i < wanted; i++) /* complete wanted number of results */
|
||||
setnilvalue(s2v(res + i));
|
||||
@@ -408,15 +454,21 @@ static void moveresults (lua_State *L, StkId res, int nres, int wanted) {
|
||||
|
||||
|
||||
/*
|
||||
** Finishes a function call: calls hook if necessary, removes CallInfo,
|
||||
** moves current number of results to proper place.
|
||||
** Finishes a function call: calls hook if necessary, moves current
|
||||
** number of results to proper place, and returns to previous call
|
||||
** info. If function has to close variables, hook must be called after
|
||||
** that.
|
||||
*/
|
||||
void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
|
||||
if (L->hookmask)
|
||||
L->top = rethook(L, ci, L->top - nres, nres);
|
||||
L->ci = ci->previous; /* back to caller */
|
||||
int wanted = ci->nresults;
|
||||
if (l_unlikely(L->hookmask && !hastocloseCfunc(wanted)))
|
||||
rethook(L, ci, nres);
|
||||
/* move results to proper place */
|
||||
moveresults(L, ci->func, nres, ci->nresults);
|
||||
moveresults(L, ci->func, nres, wanted);
|
||||
/* function cannot be in any of these cases when returning */
|
||||
lua_assert(!(ci->callstatus &
|
||||
(CIST_HOOKED | CIST_YPCALL | CIST_FIN | CIST_TRAN | CIST_CLSRET)));
|
||||
L->ci = ci->previous; /* back to caller (after closing variables) */
|
||||
}
|
||||
|
||||
|
||||
@@ -424,66 +476,101 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
|
||||
#define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L))
|
||||
|
||||
|
||||
/*
|
||||
** Prepare a function for a tail call, building its call info on top
|
||||
** of the current call info. 'narg1' is the number of arguments plus 1
|
||||
** (so that it includes the function itself).
|
||||
*/
|
||||
void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1) {
|
||||
Proto *p = clLvalue(s2v(func))->p;
|
||||
int fsize = p->maxstacksize; /* frame size */
|
||||
int nfixparams = p->numparams;
|
||||
int i;
|
||||
for (i = 0; i < narg1; i++) /* move down function and arguments */
|
||||
setobjs2s(L, ci->func + i, func + i);
|
||||
checkstackGC(L, fsize);
|
||||
func = ci->func; /* moved-down function */
|
||||
for (; narg1 <= nfixparams; narg1++)
|
||||
setnilvalue(s2v(func + narg1)); /* complete missing arguments */
|
||||
ci->top = func + 1 + fsize; /* top for new function */
|
||||
lua_assert(ci->top <= L->stack_last);
|
||||
ci->u.l.savedpc = p->code; /* starting point */
|
||||
ci->callstatus |= CIST_TAIL;
|
||||
L->top = func + narg1; /* set top */
|
||||
l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, int nret,
|
||||
int mask, StkId top) {
|
||||
CallInfo *ci = L->ci = next_ci(L); /* new frame */
|
||||
ci->func = func;
|
||||
ci->nresults = nret;
|
||||
ci->callstatus = mask;
|
||||
ci->top = top;
|
||||
return ci;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Call a function (C or Lua). The function to be called is at *func.
|
||||
** The arguments are on the stack, right after the function.
|
||||
** When returns, all the results are on the stack, starting at the original
|
||||
** function position.
|
||||
** precall for C functions
|
||||
*/
|
||||
void luaD_call (lua_State *L, StkId func, int nresults) {
|
||||
lua_CFunction f;
|
||||
l_sinline int precallC (lua_State *L, StkId func, int nresults,
|
||||
lua_CFunction f) {
|
||||
int n; /* number of returns */
|
||||
CallInfo *ci;
|
||||
checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */
|
||||
L->ci = ci = prepCallInfo(L, func, nresults, CIST_C,
|
||||
L->top + LUA_MINSTACK);
|
||||
lua_assert(ci->top <= L->stack_last);
|
||||
if (l_unlikely(L->hookmask & LUA_MASKCALL)) {
|
||||
int narg = cast_int(L->top - func) - 1;
|
||||
luaD_hook(L, LUA_HOOKCALL, -1, 1, narg);
|
||||
}
|
||||
lua_unlock(L);
|
||||
n = (*f)(L); /* do the actual call */
|
||||
lua_lock(L);
|
||||
api_checknelems(L, n);
|
||||
luaD_poscall(L, ci, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prepare a function for a tail call, building its call info on top
|
||||
** of the current call info. 'narg1' is the number of arguments plus 1
|
||||
** (so that it includes the function itself). Return the number of
|
||||
** results, if it was a C function, or -1 for a Lua function.
|
||||
*/
|
||||
int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func,
|
||||
int narg1, int delta) {
|
||||
retry:
|
||||
switch (ttypetag(s2v(func))) {
|
||||
case LUA_VCCL: /* C closure */
|
||||
f = clCvalue(s2v(func))->f;
|
||||
goto Cfunc;
|
||||
return precallC(L, func, LUA_MULTRET, clCvalue(s2v(func))->f);
|
||||
case LUA_VLCF: /* light C function */
|
||||
f = fvalue(s2v(func));
|
||||
Cfunc: {
|
||||
int n; /* number of returns */
|
||||
CallInfo *ci;
|
||||
checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */
|
||||
L->ci = ci = next_ci(L);
|
||||
ci->nresults = nresults;
|
||||
ci->callstatus = CIST_C;
|
||||
ci->top = L->top + LUA_MINSTACK;
|
||||
ci->func = func;
|
||||
return precallC(L, func, LUA_MULTRET, fvalue(s2v(func)));
|
||||
case LUA_VLCL: { /* Lua function */
|
||||
Proto *p = clLvalue(s2v(func))->p;
|
||||
int fsize = p->maxstacksize; /* frame size */
|
||||
int nfixparams = p->numparams;
|
||||
int i;
|
||||
checkstackGCp(L, fsize - delta, func);
|
||||
ci->func -= delta; /* restore 'func' (if vararg) */
|
||||
for (i = 0; i < narg1; i++) /* move down function and arguments */
|
||||
setobjs2s(L, ci->func + i, func + i);
|
||||
func = ci->func; /* moved-down function */
|
||||
for (; narg1 <= nfixparams; narg1++)
|
||||
setnilvalue(s2v(func + narg1)); /* complete missing arguments */
|
||||
ci->top = func + 1 + fsize; /* top for new function */
|
||||
lua_assert(ci->top <= L->stack_last);
|
||||
if (L->hookmask & LUA_MASKCALL) {
|
||||
int narg = cast_int(L->top - func) - 1;
|
||||
luaD_hook(L, LUA_HOOKCALL, -1, 1, narg);
|
||||
}
|
||||
lua_unlock(L);
|
||||
n = (*f)(L); /* do the actual call */
|
||||
lua_lock(L);
|
||||
api_checknelems(L, n);
|
||||
luaD_poscall(L, ci, n);
|
||||
break;
|
||||
ci->u.l.savedpc = p->code; /* starting point */
|
||||
ci->callstatus |= CIST_TAIL;
|
||||
L->top = func + narg1; /* set top */
|
||||
return -1;
|
||||
}
|
||||
default: { /* not a function */
|
||||
func = luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */
|
||||
/* return luaD_pretailcall(L, ci, func, narg1 + 1, delta); */
|
||||
narg1++;
|
||||
goto retry; /* try again */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prepares the call to a function (C or Lua). For C functions, also do
|
||||
** the call. The function to be called is at '*func'. The arguments
|
||||
** are on the stack, right after the function. Returns the CallInfo
|
||||
** to be executed, if it was a Lua function. Otherwise (a C function)
|
||||
** returns NULL, with all the results on the stack, starting at the
|
||||
** original function position.
|
||||
*/
|
||||
CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
|
||||
retry:
|
||||
switch (ttypetag(s2v(func))) {
|
||||
case LUA_VCCL: /* C closure */
|
||||
precallC(L, func, nresults, clCvalue(s2v(func))->f);
|
||||
return NULL;
|
||||
case LUA_VLCF: /* light C function */
|
||||
precallC(L, func, nresults, fvalue(s2v(func)));
|
||||
return NULL;
|
||||
case LUA_VLCL: { /* Lua function */
|
||||
CallInfo *ci;
|
||||
Proto *p = clLvalue(s2v(func))->p;
|
||||
@@ -491,22 +578,16 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
|
||||
int nfixparams = p->numparams;
|
||||
int fsize = p->maxstacksize; /* frame size */
|
||||
checkstackGCp(L, fsize, func);
|
||||
L->ci = ci = next_ci(L);
|
||||
ci->nresults = nresults;
|
||||
L->ci = ci = prepCallInfo(L, func, nresults, 0, func + 1 + fsize);
|
||||
ci->u.l.savedpc = p->code; /* starting point */
|
||||
ci->callstatus = 0;
|
||||
ci->top = func + 1 + fsize;
|
||||
ci->func = func;
|
||||
L->ci = ci;
|
||||
for (; narg < nfixparams; narg++)
|
||||
setnilvalue(s2v(L->top++)); /* complete missing arguments */
|
||||
lua_assert(ci->top <= L->stack_last);
|
||||
luaV_execute(L, ci); /* run the function */
|
||||
break;
|
||||
return ci;
|
||||
}
|
||||
default: { /* not a function */
|
||||
checkstackGCp(L, 1, func); /* space for metamethod */
|
||||
luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */
|
||||
func = luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */
|
||||
/* return luaD_precall(L, func, nresults); */
|
||||
goto retry; /* try again with metamethod */
|
||||
}
|
||||
}
|
||||
@@ -514,41 +595,108 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
|
||||
|
||||
|
||||
/*
|
||||
** Similar to 'luaD_call', but does not allow yields during the call.
|
||||
** Call a function (C or Lua) through C. 'inc' can be 1 (increment
|
||||
** number of recursive invocations in the C stack) or nyci (the same
|
||||
** plus increment number of non-yieldable calls).
|
||||
*/
|
||||
void luaD_callnoyield (lua_State *L, StkId func, int nResults) {
|
||||
incXCcalls(L);
|
||||
if (getCcalls(L) <= CSTACKERR) { /* possible C stack overflow? */
|
||||
luaE_exitCcall(L); /* to compensate decrement in next call */
|
||||
luaE_enterCcall(L); /* check properly */
|
||||
l_sinline void ccall (lua_State *L, StkId func, int nResults, int inc) {
|
||||
CallInfo *ci;
|
||||
L->nCcalls += inc;
|
||||
if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS))
|
||||
luaE_checkcstack(L);
|
||||
if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */
|
||||
ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */
|
||||
luaV_execute(L, ci); /* call it */
|
||||
}
|
||||
luaD_call(L, func, nResults);
|
||||
decXCcalls(L);
|
||||
L->nCcalls -= inc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Completes the execution of an interrupted C function, calling its
|
||||
** continuation function.
|
||||
** External interface for 'ccall'
|
||||
*/
|
||||
static void finishCcall (lua_State *L, int status) {
|
||||
CallInfo *ci = L->ci;
|
||||
int n;
|
||||
/* must have a continuation and must be able to call it */
|
||||
lua_assert(ci->u.c.k != NULL && yieldable(L));
|
||||
/* error status can only happen in a protected call */
|
||||
lua_assert((ci->callstatus & CIST_YPCALL) || status == LUA_YIELD);
|
||||
if (ci->callstatus & CIST_YPCALL) { /* was inside a pcall? */
|
||||
ci->callstatus &= ~CIST_YPCALL; /* continuation is also inside it */
|
||||
L->errfunc = ci->u.c.old_errfunc; /* with the same error function */
|
||||
void luaD_call (lua_State *L, StkId func, int nResults) {
|
||||
ccall(L, func, nResults, 1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Similar to 'luaD_call', but does not allow yields during the call.
|
||||
*/
|
||||
void luaD_callnoyield (lua_State *L, StkId func, int nResults) {
|
||||
ccall(L, func, nResults, nyci);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Finish the job of 'lua_pcallk' after it was interrupted by an yield.
|
||||
** (The caller, 'finishCcall', does the final call to 'adjustresults'.)
|
||||
** The main job is to complete the 'luaD_pcall' called by 'lua_pcallk'.
|
||||
** If a '__close' method yields here, eventually control will be back
|
||||
** to 'finishCcall' (when that '__close' method finally returns) and
|
||||
** 'finishpcallk' will run again and close any still pending '__close'
|
||||
** methods. Similarly, if a '__close' method errs, 'precover' calls
|
||||
** 'unroll' which calls ''finishCcall' and we are back here again, to
|
||||
** close any pending '__close' methods.
|
||||
** Note that, up to the call to 'luaF_close', the corresponding
|
||||
** 'CallInfo' is not modified, so that this repeated run works like the
|
||||
** first one (except that it has at least one less '__close' to do). In
|
||||
** particular, field CIST_RECST preserves the error status across these
|
||||
** multiple runs, changing only if there is a new error.
|
||||
*/
|
||||
static int finishpcallk (lua_State *L, CallInfo *ci) {
|
||||
int status = getcistrecst(ci); /* get original status */
|
||||
if (l_likely(status == LUA_OK)) /* no error? */
|
||||
status = LUA_YIELD; /* was interrupted by an yield */
|
||||
else { /* error */
|
||||
StkId func = restorestack(L, ci->u2.funcidx);
|
||||
L->allowhook = getoah(ci->callstatus); /* restore 'allowhook' */
|
||||
luaF_close(L, func, status, 1); /* can yield or raise an error */
|
||||
func = restorestack(L, ci->u2.funcidx); /* stack may be moved */
|
||||
luaD_seterrorobj(L, status, func);
|
||||
luaD_shrinkstack(L); /* restore stack size in case of overflow */
|
||||
setcistrecst(ci, LUA_OK); /* clear original status */
|
||||
}
|
||||
ci->callstatus &= ~CIST_YPCALL;
|
||||
L->errfunc = ci->u.c.old_errfunc;
|
||||
/* if it is here, there were errors or yields; unlike 'lua_pcallk',
|
||||
do not change status */
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Completes the execution of a C function interrupted by an yield.
|
||||
** The interruption must have happened while the function was either
|
||||
** closing its tbc variables in 'moveresults' or executing
|
||||
** 'lua_callk'/'lua_pcallk'. In the first case, it just redoes
|
||||
** 'luaD_poscall'. In the second case, the call to 'finishpcallk'
|
||||
** finishes the interrupted execution of 'lua_pcallk'. After that, it
|
||||
** calls the continuation of the interrupted function and finally it
|
||||
** completes the job of the 'luaD_call' that called the function. In
|
||||
** the call to 'adjustresults', we do not know the number of results
|
||||
** of the function called by 'lua_callk'/'lua_pcallk', so we are
|
||||
** conservative and use LUA_MULTRET (always adjust).
|
||||
*/
|
||||
static void finishCcall (lua_State *L, CallInfo *ci) {
|
||||
int n; /* actual number of results from C function */
|
||||
if (ci->callstatus & CIST_CLSRET) { /* was returning? */
|
||||
lua_assert(hastocloseCfunc(ci->nresults));
|
||||
n = ci->u2.nres; /* just redo 'luaD_poscall' */
|
||||
/* don't need to reset CIST_CLSRET, as it will be set again anyway */
|
||||
}
|
||||
else {
|
||||
int status = LUA_YIELD; /* default if there were no errors */
|
||||
/* must have a continuation and must be able to call it */
|
||||
lua_assert(ci->u.c.k != NULL && yieldable(L));
|
||||
if (ci->callstatus & CIST_YPCALL) /* was inside a 'lua_pcallk'? */
|
||||
status = finishpcallk(L, ci); /* finish it */
|
||||
adjustresults(L, LUA_MULTRET); /* finish 'lua_callk' */
|
||||
lua_unlock(L);
|
||||
n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation */
|
||||
lua_lock(L);
|
||||
api_checknelems(L, n);
|
||||
}
|
||||
/* finish 'lua_callk'/'lua_pcall'; CIST_YPCALL and 'errfunc' already
|
||||
handled */
|
||||
adjustresults(L, ci->nresults);
|
||||
lua_unlock(L);
|
||||
n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation function */
|
||||
lua_lock(L);
|
||||
api_checknelems(L, n);
|
||||
luaD_poscall(L, ci, n); /* finish 'luaD_call' */
|
||||
}
|
||||
|
||||
@@ -556,18 +704,14 @@ static void finishCcall (lua_State *L, int status) {
|
||||
/*
|
||||
** Executes "full continuation" (everything in the stack) of a
|
||||
** previously interrupted coroutine until the stack is empty (or another
|
||||
** interruption long-jumps out of the loop). If the coroutine is
|
||||
** recovering from an error, 'ud' points to the error status, which must
|
||||
** be passed to the first continuation function (otherwise the default
|
||||
** status is LUA_YIELD).
|
||||
** interruption long-jumps out of the loop).
|
||||
*/
|
||||
static void unroll (lua_State *L, void *ud) {
|
||||
CallInfo *ci;
|
||||
if (ud != NULL) /* error status? */
|
||||
finishCcall(L, *(int *)ud); /* finish 'lua_pcallk' callee */
|
||||
UNUSED(ud);
|
||||
while ((ci = L->ci) != &L->base_ci) { /* something in the stack */
|
||||
if (!isLua(ci)) /* C function? */
|
||||
finishCcall(L, LUA_YIELD); /* complete its execution */
|
||||
finishCcall(L, ci); /* complete its execution */
|
||||
else { /* Lua function */
|
||||
luaV_finishOp(L); /* finish interrupted instruction */
|
||||
luaV_execute(L, ci); /* execute down to higher C 'boundary' */
|
||||
@@ -590,28 +734,6 @@ static CallInfo *findpcall (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Recovers from an error in a coroutine. Finds a recover point (if
|
||||
** there is one) and completes the execution of the interrupted
|
||||
** 'luaD_pcall'. If there is no recover point, returns zero.
|
||||
*/
|
||||
static int recover (lua_State *L, int status) {
|
||||
StkId oldtop;
|
||||
CallInfo *ci = findpcall(L);
|
||||
if (ci == NULL) return 0; /* no recovery point */
|
||||
/* "finish" luaD_pcall */
|
||||
oldtop = restorestack(L, ci->u2.funcidx);
|
||||
luaF_close(L, oldtop, status); /* may change the stack */
|
||||
oldtop = restorestack(L, ci->u2.funcidx);
|
||||
luaD_seterrorobj(L, status, oldtop);
|
||||
L->ci = ci;
|
||||
L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */
|
||||
luaD_shrinkstack(L);
|
||||
L->errfunc = ci->u.c.old_errfunc;
|
||||
return 1; /* continue running the coroutine */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Signal an error in the call to 'lua_resume', not in the execution
|
||||
** of the coroutine itself. (Such errors should not be handled by any
|
||||
@@ -637,14 +759,15 @@ static void resume (lua_State *L, void *ud) {
|
||||
int n = *(cast(int*, ud)); /* number of arguments */
|
||||
StkId firstArg = L->top - n; /* first argument */
|
||||
CallInfo *ci = L->ci;
|
||||
if (L->status == LUA_OK) { /* starting a coroutine? */
|
||||
luaD_call(L, firstArg - 1, LUA_MULTRET);
|
||||
}
|
||||
if (L->status == LUA_OK) /* starting a coroutine? */
|
||||
ccall(L, firstArg - 1, LUA_MULTRET, 0); /* just call its body */
|
||||
else { /* resuming from previous yield */
|
||||
lua_assert(L->status == LUA_YIELD);
|
||||
L->status = LUA_OK; /* mark that it is running (again) */
|
||||
if (isLua(ci)) /* yielded inside a hook? */
|
||||
if (isLua(ci)) { /* yielded inside a hook? */
|
||||
L->top = firstArg; /* discard arguments */
|
||||
luaV_execute(L, ci); /* just continue running Lua code */
|
||||
}
|
||||
else { /* 'common' yield */
|
||||
if (ci->u.c.k != NULL) { /* does it have a continuation function? */
|
||||
lua_unlock(L);
|
||||
@@ -658,6 +781,26 @@ static void resume (lua_State *L, void *ud) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Unrolls a coroutine in protected mode while there are recoverable
|
||||
** errors, that is, errors inside a protected call. (Any error
|
||||
** interrupts 'unroll', and this loop protects it again so it can
|
||||
** continue.) Stops with a normal end (status == LUA_OK), an yield
|
||||
** (status == LUA_YIELD), or an unprotected error ('findpcall' doesn't
|
||||
** find a recover point).
|
||||
*/
|
||||
static int precover (lua_State *L, int status) {
|
||||
CallInfo *ci;
|
||||
while (errorstatus(status) && (ci = findpcall(L)) != NULL) {
|
||||
L->ci = ci; /* go down to recovery functions */
|
||||
setcistrecst(ci, status); /* status to finish 'pcall' */
|
||||
status = luaD_rawrunprotected(L, unroll, NULL);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
|
||||
int *nresults) {
|
||||
int status;
|
||||
@@ -670,21 +813,16 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
|
||||
}
|
||||
else if (L->status != LUA_YIELD) /* ended with errors? */
|
||||
return resume_error(L, "cannot resume dead coroutine", nargs);
|
||||
if (from == NULL)
|
||||
L->nCcalls = CSTACKTHREAD;
|
||||
else /* correct 'nCcalls' for this thread */
|
||||
L->nCcalls = getCcalls(from) - L->nci - CSTACKCF;
|
||||
if (L->nCcalls <= CSTACKERR)
|
||||
L->nCcalls = (from) ? getCcalls(from) : 0;
|
||||
if (getCcalls(L) >= LUAI_MAXCCALLS)
|
||||
return resume_error(L, "C stack overflow", nargs);
|
||||
L->nCcalls++;
|
||||
luai_userstateresume(L, nargs);
|
||||
api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
|
||||
status = luaD_rawrunprotected(L, resume, &nargs);
|
||||
/* continue running after recoverable errors */
|
||||
while (errorstatus(status) && recover(L, status)) {
|
||||
/* unroll continuation */
|
||||
status = luaD_rawrunprotected(L, unroll, &status);
|
||||
}
|
||||
if (likely(!errorstatus(status)))
|
||||
status = precover(L, status);
|
||||
if (l_likely(!errorstatus(status)))
|
||||
lua_assert(status == L->status); /* normal end or yield */
|
||||
else { /* unrecoverable error */
|
||||
L->status = cast_byte(status); /* mark thread as 'dead' */
|
||||
@@ -710,22 +848,22 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
|
||||
lua_lock(L);
|
||||
ci = L->ci;
|
||||
api_checknelems(L, nresults);
|
||||
if (unlikely(!yieldable(L))) {
|
||||
if (l_unlikely(!yieldable(L))) {
|
||||
if (L != G(L)->mainthread)
|
||||
luaG_runerror(L, "attempt to yield across a C-call boundary");
|
||||
else
|
||||
luaG_runerror(L, "attempt to yield from outside a coroutine");
|
||||
}
|
||||
L->status = LUA_YIELD;
|
||||
ci->u2.nyield = nresults; /* save number of results */
|
||||
if (isLua(ci)) { /* inside a hook? */
|
||||
lua_assert(!isLuacode(ci));
|
||||
api_check(L, nresults == 0, "hooks cannot yield values");
|
||||
api_check(L, k == NULL, "hooks cannot continue after yielding");
|
||||
ci->u2.nyield = 0; /* no results */
|
||||
}
|
||||
else {
|
||||
if ((ci->u.c.k = k) != NULL) /* is there a continuation? */
|
||||
ci->u.c.ctx = ctx; /* save context */
|
||||
ci->u2.nyield = nresults; /* save number of results */
|
||||
luaD_throw(L, LUA_YIELD);
|
||||
}
|
||||
lua_assert(ci->callstatus & CIST_HOOKED); /* must be inside a hook */
|
||||
@@ -734,6 +872,45 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Auxiliary structure to call 'luaF_close' in protected mode.
|
||||
*/
|
||||
struct CloseP {
|
||||
StkId level;
|
||||
int status;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** Auxiliary function to call 'luaF_close' in protected mode.
|
||||
*/
|
||||
static void closepaux (lua_State *L, void *ud) {
|
||||
struct CloseP *pcl = cast(struct CloseP *, ud);
|
||||
luaF_close(L, pcl->level, pcl->status, 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Calls 'luaF_close' in protected mode. Return the original status
|
||||
** or, in case of errors, the new status.
|
||||
*/
|
||||
int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status) {
|
||||
CallInfo *old_ci = L->ci;
|
||||
lu_byte old_allowhooks = L->allowhook;
|
||||
for (;;) { /* keep closing upvalues until no more errors */
|
||||
struct CloseP pcl;
|
||||
pcl.level = restorestack(L, level); pcl.status = status;
|
||||
status = luaD_rawrunprotected(L, &closepaux, &pcl);
|
||||
if (l_likely(status == LUA_OK)) /* no more errors? */
|
||||
return pcl.status;
|
||||
else { /* an error occurred; restore saved state and repeat */
|
||||
L->ci = old_ci;
|
||||
L->allowhook = old_allowhooks;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Call the C function 'func' in protected mode, restoring basic
|
||||
** thread information ('allowhook', etc.) and in particular
|
||||
@@ -747,14 +924,12 @@ int luaD_pcall (lua_State *L, Pfunc func, void *u,
|
||||
ptrdiff_t old_errfunc = L->errfunc;
|
||||
L->errfunc = ef;
|
||||
status = luaD_rawrunprotected(L, func, u);
|
||||
if (unlikely(status != LUA_OK)) { /* an error occurred? */
|
||||
StkId oldtop = restorestack(L, old_top);
|
||||
if (l_unlikely(status != LUA_OK)) { /* an error occurred? */
|
||||
L->ci = old_ci;
|
||||
L->allowhook = old_allowhooks;
|
||||
status = luaF_close(L, oldtop, status);
|
||||
oldtop = restorestack(L, old_top); /* previous call may change stack */
|
||||
luaD_seterrorobj(L, status, oldtop);
|
||||
luaD_shrinkstack(L);
|
||||
status = luaD_closeprotected(L, old_top, status);
|
||||
luaD_seterrorobj(L, status, restorestack(L, old_top));
|
||||
luaD_shrinkstack(L); /* restore stack size in case of overflow */
|
||||
}
|
||||
L->errfunc = old_errfunc;
|
||||
return status;
|
||||
|
||||
+5
-3
@@ -23,7 +23,7 @@
|
||||
** at every check.
|
||||
*/
|
||||
#define luaD_checkstackaux(L,n,pre,pos) \
|
||||
if (L->stack_last - L->top <= (n)) \
|
||||
if (l_unlikely(L->stack_last - L->top <= (n))) \
|
||||
{ pre; luaD_growstack(L, n, 1); pos; } \
|
||||
else { condmovestack(L,pre,pos); }
|
||||
|
||||
@@ -58,10 +58,12 @@ LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
|
||||
LUAI_FUNC void luaD_hook (lua_State *L, int event, int line,
|
||||
int fTransfer, int nTransfer);
|
||||
LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci);
|
||||
LUAI_FUNC void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int n);
|
||||
LUAI_FUNC int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1, int delta);
|
||||
LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults);
|
||||
LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
|
||||
LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults);
|
||||
LUAI_FUNC void luaD_tryfuncTM (lua_State *L, StkId func);
|
||||
LUAI_FUNC StkId luaD_tryfuncTM (lua_State *L, StkId func);
|
||||
LUAI_FUNC int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status);
|
||||
LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
|
||||
ptrdiff_t oldtop, ptrdiff_t ef);
|
||||
LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres);
|
||||
|
||||
+83
-89
@@ -53,7 +53,7 @@ void luaF_initupvals (lua_State *L, LClosure *cl) {
|
||||
uv->v = &uv->u.value; /* make it closed */
|
||||
setnilvalue(uv->v);
|
||||
cl->upvals[i] = uv;
|
||||
luaC_objbarrier(L, cl, o);
|
||||
luaC_objbarrier(L, cl, uv);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,115 +100,83 @@ UpVal *luaF_findupval (lua_State *L, StkId level) {
|
||||
}
|
||||
|
||||
|
||||
static void callclose (lua_State *L, void *ud) {
|
||||
UNUSED(ud);
|
||||
luaD_callnoyield(L, L->top - 3, 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prepare closing method plus its arguments for object 'obj' with
|
||||
** error message 'err'. (This function assumes EXTRA_STACK.)
|
||||
** Call closing method for object 'obj' with error message 'err'. The
|
||||
** boolean 'yy' controls whether the call is yieldable.
|
||||
** (This function assumes EXTRA_STACK.)
|
||||
*/
|
||||
static int prepclosingmethod (lua_State *L, TValue *obj, TValue *err) {
|
||||
static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) {
|
||||
StkId top = L->top;
|
||||
const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
|
||||
if (ttisnil(tm)) /* no metamethod? */
|
||||
return 0; /* nothing to call */
|
||||
setobj2s(L, top, tm); /* will call metamethod... */
|
||||
setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */
|
||||
setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */
|
||||
L->top = top + 3; /* add function and arguments */
|
||||
return 1;
|
||||
if (yy)
|
||||
luaD_call(L, top, 0);
|
||||
else
|
||||
luaD_callnoyield(L, top, 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Raise an error with message 'msg', inserting the name of the
|
||||
** local variable at position 'level' in the stack.
|
||||
** Check whether object at given level has a close metamethod and raise
|
||||
** an error if not.
|
||||
*/
|
||||
static void varerror (lua_State *L, StkId level, const char *msg) {
|
||||
int idx = cast_int(level - L->ci->func);
|
||||
const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
|
||||
if (vname == NULL) vname = "?";
|
||||
luaG_runerror(L, msg, vname);
|
||||
static void checkclosemth (lua_State *L, StkId level) {
|
||||
const TValue *tm = luaT_gettmbyobj(L, s2v(level), TM_CLOSE);
|
||||
if (ttisnil(tm)) { /* no metamethod? */
|
||||
int idx = cast_int(level - L->ci->func); /* variable index */
|
||||
const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
|
||||
if (vname == NULL) vname = "?";
|
||||
luaG_runerror(L, "variable '%s' got a non-closable value", vname);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prepare and call a closing method. If status is OK, code is still
|
||||
** inside the original protected call, and so any error will be handled
|
||||
** there. Otherwise, a previous error already activated the original
|
||||
** protected call, and so the call to the closing method must be
|
||||
** protected here. (A status == CLOSEPROTECT behaves like a previous
|
||||
** error, to also run the closing method in protected mode).
|
||||
** If status is OK, the call to the closing method will be pushed
|
||||
** at the top of the stack. Otherwise, values are pushed after
|
||||
** the 'level' of the upvalue being closed, as everything after
|
||||
** that won't be used again.
|
||||
** Prepare and call a closing method.
|
||||
** If status is CLOSEKTOP, the call to the closing method will be pushed
|
||||
** at the top of the stack. Otherwise, values can be pushed right after
|
||||
** the 'level' of the upvalue being closed, as everything after that
|
||||
** won't be used again.
|
||||
*/
|
||||
static int callclosemth (lua_State *L, StkId level, int status) {
|
||||
static void prepcallclosemth (lua_State *L, StkId level, int status, int yy) {
|
||||
TValue *uv = s2v(level); /* value being closed */
|
||||
if (likely(status == LUA_OK)) {
|
||||
if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */
|
||||
callclose(L, NULL); /* call closing method */
|
||||
else if (!l_isfalse(uv)) /* non-closable non-false value? */
|
||||
varerror(L, level, "attempt to close non-closable variable '%s'");
|
||||
TValue *errobj;
|
||||
if (status == CLOSEKTOP)
|
||||
errobj = &G(L)->nilvalue; /* error object is nil */
|
||||
else { /* 'luaD_seterrorobj' will set top to level + 2 */
|
||||
errobj = s2v(level + 1); /* error object goes after 'uv' */
|
||||
luaD_seterrorobj(L, status, level + 1); /* set error object */
|
||||
}
|
||||
else { /* must close the object in protected mode */
|
||||
ptrdiff_t oldtop;
|
||||
level++; /* space for error message */
|
||||
oldtop = savestack(L, level + 1); /* top will be after that */
|
||||
luaD_seterrorobj(L, status, level); /* set error message */
|
||||
if (prepclosingmethod(L, uv, s2v(level))) { /* something to call? */
|
||||
int newstatus = luaD_pcall(L, callclose, NULL, oldtop, 0);
|
||||
if (newstatus != LUA_OK && status == CLOSEPROTECT) /* first error? */
|
||||
status = newstatus; /* this will be the new error */
|
||||
else {
|
||||
if (newstatus != LUA_OK) /* suppressed error? */
|
||||
luaE_warnerror(L, "__close metamethod");
|
||||
/* leave original error (or nil) on top */
|
||||
L->top = restorestack(L, oldtop);
|
||||
}
|
||||
}
|
||||
/* else no metamethod; ignore this case and keep original error */
|
||||
}
|
||||
return status;
|
||||
callclosemethod(L, uv, errobj, yy);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Try to create a to-be-closed upvalue
|
||||
** (can raise a memory-allocation error)
|
||||
** Maximum value for deltas in 'tbclist', dependent on the type
|
||||
** of delta. (This macro assumes that an 'L' is in scope where it
|
||||
** is used.)
|
||||
*/
|
||||
static void trynewtbcupval (lua_State *L, void *ud) {
|
||||
newupval(L, 1, cast(StkId, ud), &L->openupval);
|
||||
}
|
||||
#define MAXDELTA \
|
||||
((256ul << ((sizeof(L->stack->tbclist.delta) - 1) * 8)) - 1)
|
||||
|
||||
|
||||
/*
|
||||
** Create a to-be-closed upvalue. If there is a memory error
|
||||
** when creating the upvalue, the closing method must be called here,
|
||||
** as there is no upvalue to call it later.
|
||||
** Insert a variable in the list of to-be-closed variables.
|
||||
*/
|
||||
void luaF_newtbcupval (lua_State *L, StkId level) {
|
||||
TValue *obj = s2v(level);
|
||||
lua_assert(L->openupval == NULL || uplevel(L->openupval) < level);
|
||||
if (!l_isfalse(obj)) { /* false doesn't need to be closed */
|
||||
int status;
|
||||
const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
|
||||
if (ttisnil(tm)) /* no metamethod? */
|
||||
varerror(L, level, "variable '%s' got a non-closable value");
|
||||
status = luaD_rawrunprotected(L, trynewtbcupval, level);
|
||||
if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */
|
||||
lua_assert(status == LUA_ERRMEM);
|
||||
luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */
|
||||
/* next call must succeed, as object is closable */
|
||||
prepclosingmethod(L, s2v(level), s2v(level + 1));
|
||||
callclose(L, NULL); /* call closing method */
|
||||
luaD_throw(L, LUA_ERRMEM); /* throw memory error */
|
||||
}
|
||||
lua_assert(level > L->tbclist);
|
||||
if (l_isfalse(s2v(level)))
|
||||
return; /* false doesn't need to be closed */
|
||||
checkclosemth(L, level); /* value must have a close method */
|
||||
while (cast_uint(level - L->tbclist) > MAXDELTA) {
|
||||
L->tbclist += MAXDELTA; /* create a dummy node at maximum delta */
|
||||
L->tbclist->tbclist.delta = 0;
|
||||
}
|
||||
level->tbclist.delta = cast(unsigned short, level - L->tbclist);
|
||||
L->tbclist = level;
|
||||
}
|
||||
|
||||
|
||||
@@ -220,18 +188,16 @@ void luaF_unlinkupval (UpVal *uv) {
|
||||
}
|
||||
|
||||
|
||||
int luaF_close (lua_State *L, StkId level, int status) {
|
||||
/*
|
||||
** Close all upvalues up to the given stack level.
|
||||
*/
|
||||
void luaF_closeupval (lua_State *L, StkId level) {
|
||||
UpVal *uv;
|
||||
while ((uv = L->openupval) != NULL && uplevel(uv) >= level) {
|
||||
StkId upl; /* stack index pointed by 'uv' */
|
||||
while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) {
|
||||
TValue *slot = &uv->u.value; /* new position for value */
|
||||
lua_assert(uplevel(uv) < L->top);
|
||||
if (uv->tbc && status != NOCLOSINGMETH) {
|
||||
/* must run closing method, which may change the stack */
|
||||
ptrdiff_t levelrel = savestack(L, level);
|
||||
status = callclosemth(L, uplevel(uv), status);
|
||||
level = restorestack(L, levelrel);
|
||||
}
|
||||
luaF_unlinkupval(uv);
|
||||
luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */
|
||||
setobj(L, slot, uv->v); /* move value to upvalue slot */
|
||||
uv->v = slot; /* now current value lives here */
|
||||
if (!iswhite(uv)) { /* neither white nor dead? */
|
||||
@@ -239,7 +205,35 @@ int luaF_close (lua_State *L, StkId level, int status) {
|
||||
luaC_barrier(L, uv, slot);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Remove firt element from the tbclist plus its dummy nodes.
|
||||
*/
|
||||
static void poptbclist (lua_State *L) {
|
||||
StkId tbc = L->tbclist;
|
||||
lua_assert(tbc->tbclist.delta > 0); /* first element cannot be dummy */
|
||||
tbc -= tbc->tbclist.delta;
|
||||
while (tbc > L->stack && tbc->tbclist.delta == 0)
|
||||
tbc -= MAXDELTA; /* remove dummy nodes */
|
||||
L->tbclist = tbc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Close all upvalues and to-be-closed variables up to the given stack
|
||||
** level.
|
||||
*/
|
||||
void luaF_close (lua_State *L, StkId level, int status, int yy) {
|
||||
ptrdiff_t levelrel = savestack(L, level);
|
||||
luaF_closeupval(L, level); /* first, close the upvalues */
|
||||
while (L->tbclist >= level) { /* traverse tbc's down to that level */
|
||||
StkId tbc = L->tbclist; /* get variable index */
|
||||
poptbclist(L); /* remove it from list */
|
||||
prepcallclosemth(L, tbc, status, yy); /* close variable */
|
||||
level = restorestack(L, levelrel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+4
-9
@@ -42,15 +42,9 @@
|
||||
#define MAXMISS 10
|
||||
|
||||
|
||||
/*
|
||||
** Special "status" for 'luaF_close'
|
||||
*/
|
||||
|
||||
/* close upvalues without running their closing methods */
|
||||
#define NOCLOSINGMETH (-1)
|
||||
|
||||
/* close upvalues running all closing methods in protected mode */
|
||||
#define CLOSEPROTECT (-2)
|
||||
/* special status to close upvalues preserving the top of the stack */
|
||||
#define CLOSEKTOP (-1)
|
||||
|
||||
|
||||
LUAI_FUNC Proto *luaF_newproto (lua_State *L);
|
||||
@@ -59,7 +53,8 @@ LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nupvals);
|
||||
LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl);
|
||||
LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
|
||||
LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level);
|
||||
LUAI_FUNC int luaF_close (lua_State *L, StkId level, int status);
|
||||
LUAI_FUNC void luaF_closeupval (lua_State *L, StkId level);
|
||||
LUAI_FUNC void luaF_close (lua_State *L, StkId level, int status, int yy);
|
||||
LUAI_FUNC void luaF_unlinkupval (UpVal *uv);
|
||||
LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
|
||||
LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,
|
||||
|
||||
+65
-44
@@ -161,18 +161,17 @@ static void linkgclist_ (GCObject *o, GCObject **pnext, GCObject **list) {
|
||||
|
||||
|
||||
/*
|
||||
** Clear keys for empty entries in tables. If entry is empty
|
||||
** and its key is not marked, mark its entry as dead. This allows the
|
||||
** collection of the key, but keeps its entry in the table (its removal
|
||||
** could break a chain). The main feature of a dead key is that it must
|
||||
** be different from any other value, to do not disturb searches.
|
||||
** Other places never manipulate dead keys, because its associated empty
|
||||
** value is enough to signal that the entry is logically empty.
|
||||
** Clear keys for empty entries in tables. If entry is empty, mark its
|
||||
** entry as dead. This allows the collection of the key, but keeps its
|
||||
** entry in the table: its removal could break a chain and could break
|
||||
** a table traversal. Other places never manipulate dead keys, because
|
||||
** its associated empty value is enough to signal that the entry is
|
||||
** logically empty.
|
||||
*/
|
||||
static void clearkey (Node *n) {
|
||||
lua_assert(isempty(gval(n)));
|
||||
if (keyiswhite(n))
|
||||
setdeadkey(n); /* unused and unmarked key; remove it */
|
||||
if (keyiscollectable(n))
|
||||
setdeadkey(n); /* unused key; remove it */
|
||||
}
|
||||
|
||||
|
||||
@@ -301,7 +300,7 @@ static void reallymarkobject (global_State *g, GCObject *o) {
|
||||
if (upisopen(uv))
|
||||
set2gray(uv); /* open upvalues are kept gray */
|
||||
else
|
||||
set2black(o); /* closed upvalues are visited here */
|
||||
set2black(uv); /* closed upvalues are visited here */
|
||||
markvalue(g, uv->v); /* mark its content */
|
||||
break;
|
||||
}
|
||||
@@ -309,7 +308,7 @@ static void reallymarkobject (global_State *g, GCObject *o) {
|
||||
Udata *u = gco2u(o);
|
||||
if (u->nuvalue == 0) { /* no user values? */
|
||||
markobjectN(g, u->metatable); /* mark its metatable */
|
||||
set2black(o); /* nothing else to mark */
|
||||
set2black(u); /* nothing else to mark */
|
||||
break;
|
||||
}
|
||||
/* else... */
|
||||
@@ -633,9 +632,8 @@ static int traversethread (global_State *g, lua_State *th) {
|
||||
for (uv = th->openupval; uv != NULL; uv = uv->u.open.next)
|
||||
markobject(g, uv); /* open upvalues cannot be collected */
|
||||
if (g->gcstate == GCSatomic) { /* final traversal? */
|
||||
StkId lim = th->stack + th->stacksize; /* real end of stack */
|
||||
for (; o < lim; o++) /* clear not-marked stack slice */
|
||||
setnilvalue(s2v(o));
|
||||
for (; o < th->stack_last + EXTRA_STACK; o++)
|
||||
setnilvalue(s2v(o)); /* clear dead stack slice */
|
||||
/* 'remarkupvals' may have removed thread from 'twups' list */
|
||||
if (!isintwups(th) && th->openupval != NULL) {
|
||||
th->twups = g->twups; /* link it back to the list */
|
||||
@@ -644,7 +642,7 @@ static int traversethread (global_State *g, lua_State *th) {
|
||||
}
|
||||
else if (!g->gcemergency)
|
||||
luaD_shrinkstack(th); /* do not change stack in emergency cycle */
|
||||
return 1 + th->stacksize;
|
||||
return 1 + stacksize(th);
|
||||
}
|
||||
|
||||
|
||||
@@ -771,12 +769,16 @@ static void freeobj (lua_State *L, GCObject *o) {
|
||||
case LUA_VUPVAL:
|
||||
freeupval(L, gco2upv(o));
|
||||
break;
|
||||
case LUA_VLCL:
|
||||
luaM_freemem(L, o, sizeLclosure(gco2lcl(o)->nupvalues));
|
||||
case LUA_VLCL: {
|
||||
LClosure *cl = gco2lcl(o);
|
||||
luaM_freemem(L, cl, sizeLclosure(cl->nupvalues));
|
||||
break;
|
||||
case LUA_VCCL:
|
||||
luaM_freemem(L, o, sizeCclosure(gco2ccl(o)->nupvalues));
|
||||
}
|
||||
case LUA_VCCL: {
|
||||
CClosure *cl = gco2ccl(o);
|
||||
luaM_freemem(L, cl, sizeCclosure(cl->nupvalues));
|
||||
break;
|
||||
}
|
||||
case LUA_VTABLE:
|
||||
luaH_free(L, gco2t(o));
|
||||
break;
|
||||
@@ -788,13 +790,17 @@ static void freeobj (lua_State *L, GCObject *o) {
|
||||
luaM_freemem(L, o, sizeudata(u->nuvalue, u->len));
|
||||
break;
|
||||
}
|
||||
case LUA_VSHRSTR:
|
||||
luaS_remove(L, gco2ts(o)); /* remove it from hash table */
|
||||
luaM_freemem(L, o, sizelstring(gco2ts(o)->shrlen));
|
||||
case LUA_VSHRSTR: {
|
||||
TString *ts = gco2ts(o);
|
||||
luaS_remove(L, ts); /* remove it from hash table */
|
||||
luaM_freemem(L, ts, sizelstring(ts->shrlen));
|
||||
break;
|
||||
case LUA_VLNGSTR:
|
||||
luaM_freemem(L, o, sizelstring(gco2ts(o)->u.lnglen));
|
||||
}
|
||||
case LUA_VLNGSTR: {
|
||||
TString *ts = gco2ts(o);
|
||||
luaM_freemem(L, ts, sizelstring(ts->u.lnglen));
|
||||
break;
|
||||
}
|
||||
default: lua_assert(0);
|
||||
}
|
||||
}
|
||||
@@ -900,18 +906,18 @@ static void GCTM (lua_State *L) {
|
||||
if (!notm(tm)) { /* is there a finalizer? */
|
||||
int status;
|
||||
lu_byte oldah = L->allowhook;
|
||||
int running = g->gcrunning;
|
||||
int oldgcstp = g->gcstp;
|
||||
g->gcstp |= GCSTPGC; /* avoid GC steps */
|
||||
L->allowhook = 0; /* stop debug hooks during GC metamethod */
|
||||
g->gcrunning = 0; /* avoid GC steps */
|
||||
setobj2s(L, L->top++, tm); /* push finalizer... */
|
||||
setobj2s(L, L->top++, &v); /* ... and its argument */
|
||||
L->ci->callstatus |= CIST_FIN; /* will run a finalizer */
|
||||
status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0);
|
||||
L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */
|
||||
L->allowhook = oldah; /* restore hooks */
|
||||
g->gcrunning = running; /* restore state */
|
||||
if (unlikely(status != LUA_OK)) { /* error while running __gc? */
|
||||
luaE_warnerror(L, "__gc metamethod");
|
||||
g->gcstp = oldgcstp; /* restore state */
|
||||
if (l_unlikely(status != LUA_OK)) { /* error while running __gc? */
|
||||
luaE_warnerror(L, "__gc");
|
||||
L->top--; /* pops error object */
|
||||
}
|
||||
}
|
||||
@@ -1005,7 +1011,8 @@ static void correctpointers (global_State *g, GCObject *o) {
|
||||
void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
|
||||
global_State *g = G(L);
|
||||
if (tofinalize(o) || /* obj. is already marked... */
|
||||
gfasttm(g, mt, TM_GC) == NULL) /* or has no finalizer? */
|
||||
gfasttm(g, mt, TM_GC) == NULL || /* or has no finalizer... */
|
||||
(g->gcstp & GCSTPCLS)) /* or closing state? */
|
||||
return; /* nothing to be done */
|
||||
else { /* move 'o' to 'finobj' list */
|
||||
GCObject **p;
|
||||
@@ -1496,12 +1503,13 @@ static void deletelist (lua_State *L, GCObject *p, GCObject *limit) {
|
||||
*/
|
||||
void luaC_freeallobjects (lua_State *L) {
|
||||
global_State *g = G(L);
|
||||
g->gcstp = GCSTPCLS; /* no extra finalizers after here */
|
||||
luaC_changemode(L, KGC_INC);
|
||||
separatetobefnz(g, 1); /* separate all objects with finalizers */
|
||||
lua_assert(g->finobj == NULL);
|
||||
callallpendingfinalizers(L);
|
||||
deletelist(L, g->allgc, obj2gco(g->mainthread));
|
||||
deletelist(L, g->finobj, NULL);
|
||||
lua_assert(g->finobj == NULL); /* no new finalizers */
|
||||
deletelist(L, g->fixedgc, NULL); /* collect fixed objects */
|
||||
lua_assert(g->strt.nuse == 0);
|
||||
}
|
||||
@@ -1569,52 +1577,64 @@ static int sweepstep (lua_State *L, global_State *g,
|
||||
|
||||
static lu_mem singlestep (lua_State *L) {
|
||||
global_State *g = G(L);
|
||||
lu_mem work;
|
||||
lua_assert(!g->gcstopem); /* collector is not reentrant */
|
||||
g->gcstopem = 1; /* no emergency collections while collecting */
|
||||
switch (g->gcstate) {
|
||||
case GCSpause: {
|
||||
restartcollection(g);
|
||||
g->gcstate = GCSpropagate;
|
||||
return 1;
|
||||
work = 1;
|
||||
break;
|
||||
}
|
||||
case GCSpropagate: {
|
||||
if (g->gray == NULL) { /* no more gray objects? */
|
||||
g->gcstate = GCSenteratomic; /* finish propagate phase */
|
||||
return 0;
|
||||
work = 0;
|
||||
}
|
||||
else
|
||||
return propagatemark(g); /* traverse one gray object */
|
||||
work = propagatemark(g); /* traverse one gray object */
|
||||
break;
|
||||
}
|
||||
case GCSenteratomic: {
|
||||
lu_mem work = atomic(L); /* work is what was traversed by 'atomic' */
|
||||
work = atomic(L); /* work is what was traversed by 'atomic' */
|
||||
entersweep(L);
|
||||
g->GCestimate = gettotalbytes(g); /* first estimate */;
|
||||
return work;
|
||||
break;
|
||||
}
|
||||
case GCSswpallgc: { /* sweep "regular" objects */
|
||||
return sweepstep(L, g, GCSswpfinobj, &g->finobj);
|
||||
work = sweepstep(L, g, GCSswpfinobj, &g->finobj);
|
||||
break;
|
||||
}
|
||||
case GCSswpfinobj: { /* sweep objects with finalizers */
|
||||
return sweepstep(L, g, GCSswptobefnz, &g->tobefnz);
|
||||
work = sweepstep(L, g, GCSswptobefnz, &g->tobefnz);
|
||||
break;
|
||||
}
|
||||
case GCSswptobefnz: { /* sweep objects to be finalized */
|
||||
return sweepstep(L, g, GCSswpend, NULL);
|
||||
work = sweepstep(L, g, GCSswpend, NULL);
|
||||
break;
|
||||
}
|
||||
case GCSswpend: { /* finish sweeps */
|
||||
checkSizes(L, g);
|
||||
g->gcstate = GCScallfin;
|
||||
return 0;
|
||||
work = 0;
|
||||
break;
|
||||
}
|
||||
case GCScallfin: { /* call remaining finalizers */
|
||||
if (g->tobefnz && !g->gcemergency) {
|
||||
int n = runafewfinalizers(L, GCFINMAX);
|
||||
return n * GCFINALIZECOST;
|
||||
g->gcstopem = 0; /* ok collections during finalizers */
|
||||
work = runafewfinalizers(L, GCFINMAX) * GCFINALIZECOST;
|
||||
}
|
||||
else { /* emergency mode or no more finalizers */
|
||||
g->gcstate = GCSpause; /* finish collection */
|
||||
return 0;
|
||||
work = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: lua_assert(0); return 0;
|
||||
}
|
||||
g->gcstopem = 0;
|
||||
return work;
|
||||
}
|
||||
|
||||
|
||||
@@ -1629,6 +1649,7 @@ void luaC_runtilstate (lua_State *L, int statesmask) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Performs a basic incremental step. The debt and step size are
|
||||
** converted from bytes to "units of work"; then the function loops
|
||||
@@ -1660,7 +1681,7 @@ static void incstep (lua_State *L, global_State *g) {
|
||||
void luaC_step (lua_State *L) {
|
||||
global_State *g = G(L);
|
||||
lua_assert(!g->gcemergency);
|
||||
if (g->gcrunning) { /* running? */
|
||||
if (gcrunning(g)) { /* running? */
|
||||
if(isdecGCmodegen(g))
|
||||
genstep(L, g);
|
||||
else
|
||||
|
||||
@@ -148,6 +148,16 @@
|
||||
*/
|
||||
#define isdecGCmodegen(g) (g->gckind == KGC_GEN || g->lastatomic != 0)
|
||||
|
||||
|
||||
/*
|
||||
** Control when GC is running:
|
||||
*/
|
||||
#define GCSTPUSR 1 /* bit true when GC stopped by user */
|
||||
#define GCSTPGC 2 /* bit true when GC stopped by itself */
|
||||
#define GCSTPCLS 4 /* bit true when closing Lua state */
|
||||
#define gcrunning(g) ((g)->gcstp == 0)
|
||||
|
||||
|
||||
/*
|
||||
** Does one step of collection when debt becomes positive. 'pre'/'pos'
|
||||
** allows some adjustments to be done only when needed. macro
|
||||
|
||||
+2
-2
@@ -50,9 +50,9 @@ static const luaL_Reg loadedlibs[] = {
|
||||
{LUA_MATHLIBNAME, luaopen_math},
|
||||
{LUA_UTF8LIBNAME, luaopen_utf8},
|
||||
{LUA_DBLIBNAME, luaopen_debug},
|
||||
/************** Pi-hole modification ***************/
|
||||
/****** Pi-hole modification ******/
|
||||
{LUA_PIHOLELIBNAME, luaopen_pihole},
|
||||
/***************************************************/
|
||||
/**********************************/
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
+21
-14
@@ -52,12 +52,6 @@ static int l_checkmode (const char *mode) {
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
#if !defined(l_checkmodep)
|
||||
/* By default, Lua accepts only "r" or "w" as mode */
|
||||
#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && m[1] == '\0')
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(l_popen) /* { */
|
||||
|
||||
#if defined(LUA_USE_POSIX) /* { */
|
||||
@@ -70,6 +64,12 @@ static int l_checkmode (const char *mode) {
|
||||
#define l_popen(L,c,m) (_popen(c,m))
|
||||
#define l_pclose(L,file) (_pclose(file))
|
||||
|
||||
#if !defined(l_checkmodep)
|
||||
/* Windows accepts "[rw][bt]?" as valid modes */
|
||||
#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && \
|
||||
(m[1] == '\0' || ((m[1] == 'b' || m[1] == 't') && m[2] == '\0')))
|
||||
#endif
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
/* ISO C definitions */
|
||||
@@ -83,6 +83,12 @@ static int l_checkmode (const char *mode) {
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
#if !defined(l_checkmodep)
|
||||
/* By default, Lua accepts only "r" or "w" as valid modes */
|
||||
#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && m[1] == '\0')
|
||||
#endif
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
@@ -180,7 +186,7 @@ static int f_tostring (lua_State *L) {
|
||||
|
||||
static FILE *tofile (lua_State *L) {
|
||||
LStream *p = tolstream(L);
|
||||
if (isclosed(p))
|
||||
if (l_unlikely(isclosed(p)))
|
||||
luaL_error(L, "attempt to use a closed file");
|
||||
lua_assert(p->f);
|
||||
return p->f;
|
||||
@@ -255,7 +261,7 @@ static LStream *newfile (lua_State *L) {
|
||||
static void opencheck (lua_State *L, const char *fname, const char *mode) {
|
||||
LStream *p = newfile(L);
|
||||
p->f = fopen(fname, mode);
|
||||
if (p->f == NULL)
|
||||
if (l_unlikely(p->f == NULL))
|
||||
luaL_error(L, "cannot open file '%s' (%s)", fname, strerror(errno));
|
||||
}
|
||||
|
||||
@@ -303,7 +309,7 @@ static FILE *getiofile (lua_State *L, const char *findex) {
|
||||
LStream *p;
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, findex);
|
||||
p = (LStream *)lua_touserdata(L, -1);
|
||||
if (isclosed(p))
|
||||
if (l_unlikely(isclosed(p)))
|
||||
luaL_error(L, "default %s file is closed", findex + IOPREF_LEN);
|
||||
return p->f;
|
||||
}
|
||||
@@ -430,7 +436,7 @@ typedef struct {
|
||||
** Add current char to buffer (if not out of space) and read next one
|
||||
*/
|
||||
static int nextc (RN *rn) {
|
||||
if (rn->n >= L_MAXLENNUM) { /* buffer overflow? */
|
||||
if (l_unlikely(rn->n >= L_MAXLENNUM)) { /* buffer overflow? */
|
||||
rn->buff[0] = '\0'; /* invalidate result */
|
||||
return 0; /* fail */
|
||||
}
|
||||
@@ -493,8 +499,8 @@ static int read_number (lua_State *L, FILE *f) {
|
||||
ungetc(rn.c, rn.f); /* unread look-ahead char */
|
||||
l_unlockfile(rn.f);
|
||||
rn.buff[rn.n] = '\0'; /* finish string */
|
||||
if (lua_stringtonumber(L, rn.buff)) /* is this a valid number? */
|
||||
return 1; /* ok */
|
||||
if (l_likely(lua_stringtonumber(L, rn.buff)))
|
||||
return 1; /* ok, it is a valid number */
|
||||
else { /* invalid format */
|
||||
lua_pushnil(L); /* "result" to be removed */
|
||||
return 0; /* read fails */
|
||||
@@ -670,7 +676,8 @@ static int g_write (lua_State *L, FILE *f, int arg) {
|
||||
status = status && (fwrite(s, sizeof(char), l, f) == l);
|
||||
}
|
||||
}
|
||||
if (status) return 1; /* file handle already on stack top */
|
||||
if (l_likely(status))
|
||||
return 1; /* file handle already on stack top */
|
||||
else return luaL_fileresult(L, status, NULL);
|
||||
}
|
||||
|
||||
@@ -697,7 +704,7 @@ static int f_seek (lua_State *L) {
|
||||
luaL_argcheck(L, (lua_Integer)offset == p3, 3,
|
||||
"not an integer in proper range");
|
||||
op = l_fseek(f, offset, mode[op]);
|
||||
if (op)
|
||||
if (l_unlikely(op))
|
||||
return luaL_fileresult(L, 0, NULL); /* error */
|
||||
else {
|
||||
lua_pushinteger(L, (lua_Integer)l_ftell(f));
|
||||
|
||||
+30
-26
@@ -122,26 +122,29 @@ l_noret luaX_syntaxerror (LexState *ls, const char *msg) {
|
||||
|
||||
|
||||
/*
|
||||
** creates a new string and anchors it in scanner's table so that
|
||||
** it will not be collected until the end of the compilation
|
||||
** (by that time it should be anchored somewhere)
|
||||
** Creates a new string and anchors it in scanner's table so that it
|
||||
** will not be collected until the end of the compilation; by that time
|
||||
** it should be anchored somewhere. It also internalizes long strings,
|
||||
** ensuring there is only one copy of each unique string. The table
|
||||
** here is used as a set: the string enters as the key, while its value
|
||||
** is irrelevant. We use the string itself as the value only because it
|
||||
** is a TValue readly available. Later, the code generation can change
|
||||
** this value.
|
||||
*/
|
||||
TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
|
||||
lua_State *L = ls->L;
|
||||
TValue *o; /* entry for 'str' */
|
||||
TString *ts = luaS_newlstr(L, str, l); /* create new string */
|
||||
setsvalue2s(L, L->top++, ts); /* temporarily anchor it in stack */
|
||||
o = luaH_set(L, ls->h, s2v(L->top - 1));
|
||||
if (isempty(o)) { /* not in use yet? */
|
||||
/* boolean value does not need GC barrier;
|
||||
table is not a metatable, so it does not need to invalidate cache */
|
||||
setbtvalue(o); /* t[string] = true */
|
||||
const TValue *o = luaH_getstr(ls->h, ts);
|
||||
if (!ttisnil(o)) /* string already present? */
|
||||
ts = keystrval(nodefromval(o)); /* get saved copy */
|
||||
else { /* not in use yet */
|
||||
TValue *stv = s2v(L->top++); /* reserve stack space for string */
|
||||
setsvalue(L, stv, ts); /* temporarily anchor the string */
|
||||
luaH_finishset(L, ls->h, stv, o, stv); /* t[string] = string */
|
||||
/* table is not a metatable, so it does not need to invalidate cache */
|
||||
luaC_checkGC(L);
|
||||
L->top--; /* remove string from stack */
|
||||
}
|
||||
else { /* string already present */
|
||||
ts = keystrval(nodefromval(o)); /* re-use value previously stored */
|
||||
}
|
||||
L->top--; /* remove string from stack */
|
||||
return ts;
|
||||
}
|
||||
|
||||
@@ -254,9 +257,10 @@ static int read_numeral (LexState *ls, SemInfo *seminfo) {
|
||||
|
||||
|
||||
/*
|
||||
** reads a sequence '[=*[' or ']=*]', leaving the last bracket.
|
||||
** If sequence is well formed, return its number of '='s + 2; otherwise,
|
||||
** return 1 if there is no '='s or 0 otherwise (an unfinished '[==...').
|
||||
** read a sequence '[=*[' or ']=*]', leaving the last bracket. If
|
||||
** sequence is well formed, return its number of '='s + 2; otherwise,
|
||||
** return 1 if it is a single bracket (no '='s and no 2nd bracket);
|
||||
** otherwise (an unfinished '[==...') return 0.
|
||||
*/
|
||||
static size_t skip_sep (LexState *ls) {
|
||||
size_t count = 0;
|
||||
@@ -481,34 +485,34 @@ static int llex (LexState *ls, SemInfo *seminfo) {
|
||||
}
|
||||
case '=': {
|
||||
next(ls);
|
||||
if (check_next1(ls, '=')) return TK_EQ;
|
||||
if (check_next1(ls, '=')) return TK_EQ; /* '==' */
|
||||
else return '=';
|
||||
}
|
||||
case '<': {
|
||||
next(ls);
|
||||
if (check_next1(ls, '=')) return TK_LE;
|
||||
else if (check_next1(ls, '<')) return TK_SHL;
|
||||
if (check_next1(ls, '=')) return TK_LE; /* '<=' */
|
||||
else if (check_next1(ls, '<')) return TK_SHL; /* '<<' */
|
||||
else return '<';
|
||||
}
|
||||
case '>': {
|
||||
next(ls);
|
||||
if (check_next1(ls, '=')) return TK_GE;
|
||||
else if (check_next1(ls, '>')) return TK_SHR;
|
||||
if (check_next1(ls, '=')) return TK_GE; /* '>=' */
|
||||
else if (check_next1(ls, '>')) return TK_SHR; /* '>>' */
|
||||
else return '>';
|
||||
}
|
||||
case '/': {
|
||||
next(ls);
|
||||
if (check_next1(ls, '/')) return TK_IDIV;
|
||||
if (check_next1(ls, '/')) return TK_IDIV; /* '//' */
|
||||
else return '/';
|
||||
}
|
||||
case '~': {
|
||||
next(ls);
|
||||
if (check_next1(ls, '=')) return TK_NE;
|
||||
if (check_next1(ls, '=')) return TK_NE; /* '~=' */
|
||||
else return '~';
|
||||
}
|
||||
case ':': {
|
||||
next(ls);
|
||||
if (check_next1(ls, ':')) return TK_DBCOLON;
|
||||
if (check_next1(ls, ':')) return TK_DBCOLON; /* '::' */
|
||||
else return ':';
|
||||
}
|
||||
case '"': case '\'': { /* short literal strings */
|
||||
@@ -547,7 +551,7 @@ static int llex (LexState *ls, SemInfo *seminfo) {
|
||||
return TK_NAME;
|
||||
}
|
||||
}
|
||||
else { /* single-char tokens (+ - / ...) */
|
||||
else { /* single-char tokens ('+', '*', '%', '{', '}', ...) */
|
||||
int c = ls->current;
|
||||
next(ls);
|
||||
return c;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user