Merge branch 'development' into new/http

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER
2022-12-30 15:12:14 +01:00
158 changed files with 13380 additions and 5603 deletions
+10
View File
@@ -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"
],
}
-38
View File
@@ -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)._
-18
View File
@@ -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)._
+3
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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
+21
View File
@@ -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."
+3 -3
View File
@@ -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'
+3 -12
View File
@@ -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 }}
+7
View File
@@ -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
View File
@@ -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
View File
@@ -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"
+22 -10
View File
@@ -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.
+9 -1
View File
@@ -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"
Executable
+58
View File
@@ -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}/* ./"
+4
View File
@@ -0,0 +1,4 @@
#!/bin/sh
set -e
patch -p1 < patch/lua/0001-add-pihole-library.patch
+94
View File
@@ -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);
+5
View File
@@ -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
View File
@@ -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)
+2 -2
View File
@@ -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.
+59
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+1 -1
View File
@@ -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
+1 -1
View 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
View File
@@ -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
View File
@@ -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);
+2 -2
View File
@@ -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;
+4 -1
View File
@@ -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
View File
@@ -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);
}
+2 -1
View File
@@ -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);
+70 -57
View File
@@ -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);
}
+1
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+3591 -1636
View File
File diff suppressed because it is too large Load Diff
+85 -34
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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";
+14
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+12 -2
View File
@@ -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
+1
View File
@@ -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
View File
@@ -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
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
}
+3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
+32
View File
@@ -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
View File
@@ -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;
}
+5 -7
View File
@@ -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
View File
@@ -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 {
+15 -6
View File
@@ -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)
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 */
}
+11
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+10
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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