Merge branch 'development-v6' into new/teleporter_v5

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER
2023-11-22 22:18:00 +01:00
35 changed files with 2812 additions and 1398 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "FTL x86_64 Build Env",
"image": "ghcr.io/pi-hole/ftl-build:v2.3-alpine",
"image": "ghcr.io/pi-hole/ftl-build:v2.4.1",
"runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ],
"customizations": {
"vscode": {
+1 -2
View File
@@ -1,5 +1,4 @@
ARG BUILDER="alpine"
FROM ghcr.io/pi-hole/ftl-build:v2.3-${BUILDER} AS builder
FROM ghcr.io/pi-hole/ftl-build:v2.4.1 AS builder
WORKDIR /app
-1
View File
@@ -7,7 +7,6 @@ patch -p1 < patch/civetweb/0001-Always-Kepler-syntax-for-Lua-server-pages.patch
patch -p1 < patch/civetweb/0001-Add-FTL-URI-rewriting-changes-to-CivetWeb.patch
patch -p1 < patch/civetweb/0001-Add-mbedTLS-debug-logging-hook.patch
patch -p1 < patch/civetweb/0001-Register-CSRF-token-in-conn-request_info.patch
patch -p1 < patch/civetweb/0001-Do-not-try-to-guess-server-hostname-in-Civetweb-when.patch
patch -p1 < patch/civetweb/0001-Log-debug-messages-to-webserver.log-when-debug.webse.patch
patch -p1 < patch/civetweb/0001-Allow-extended-ASCII-characters-in-URIs.patch
@@ -1,29 +0,0 @@
From e41d902b5b01896360e1235aebabd3eb352158aa Mon Sep 17 00:00:00 2001
From: DL6ER <dl6er@dl6er.de>
Date: Sun, 8 Oct 2023 14:31:20 +0200
Subject: [PATCH] Do not try to guess server hostname in Civetweb when
redirecting directory URIs to end with a slash
Signed-off-by: DL6ER <dl6er@dl6er.de>
---
src/webserver/civetweb/civetweb.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/webserver/civetweb/civetweb.c b/src/webserver/civetweb/civetweb.c
index f44b17ba..3df8eab9 100644
--- a/src/webserver/civetweb/civetweb.c
+++ b/src/webserver/civetweb/civetweb.c
@@ -15306,7 +15306,9 @@ handle_request(struct mg_connection *conn)
if (!new_path) {
mg_send_http_error(conn, 500, "out or memory");
} else {
- mg_get_request_link(conn, new_path, buflen - 1);
+ /* Pi-hole modification */
+ //mg_get_request_link(conn, new_path, buflen - 1);
+ strcpy(new_path, ri->local_uri_raw);
strcat(new_path, "/");
if (ri->query_string) {
/* Append ? and query string */
--
2.34.1
@@ -25,6 +25,6 @@ index 6280ebf6..a5e82f70 100644
char *zHistory;
int nHistory;
+ print_FTL_version();
#if SHELL_WIN_UTF8_OPT
switch( console_utf8_in+2*console_utf8_out ){
default: case 0: break;
#if CIO_WIN_WC_XLATE
# define SHELL_CIO_CHAR_SET (stdout_is_console? " (UTF-16 console I/O)" : "")
#else
+5 -2
View File
@@ -283,9 +283,12 @@ find_package(Threads REQUIRED)
find_library(LIBHOGWEED NAMES libhogweed${CMAKE_STATIC_LIBRARY_SUFFIX} hogweed HINTS /usr/local/lib64)
find_library(LIBGMP NAMES libgmp${CMAKE_STATIC_LIBRARY_SUFFIX} gmp)
find_library(LIBNETTLE NAMES libnettle${CMAKE_STATIC_LIBRARY_SUFFIX} nettle HINTS /usr/local/lib64)
find_library(LIBIDN NAMES libidn${CMAKE_STATIC_LIBRARY_SUFFIX} idn)
target_link_libraries(pihole-FTL rt Threads::Threads ${LIBHOGWEED} ${LIBGMP} ${LIBNETTLE} ${LIBIDN})
# for IDN2 we need the idn2 library which in turn depends on the unistring library
find_library(LIBIDN2 NAMES libidn2${CMAKE_STATIC_LIBRARY_SUFFIX} idn)
find_library(LIBUNISTRING NAMES libunistring${CMAKE_STATIC_LIBRARY_SUFFIX} unistring)
target_link_libraries(pihole-FTL rt Threads::Threads ${LIBHOGWEED} ${LIBGMP} ${LIBNETTLE} ${LIBIDN2} ${LIBUNISTRING})
if(LUA_DL STREQUAL "true")
find_library(LIBDL dl)
+16
View File
@@ -161,6 +161,22 @@
#define pthread_mutex_lock(mutex) FTLpthread_mutex_lock(mutex, __FILE__, __FUNCTION__, __LINE__)
#define fopen(pathname, mode) FTLfopen(pathname, mode, __FILE__, __FUNCTION__, __LINE__)
#define ftlallocate(fd, offset, len) FTLfallocate(fd, offset, len, __FILE__, __FUNCTION__, __LINE__)
#define strlen(str) FTLstrlen(str, __FILE__, __FUNCTION__, __LINE__)
#define strnlen(str, maxlen) FTLstrnlen(str, maxlen, __FILE__, __FUNCTION__, __LINE__)
#define strcpy(dest, src) FTLstrcpy(dest, src, __FILE__, __FUNCTION__, __LINE__)
#define strncpy(dest, src, n) FTLstrncpy(dest, src, n, __FILE__, __FUNCTION__, __LINE__)
#define memset(s, c, n) FTLmemset(s, c, n, __FILE__, __FUNCTION__, __LINE__)
#define memcpy(dest, src, n) FTLmemcpy(dest, src, n, __FILE__, __FUNCTION__, __LINE__)
#define memmove(dest, src, n) FTLmemmove(dest, src, n, __FILE__, __FUNCTION__, __LINE__)
#define strstr(haystack, needle) FTLstrstr(haystack, needle, __FILE__, __FUNCTION__, __LINE__)
#define strcmp(s1, s2) FTLstrcmp(s1, s2, __FILE__, __FUNCTION__, __LINE__)
#define strncmp(s1, s2, n) FTLstrncmp(s1, s2, n, __FILE__, __FUNCTION__, __LINE__)
#define strcasecmp(s1, s2) FTLstrcasecmp(s1, s2, __FILE__, __FUNCTION__, __LINE__)
#define strncasecmp(s1, s2, n) FTLstrncasecmp(s1, s2, n, __FILE__, __FUNCTION__, __LINE__)
#define strcat(dest, src) FTLstrcat(dest, src, __FILE__, __FUNCTION__, __LINE__)
#define strncat(dest, src, n) FTLstrncat(dest, src, n, __FILE__, __FUNCTION__, __LINE__)
#define memcmp(s1, s2, n) FTLmemcmp(s1, s2, n, __FILE__, __FUNCTION__, __LINE__)
#define memmem(haystack, haystacklen, needle, needlelen) FTLmemmem(haystack, haystacklen, needle, needlelen, __FILE__, __FUNCTION__, __LINE__)
#include "syscalls/syscalls.h"
// Preprocessor help functions
+11 -1
View File
@@ -223,6 +223,7 @@ components:
items:
allOf:
- $ref: 'domains.yaml#/components/schemas/domain'
- $ref: 'domains.yaml#/components/schemas/unicode'
- $ref: 'domains.yaml#/components/schemas/type'
- $ref: 'domains.yaml#/components/schemas/kind'
- $ref: 'domains.yaml#/components/schemas/comment'
@@ -249,6 +250,13 @@ components:
description: Domain
type: string
example: testdomain.com
unicode:
type: object
properties:
unicode:
description: Unicode domain (may be different from `domain` if punycode-encoding is used)
type: string
example: "äbc.com"
domain_array:
type: object
properties:
@@ -368,6 +376,7 @@ components:
value:
domains:
- domain: "allowed.com"
unicode: "allowed.com"
type: allow
kind: exact
comment: null
@@ -377,7 +386,8 @@ components:
id: 299
date_added: 1611239095
date_modified: 1612163756
- domain: "allowed2.comm"
- domain: "xn--4ca.com"
unicode: "ä.com"
type: allow
kind: regex
comment: "Some text"
+53 -2
View File
@@ -17,6 +17,9 @@
#include "shmem.h"
// getNameFromIP()
#include "database/network-table.h"
// valid_domain()
#include "tools/gravity-parseList.h"
#include <idn2.h>
static int api_list_read(struct ftl_conn *api,
const int code,
@@ -55,9 +58,11 @@ static int api_list_read(struct ftl_conn *api,
char *name = NULL;
if(table.client != NULL)
{
// Try to obtain hostname if this is a valid IP address
// Try to obtain hostname
if(isValidIPv4(table.client) || isValidIPv6(table.client))
name = getNameFromIP(NULL, table.client);
else if(isMAC(table.client))
name = getNameFromMAC(table.client);
}
JSON_COPY_STR_TO_OBJECT(row, "client", table.client);
@@ -70,10 +75,18 @@ static int api_list_read(struct ftl_conn *api,
}
else // domainlists
{
char *unicode = NULL;
const int rc = idn2_to_unicode_lzlz(table.domain, &unicode, IDN2_NONTRANSITIONAL);
JSON_COPY_STR_TO_OBJECT(row, "domain", table.domain);
if(rc == IDN2_OK)
JSON_COPY_STR_TO_OBJECT(row, "unicode", unicode);
else
JSON_COPY_STR_TO_OBJECT(row, "unicode", table.domain);
JSON_REF_STR_IN_OBJECT(row, "type", table.type);
JSON_REF_STR_IN_OBJECT(row, "kind", table.kind);
JSON_COPY_STR_TO_OBJECT(row, "comment", table.comment);
if(unicode != NULL)
free(unicode);
}
// Groups don't have the groups property
@@ -396,9 +409,47 @@ static int api_list_write(struct ftl_conn *api,
if(allocated_json)
cJSON_free(row.items);
return send_json_error(api, 400, // 400 Bad Request
"bad_request",
"Spaces, newlines and tabs are not allowed in domains and URLs",
it->valuestring);
}
if(listtype == GRAVITY_DOMAINLIST_ALLOW_EXACT ||
listtype == GRAVITY_DOMAINLIST_DENY_EXACT)
{
char *punycode = NULL;
const int rc = idn2_to_ascii_lz(it->valuestring, &punycode, IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL);
if (rc != IDN2_OK)
{
// Invalid domain name
return send_json_error(api, 400,
"bad_request",
"Invalid request: Invalid domain name",
idn2_strerror(rc));
}
// Convert punycode domain to lowercase
for(unsigned int i = 0u; i < strlen(punycode); i++)
punycode[i] = tolower(punycode[i]);
// Validate punycode domain
// This will reject domains like äöü{{{.com
// which convert to xn--{{{-pla4gpb.com
if(!valid_domain(punycode, strlen(punycode), false))
{
if(allocated_json)
cJSON_free(row.items);
return send_json_error(api, 400, // 400 Bad Request
"bad_request",
"Spaces, newlines and tabs are not allowed in domains and URLs",
"Invalid domain",
it->valuestring);
}
// Replace domain with punycode version
if(!(it->type & cJSON_IsReference))
free(it->valuestring);
it->valuestring = punycode;
// Remove reference flag
it->type &= ~cJSON_IsReference;
}
}
}
+8 -5
View File
@@ -15,7 +15,7 @@
#include "database/gravity-db.h"
// match_regex()
#include "regex_r.h"
#include <idna.h>
#include <idn2.h>
#define MAX_SEARCH_RESULTS 10000u
@@ -182,18 +182,21 @@ int api_search(struct ftl_conn *api)
// use characters drawn from a large repertoire (Unicode), but IDNA
// allows the non-ASCII characters to be represented using only the
// ASCII characters already allowed in so-called host names today.
// idna_to_ascii_lz() convert domain name in the locale’s encoding to an
// idn2_to_ascii_lz() convert domain name in the locale’s encoding to an
// ASCII string. The domain name may contain several labels, separated
// by dots. The output buffer must be deallocated by the caller.
// Used flags:
// - IDN2_NFC_INPUT: Input is in Unicode Normalization Form C (NFC)
// - IDN2_NONTRANSITIONAL: Use Unicode TR46 non-transitional processing
char *punycode = NULL;
const Idna_rc rc = idna_to_ascii_lz(domain, &punycode, 0);
if (rc != IDNA_SUCCESS)
const int rc = idn2_to_ascii_lz(domain, &punycode, IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL);
if (rc != IDN2_OK)
{
// Invalid domain name
return send_json_error(api, 400,
"bad_request",
"Invalid request: Invalid domain name",
idna_strerror(rc));
idn2_strerror(rc));
}
// Convert punycode domain to lowercase
+55
View File
@@ -60,6 +60,8 @@
#include "tools/arp-scan.h"
// run_performance_test()
#include "config/password.h"
// idn2_to_ascii_lz()
#include <idn2.h>
// defined in dnsmasq.c
extern void print_dnsmasq_version(const char *yellow, const char *green, const char *bold, const char *normal);
@@ -427,6 +429,53 @@ void parse_args(int argc, char* argv[])
exit(run_arp_scan(scan_all, extreme_mode));
}
// IDN2 conversion mode
if(argc > 1 && strcmp(argv[1], "idn2") == 0)
{
// Enable stdout printing
cli_mode = true;
if(argc == 3)
{
// Convert unicode domain to punycode
char *punycode = NULL;
const int rc = idn2_to_ascii_lz(argv[2], &punycode, IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL);
if (rc != IDN2_OK)
{
// Invalid domain name
printf("Invalid domain name: %s\n", argv[2]);
exit(EXIT_FAILURE);
}
// Convert punycode domain to lowercase
for(unsigned int i = 0u; i < strlen(punycode); i++)
punycode[i] = tolower(punycode[i]);
printf("%s\n", punycode);
exit(EXIT_SUCCESS);
}
else if(argc == 4 && (strcmp(argv[2], "-d") == 0 || strcmp(argv[2], "--decode") == 0))
{
// Convert punycode domain to unicode
char *unicode = NULL;
const int rc = idn2_to_unicode_lzlz(argv[3], &unicode, IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL);
if (rc != IDN2_OK)
{
// Invalid domain name
printf("Invalid domain name: %s\n", argv[3]);
exit(EXIT_FAILURE);
}
printf("%s\n", unicode);
exit(EXIT_SUCCESS);
}
else
{
printf("Usage: %s idn2 [--decode] <domain>\n", argv[0]);
exit(EXIT_FAILURE);
}
}
// start from 1, as argv[0] is the executable name
for(int i = 1; i < argc; i++)
{
@@ -878,6 +927,12 @@ void parse_args(int argc, char* argv[])
printf(" per line (no HOSTS lists, etc.)\n\n");
printf(" Usage: %spihole-FTL gravity checkList %sinfile%s\n\n", green, cyan, normal);
printf("%sIDN2 conversion:%s\n", yellow, normal);
printf(" Convert a given internationalized domain name (IDN) to\n");
printf(" punycode or vice versa.\n\n");
printf(" Encoding: %spihole-FTL idn2 %sdomain%s\n", green, cyan, normal);
printf(" Decoding: %spihole-FTL idn2 -d %spunycode%s\n\n", green, cyan, 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");
+49 -15
View File
@@ -833,6 +833,7 @@ void initConfig(struct config *conf)
conf->webserver.domain.h = "On which domain is the web interface served?";
conf->webserver.domain.a = cJSON_CreateStringReference("<valid domain>");
conf->webserver.domain.t = CONF_STRING;
conf->webserver.domain.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL;
conf->webserver.domain.d.s = (char*)"pi.hole";
conf->webserver.acl.k = "webserver.acl";
@@ -1303,18 +1304,7 @@ void initConfig(struct config *conf)
// Initialize config value with default one for all *except* the log file path
if(conf_item != &conf->files.log.ftl)
{
if(conf_item->t == CONF_JSON_STRING_ARRAY)
// JSON objects really need to be duplicated as the config
// structure stores only a pointer to memory somewhere else
conf_item->v.json = cJSON_Duplicate(conf_item->d.json, true);
else if(conf_item->t == CONF_STRING_ALLOCATED)
// Allocated string: Make our own copy
conf_item->v.s = strdup(conf_item->d.s);
else
// Ordinary value: Simply copy the union over
memcpy(&conf_item->v, &conf_item->d, sizeof(conf_item->d));
}
reset_config(conf_item);
// Parse and split paths
conf_item->p = gen_config_path(conf_item->k, '.');
@@ -1332,10 +1322,52 @@ void initConfig(struct config *conf)
log_err("Config option %s has no type!", conf_item->k);
continue;
}
// Verify we have no default string pointers to NULL
if((conf_item->t == CONF_STRING || conf_item->t == CONF_STRING_ALLOCATED) && conf_item->d.s == NULL)
{
log_err("Config option %s has NULL default string!", conf_item->k);
continue;
}
// Verify we have no default JSON pointers to NULL
if(conf_item->t == CONF_JSON_STRING_ARRAY && conf_item->d.json == NULL)
{
log_err("Config option %s has NULL default JSON array!", conf_item->k);
continue;
}
}
}
void readFTLconf(struct config *conf, const bool rewrite)
void reset_config(struct conf_item *conf_item)
{
if(conf_item->t == CONF_JSON_STRING_ARRAY)
{
// Free allocated memory (if any)
if(conf_item->v.json != NULL)
cJSON_Delete(conf_item->v.json);
// JSON objects really need to be duplicated as the config
// structure stores only a pointer to memory somewhere else
conf_item->v.json = cJSON_Duplicate(conf_item->d.json, true);
}
else if(conf_item->t == CONF_STRING_ALLOCATED)
{
// Free allocated memory (if any)
if(conf_item->v.s != NULL)
free(conf_item->v.s);
// Allocated string: Make our own copy
conf_item->v.s = strdup(conf_item->d.s);
}
else
{
// Ordinary value: Simply copy the union over
memcpy(&conf_item->v, &conf_item->d, sizeof(conf_item->d));
}
}
bool readFTLconf(struct config *conf, const bool rewrite)
{
// Initialize config with default values
initConfig(conf);
@@ -1352,7 +1384,7 @@ void readFTLconf(struct config *conf, const bool rewrite)
write_dnsmasq_config(conf, false, NULL);
write_custom_list();
}
return;
return true;
}
// On error, try to read legacy (pre-v6.0) config file. If successful,
@@ -1398,7 +1430,7 @@ void readFTLconf(struct config *conf, const bool rewrite)
if(ports == NULL)
{
log_err("Unable to allocate memory for default ports string");
return;
return false;
}
// Create the string
snprintf(ports, 32, "%d,%ds", http_port, https_port);
@@ -1423,6 +1455,8 @@ void readFTLconf(struct config *conf, const bool rewrite)
writeFTLtoml(true);
write_dnsmasq_config(conf, false, NULL);
write_custom_list();
return false;
}
bool getLogFilePath(void)
+2 -1
View File
@@ -327,7 +327,8 @@ extern struct config config;
void set_debug_flags(struct config *conf);
void set_all_debug(struct config *conf, const bool status);
void initConfig(struct config *conf);
void readFTLconf(struct config *conf, const bool rewrite);
void reset_config(struct conf_item *conf_item);
bool readFTLconf(struct config *conf, const bool rewrite);
bool getLogFilePath(void);
struct conf_item *get_conf_item(struct config *conf, const unsigned int n);
struct conf_item *get_debug_item(struct config *conf, const enum debug_flag debug);
+22 -2
View File
@@ -106,18 +106,38 @@ bool check_inotify_event(void)
// Check if this is the event we are looking for
if(event->mask & IN_CLOSE_WRITE)
{
// File opened for writing was closed
log_debug(DEBUG_INOTIFY, "File written: "WATCHDIR"/%s", event->name);
if(strcmp(event->name, "pihole.toml") == 0)
config_changed = true;
}
else if(event->mask & IN_CREATE)
{
// File was created
log_debug(DEBUG_INOTIFY, "File created: "WATCHDIR"/%s", event->name);
else if(event->mask & IN_MOVE)
log_debug(DEBUG_INOTIFY, "File moved: "WATCHDIR"/%s", event->name);
}
else if(event->mask & IN_MOVED_FROM)
{
// File was moved (source)
log_debug(DEBUG_INOTIFY, "File moved from: "WATCHDIR"/%s", event->name);
}
else if(event->mask & IN_MOVED_TO)
{
// File was moved (target)
log_debug(DEBUG_INOTIFY, "File moved to: "WATCHDIR"/%s", event->name);
if(strcmp(event->name, "pihole.toml") == 0)
config_changed = true;
}
else if(event->mask & IN_DELETE)
{
// File was deleted
log_debug(DEBUG_INOTIFY, "File deleted: "WATCHDIR"/%s", event->name);
}
else if(event->mask & IN_IGNORED)
{
// Watch descriptor was removed
log_warn("Inotify watch descriptor for "WATCHDIR" was removed (directory deleted or unmounted?)");
}
else
log_debug(DEBUG_INOTIFY, "Unknown event (%X) on watched file: "WATCHDIR"/%s", event->mask, event->name);
}
+6 -6
View File
@@ -404,7 +404,7 @@ const char *readFTLlegacy(struct config *conf)
conf->dns.reply.host.force4.v.b = false;
conf->dns.reply.host.v4.v.in_addr.s_addr = 0;
buffer = parseFTLconf(fp, "LOCAL_IPV4");
if(buffer != NULL && inet_pton(AF_INET, buffer, &conf->dns.reply.host.v4.v.in_addr))
if(buffer != NULL && strlen(buffer) > 0 && inet_pton(AF_INET, buffer, &conf->dns.reply.host.v4.v.in_addr))
conf->dns.reply.host.force4.v.b = true;
// LOCAL_IPV6
@@ -414,7 +414,7 @@ const char *readFTLlegacy(struct config *conf)
conf->dns.reply.host.force6.v.b = false;
memset(&conf->dns.reply.host.v6.v.in6_addr, 0, sizeof(conf->dns.reply.host.v6.v.in6_addr));
buffer = parseFTLconf(fp, "LOCAL_IPV6");
if(buffer != NULL && inet_pton(AF_INET6, buffer, &conf->dns.reply.host.v6.v.in6_addr))
if(buffer != NULL && strlen(buffer) > 0 && inet_pton(AF_INET6, buffer, &conf->dns.reply.host.v6.v.in6_addr))
conf->dns.reply.host.force6.v.b = true;
// BLOCK_IPV4
@@ -423,7 +423,7 @@ const char *readFTLlegacy(struct config *conf)
conf->dns.reply.blocking.force4.v.b = false;
conf->dns.reply.blocking.v4.v.in_addr.s_addr = 0;
buffer = parseFTLconf(fp, "BLOCK_IPV4");
if(buffer != NULL && inet_pton(AF_INET, buffer, &conf->dns.reply.blocking.v4.v.in_addr))
if(buffer != NULL && strlen(buffer) > 0 && inet_pton(AF_INET, buffer, &conf->dns.reply.blocking.v4.v.in_addr))
conf->dns.reply.blocking.force4.v.b = true;
// BLOCK_IPV6
@@ -432,7 +432,7 @@ const char *readFTLlegacy(struct config *conf)
conf->dns.reply.blocking.force6.v.b = false;
memset(&conf->dns.reply.blocking.v6.v.in6_addr, 0, sizeof(conf->dns.reply.host.v6.v.in6_addr));
buffer = parseFTLconf(fp, "BLOCK_IPV6");
if(buffer != NULL && inet_pton(AF_INET6, buffer, &conf->dns.reply.blocking.v6.v.in6_addr))
if(buffer != NULL && strlen(buffer) > 0 && inet_pton(AF_INET6, buffer, &conf->dns.reply.blocking.v6.v.in6_addr))
conf->dns.reply.blocking.force6.v.b = true;
// REPLY_ADDR4 (deprecated setting)
@@ -441,7 +441,7 @@ const char *readFTLlegacy(struct config *conf)
// defaults to: not set
struct in_addr reply_addr4;
buffer = parseFTLconf(fp, "REPLY_ADDR4");
if(buffer != NULL && inet_pton(AF_INET, buffer, &reply_addr4))
if(buffer != NULL && strlen(buffer) > 0 && inet_pton(AF_INET, buffer, &reply_addr4))
{
if(conf->dns.reply.host.force4.v.b || conf->dns.reply.blocking.force4.v.b)
{
@@ -462,7 +462,7 @@ const char *readFTLlegacy(struct config *conf)
// defaults to: not set
struct in6_addr reply_addr6;
buffer = parseFTLconf(fp, "REPLY_ADDR6");
if(buffer != NULL && inet_pton(AF_INET, buffer, &reply_addr6))
if(buffer != NULL && strlen(buffer) > 0 && inet_pton(AF_INET, buffer, &reply_addr6))
{
if(conf->dns.reply.host.force6.v.b || conf->dns.reply.blocking.force6.v.b)
{
+5 -5
View File
@@ -259,8 +259,8 @@ static char* get_client_querystr(const char *table, const char *column, const ch
}
// Determine whether to show IP or hardware address
static inline const char *show_client_string(const char *hwaddr, const char *hostname,
const char *ip)
static const char *show_client_string(const char *hwaddr, const char *hostname,
const char *ip)
{
if(hostname != NULL && strlen(hostname) > 0)
{
@@ -1626,7 +1626,7 @@ bool gravityDB_addToTable(const enum gravity_list_type listtype, tablerow *row,
// The item is the item for all POST requests
if(listtype == GRAVITY_GROUPS)
{
querystr = "INSERT INTO \"group\" (name,enabled,description) VALUES (:item,:enabled,:description);";
querystr = "INSERT INTO \"group\" (name,enabled,description) VALUES (:item,:enabled,:comment);";
}
else if(listtype == GRAVITY_ADLISTS)
{
@@ -1648,8 +1648,8 @@ bool gravityDB_addToTable(const enum gravity_list_type listtype, tablerow *row,
if(row->name == NULL)
{
// Name is not to be changed
querystr = "INSERT INTO \"group\" (name,enabled,description) VALUES (:item,:enabled,:description) "
"ON CONFLICT(name) DO UPDATE SET enabled = :enabled, description = :description;";
querystr = "INSERT INTO \"group\" (name,enabled,description) VALUES (:item,:enabled,:comment) "
"ON CONFLICT(name) DO UPDATE SET enabled = :enabled, description = :comment;";
}
else
{
+104
View File
@@ -2144,6 +2144,84 @@ char *__attribute__((malloc)) getNameFromIP(sqlite3 *db, const char *ipaddr)
return name;
}
// Get most recently seen host name of device identified by MAC address
char *__attribute__((malloc)) getNameFromMAC(const char *client)
{
// Return early if database is known to be broken
if(FTLDBerror())
return NULL;
log_debug(DEBUG_DATABASE,"Looking up host name for %s", client);
// Open pihole-FTL.db database file
sqlite3 *db = NULL;
if((db = dbopen(false, false)) == NULL)
{
log_warn("getNameFromMAC(\"%s\") - Failed to open DB", client);
return NULL;
}
// Check for a host name associated with the given client as MAC address
// COLLATE NOCASE: Case-insensitive comparison
const char *querystr = "SELECT name FROM network_addresses "
"WHERE name IS NOT NULL AND "
"network_id = (SELECT id FROM network WHERE hwaddr = ? COLLATE NOCASE) "
"ORDER BY lastSeen DESC LIMIT 1";
sqlite3_stmt *stmt = NULL;
int rc = sqlite3_prepare_v2(db, querystr, -1, &stmt, NULL);
if(rc != SQLITE_OK)
{
log_err("getNameFromMAC(\"%s\") - SQL error prepare: %s",
client, sqlite3_errstr(rc));
dbclose(&db);
return NULL;
}
// Bind client to prepared statement
if((rc = sqlite3_bind_text(stmt, 1, client, -1, SQLITE_STATIC)) != SQLITE_OK)
{
log_warn("getNameFromMAC(\"%s\"): Failed to bind ip: %s",
client, sqlite3_errstr(rc));
checkFTLDBrc(rc);
sqlite3_reset(stmt);
sqlite3_finalize(stmt);
dbclose(&db);
return NULL;
}
char *name = NULL;
rc = sqlite3_step(stmt);
if(rc == SQLITE_ROW)
{
// Database record found (result might be empty)
name = strdup((char*)sqlite3_column_text(stmt, 0));
if(config.debug.resolver.v.b)
log_debug(DEBUG_RESOLVER, "Found database host name (by MAC) %s -> %s",
client, name);
}
else if(rc == SQLITE_DONE)
{
// Not found
if(config.debug.resolver.v.b)
log_debug(DEBUG_RESOLVER, " ---> not found");
}
else
{
// Error
checkFTLDBrc(rc);
return NULL;
}
// Finalize statement and close database handle
sqlite3_reset(stmt);
sqlite3_finalize(stmt);
dbclose(&db);
return name;
}
// Get interface of device identified by IP address
char *__attribute__((malloc)) getIfaceFromIP(sqlite3 *db, const char *ipaddr)
{
@@ -2425,3 +2503,29 @@ bool networkTable_deleteDevice(sqlite3 *db, const int id, const char **message)
return true;
}
// Counting number of occurrences of a specific char in a string
static size_t __attribute__ ((pure)) count_char(const char *haystack, const char needle)
{
size_t count = 0u;
while(*haystack)
if (*haystack++ == needle)
++count;
return count;
}
// Identify MAC addresses using a set of suitable criteria
bool __attribute__ ((pure)) isMAC(const char *input)
{
if(input != NULL && // Valid input
strlen(input) == 17u && // MAC addresses are always 17 chars long (6 bytes + 5 colons)
count_char(input, ':') == 5u && // MAC addresses always have 5 colons
strstr(input, "::") == NULL) // No double-colons (IPv6 address abbreviation)
{
// This is a MAC address of the form AA:BB:CC:DD:EE:FF
return true;
}
// Not a MAC address
return false;
}
+5 -3
View File
@@ -18,12 +18,14 @@ bool create_network_addresses_with_names_table(sqlite3 *db);
void parse_neighbor_cache(sqlite3 *db);
void updateMACVendorRecords(sqlite3 *db);
bool unify_hwaddr(sqlite3 *db);
char* __attribute__((malloc)) getMACfromIP(sqlite3 *db, const char* ipaddr);
char *getMACfromIP(sqlite3 *db, const char* ipaddr) __attribute__((malloc));
int getAliasclientIDfromIP(sqlite3 *db, const char *ipaddr);
char* __attribute__((malloc)) getNameFromIP(sqlite3 *db, const char* ipaddr);
char* __attribute__((malloc)) getIfaceFromIP(sqlite3 *db, const char* ipaddr);
char *getNameFromIP(sqlite3 *db, const char* ipaddr) __attribute__((malloc));
char *getNameFromMAC(const char *client) __attribute__((malloc));
char *getIfaceFromIP(sqlite3 *db, const char* ipaddr) __attribute__((malloc));
void resolveNetworkTableNames(void);
bool flush_network_table(void);
bool isMAC(const char *input) __attribute__ ((pure));
typedef struct {
unsigned int id;
+4 -4
View File
@@ -198,8 +198,8 @@ bool backup_db_sessions(struct session *sessions, const uint16_t max_sessions)
return false;
}
log_info("Stored %u/%u API session%s in the database",
api_sessions, max_sessions, max_sessions == 1 ? "" : "s");
log_info("Stored %u API session%s in the database",
api_sessions, api_sessions == 1 ? "" : "s");
// Close database connection
dbclose(&db);
@@ -296,8 +296,8 @@ bool restore_db_sessions(struct session *sessions, const uint16_t max_sessions)
i++;
}
log_info("Restored %u/%u API session%s from the database",
i, max_sessions, max_sessions == 1 ? "" : "s");
log_info("Restored %u API session%s from the database",
i, i == 1 ? "" : "s");
// Finalize statement
if(sqlite3_finalize(stmt) != SQLITE_OK)
+1859 -1171
View File
File diff suppressed because it is too large Load Diff
-26
View File
@@ -29,32 +29,6 @@
// isMAC()
#include "network-table.h"
// Counting number of occurrences of a specific char in a string
static size_t __attribute__ ((pure)) count_char(const char *haystack, const char needle)
{
size_t count = 0u;
while(*haystack)
if (*haystack++ == needle)
++count;
return count;
}
// Identify MAC addresses using a set of suitable criteria
static bool __attribute__ ((pure)) isMAC(const char *input)
{
if(input != NULL && // Valid input
strlen(input) == 17u && // MAC addresses are always 17 chars long (6 bytes + 5 colons)
count_char(input, ':') == 5u && // MAC addresses always have 5 colons
strstr(input, "::") == NULL) // No double-colons (IPv6 address abbreviation)
{
// This is a MAC address of the form AA:BB:CC:DD:EE:FF
return true;
}
// Not a MAC address
return false;
}
static void subnet_match_impl(sqlite3_context *context, int argc, sqlite3_value **argv)
{
// Exactly two arguments should be submitted to this routine
+176 -79
View File
@@ -1,6 +1,6 @@
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
** version 3.44.0. By combining all the individual C code files into this
** version 3.44.1. By combining all the individual C code files into this
** single large file, the entire code can be compiled as a single translation
** unit. This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately. Performance improvements
@@ -18,7 +18,7 @@
** separate file. This file contains only code for the core SQLite library.
**
** The content in this amalgamation comes from Fossil check-in
** 17129ba1ff7f0daf37100ee82d507aef7827.
** d295f48e8f367b066b881780c98bdf980a1d.
*/
#define SQLITE_CORE 1
#define SQLITE_AMALGAMATION 1
@@ -459,9 +459,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION "3.44.0"
#define SQLITE_VERSION_NUMBER 3044000
#define SQLITE_SOURCE_ID "2023-11-01 11:23:50 17129ba1ff7f0daf37100ee82d507aef7827cf38de1866e2633096ae6ad81301"
#define SQLITE_VERSION "3.44.1"
#define SQLITE_VERSION_NUMBER 3044001
#define SQLITE_SOURCE_ID "2023-11-22 14:18:12 d295f48e8f367b066b881780c98bdf980a1d550397d5ba0b0e49842c95b3e8b4"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -5886,13 +5886,27 @@ SQLITE_API int sqlite3_create_window_function(
** </dd>
**
** [[SQLITE_SUBTYPE]] <dt>SQLITE_SUBTYPE</dt><dd>
** The SQLITE_SUBTYPE flag indicates to SQLite that a function may call
** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call
** [sqlite3_value_subtype()] to inspect the sub-types of its arguments.
** Specifying this flag makes no difference for scalar or aggregate user
** functions. However, if it is not specified for a user-defined window
** function, then any sub-types belonging to arguments passed to the window
** function may be discarded before the window function is called (i.e.
** sqlite3_value_subtype() will always return 0).
** This flag instructs SQLite to omit some corner-case optimizations that
** might disrupt the operation of the [sqlite3_value_subtype()] function,
** causing it to return zero rather than the correct subtype().
** SQL functions that invokes [sqlite3_value_subtype()] should have this
** property. If the SQLITE_SUBTYPE property is omitted, then the return
** value from [sqlite3_value_subtype()] might sometimes be zero even though
** a non-zero subtype was specified by the function argument expression.
**
** [[SQLITE_RESULT_SUBTYPE]] <dt>SQLITE_RESULT_SUBTYPE</dt><dd>
** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call
** [sqlite3_result_subtype()] to cause a sub-type to be associated with its
** result.
** Every function that invokes [sqlite3_result_subtype()] should have this
** property. If it does not, then the call to [sqlite3_result_subtype()]
** might become a no-op if the function is used as term in an
** [expression index]. On the other hand, SQL functions that never invoke
** [sqlite3_result_subtype()] should avoid setting this property, as the
** purpose of this property is to disable certain optimizations that are
** incompatible with subtypes.
** </dd>
** </dl>
*/
@@ -5900,6 +5914,7 @@ SQLITE_API int sqlite3_create_window_function(
#define SQLITE_DIRECTONLY 0x000080000
#define SQLITE_SUBTYPE 0x000100000
#define SQLITE_INNOCUOUS 0x000200000
#define SQLITE_RESULT_SUBTYPE 0x001000000
/*
** CAPI3REF: Deprecated Functions
@@ -6096,6 +6111,12 @@ SQLITE_API int sqlite3_value_encoding(sqlite3_value*);
** information can be used to pass a limited amount of context from
** one SQL function to another. Use the [sqlite3_result_subtype()]
** routine to set the subtype for the return value of an SQL function.
**
** Every [application-defined SQL function] that invoke this interface
** should include the [SQLITE_SUBTYPE] property in the text
** encoding argument when the function is [sqlite3_create_function|registered].
** If the [SQLITE_SUBTYPE] property is omitted, then sqlite3_value_subtype()
** might return zero instead of the upstream subtype in some corner cases.
*/
SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*);
@@ -6226,14 +6247,22 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
** <li> ^(when sqlite3_set_auxdata() is invoked again on the same
** parameter)^, or
** <li> ^(during the original sqlite3_set_auxdata() call when a memory
** allocation error occurs.)^ </ul>
** allocation error occurs.)^
** <li> ^(during the original sqlite3_set_auxdata() call if the function
** is evaluated during query planning instead of during query execution,
** as sometimes happens with [SQLITE_ENABLE_STAT4].)^ </ul>
**
** Note the last bullet in particular. The destructor X in
** Note the last two bullets in particular. The destructor X in
** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the
** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata()
** should be called near the end of the function implementation and the
** function implementation should not make any use of P after
** sqlite3_set_auxdata() has been called.
** sqlite3_set_auxdata() has been called. Furthermore, a call to
** sqlite3_get_auxdata() that occurs immediately after a corresponding call
** to sqlite3_set_auxdata() might still return NULL if an out-of-memory
** condition occurred during the sqlite3_set_auxdata() call or if the
** function is being evaluated during query planning rather than during
** query execution.
**
** ^(In practice, auxiliary data is preserved between function calls for
** function parameters that are compile-time constants, including literal
@@ -6507,6 +6536,20 @@ SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n);
** higher order bits are discarded.
** The number of subtype bytes preserved by SQLite might increase
** in future releases of SQLite.
**
** Every [application-defined SQL function] that invokes this interface
** should include the [SQLITE_RESULT_SUBTYPE] property in its
** text encoding argument when the SQL function is
** [sqlite3_create_function|registered]. If the [SQLITE_RESULT_SUBTYPE]
** property is omitted from the function that invokes sqlite3_result_subtype(),
** then in some cases the sqlite3_result_subtype() might fail to set
** the result subtype.
**
** If SQLite is compiled with -DSQLITE_STRICT_SUBTYPE=1, then any
** SQL function that invokes the sqlite3_result_subtype() interface
** and that does not have the SQLITE_RESULT_SUBTYPE property will raise
** an error. Future versions of SQLite might enable -DSQLITE_STRICT_SUBTYPE=1
** by default.
*/
SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int);
@@ -17811,14 +17854,15 @@ struct FuncDestructor {
#define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a
** single query - might change over time */
#define SQLITE_FUNC_TEST 0x4000 /* Built-in testing functions */
/* 0x8000 -- available for reuse */
#define SQLITE_FUNC_RUNONLY 0x8000 /* Cannot be used by valueFromFunction */
#define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */
#define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */
#define SQLITE_FUNC_DIRECT 0x00080000 /* Not for use in TRIGGERs or VIEWs */
#define SQLITE_FUNC_SUBTYPE 0x00100000 /* Result likely to have sub-type */
/* SQLITE_SUBTYPE 0x00100000 // Consumer of subtypes */
#define SQLITE_FUNC_UNSAFE 0x00200000 /* Function has side effects */
#define SQLITE_FUNC_INLINE 0x00400000 /* Functions implemented in-line */
#define SQLITE_FUNC_BUILTIN 0x00800000 /* This is a built-in function */
/* SQLITE_RESULT_SUBTYPE 0x01000000 // Generator of subtypes */
#define SQLITE_FUNC_ANYORDER 0x08000000 /* count/min/max aggregate */
/* Identifier numbers for each in-line function */
@@ -17910,9 +17954,10 @@ struct FuncDestructor {
#define MFUNCTION(zName, nArg, xPtr, xFunc) \
{nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \
xPtr, 0, xFunc, 0, 0, 0, #zName, {0} }
#define JFUNCTION(zName, nArg, iArg, xFunc) \
{nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|\
SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \
#define JFUNCTION(zName, nArg, bUseCache, bWS, bRS, iArg, xFunc) \
{nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|SQLITE_FUNC_CONSTANT|\
SQLITE_UTF8|((bUseCache)*SQLITE_FUNC_RUNONLY)|\
((bRS)*SQLITE_SUBTYPE)|((bWS)*SQLITE_RESULT_SUBTYPE), \
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
#define INLINE_FUNC(zName, nArg, iArg, mFlags) \
{nArg, SQLITE_FUNC_BUILTIN|\
@@ -29453,7 +29498,7 @@ SQLITE_PRIVATE void sqlite3MemoryBarrier(void){
SQLITE_MEMORY_BARRIER;
#elif defined(__GNUC__)
__sync_synchronize();
#elif MSVC_VERSION>=1300
#elif MSVC_VERSION>=1400
_ReadWriteBarrier();
#elif defined(MemoryBarrier)
MemoryBarrier();
@@ -61447,10 +61492,13 @@ act_like_temp_file:
*/
SQLITE_API sqlite3_file *sqlite3_database_file_object(const char *zName){
Pager *pPager;
const char *p;
while( zName[-1]!=0 || zName[-2]!=0 || zName[-3]!=0 || zName[-4]!=0 ){
zName--;
}
pPager = *(Pager**)(zName - 4 - sizeof(Pager*));
p = zName - 4 - sizeof(Pager*);
assert( EIGHT_BYTE_ALIGNMENT(p) );
pPager = *(Pager**)p;
return pPager->fd;
}
@@ -83411,7 +83459,7 @@ static int valueFromFunction(
#endif
assert( pFunc );
if( (pFunc->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0
|| (pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)
|| (pFunc->funcFlags & (SQLITE_FUNC_NEEDCOLL|SQLITE_FUNC_RUNONLY))!=0
){
return SQLITE_OK;
}
@@ -89952,6 +90000,18 @@ SQLITE_API void sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubt
#ifdef SQLITE_ENABLE_API_ARMOR
if( pCtx==0 ) return;
#endif
#if defined(SQLITE_STRICT_SUBTYPE) && SQLITE_STRICT_SUBTYPE+0!=0
if( pCtx->pFunc!=0
&& (pCtx->pFunc->funcFlags & SQLITE_RESULT_SUBTYPE)==0
){
char zErr[200];
sqlite3_snprintf(sizeof(zErr), zErr,
"misuse of sqlite3_result_subtype() by %s()",
pCtx->pFunc->zName);
sqlite3_result_error(pCtx, zErr, -1);
return;
}
#endif /* SQLITE_STRICT_SUBTYPE */
pOut = pCtx->pOut;
assert( sqlite3_mutex_held(pOut->db->mutex) );
pOut->eSubtype = eSubtype & 0xff;
@@ -100321,7 +100381,7 @@ case OP_VCheck: { /* out2 */
pTab = pOp->p4.pTab;
assert( pTab!=0 );
assert( IsVirtual(pTab) );
assert( pTab->u.vtab.p!=0 );
if( pTab->u.vtab.p==0 ) break;
pVtab = pTab->u.vtab.p->pVtab;
assert( pVtab!=0 );
pModule = pVtab->pModule;
@@ -113917,8 +113977,8 @@ SQLITE_PRIVATE int sqlite3ExprListCompare(const ExprList *pA, const ExprList *pB
*/
SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr *pA,Expr *pB, int iTab){
return sqlite3ExprCompare(0,
sqlite3ExprSkipCollateAndLikely(pA),
sqlite3ExprSkipCollateAndLikely(pB),
sqlite3ExprSkipCollate(pA),
sqlite3ExprSkipCollate(pB),
iTab);
}
@@ -147605,10 +147665,11 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
SrcList *pTabList;
SrcItem *pFrom;
assert( p->selFlags & SF_Resolved );
if( p->selFlags & SF_HasTypeInfo ) return;
p->selFlags |= SF_HasTypeInfo;
pParse = pWalker->pParse;
testcase( (p->selFlags & SF_Resolved)==0 );
assert( (p->selFlags & SF_Resolved) || IN_RENAME_OBJECT );
pTabList = p->pSrc;
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
Table *pTab = pFrom->pTab;
@@ -148630,6 +148691,7 @@ SQLITE_PRIVATE int sqlite3Select(
TREETRACE(0x1000,pParse,p,
("LEFT-JOIN simplifies to JOIN on term %d\n",i));
pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER);
unsetJoinExpr(p->pWhere, pItem->iCursor, 0);
}
}
if( pItem->fg.jointype & JT_LTORJ ){
@@ -148644,17 +148706,15 @@ SQLITE_PRIVATE int sqlite3Select(
TREETRACE(0x1000,pParse,p,
("RIGHT-JOIN simplifies to JOIN on term %d\n",j));
pI2->fg.jointype &= ~(JT_RIGHT|JT_OUTER);
unsetJoinExpr(p->pWhere, pI2->iCursor, 1);
}
}
}
for(j=pTabList->nSrc-1; j>=i; j--){
for(j=pTabList->nSrc-1; j>=0; j--){
pTabList->a[j].fg.jointype &= ~JT_LTORJ;
if( pTabList->a[j].fg.jointype & JT_RIGHT ) break;
}
}
assert( pItem->iCursor>=0 );
unsetJoinExpr(p->pWhere, pItem->iCursor,
pTabList->a[0].fg.jointype & JT_LTORJ);
}
/* No further action if this term of the FROM clause is not a subquery */
@@ -166058,6 +166118,20 @@ static SQLITE_NOINLINE void whereAddIndexedExpr(
continue;
}
if( sqlite3ExprIsConstant(pExpr) ) continue;
if( pExpr->op==TK_FUNCTION ){
/* Functions that might set a subtype should not be replaced by the
** value taken from an expression index since the index omits the
** subtype. https://sqlite.org/forum/forumpost/68d284c86b082c3e */
int n;
FuncDef *pDef;
sqlite3 *db = pParse->db;
assert( ExprUseXList(pExpr) );
n = pExpr->x.pList ? pExpr->x.pList->nExpr : 0;
pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0);
if( pDef==0 || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){
continue;
}
}
p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr));
if( p==0 ) break;
p->pIENext = pParse->pIdxEpr;
@@ -168240,7 +168314,7 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
assert( ExprUseXList(pWin->pOwner) );
assert( pWin->pWFunc!=0 );
pArgs = pWin->pOwner->x.pList;
if( pWin->pWFunc->funcFlags & SQLITE_FUNC_SUBTYPE ){
if( pWin->pWFunc->funcFlags & SQLITE_SUBTYPE ){
selectWindowRewriteEList(pParse, pMWin, pSrc, pArgs, pTab, &pSublist);
pWin->iArgCol = (pSublist ? pSublist->nExpr : 0);
pWin->bExprArgs = 1;
@@ -179412,7 +179486,7 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
assert( SQLITE_FUNC_CONSTANT==SQLITE_DETERMINISTIC );
assert( SQLITE_FUNC_DIRECT==SQLITE_DIRECTONLY );
extraFlags = enc & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY|
SQLITE_SUBTYPE|SQLITE_INNOCUOUS);
SQLITE_SUBTYPE|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE);
enc &= (SQLITE_FUNC_ENCMASK|SQLITE_ANY);
/* The SQLITE_INNOCUOUS flag is the same bit as SQLITE_FUNC_UNSAFE. But
@@ -202993,13 +203067,19 @@ static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){
zIn++;
N -= 2;
while( N>0 ){
for(i=0; i<N && zIn[i]!='\\'; i++){}
for(i=0; i<N && zIn[i]!='\\' && zIn[i]!='"'; i++){}
if( i>0 ){
jsonAppendRawNZ(p, zIn, i);
zIn += i;
N -= i;
if( N==0 ) break;
}
if( zIn[0]=='"' ){
jsonAppendRawNZ(p, "\\\"", 2);
zIn++;
N--;
continue;
}
assert( zIn[0]=='\\' );
switch( (u8)zIn[1] ){
case '\'':
@@ -203394,7 +203474,8 @@ static void jsonReturnJson(
JsonParse *pParse, /* The complete JSON */
JsonNode *pNode, /* Node to return */
sqlite3_context *pCtx, /* Return value for this function */
int bGenerateAlt /* Also store the rendered text in zAlt */
int bGenerateAlt, /* Also store the rendered text in zAlt */
int omitSubtype /* Do not call sqlite3_result_subtype() */
){
JsonString s;
if( pParse->oom ){
@@ -203409,7 +203490,7 @@ static void jsonReturnJson(
pParse->nAlt = s.nUsed;
}
jsonResult(&s);
sqlite3_result_subtype(pCtx, JSON_SUBTYPE);
if( !omitSubtype ) sqlite3_result_subtype(pCtx, JSON_SUBTYPE);
}
}
@@ -203450,7 +203531,8 @@ static u32 jsonHexToInt4(const char *z){
static void jsonReturn(
JsonParse *pParse, /* Complete JSON parse tree */
JsonNode *pNode, /* Node to return */
sqlite3_context *pCtx /* Return value for this function */
sqlite3_context *pCtx, /* Return value for this function */
int omitSubtype /* Do not call sqlite3_result_subtype() */
){
switch( pNode->eType ){
default: {
@@ -203596,7 +203678,7 @@ static void jsonReturn(
}
case JSON_ARRAY:
case JSON_OBJECT: {
jsonReturnJson(pParse, pNode, pCtx, 0);
jsonReturnJson(pParse, pNode, pCtx, 0, omitSubtype);
break;
}
}
@@ -204948,7 +205030,7 @@ static void jsonParseFunc(
printf("iSubst = %u\n", p->iSubst);
printf("iHold = %u\n", p->iHold);
jsonDebugPrintNodeEntries(p->aNode, p->nNode);
jsonReturnJson(p, p->aNode, ctx, 1);
jsonReturnJson(p, p->aNode, ctx, 1, 0);
}
/*
@@ -205134,15 +205216,14 @@ static void jsonExtractFunc(
}
if( pNode ){
if( flags & JSON_JSON ){
jsonReturnJson(p, pNode, ctx, 0);
jsonReturnJson(p, pNode, ctx, 0, 0);
}else{
jsonReturn(p, pNode, ctx);
sqlite3_result_subtype(ctx, 0);
jsonReturn(p, pNode, ctx, 1);
}
}
}else{
pNode = jsonLookup(p, zPath, 0, ctx);
if( p->nErr==0 && pNode ) jsonReturn(p, pNode, ctx);
if( p->nErr==0 && pNode ) jsonReturn(p, pNode, ctx, 0);
}
}else{
/* Two or more PATH arguments results in a JSON array with each
@@ -205268,7 +205349,7 @@ static void jsonPatchFunc(
if( pResult && pX->oom==0 ){
jsonDebugPrintParse(pX);
jsonDebugPrintNode(pResult);
jsonReturnJson(pX, pResult, ctx, 0);
jsonReturnJson(pX, pResult, ctx, 0, 0);
}else{
sqlite3_result_error_nomem(ctx);
}
@@ -205347,7 +205428,7 @@ static void jsonRemoveFunc(
}
}
if( (pParse->aNode[0].jnFlags & JNODE_REMOVE)==0 ){
jsonReturnJson(pParse, pParse->aNode, ctx, 1);
jsonReturnJson(pParse, pParse->aNode, ctx, 1, 0);
}
remove_done:
jsonDebugPrintParse(p);
@@ -205476,7 +205557,7 @@ static void jsonReplaceFunc(
jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]);
}
}
jsonReturnJson(pParse, pParse->aNode, ctx, 1);
jsonReturnJson(pParse, pParse->aNode, ctx, 1, 0);
replace_err:
jsonDebugPrintParse(pParse);
jsonParseFree(pParse);
@@ -205530,7 +205611,7 @@ static void jsonSetFunc(
}
}
jsonDebugPrintParse(pParse);
jsonReturnJson(pParse, pParse->aNode, ctx, 1);
jsonReturnJson(pParse, pParse->aNode, ctx, 1, 0);
jsonSetDone:
jsonParseFree(pParse);
}
@@ -206045,7 +206126,7 @@ static int jsonEachColumn(
case JEACH_KEY: {
if( p->i==0 ) break;
if( p->eType==JSON_OBJECT ){
jsonReturn(&p->sParse, pThis, ctx);
jsonReturn(&p->sParse, pThis, ctx, 0);
}else if( p->eType==JSON_ARRAY ){
u32 iKey;
if( p->bRecursive ){
@@ -206061,7 +206142,7 @@ static int jsonEachColumn(
}
case JEACH_VALUE: {
if( pThis->jnFlags & JNODE_LABEL ) pThis++;
jsonReturn(&p->sParse, pThis, ctx);
jsonReturn(&p->sParse, pThis, ctx, 0);
break;
}
case JEACH_TYPE: {
@@ -206072,7 +206153,7 @@ static int jsonEachColumn(
case JEACH_ATOM: {
if( pThis->jnFlags & JNODE_LABEL ) pThis++;
if( pThis->eType>=JSON_ARRAY ) break;
jsonReturn(&p->sParse, pThis, ctx);
jsonReturn(&p->sParse, pThis, ctx, 0);
break;
}
case JEACH_ID: {
@@ -206365,34 +206446,43 @@ static sqlite3_module jsonTreeModule = {
SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void){
#ifndef SQLITE_OMIT_JSON
static FuncDef aJsonFunc[] = {
JFUNCTION(json, 1, 0, jsonRemoveFunc),
JFUNCTION(json_array, -1, 0, jsonArrayFunc),
JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc),
JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc),
JFUNCTION(json_error_position,1, 0, jsonErrorFunc),
JFUNCTION(json_extract, -1, 0, jsonExtractFunc),
JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc),
JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc),
JFUNCTION(json_insert, -1, 0, jsonSetFunc),
JFUNCTION(json_object, -1, 0, jsonObjectFunc),
JFUNCTION(json_patch, 2, 0, jsonPatchFunc),
JFUNCTION(json_quote, 1, 0, jsonQuoteFunc),
JFUNCTION(json_remove, -1, 0, jsonRemoveFunc),
JFUNCTION(json_replace, -1, 0, jsonReplaceFunc),
JFUNCTION(json_set, -1, JSON_ISSET, jsonSetFunc),
JFUNCTION(json_type, 1, 0, jsonTypeFunc),
JFUNCTION(json_type, 2, 0, jsonTypeFunc),
JFUNCTION(json_valid, 1, 0, jsonValidFunc),
#if SQLITE_DEBUG
JFUNCTION(json_parse, 1, 0, jsonParseFunc),
JFUNCTION(json_test1, 1, 0, jsonTest1Func),
/* calls sqlite3_result_subtype() */
/* | */
/* Uses cache ______ | __ calls sqlite3_value_subtype() */
/* | | | */
/* Num args _________ | | | ___ Flags */
/* | | | | | */
/* | | | | | */
JFUNCTION(json, 1, 1, 1, 0, 0, jsonRemoveFunc),
JFUNCTION(json_array, -1, 0, 1, 1, 0, jsonArrayFunc),
JFUNCTION(json_array_length, 1, 1, 0, 0, 0, jsonArrayLengthFunc),
JFUNCTION(json_array_length, 2, 1, 0, 0, 0, jsonArrayLengthFunc),
JFUNCTION(json_error_position,1, 1, 0, 0, 0, jsonErrorFunc),
JFUNCTION(json_extract, -1, 1, 1, 0, 0, jsonExtractFunc),
JFUNCTION(->, 2, 1, 1, 0, JSON_JSON, jsonExtractFunc),
JFUNCTION(->>, 2, 1, 0, 0, JSON_SQL, jsonExtractFunc),
JFUNCTION(json_insert, -1, 1, 1, 1, 0, jsonSetFunc),
JFUNCTION(json_object, -1, 0, 1, 1, 0, jsonObjectFunc),
JFUNCTION(json_patch, 2, 1, 1, 0, 0, jsonPatchFunc),
JFUNCTION(json_quote, 1, 0, 1, 1, 0, jsonQuoteFunc),
JFUNCTION(json_remove, -1, 1, 1, 0, 0, jsonRemoveFunc),
JFUNCTION(json_replace, -1, 1, 1, 1, 0, jsonReplaceFunc),
JFUNCTION(json_set, -1, 1, 1, 1, JSON_ISSET, jsonSetFunc),
JFUNCTION(json_type, 1, 1, 0, 0, 0, jsonTypeFunc),
JFUNCTION(json_type, 2, 1, 0, 0, 0, jsonTypeFunc),
JFUNCTION(json_valid, 1, 1, 0, 0, 0, jsonValidFunc),
#ifdef SQLITE_DEBUG
JFUNCTION(json_parse, 1, 1, 1, 0, 0, jsonParseFunc),
JFUNCTION(json_test1, 1, 1, 0, 1, 0, jsonTest1Func),
#endif
WAGGREGATE(json_group_array, 1, 0, 0,
jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse,
SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|
SQLITE_DETERMINISTIC),
WAGGREGATE(json_group_object, 2, 0, 0,
jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse,
SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC)
SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|
SQLITE_DETERMINISTIC)
};
sqlite3InsertBuiltinFuncs(aJsonFunc, ArraySize(aJsonFunc));
#endif
@@ -236129,10 +236219,8 @@ static Fts5HashEntry *fts5HashEntryMerge(
}
/*
** Extract all tokens from hash table iHash and link them into a list
** in sorted order. The hash table is cleared before returning. It is
** the responsibility of the caller to free the elements of the returned
** list.
** Link all tokens from hash table iHash into a list in sorted order. The
** tokens are not removed from the hash table.
*/
static int fts5HashEntrySort(
Fts5Hash *pHash,
@@ -238998,6 +239086,14 @@ static void fts5SegIterHashInit(
pLeaf->p = (u8*)pList;
}
}
/* The call to sqlite3Fts5HashScanInit() causes the hash table to
** fill the size field of all existing position lists. This means they
** can no longer be appended to. Since the only scenario in which they
** can be appended to is if the previous operation on this table was
** a DELETE, by clearing the Fts5Index.bDelete flag we can avoid this
** possibility altogether. */
p->bDelete = 0;
}else{
p->rc = sqlite3Fts5HashQuery(p->pHash, sizeof(Fts5Data),
(const char*)pTerm, nTerm, (void**)&pLeaf, &nList
@@ -240675,7 +240771,7 @@ static void fts5WriteAppendPoslistData(
const u8 *a = aData;
int n = nData;
assert( p->pConfig->pgsz>0 );
assert( p->pConfig->pgsz>0 || p->rc!=SQLITE_OK );
while( p->rc==SQLITE_OK
&& (pPage->buf.n + pPage->pgidx.n + n)>=p->pConfig->pgsz
){
@@ -241935,8 +242031,9 @@ static int sqlite3Fts5IndexOptimize(Fts5Index *p){
assert( p->rc==SQLITE_OK );
fts5IndexFlush(p);
assert( p->nContentlessDelete==0 );
assert( p->rc!=SQLITE_OK || p->nContentlessDelete==0 );
pStruct = fts5StructureRead(p);
assert( p->rc!=SQLITE_OK || pStruct!=0 );
fts5StructureInvalidate(p);
if( pStruct ){
@@ -247513,7 +247610,7 @@ static void fts5SourceIdFunc(
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
sqlite3_result_text(pCtx, "fts5: 2023-11-01 11:23:50 17129ba1ff7f0daf37100ee82d507aef7827cf38de1866e2633096ae6ad81301", -1, SQLITE_TRANSIENT);
sqlite3_result_text(pCtx, "fts5: 2023-11-22 14:18:12 d295f48e8f367b066b881780c98bdf980a1d550397d5ba0b0e49842c95b3e8b4", -1, SQLITE_TRANSIENT);
}
/*
+55 -12
View File
@@ -146,9 +146,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION "3.44.0"
#define SQLITE_VERSION_NUMBER 3044000
#define SQLITE_SOURCE_ID "2023-11-01 11:23:50 17129ba1ff7f0daf37100ee82d507aef7827cf38de1866e2633096ae6ad81301"
#define SQLITE_VERSION "3.44.1"
#define SQLITE_VERSION_NUMBER 3044001
#define SQLITE_SOURCE_ID "2023-11-22 14:18:12 d295f48e8f367b066b881780c98bdf980a1d550397d5ba0b0e49842c95b3e8b4"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -5573,13 +5573,27 @@ SQLITE_API int sqlite3_create_window_function(
** </dd>
**
** [[SQLITE_SUBTYPE]] <dt>SQLITE_SUBTYPE</dt><dd>
** The SQLITE_SUBTYPE flag indicates to SQLite that a function may call
** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call
** [sqlite3_value_subtype()] to inspect the sub-types of its arguments.
** Specifying this flag makes no difference for scalar or aggregate user
** functions. However, if it is not specified for a user-defined window
** function, then any sub-types belonging to arguments passed to the window
** function may be discarded before the window function is called (i.e.
** sqlite3_value_subtype() will always return 0).
** This flag instructs SQLite to omit some corner-case optimizations that
** might disrupt the operation of the [sqlite3_value_subtype()] function,
** causing it to return zero rather than the correct subtype().
** SQL functions that invokes [sqlite3_value_subtype()] should have this
** property. If the SQLITE_SUBTYPE property is omitted, then the return
** value from [sqlite3_value_subtype()] might sometimes be zero even though
** a non-zero subtype was specified by the function argument expression.
**
** [[SQLITE_RESULT_SUBTYPE]] <dt>SQLITE_RESULT_SUBTYPE</dt><dd>
** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call
** [sqlite3_result_subtype()] to cause a sub-type to be associated with its
** result.
** Every function that invokes [sqlite3_result_subtype()] should have this
** property. If it does not, then the call to [sqlite3_result_subtype()]
** might become a no-op if the function is used as term in an
** [expression index]. On the other hand, SQL functions that never invoke
** [sqlite3_result_subtype()] should avoid setting this property, as the
** purpose of this property is to disable certain optimizations that are
** incompatible with subtypes.
** </dd>
** </dl>
*/
@@ -5587,6 +5601,7 @@ SQLITE_API int sqlite3_create_window_function(
#define SQLITE_DIRECTONLY 0x000080000
#define SQLITE_SUBTYPE 0x000100000
#define SQLITE_INNOCUOUS 0x000200000
#define SQLITE_RESULT_SUBTYPE 0x001000000
/*
** CAPI3REF: Deprecated Functions
@@ -5783,6 +5798,12 @@ SQLITE_API int sqlite3_value_encoding(sqlite3_value*);
** information can be used to pass a limited amount of context from
** one SQL function to another. Use the [sqlite3_result_subtype()]
** routine to set the subtype for the return value of an SQL function.
**
** Every [application-defined SQL function] that invoke this interface
** should include the [SQLITE_SUBTYPE] property in the text
** encoding argument when the function is [sqlite3_create_function|registered].
** If the [SQLITE_SUBTYPE] property is omitted, then sqlite3_value_subtype()
** might return zero instead of the upstream subtype in some corner cases.
*/
SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*);
@@ -5913,14 +5934,22 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
** <li> ^(when sqlite3_set_auxdata() is invoked again on the same
** parameter)^, or
** <li> ^(during the original sqlite3_set_auxdata() call when a memory
** allocation error occurs.)^ </ul>
** allocation error occurs.)^
** <li> ^(during the original sqlite3_set_auxdata() call if the function
** is evaluated during query planning instead of during query execution,
** as sometimes happens with [SQLITE_ENABLE_STAT4].)^ </ul>
**
** Note the last bullet in particular. The destructor X in
** Note the last two bullets in particular. The destructor X in
** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the
** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata()
** should be called near the end of the function implementation and the
** function implementation should not make any use of P after
** sqlite3_set_auxdata() has been called.
** sqlite3_set_auxdata() has been called. Furthermore, a call to
** sqlite3_get_auxdata() that occurs immediately after a corresponding call
** to sqlite3_set_auxdata() might still return NULL if an out-of-memory
** condition occurred during the sqlite3_set_auxdata() call or if the
** function is being evaluated during query planning rather than during
** query execution.
**
** ^(In practice, auxiliary data is preserved between function calls for
** function parameters that are compile-time constants, including literal
@@ -6194,6 +6223,20 @@ SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n);
** higher order bits are discarded.
** The number of subtype bytes preserved by SQLite might increase
** in future releases of SQLite.
**
** Every [application-defined SQL function] that invokes this interface
** should include the [SQLITE_RESULT_SUBTYPE] property in its
** text encoding argument when the SQL function is
** [sqlite3_create_function|registered]. If the [SQLITE_RESULT_SUBTYPE]
** property is omitted from the function that invokes sqlite3_result_subtype(),
** then in some cases the sqlite3_result_subtype() might fail to set
** the result subtype.
**
** If SQLite is compiled with -DSQLITE_STRICT_SUBTYPE=1, then any
** SQL function that invokes the sqlite3_result_subtype() interface
** and that does not have the SQLITE_RESULT_SUBTYPE property will raise
** an error. Future versions of SQLite might enable -DSQLITE_STRICT_SUBTYPE=1
** by default.
*/
SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int);
+1 -1
View File
@@ -205,7 +205,7 @@ RESOLVFILE
/* Pi-hole definitions */
#define HAVE_LUASCRIPT
#define HAVE_IDN
#define HAVE_LIBIDN2
#define HAVE_DNSSEC
#ifdef DNSMASQ_ALL_OPTS
#define HAVE_DBUS
+2 -1
View File
@@ -77,7 +77,8 @@ int main (int argc, char *argv[])
// Process pihole.toml configuration file
// The file is rewritten after parsing to ensure that all
// settings are present and have a valid value
readFTLconf(&config, true);
if(readFTLconf(&config, true))
log_info("Parsed config file "GLOBALTOMLPATH" successfully");
// Set process priority
set_nice();
+66 -3
View File
@@ -19,6 +19,13 @@ char ** setupVarsArray = NULL;
static void get_conf_string_from_setupVars(const char *key, struct conf_item *conf_item)
{
// Verify we are allowed to use this function
if(conf_item->t != CONF_STRING && conf_item->t != CONF_STRING_ALLOCATED)
{
log_err("get_conf_string_from_setupVars(%s) failed: conf_item->t is neither CONF_STRING nor CONF_STRING_ALLOCATED", key);
return;
}
const char *setupVarsValue = read_setupVarsconf(key);
if(setupVarsValue == NULL)
{
@@ -44,8 +51,50 @@ static void get_conf_string_from_setupVars(const char *key, struct conf_item *co
log_debug(DEBUG_CONFIG, "setupVars.conf:%s -> Setting %s to %s", key, conf_item->k, conf_item->v.s);
}
static void get_conf_ipv4_from_setupVars(const char *key, struct conf_item *conf_item)
{
// Verify we are allowed to use this function
if(conf_item->t != CONF_STRUCT_IN_ADDR)
{
log_err("get_conf_ipv4_from_setupVars(%s) failed: conf_item->t != CONF_STRUCT_IN_ADDR", key);
return;
}
const char *setupVarsValue = read_setupVarsconf(key);
if(setupVarsValue == NULL)
{
// Do not change default value, this value is not set in setupVars.conf
log_debug(DEBUG_CONFIG, "setupVars.conf:%s -> Not set", key);
// Free memory, harmless to call if read_setupVarsconf() didn't return a result
clearSetupVarsArray();
return;
}
if(strlen(setupVarsValue) == 0)
memset(&conf_item->v.in_addr, 0, sizeof(struct in_addr));
else if(inet_pton(AF_INET, setupVarsValue, &conf_item->v.in_addr) != 1)
{
log_debug(DEBUG_CONFIG, "setupVars.conf:%s -> Invalid IPv4 address: %s", key, setupVarsValue);
memset(&conf_item->v.in_addr, 0, sizeof(struct in_addr));
}
// Free memory, harmless to call if read_setupVarsconf() didn't return a result
clearSetupVarsArray();
// Parameter present in setupVars.conf
log_debug(DEBUG_CONFIG, "setupVars.conf:%s -> Setting %s to %s", key, conf_item->k, inet_ntoa(conf_item->v.in_addr));
}
static void get_conf_bool_from_setupVars(const char *key, struct conf_item *conf_item)
{
// Verify we are allowed to use this function
if(conf_item->t != CONF_BOOL)
{
log_err("get_conf_bool_from_setupVars(%s) failed: conf_item->t != CONF_BOOL", key);
return;
}
const char *boolean = read_setupVarsconf(key);
if(boolean == NULL)
@@ -73,6 +122,13 @@ static void get_conf_bool_from_setupVars(const char *key, struct conf_item *conf
static void get_conf_string_array_from_setupVars(const char *key, struct conf_item *conf_item)
{
// Verify we are allowed to use this function
if(conf_item->t != CONF_JSON_STRING_ARRAY)
{
log_err("get_conf_string_array_from_setupVars(%s) failed: conf_item->t != CONF_JSON_STRING_ARRAY", key);
return;
}
// Get clients which the user doesn't want to see
const char *array = read_setupVarsconf(key);
@@ -96,6 +152,13 @@ static void get_conf_string_array_from_setupVars(const char *key, struct conf_it
static void get_conf_upstream_servers_from_setupVars(struct conf_item *conf_item)
{
// Verify we are allowed to use this function
if(conf_item->t != CONF_JSON_STRING_ARRAY)
{
log_err("get_conf_upstream_servers_from_setupVars() failed: conf_item->t != CONF_JSON_STRING_ARRAY");
return;
}
// Try to import up to 50 servers...
#define MAX_SERVERS 50
for(unsigned int j = 0; j < MAX_SERVERS; j++)
@@ -363,9 +426,9 @@ void importsetupVarsConf(void)
// Try to obtain DHCP settings
get_conf_bool_from_setupVars("DHCP_ACTIVE", &config.dhcp.active);
get_conf_string_from_setupVars("DHCP_START", &config.dhcp.start);
get_conf_string_from_setupVars("DHCP_END", &config.dhcp.end);
get_conf_string_from_setupVars("DHCP_ROUTER", &config.dhcp.router);
get_conf_ipv4_from_setupVars("DHCP_START", &config.dhcp.start);
get_conf_ipv4_from_setupVars("DHCP_END", &config.dhcp.end);
get_conf_ipv4_from_setupVars("DHCP_ROUTER", &config.dhcp.router);
get_conf_string_from_setupVars("DHCP_LEASETIME", &config.dhcp.leaseTime);
// If the DHCP lease time is set to "24", it is interpreted as "24h".
+1
View File
@@ -25,6 +25,7 @@ set(sources
snprintf.c
sprintf.c
strdup.c
string.c
syscalls.h
vasprintf.c
vfprintf.c
+251
View File
@@ -0,0 +1,251 @@
/* Pi-hole: A black hole for Internet advertisements
* (c) 2023 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* FTL Engine
* Pi-hole syscall implementation for string-related functions
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
#include "../FTL.h"
//#include "syscalls.h" is implicitly done in FTL.h
#include "../log.h"
#undef strlen
size_t FTLstrlen(const char *s, const char *file, const char *func, const int line)
{
// The strlen() function calculates the length of the string s, not
// including the terminating '\0' character.
if(s == NULL)
{
log_err("Trying to get the length of a NULL string in %s() (%s:%i)", func, file, line);
return 0;
}
return strlen(s);
}
#undef strnlen
size_t FTLstrnlen(const char *s, const size_t maxlen, const char *file, const char *func, const int line)
{
// The strnlen() function returns the number of characters in the string s,
// not including the terminating '\0' character, but at most maxlen. In
// doing this, strnlen() looks only at the first maxlen characters at s and
// never beyond s+maxlen.
if(s == NULL)
{
log_err("Trying to get the length of a NULL string in %s() (%s:%i)", func, file, line);
return 0;
}
return strnlen(s, maxlen);
}
#undef strstr
char *FTLstrstr(const char *haystack, const char *needle, const char *file, const char *func, const int line)
{
// The strstr() function finds the first occurrence of the substring needle
// in the string haystack. The terminating '\0' characters are not
// compared.
if(haystack == NULL || needle == NULL)
{
log_err("Trying to find a NULL (%s%s) string in %s() (%s:%i)",
haystack == NULL ? "L" : "", needle == NULL ? "R" : "", func, file, line);
return NULL;
}
return strstr(haystack, needle);
}
#undef strcmp
int FTLstrcmp(const char *s1, const char *s2, const char *file, const char *func, const int line)
{
// The strcmp() function compares the two strings s1 and s2. It returns an
// integer less than, equal to, or greater than zero if s1 is found,
// respectively, to be less than, to match, or be greater than s2.
if(s1 == NULL || s2 == NULL)
{
log_err("Trying to compare a NULL (%s%s) string in %s() (%s:%i)",
s1 == NULL ? "L" : "", s2 == NULL ? "R" : "", func, file, line);
return -1;
}
return strcmp(s1, s2);
}
#undef strncmp
int FTLstrncmp(const char *s1, const char *s2, const size_t n, const char *file, const char *func, const int line)
{
// The strncmp() function is similar, except it compares only the first (at
// most) n bytes of s1 and s2.
if(s1 == NULL || s2 == NULL)
{
log_err("Trying to compare a NULL (%s%s) string in %s() (%s:%i)",
s1 == NULL ? "L" : "", s2 == NULL ? "R" : "", func, file, line);
return -1;
}
return strncmp(s1, s2, n);
}
#undef strcasecmp
int FTLstrcasecmp(const char *s1, const char *s2, const char *file, const char *func, const int line)
{
// The strcasecmp() function performs a byte-by-byte comparison of the
// strings s1 and s2, ignoring the case of the characters. It returns an
// integer less than, equal to, or greater than zero if s1 is found,
// respectively, to be less than, to match, or be greater than s2.
if(s1 == NULL || s2 == NULL)
{
log_err("Trying to compare a NULL (%s%s) string in %s() (%s:%i)",
s1 == NULL ? "L" : "", s2 == NULL ? "R" : "", func, file, line);
return -1;
}
return strcasecmp(s1, s2);
}
#undef strncasecmp
int FTLstrncasecmp(const char *s1, const char *s2, const size_t n, const char *file, const char *func, const int line)
{
// The strncasecmp() function is similar, except it compares only the first
// (at most) n bytes of s1 and s2.
if(s1 == NULL || s2 == NULL)
{
log_err("Trying to compare a NULL (%s%s) string in %s() (%s:%i)",
s1 == NULL ? "L" : "", s2 == NULL ? "R" : "", func, file, line);
return -1;
}
return strncasecmp(s1, s2, n);
}
#undef strcat
char *FTLstrcat(char *dest, const char *src, const char *file, const char *func, const int line)
{
// The strcat() function appends the src string to the dest string,
// overwriting the terminating null byte ('\0') at the end of dest, and then
// adds a terminating null byte. The strings may not overlap, and the dest
// string must have enough space for the result. If dest is not large enough,
// program behavior is unpredictable; buffer overruns are a favorite avenue
// for attacking secure programs.
if(dest == NULL || src == NULL)
{
log_err("Trying to concatenate a NULL (%s%s) string in %s() (%s:%i)",
dest == NULL ? "L" : "", src == NULL ? "R" : "", func, file, line);
return NULL;
}
return strcat(dest, src);
}
#undef strncat
char *FTLstrncat(char *dest, const char *src, const size_t n, const char *file, const char *func, const int line)
{
// The strncat() function is similar, except that it will use at most n bytes
// from src; and src does not need to be null-terminated if it contains n or
// more bytes.
if(dest == NULL || src == NULL)
{
log_err("Trying to concatenate a NULL (%s%s) string in %s() (%s:%i)",
dest == NULL ? "L" : "", src == NULL ? "R" : "", func, file, line);
return NULL;
}
return strncat(dest, src, n);
}
#undef strcpy
char *FTLstrcpy(char *dest, const char *src, const char *file, const char *func, const int line)
{
// The strcpy() function copies the string src to dest (including the
// terminating '\0' character.)
if(dest == NULL || src == NULL)
{
log_err("Trying to copy a NULL (%s%s) string in %s() (%s:%i)",
dest == NULL ? "L" : "", src == NULL ? "R" : "", func, file, line);
return NULL;
}
return strcpy(dest, src);
}
#undef strncpy
char *FTLstrncpy(char *dest, const char *src, const size_t n, const char *file, const char *func, const int line)
{
// The strncpy() function is similar, except that at most n bytes of src are
// copied. Warning: If there is no null byte among the first n bytes of src,
// the string placed in dest will not be null-terminated.
if(dest == NULL || src == NULL)
{
log_err("Trying to copy a NULL (%s%s) string in %s() (%s:%i)",
dest == NULL ? "L" : "", src == NULL ? "R" : "", func, file, line);
return NULL;
}
return strncpy(dest, src, n);
}
#undef memset
void *FTLmemset(void *s, const int c, const size_t n, const char *file, const char *func, const int line)
{
// The memset() function fills the first n bytes of the memory area pointed
// to by s with the constant byte c.
if(s == NULL)
{
log_err("Trying to fill a NULL memory area in %s() (%s:%i)", func, file, line);
return NULL;
}
return memset(s, c, n);
}
#undef memcpy
void *FTLmemcpy(void *dest, const void *src, const size_t n, const char *file, const char *func, const int line)
{
// The memcpy() function copies n bytes from memory area src to memory area
// dest. The memory areas must not overlap. Use memmove(3) if the memory
// areas do overlap.
if(dest == NULL || src == NULL)
{
log_err("Trying to copy a NULL (%s%s) memory area in %s() (%s:%i)",
dest == NULL ? "L" : "", src == NULL ? "R" : "", func, file, line);
return NULL;
}
return memcpy(dest, src, n);
}
#undef memmove
void *FTLmemmove(void *dest, const void *src, const size_t n, const char *file, const char *func, const int line)
{
// The memmove() function copies n bytes from memory area src to memory area
// dest. The memory areas may overlap: copying takes place as though the
// bytes in src are first copied into a temporary array that does not
// overlap src or dest, and the bytes are then copied from the temporary
// array to dest.
if(dest == NULL || src == NULL)
{
log_err("Trying to move a NULL (%s%s) memory area in %s() (%s:%i)",
dest == NULL ? "L" : "", src == NULL ? "R" : "", func, file, line);
return NULL;
}
return memmove(dest, src, n);
}
#undef memcmp
int FTLmemcmp(const void *s1, const void *s2, const size_t n, const char *file, const char *func, const int line)
{
// The memcmp() function compares the first n bytes (each interpreted as
// unsigned char) of the memory areas s1 and s2.
if(s1 == NULL || s2 == NULL)
{
log_err("Trying to compare a NULL (%s%s) memory area in %s() (%s:%i)",
s1 == NULL ? "L" : "", s2 == NULL ? "R" : "", func, file, line);
return -1;
}
return memcmp(s1, s2, n);
}
#undef memmem
void *FTLmemmem(const void *haystack, const size_t haystacklen, const void *needle, const size_t needlelen, const char *file, const char *func, const int line)
{
// The memmem() function finds the start of the first occurrence of the
// substring needle of length needlelen in the memory area haystack of
// length haystacklen.
if(haystack == NULL || needle == NULL)
{
log_err("Trying to find a NULL (%s%s) memory area in %s() (%s:%i)",
haystack == NULL ? "L" : "", needle == NULL ? "R" : "", func, file, line);
return NULL;
}
return memmem(haystack, haystacklen, needle, needlelen);
}
+18
View File
@@ -50,4 +50,22 @@ FILE *FTLfopen(const char *pathname, const char *mode, const char *file, const c
// Syscall helpers
void syscalls_report_error(const char *error, FILE *stream, const int _errno, const char *format, const char *func, const char *file, const int line);
// String-related functions
size_t FTLstrlen(const char *s, const char *file, const char *func, const int line);
size_t FTLstrnlen(const char *s, const size_t maxlen, const char *file, const char *func, const int line);
char *FTLstrcpy(char *dest, const char *src, const char *file, const char *func, const int line);
char *FTLstrncpy(char *dest, const char *src, const size_t n, const char *file, const char *func, const int line);
void *FTLmemset(void *s, const int c, const size_t n, const char *file, const char *func, const int line);
void *FTLmemcpy(void *dest, const void *src, const size_t n, const char *file, const char *func, const int line);
void *FTLmemmove(void *dest, const void *src, const size_t n, const char *file, const char *func, const int line);
char *FTLstrstr(const char *haystack, const char *needle, const char *file, const char *func, const int line);
int FTLstrcmp(const char *s1, const char *s2, const char *file, const char *func, const int line);
int FTLstrncmp(const char *s1, const char *s2, const size_t n, const char *file, const char *func, const int line);
int FTLstrcasecmp(const char *s1, const char *s2, const char *file, const char *func, const int line);
int FTLstrncasecmp(const char *s1, const char *s2, const size_t n, const char *file, const char *func, const int line);
char *FTLstrcat(char *dest, const char *src, const char *file, const char *func, const int line);
char *FTLstrncat(char *dest, const char *src, const size_t n, const char *file, const char *func, const int line);
int FTLmemcmp(const void *s1, const void *s2, const size_t n, const char *file, const char *func, const int line);
void *FTLmemmem(const void *haystack, const size_t haystacklen, const void *needle, const size_t needlelen, const char *file, const char *func, const int line);
#endif //SYSCALLS_H
+6 -6
View File
@@ -40,7 +40,7 @@ static const char *false_positives[] = {
#define MAX_INVALID_DOMAINS 5
// Validate domain name
static inline bool __attribute__((pure)) valid_domain(const char *domain, const size_t len, const bool abp)
inline bool __attribute__((pure)) valid_domain(const char *domain, const size_t len, const bool fqdn_only)
{
// Domain must not be NULL or empty, and they should not be longer than
// 255 characters
@@ -85,9 +85,9 @@ static inline bool __attribute__((pure)) valid_domain(const char *domain, const
// There must be at least two labels (i.e. one dot)
// e.g., "example.com" but not "localhost" for exact domain
// We do not enforce this for ABP domains
// We do not enforce this for ABP domains and domainlist input
// (see https://github.com/pi-hole/pi-hole/pull/5240)
if(last_dot == -1 && !abp)
if(last_dot == -1 && fqdn_only)
return false;
// TLD must not start or end with a hyphen
@@ -123,7 +123,7 @@ static inline bool __attribute__((pure)) valid_abp_domain(const char *line, cons
return false;
// Domain must be valid
return valid_domain(line+4, len-5, true);
return valid_domain(line+4, len-5, false);
}
else
{
@@ -140,7 +140,7 @@ static inline bool __attribute__((pure)) valid_abp_domain(const char *line, cons
return false;
// Domain must be valid
return valid_domain(line+2, len-3, true);
return valid_domain(line+2, len-3, false);
}
}
@@ -281,7 +281,7 @@ int gravity_parseList(const char *infile, const char *outfile, const char *adlis
// Validate line
if(line[0] != (antigravity ? '@' : '|') && // <- Not an ABP-style match
valid_domain(line, read, false))
valid_domain(line, read, true))
{
// Exact match found
if(checkOnly)
+1
View File
@@ -11,3 +11,4 @@
#include "FTL.h"
int gravity_parseList(const char *infile, const char *outfile, const char *adlistID, const bool checkOnly, const bool antigravity);
bool __attribute__((pure)) valid_domain(const char *domain, const size_t len, const bool fqdn_only);
+1 -3
View File
@@ -15307,9 +15307,7 @@ handle_request(struct mg_connection *conn)
if (!new_path) {
mg_send_http_error(conn, 500, "out or memory");
} else {
/* Pi-hole modification */
//mg_get_request_link(conn, new_path, buflen - 1);
strcpy(new_path, ri->local_uri_raw);
mg_get_request_link(conn, new_path, buflen - 1);
strcat(new_path, "/");
if (ri->query_string) {
/* Append ? and query string */
+1 -1
View File
@@ -304,13 +304,13 @@ void http_init(void)
"decode_url", "yes",
"enable_directory_listing", "no",
"num_threads", num_threads,
"authentication_domain", config.webserver.domain.v.s,
"additional_header", "Content-Security-Policy: default-src 'self' 'unsafe-inline';\r\n"
"X-Frame-Options: DENY\r\n"
"X-XSS-Protection: 0\r\n"
"X-Content-Type-Options: nosniff\r\n"
"Referrer-Policy: strict-origin-when-cross-origin",
"index_files", "index.html,index.htm,index.lp",
"enable_auth_domain_check", "no",
NULL, NULL,
NULL, NULL, // Leave slots for access control list (ACL) and TLS configuration at the end
NULL
+4 -4
View File
@@ -316,26 +316,26 @@ enum cert_check read_certificate(const char* certfile, const char *domain, const
mbedtls_entropy_init(&entropy);
mbedtls_ctr_drbg_init(&ctr_drbg);
printf("Reading certificate from %s ...\n\n", certfile);
log_info("Reading certificate from %s ...", certfile);
// Check if the file exists and is readable
if(access(certfile, R_OK) != 0)
{
log_err("Could not read certificate file: %s\n", strerror(errno));
log_err("Could not read certificate file: %s", strerror(errno));
return CERT_FILE_NOT_FOUND;
}
int rc = mbedtls_pk_parse_keyfile(&key, certfile, NULL, mbedtls_ctr_drbg_random, &ctr_drbg);
if (rc != 0)
{
log_err("Cannot parse key: Error code %d\n", rc);
log_err("Cannot parse key: Error code %d", rc);
return CERT_CANNOT_PARSE_KEY;
}
rc = mbedtls_x509_crt_parse_file(&crt, certfile);
if (rc != 0)
{
log_err("Cannot parse certificate: Error code %d\n", rc);
log_err("Cannot parse certificate: Error code %d", rc);
return CERT_CANNOT_PARSE_CERT;
}
+20 -9
View File
@@ -35,11 +35,7 @@
@test "dnsmasq options as expected" {
run bash -c './pihole-FTL -vv | grep "cryptohash"'
printf "%s\n" "${lines[@]}"
if [[ "${CI_ARCH}" == "x86_64_full" ]]; then
[[ ${lines[0]} == "Features: IPv6 GNU-getopt DBus no-UBus no-i18n IDN DHCP DHCPv6 Lua TFTP conntrack ipset nftset auth cryptohash DNSSEC loop-detect inotify dumpfile" ]]
else
[[ ${lines[0]} == "Features: IPv6 GNU-getopt no-DBus no-UBus no-i18n IDN DHCP DHCPv6 Lua TFTP no-conntrack ipset no-nftset auth cryptohash DNSSEC loop-detect inotify dumpfile" ]]
fi
[[ ${lines[0]} == "Features: IPv6 GNU-getopt no-DBus no-UBus no-i18n IDN2 DHCP DHCPv6 Lua TFTP no-conntrack ipset no-nftset auth cryptohash DNSSEC loop-detect inotify dumpfile" ]]
[[ ${lines[1]} == "" ]]
}
@@ -1262,7 +1258,7 @@
[[ "${lines[0]}" == "1.1.1.1" ]]
}
@test "Custom DNS records: International domains are converted to IDNA form" {
@test "Custom DNS records: International domains are converted to IDN form" {
# äste.com ---> xn--ste-pla.com
run bash -c "dig A xn--ste-pla.com +short @127.0.0.1"
printf "%s\n" "${lines[@]}"
@@ -1273,7 +1269,7 @@
[[ "${lines[0]}" == "2.2.2.2" ]]
}
@test "Local CNAME records: International domains are converted to IDNA form" {
@test "Local CNAME records: International domains are converted to IDN form" {
# brücke.com ---> xn--brcke-lva.com
run bash -c "dig A xn--brcke-lva.com +short @127.0.0.1"
printf "%s\n" "${lines[@]}"
@@ -1282,6 +1278,21 @@
[[ "${lines[1]}" == "2.2.2.2" ]]
}
@test "IDN2 CLI interface correctly encodes/decodes domain according to IDNA2008 + TR46" {
run bash -c './pihole-FTL idn2 äste.com'
printf "%s\n" "${lines[@]}"
[[ "${lines[0]}" == "xn--ste-pla.com" ]]
run bash -c './pihole-FTL idn2 -d xn--ste-pla.com'
printf "%s\n" "${lines[@]}"
[[ "${lines[0]}" == "äste.com" ]]
run bash -c './pihole-FTL idn2 ß.de'
printf "%s\n" "${lines[@]}"
[[ "${lines[0]}" == "xn--zca.de" ]]
run bash -c './pihole-FTL idn2 -d xn--zca.de'
printf "%s\n" "${lines[@]}"
[[ "${lines[0]}" == "ß.de" ]]
}
@test "Environmental variable is favored over config file" {
# The config file has -10 but we set FTLCONF_misc_nice="-11"
run bash -c 'grep -B1 "nice = -11" /etc/pihole/pihole.toml'
@@ -1564,7 +1575,7 @@
[[ ${lines[0]} == "3" ]]
run bash -c 'grep -c "DEBUG_CONFIG: pihole.toml unchanged" /var/log/pihole/FTL.log'
printf "%s\n" "${lines[@]}"
[[ ${lines[0]} == "3" ]]
[[ ${lines[0]} == "4" ]]
run bash -c 'grep -c "DEBUG_CONFIG: Config file written to /etc/pihole/dnsmasq.conf" /var/log/pihole/FTL.log'
printf "%s\n" "${lines[@]}"
[[ ${lines[0]} == "1" ]]
@@ -1576,5 +1587,5 @@
[[ ${lines[0]} == "1" ]]
run bash -c 'grep -c "DEBUG_CONFIG: custom.list unchanged" /var/log/pihole/FTL.log'
printf "%s\n" "${lines[@]}"
[[ ${lines[0]} == "3" ]]
[[ ${lines[0]} == "4" ]]
}