mirror of
https://github.com/pi-hole/FTL.git
synced 2024-10-26 16:52:18 +02:00
Merge branch 'development-v6' into new/teleporter_v5
Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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".
|
||||
|
||||
@@ -25,6 +25,7 @@ set(sources
|
||||
snprintf.c
|
||||
sprintf.c
|
||||
strdup.c
|
||||
string.c
|
||||
syscalls.h
|
||||
vasprintf.c
|
||||
vfprintf.c
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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" ]]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user