Skip to content

Commit b7eb53b

Browse files
authored
Merge pull request #2308 from pi-hole/development
Pi-hole FTL v6.0.4
2 parents 37f9a96 + 7de40ae commit b7eb53b

25 files changed

+364
-127
lines changed

CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ set(CMAKE_C_STANDARD 17)
1616

1717
project(PIHOLE_FTL C)
1818

19-
set(DNSMASQ_VERSION pi-hole-v2.91rc5)
19+
set(DNSMASQ_VERSION pi-hole-v2.91rc5+1)
2020

2121
add_subdirectory(src)

patch/civetweb.sh

+3
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,7 @@ patch -p1 < patch/civetweb/0001-Log-debug-messages-to-webserver.log-when-debug.w
2323
echo "Applying patch 0001-Expose-bound-to-addresses-from-CivetWeb-to-the-front.patch"
2424
patch -p1 < patch/civetweb/0001-Expose-bound-to-addresses-from-CivetWeb-to-the-front.patch
2525

26+
echo "Applying patch 0001-Increase-niceness-of-all-civetweb-threads-as-DNS-ope.patch"
27+
patch -p1 < patch/civetweb/0001-Increase-niceness-of-all-civetweb-threads-as-DNS-ope.patch
28+
2629
echo "ALL PATCHES APPLIED OKAY"

patch/civetweb/0001-Expose-bound-to-addresses-from-CivetWeb-to-the-front.patch

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ index fa908e54..66fcab01 100644
1414
--- a/src/webserver/civetweb/civetweb.c
1515
+++ b/src/webserver/civetweb/civetweb.c
1616
@@ -3339,6 +3339,7 @@ mg_get_server_ports(const struct mg_context *ctx,
17-
ports[cnt].is_ssl = ctx->listening_sockets[i].is_ssl;
1817
ports[cnt].is_redirect = ctx->listening_sockets[i].ssl_redir;
1918
ports[cnt].is_optional = ctx->listening_sockets[i].is_optional;
19+
ports[cnt].is_bound = ctx->listening_sockets[i].sock != INVALID_SOCKET;
2020
+ memcpy(&ports[cnt].addr, &ctx->listening_sockets[i].lsa, sizeof(ports[cnt].addr));
2121

2222
if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET) {
@@ -35,7 +35,7 @@ index eee958b4..6dcfa457 100644
3535
#define CIVETWEB_VERSION_MAJOR (1)
3636
#define CIVETWEB_VERSION_MINOR (17)
3737
@@ -721,6 +723,10 @@ struct mg_server_port {
38-
int _reserved2;
38+
int is_bound; /* bound: 0 = no, 1 = yes, relevant for optional ports */
3939
int _reserved3;
4040
int _reserved4;
4141
+ union {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
From ed07ef026f1ba4ad26b8740e6280263f808aeb44 Mon Sep 17 00:00:00 2001
2+
From: DL6ER <dl6er@dl6er.de>
3+
Date: Mon, 3 Mar 2025 17:03:19 +0100
4+
Subject: [PATCH] Increase niceness of all civetweb threads as DNS operation is
5+
more important than the API and dashboard
6+
7+
Signed-off-by: DL6ER <dl6er@dl6er.de>
8+
---
9+
src/webserver/civetweb/civetweb.c | 4 ++++
10+
1 file changed, 4 insertions(+)
11+
12+
diff --git a/src/webserver/civetweb/civetweb.c b/src/webserver/civetweb/civetweb.c
13+
index da254bb4..b1e5b9a6 100644
14+
--- a/src/webserver/civetweb/civetweb.c
15+
+++ b/src/webserver/civetweb/civetweb.c
16+
@@ -244,6 +244,7 @@ static void DEBUG_TRACE_FUNC(const char *func,
17+
18+
#else
19+
#include "log.h"
20+
+#include <sys/resource.h>
21+
#define DEBUG_TRACE(fmt, ...) \
22+
if(debug_flags[DEBUG_WEBSERVER]) {\
23+
log_web("DEBUG: " fmt " (%s:%d)", ##__VA_ARGS__, short_path(__FILE__), __LINE__); }
24+
@@ -2890,6 +2891,9 @@ mg_set_thread_name(const char *name)
25+
*/
26+
(void)prctl(PR_SET_NAME, threadName, 0, 0, 0);
27+
#endif
28+
+
29+
+ // Pi-hole modification: Increase niceness of threads
30+
+ setpriority(PRIO_PROCESS, gettid(), 5);
31+
}
32+
#else /* !defined(NO_THREAD_NAME) */
33+
static void
34+
--
35+
2.43.0
36+

src/CMakeLists.txt

+3-3
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,15 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
3232
# SQLITE_DEFAULT_FOREIGN_KEYS=1: This macro determines whether enforcement of foreign key constraints is enabled or disabled by default for new database connections.
3333
# SQLITE_DQS=0: This setting disables the double-quoted string literal misfeature.
3434
# SQLITE_ENABLE_DBPAGE_VTAB: Enables the SQLITE_DBPAGE virtual table. Warning: writing to the SQLITE_DBPAGE virtual table can very easily cause unrecoverably database corruption.
35-
# SQLITE_TEMP_STORE=2: Store temporary tables in memory for reduced IO and higher performance (can be overwritten by the user at runtime).
35+
# SQLITE_TEMP_STORE=1: This option sets the default value for the temp_store pragma to 1. This means that temporary tables and indices are always stored on disk. This is the default setting.
3636
# SQLITE_DEFAULT_CACHE_SIZE=-16384: Allow up to 16 MiB of cache to be used by SQLite3 (default is 2000 kiB)
3737
# SQLITE_DEFAULT_SYNCHRONOUS=1: Use normal synchronous mode (default is 2)
3838
# SQLITE_LIKE_DOESNT_MATCH_BLOBS: This option causes the LIKE operator to only match BLOB values against BLOB values and TEXT values against TEXT values. This compile-time option makes SQLite run more efficiently when processing queries that use the LIKE operator.
3939
# HAVE_MALLOC_USABLE_SIZE: This option causes SQLite to try to use the malloc_usable_size() function to obtain the actual size of memory allocations from the underlying malloc() system interface. Applications are encouraged to use HAVE_MALLOC_USABLE_SIZE whenever possible.
4040
# HAVE_FDATASYNC: This option causes SQLite to try to use the fdatasync() system call to sync the database file to disk when committing a transaction. Syncing using fdatasync() is faster than syncing using fsync() as fdatasync() does not wait for the file metadata to be written to disk.
41-
# SQLITE_DEFAULT_WORKER_THREADS=4: This option sets the default number of worker threads to use when doing parallel sorting and indexing. The default is 0 which means to use a single thread. The default for SQLITE_MAX_WORKER_THREADS is 8.
41+
# SQLITE_DEFAULT_WORKER_THREADS=0: This option sets the default number of worker threads to use when doing parallel sorting and indexing. The default is 0 which means to use a single thread. Do not increase this value as it, ironically, can cause performance degradation and definitely increases total memory usage.
4242
# SQLITE_MAX_PREPARE_RETRY=200: This option sets the maximum number of automatic re-preparation attempts that can occur after encountering a schema change. This can be caused by running ANALYZE which is done periodically by FTL.
43-
set(SQLITE_DEFINES "-DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_DEFAULT_FOREIGN_KEYS=1 -DSQLITE_DQS=0 -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TEMP_STORE=2 -DSQLITE_DEFAULT_CACHE_SIZE=16384 -DSQLITE_DEFAULT_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DHAVE_MALLOC_USABLE_SIZE -DHAVE_FDATASYNC -DSQLITE_DEFAULT_WORKER_THREADS=4 -DSQLITE_MAX_PREPARE_RETRY=200")
43+
set(SQLITE_DEFINES "-DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_DEFAULT_FOREIGN_KEYS=1 -DSQLITE_DQS=0 -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TEMP_STORE=1 -DSQLITE_DEFAULT_CACHE_SIZE=16384 -DSQLITE_DEFAULT_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DHAVE_MALLOC_USABLE_SIZE -DHAVE_FDATASYNC -DSQLITE_DEFAULT_WORKER_THREADS=0 -DSQLITE_MAX_PREPARE_RETRY=200")
4444

4545
# Code hardening and debugging improvements
4646
# -fstack-protector-strong: The program will be resistant to having its stack overflowed

src/config/config.c

+10-7
Original file line numberDiff line numberDiff line change
@@ -1008,10 +1008,10 @@ static void initConfig(struct config *conf)
10081008
conf->webserver.port.c = validate_stub; // Type-based checking + civetweb syntax checking
10091009

10101010
conf->webserver.threads.k = "webserver.threads";
1011-
conf->webserver.threads.h = "Maximum number of worker threads allowed.\n The Pi-hole web server handles each incoming connection in a separate thread. Therefore, the value of this option is effectively the number of concurrent HTTP connections that can be handled. Any other connections are queued until they can be processed by a unoccupied thread.\n The default value of 0 means that the number of threads is automatically determined by the number of online CPU cores minus 1 (e.g., launching up to 8-1 = 7 threads on 8 cores). Any other value specifies the number of threads explicitly. A hard-coded maximum of 64 threads is enforced for this option.\n The total number of threads you see may be lower than the configured value as threads are only created when needed due to incoming connections.";
1011+
conf->webserver.threads.h = "Maximum number of worker threads allowed.\n The Pi-hole web server handles each incoming connection in a separate thread. Therefore, the value of this option is effectively the number of concurrent HTTP connections that can be handled. Any other connections are queued until they can be processed by a unoccupied thread.\n The total number of threads you see may be lower than the configured value as threads are only created when needed due to incoming connections.\n The value 0 means the number of threads is 50 (as per default settings of CivetWeb) for backwards-compatible behavior.";
10121012
conf->webserver.threads.t = CONF_UINT;
10131013
conf->webserver.threads.f = FLAG_RESTART_FTL;
1014-
conf->webserver.threads.d.ui = 0;
1014+
conf->webserver.threads.d.ui = 50;
10151015
conf->webserver.threads.c = validate_stub; // Only type-based checking
10161016

10171017
conf->webserver.headers.k = "webserver.headers";
@@ -1062,7 +1062,7 @@ static void initConfig(struct config *conf)
10621062
conf->webserver.paths.webhome.t = CONF_STRING;
10631063
conf->webserver.paths.webhome.f = FLAG_RESTART_FTL;
10641064
conf->webserver.paths.webhome.d.s = (char*)"/admin/";
1065-
conf->webserver.paths.webhome.c = validate_filepath;
1065+
conf->webserver.paths.webhome.c = validate_filepath_two_slash;
10661066

10671067
// sub-struct interface
10681068
conf->webserver.interface.boxed.k = "webserver.interface.boxed";
@@ -1317,7 +1317,7 @@ static void initConfig(struct config *conf)
13171317
conf->misc.addr2line.c = validate_stub; // Only type-based checking
13181318

13191319
conf->misc.etc_dnsmasq_d.k = "misc.etc_dnsmasq_d";
1320-
conf->misc.etc_dnsmasq_d.h = "Should FTL load additional dnsmasq configuration files from /etc/dnsmasq.d/?";
1320+
conf->misc.etc_dnsmasq_d.h = "Should FTL load additional dnsmasq configuration files from /etc/dnsmasq.d/?\n Warning: This is an advanced setting and should only be used with care.\n Incorrectly formatted or config files specifying options which can only be defined once can result in conflicts with the automatic configuration of Pi-hole (see "DNSMASQ_PH_CONFIG") and may stop DNS resolution from working.";
13211321
conf->misc.etc_dnsmasq_d.t = CONF_BOOL;
13221322
conf->misc.etc_dnsmasq_d.f = FLAG_RESTART_FTL;
13231323
conf->misc.etc_dnsmasq_d.d.b = false;
@@ -1740,7 +1740,8 @@ bool migrate_config_v6(void)
17401740

17411741
// Initialize the TOML config file
17421742
writeFTLtoml(true);
1743-
write_dnsmasq_config(&config, false, NULL);
1743+
char errbuf[ERRBUF_SIZE] = { 0 };
1744+
write_dnsmasq_config(&config, false, errbuf);
17441745
write_custom_list();
17451746

17461747
return true;
@@ -1768,7 +1769,8 @@ bool readFTLconf(struct config *conf, const bool rewrite)
17681769
if(rewrite)
17691770
{
17701771
writeFTLtoml(true);
1771-
write_dnsmasq_config(conf, false, NULL);
1772+
char errbuf[ERRBUF_SIZE] = { 0 };
1773+
write_dnsmasq_config(conf, false, errbuf);
17721774
write_custom_list();
17731775
}
17741776
return true;
@@ -1800,7 +1802,8 @@ bool readFTLconf(struct config *conf, const bool rewrite)
18001802

18011803
// Initialize the TOML config file
18021804
writeFTLtoml(true);
1803-
write_dnsmasq_config(conf, false, NULL);
1805+
char errbuf[ERRBUF_SIZE] = { 0 };
1806+
write_dnsmasq_config(conf, false, errbuf);
18041807
write_custom_list();
18051808

18061809
return false;

src/config/dnsmasq_config.c

+7-6
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ static void write_config_header(FILE *fp, const char *description)
244244
CONFIG_CENTER(fp, HEADER_WIDTH, "%s", "################################################################################");
245245
}
246246

247-
bool __attribute__((const)) write_dnsmasq_config(struct config *conf, bool test_config, char errbuf[ERRBUF_SIZE])
247+
bool __attribute__((nonnull(1,3))) write_dnsmasq_config(struct config *conf, bool test_config, char errbuf[ERRBUF_SIZE])
248248
{
249249
// Early config checks
250250
if(conf->dhcp.active.v.b)
@@ -296,9 +296,9 @@ bool __attribute__((const)) write_dnsmasq_config(struct config *conf, bool test_
296296
}
297297

298298
// Check if the DHCP range is valid (start needs to be smaller than end)
299-
if(ntohl(conf->dhcp.start.v.in_addr.s_addr) >= ntohl(conf->dhcp.end.v.in_addr.s_addr))
299+
if(ntohl(conf->dhcp.start.v.in_addr.s_addr) > ntohl(conf->dhcp.end.v.in_addr.s_addr))
300300
{
301-
strncpy(errbuf, "DHCP range start address is larger than or equal to the end address", ERRBUF_SIZE);
301+
strncpy(errbuf, "DHCP range start address is larger than the end address", ERRBUF_SIZE);
302302
log_err("Unable to update dnsmasq configuration: %s", errbuf);
303303
return false;
304304
}
@@ -703,10 +703,11 @@ bool __attribute__((const)) write_dnsmasq_config(struct config *conf, bool test_
703703

704704
if(directory_exists("/etc/dnsmasq.d") && conf->misc.etc_dnsmasq_d.v.b)
705705
{
706-
// Load additional user scripts from /etc/dnsmasq.d if the
706+
// Load additional user configs from /etc/dnsmasq.d if the
707707
// directory exists (it may not, e.g., in a container)
708-
fputs("# Load additional user scripts\n", pihole_conf);
709-
fputs("conf-dir=/etc/dnsmasq.d\n", pihole_conf);
708+
// Load only files ending in .conf
709+
fputs("# Load additional user configs\n", pihole_conf);
710+
fputs("conf-dir=/etc/dnsmasq.d,*.conf\n", pihole_conf);
710711
fputs("\n", pihole_conf);
711712
}
712713

src/config/dnsmasq_config.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
#define ERRBUF_SIZE 1024
1616

17-
bool write_dnsmasq_config(struct config *conf, bool test_config, char errbuf[ERRBUF_SIZE]);
17+
bool write_dnsmasq_config(struct config *conf, bool test_config, char errbuf[ERRBUF_SIZE]) __attribute__((nonnull(1,3)));
1818
int get_lineno_from_string(const char *string);
1919
char *get_dnsmasq_line(const unsigned int lineno);
2020
bool read_legacy_dhcp_static_config(void);

src/config/env.c

+16-2
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,10 @@ void printFTLenv(void)
122122
else
123123
{
124124
if(item->error != NULL)
125-
log_err(" %s %s %s",
125+
log_err(" %s %s %s, using default instead",
126126
cli_cross(), item->key, item->error);
127127
else
128-
log_err(" %s %s is invalid",
128+
log_err(" %s %s is invalid, using default instead",
129129
cli_cross(), item->key);
130130

131131
if(item->error_allocated)
@@ -613,6 +613,20 @@ bool __attribute__((nonnull(1,2,3))) readEnvValue(struct conf_item *conf_item, s
613613
}
614614
}
615615

616+
// Validate enum value if it is valid and validation function is defined
617+
char errbuf[VALIDATOR_ERRBUF_LEN] = { 0 };
618+
if(conf_item->c != NULL && !conf_item->c(&conf_item->v, conf_item->k, errbuf))
619+
{
620+
log_debug(DEBUG_CONFIG, "Config item validation failed for %s: %s", conf_item->k, errbuf);
621+
item->error = strdup(errbuf);
622+
item->error_allocated = true;
623+
item->valid = false;
624+
return false;
625+
}
626+
627+
628+
log_debug(DEBUG_CONFIG, "Env var %s passed validation successfully", conf_item->k);
629+
616630
return true;
617631
}
618632

src/config/password.c

+32-13
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ static char * __attribute__((malloc)) double_sha256_password(const char *passwor
9393

9494
bool get_secure_randomness(uint8_t *buffer, const size_t length)
9595
{
96-
ssize_t result;
97-
96+
ssize_t result = -1;
97+
const char *generator = "getrandom";
9898
// First try to get randomness in non-blocking mode and print a warning when not enough entropy is available right now
9999
do {
100100
result = getrandom(buffer, length, GRND_NONBLOCK);
@@ -103,32 +103,51 @@ bool get_secure_randomness(uint8_t *buffer, const size_t length)
103103
// If not enough entropy is available right now, try again in blocking mode
104104
if (result < 0 && errno == EAGAIN)
105105
{
106-
log_warn("getrandom() failed in get_secure_randomness(): Not enough entropy available right now, retrying in blocking mode");
106+
log_warn("Not enough entropy available right now for generating secure randomness, retrying in blocking mode");
107107
// Sleep for 1 second to give the kernel some time to gather entropy
108108
sleepms(1000);
109109
}
110110
else
111111
{
112112
// If the first try was successful, return the result
113113
if (result >= 0)
114-
return true;
114+
goto random_success;
115115
}
116116
do {
117117
result = getrandom(buffer, length, 0);
118118
} while (result < 0 && errno == EINTR);
119119

120-
if (result < 0)
120+
if(result < 0)
121121
{
122-
const int err = errno;
123-
log_err("getrandom() failed in get_secure_randomness(): %s", strerror(errno));
124-
errno = err;
125-
return false;
122+
log_debug(DEBUG_API, "Getting secure randomness failed (%s), trying /dev/urandom", strerror(errno));
123+
generator = "/dev/urandom";
124+
result = getrandom_fallback(buffer, length, 0);
125+
if(result < 0 || result < (ssize_t)length)
126+
{
127+
log_debug(DEBUG_API, "Fallback failed, trying internal DRBG generator");
128+
generator = "internal DRBG";
129+
result = drbg_random(buffer, length);
130+
if(result < 0)
131+
{
132+
// Warning will be printed by drbg_random()
133+
return false;
134+
}
135+
136+
log_debug(DEBUG_API, "Internal DRBG generator successfully used");
137+
}
138+
else
139+
log_debug(DEBUG_API, "Fallback to /dev/urandom successful");
126140
}
127-
else if((size_t)result != length)
141+
142+
// Check if enough bytes were generated
143+
if(result != (ssize_t)length)
128144
{
129-
log_err("getrandom() failed in get_secure_randomness(): Not enough bytes generated (%zu != %zu)", (size_t)result, length);
145+
log_err("Randomness generator (%s) failed: not enough bytes generated (%zd != %zu)", generator, result, length);
130146
return false;
131147
}
148+
149+
random_success:
150+
log_debug(DEBUG_ANY, "Generated %zd bytes of secure randomness", result);
132151
return true;
133152
}
134153

@@ -185,7 +204,7 @@ static char * __attribute__((malloc)) balloon_password(const char *password,
185204
{
186205
// Parameter check
187206
if(password == NULL || salt == NULL)
188-
return NULL;
207+
return strdup("");
189208

190209
struct timespec start, end;
191210
// Record starting time
@@ -370,7 +389,7 @@ char * __attribute__((malloc)) create_password(const char *password)
370389
// genrandom() returns cryptographically secure random data
371390
uint8_t salt[SALT_LEN] = { 0 };
372391
if(!get_secure_randomness(salt, sizeof(salt)))
373-
return NULL;
392+
return strdup("");
374393

375394
// Generate balloon PHC-encoded password hash
376395
return balloon_password(password, salt, true);

src/config/setupVars.c

+14-3
Original file line numberDiff line numberDiff line change
@@ -288,15 +288,26 @@ static void get_conf_string_array_from_setupVars_regex(const char *key, struct c
288288
p++;
289289
}
290290

291-
// Add ^ and $ to the string
291+
// Add ^ and $ to the string, but only add ^ if the
292+
// string does not start with *. In the latter case, we
293+
// only add $ to the string (and remove the *).
292294
char *regex2 = calloc(strlen(regex) + 3, sizeof(char));
293295
if(regex2 == NULL)
294296
{
295297
log_warn("get_conf_string_array_from_setupVars(%s) failed: Could not allocate memory for regex2", key);
296298
free(regex);
297299
continue;
298300
}
299-
sprintf(regex2, "^%s$", regex);
301+
if(regex[0] != '*')
302+
{
303+
// Add ^ and $ to the string
304+
sprintf(regex2, "^%s$", regex);
305+
}
306+
else
307+
{
308+
// Skip the * and add $ to the string
309+
sprintf(regex2, "%s$", regex + 1);
310+
}
300311

301312
// Free memory
302313
free(regex);
@@ -305,7 +316,7 @@ static void get_conf_string_array_from_setupVars_regex(const char *key, struct c
305316
cJSON *item = cJSON_CreateString(regex2);
306317
cJSON_AddItemToArray(conf_item->v.json, item);
307318

308-
log_info("setupVars.conf:%s -> Setting %s[%u] = %s\n",
319+
log_info("setupVars.conf:%s -> Setting %s[%u] = %s",
309320
key, conf_item->k, i, item->valuestring);
310321

311322
// Free memory

src/config/validator.c

+15
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,21 @@ bool validate_filepath(union conf_value *val, const char *key, char err[VALIDATO
270270
return true;
271271
}
272272

273+
// Validate a file path that needs to have both a slash at the beginning and at
274+
// the end
275+
bool validate_filepath_two_slash(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN])
276+
{
277+
// Check if the path starts and ends with a slash
278+
if(strlen(val->s) < 1 || val->s[0] != '/' || val->s[strlen(val->s) - 1] != '/')
279+
{
280+
snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: file path does not start and end with a slash (\"%s\")", key, val->s);
281+
return false;
282+
}
283+
284+
// Check if the path contains only valid characters
285+
return validate_filepath(val, key, err);
286+
}
287+
273288
// Validate file path (empty allowed)
274289
bool validate_filepath_empty(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN])
275290
{

src/config/validator.h

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ bool validate_dns_domain(union conf_value *val, const char *key, char err[VALIDA
2121
bool validate_cidr(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]);
2222
bool validate_domain(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]);
2323
bool validate_filepath(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]);
24+
bool validate_filepath_two_slash(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]);
2425
bool validate_filepath_empty(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]);
2526
bool validate_filepath_dash(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]);
2627
bool validate_regex_array(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]);

0 commit comments

Comments
 (0)