Implement proper testing of dnsmasq options before applying new configuration

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER 2023-01-18 20:12:11 +01:00
parent 2d6c25d573
commit 7b479782fd
No known key found for this signature in database
GPG Key ID: 00135ACBD90B28DD
14 changed files with 447 additions and 102 deletions

View File

@ -113,6 +113,7 @@ int api_handler(struct mg_connection *conn, void *ignored)
int ret = 0;
// Loop over all API endpoints and check if the requested URI matches
bool unauthorized = false;
for(unsigned int i = 0; i < sizeof(api_request)/sizeof(api_request[0]); i++)
{
// Check if the requested method is allowed
@ -127,7 +128,10 @@ int api_handler(struct mg_connection *conn, void *ignored)
// Verify requesting client is allowed to see this ressource
if(api_request[i].require_auth && check_client_auth(&api) == API_AUTH_UNAUTHORIZED)
return send_json_unauthorized(&api);
{
unauthorized = true;
break;
}
// Call the API function and get the return code
log_debug(DEBUG_API, "Sending to %s", api_request[i].uri);
@ -163,11 +167,16 @@ int api_handler(struct mg_connection *conn, void *ignored)
"Not found",
api.request->local_uri_raw);
}
else if(unauthorized)
{
// Return with unauthorized payload
// Do this only after having cleaned up above
return send_json_unauthorized(&api);
}
// Restart FTL if requested
if(api.ftl.restart)
{
// Trigger an automatic restart by systemd
exit_code = RESTART_FTL_CODE;
FTL_terminate = 1;
}

View File

@ -22,6 +22,8 @@
#include "config/toml_writer.h"
// write_dnsmasq_config()
#include "config/dnsmasq_config.h"
// shm_lock()
#include "shmem.h"
// The following functions are used to create the JSON output
// of the /api/config endpoint.
@ -352,7 +354,7 @@ static int api_config_get(struct ftl_conn *api)
for(unsigned int i = 0; i < CONFIG_ELEMENTS; i++)
{
// Get pointer to memory location of this conf_item
struct conf_item *conf_item = get_conf_item(i);
struct conf_item *conf_item = get_conf_item(&config, i);
// Get path depth
unsigned int level = config_path_depth(conf_item->p);
@ -455,10 +457,12 @@ static int api_config_patch(struct ftl_conn *api)
// Read all known config items
bool dnsmasq_changed = false;
struct config conf_copy;
duplicate_config(&conf_copy);
for(unsigned int i = 0; i < CONFIG_ELEMENTS; i++)
{
// Get pointer to memory location of this conf_item
struct conf_item *conf_item = get_conf_item(i);
struct conf_item *conf_item = get_conf_item(&conf_copy, i);
// Get path depth
unsigned int level = config_path_depth(conf_item->p);
@ -489,16 +493,37 @@ static int api_config_patch(struct ftl_conn *api)
dnsmasq_changed = true;
}
// Reload debug levels
set_debug_flags();
// Request restart of FTL
if(dnsmasq_changed)
{
char errbuf[ERRBUF_SIZE] = { 0 };
if(write_dnsmasq_config(&conf_copy, true, errbuf))
api->ftl.restart = true;
else
{
return send_json_error(api, 400,
"bad_request",
"Invalid configuration",
errbuf);
}
}
// Install new configuration
lock_shm();
// Backup old config struct (so we can free it)
struct config old_conf;
memcpy(&old_conf, &config, sizeof(struct config));
// Replace old config struct by changed one
memcpy(&config, &conf_copy, sizeof(struct config));
// Free old backup struct
free_config(&old_conf);
unlock_shm();
// Store changed configuration to disk
writeFTLtoml(true);
// Request restart of FTL
if(dnsmasq_changed)
if(write_dnsmasq_config(true))
api->ftl.restart = true;
// Reload debug levels
set_debug_flags();
// Return full config after possible changes above
return api_config_get(api);
@ -546,10 +571,12 @@ static int api_config_put_delete(struct ftl_conn *api)
// Read all known config items
bool dnsmasq_changed = false;
bool found = false;
struct config conf_copy;
duplicate_config(&conf_copy);
for(unsigned int i = 0; i < CONFIG_ELEMENTS; i++)
{
// Get pointer to memory location of this conf_item
struct conf_item *conf_item = get_conf_item(i);
struct conf_item *conf_item = get_conf_item(&conf_copy, i);
// We support PUT only for adding to string arrays
if(conf_item->t != CONF_JSON_STRING_ARRAY)
@ -637,13 +664,40 @@ static int api_config_put_delete(struct ftl_conn *api)
hint);
}
// We need to build a new config (and carefully test it!) whenever dnsmasq
// options have changed that need a restart of the resolver
if(dnsmasq_changed)
{
char errbuf[ERRBUF_SIZE] = { 0 };
// Request restart of FTL
if(write_dnsmasq_config(&conf_copy, true, errbuf))
api->ftl.restart = true;
else
{
// The new config did not work
return send_json_error(api, 400,
"bad_request",
"Invalid configuration",
errbuf);
}
}
// Install new configuration
lock_shm();
// Backup old config struct (so we can free it)
struct config old_conf;
memcpy(&old_conf, &config, sizeof(struct config));
// Replace old config struct by changed one
memcpy(&config, &conf_copy, sizeof(struct config));
// Free old backup struct
free_config(&old_conf);
unlock_shm();
// Store changed configuration to disk
writeFTLtoml(true);
// Request restart of FTL
if(dnsmasq_changed)
if(write_dnsmasq_config(true))
api->ftl.restart = false;
// Reload debug levels
set_debug_flags();
return api->method == HTTP_PUT ? 201 : 204; // 201 - Created or 204 - No content
}

View File

@ -644,3 +644,15 @@ void parse_args(int argc, char* argv[])
}
}
}
// defined in src/dnsmasq/option.c
extern void reset_usage_indicator(void);
void test_dnsmasq_options(int argc, const char *argv[])
{
// Reset getopt before calling read_opts
optind = 0;
// Call dnsmasq's option parser
reset_usage_indicator();
// Call read_opts
read_opts(argc, (char**)argv, NULL);
}

View File

@ -24,6 +24,8 @@ const char *cli_done(void) __attribute__ ((pure));
const char *cli_bold(void) __attribute__ ((pure));
const char *cli_normal(void) __attribute__ ((pure));
void test_dnsmasq_options(int argc, const char *argv[]);
// defined in dnsmasq_interface.c
int check_struct_sizes(void);

View File

@ -13,6 +13,7 @@
#include "config/config.h"
#include "config/toml_helper.h"
#include "config/toml_writer.h"
#include "config/dnsmasq_config.h"
#include "log.h"
#include "datastructure.h"
@ -255,11 +256,13 @@ static bool readStringvalue(struct conf_item *conf_item, const char *value)
bool set_config_from_CLI(const char *key, const char *value)
{
// Identify config option
struct config conf_copy;
duplicate_config(&conf_copy);
struct conf_item *conf_item = NULL;
for(unsigned int i = 0; i < CONFIG_ELEMENTS; i++)
{
// Get pointer to memory location of this conf_item
struct conf_item *item = get_conf_item(i);
struct conf_item *item = get_conf_item(&conf_copy, i);
if(strcmp(item->k, key) != 0)
continue;
@ -277,16 +280,37 @@ bool set_config_from_CLI(const char *key, const char *value)
}
// Parse value
if(readStringvalue(conf_item, value))
if(!readStringvalue(conf_item, value))
return false;
// Is this a dnsmasq option we need to check?
if(conf_item->restart_dnsmasq)
{
// Print value
writeTOMLvalue(stdout, -1, conf_item->t, &conf_item->v);
putchar('\n');
writeFTLtoml(false);
return true;
char errbuf[ERRBUF_SIZE] = { 0 };
if(!write_dnsmasq_config(&conf_copy, true, errbuf))
{
int lineno = get_lineno_from_string(errbuf);
char *line = get_dnsmasq_line(lineno);
log_err("Corresponding line: \"%s\"", line);
free(line);
return false;
}
}
return false;
// Install new configuration
// Backup old config struct (so we can free it)
struct config old_conf;
memcpy(&old_conf, &config, sizeof(struct config));
// Replace old config struct by changed one
memcpy(&config, &conf_copy, sizeof(struct config));
// Free old backup struct
free_config(&old_conf);
// Print value
writeTOMLvalue(stdout, -1, conf_item->t, &conf_item->v);
putchar('\n');
writeFTLtoml(false);
return true;
}
bool get_config_from_CLI(const char *key)
@ -296,7 +320,7 @@ bool get_config_from_CLI(const char *key)
for(unsigned int i = 0; i < CONFIG_ELEMENTS; i++)
{
// Get pointer to memory location of this conf_item
struct conf_item *item = get_conf_item(i);
struct conf_item *item = get_conf_item(&config, i);
if(strcmp(item->k, key) != 0)
continue;

View File

@ -29,7 +29,7 @@ void set_all_debug(const bool status)
for(unsigned int i = 0; i < CONFIG_ELEMENTS; i++)
{
// Get pointer to memory location of this conf_item
struct conf_item *conf_item = get_conf_item(i);
struct conf_item *conf_item = get_conf_item(&config, i);
// Skip config entries whose path's are not starting in "debug."
if(strcmp("debug", conf_item->p[0]) != 0)
@ -131,8 +131,7 @@ bool __attribute__ ((pure)) check_paths_equal(char **paths1, char **paths2, unsi
}
return true;
}
struct conf_item *get_conf_item(const unsigned int n)
struct conf_item *get_conf_item(struct config *conf, const unsigned int n)
{
// Sanity check
if(n > CONFIG_ELEMENTS-1)
@ -142,7 +141,7 @@ struct conf_item *get_conf_item(const unsigned int n)
}
// Return n-th config element
return (void*)&config + n*sizeof(struct conf_item);
return (void*)conf + n*sizeof(struct conf_item);
}
struct conf_item *get_debug_item(const enum debug_flag debug)
@ -171,6 +170,92 @@ unsigned int __attribute__ ((pure)) config_path_depth(char **paths)
}
void duplicate_config(struct config *conf)
{
// Post-processing:
// Initialize and verify config data
for(unsigned int i = 0; i < CONFIG_ELEMENTS; i++)
{
// Get pointer to memory location of this conf_item (original)
struct conf_item *conf_item = get_conf_item(&config, i);
// Get pointer to memory location of this conf_item (copy)
struct conf_item *copy_item = get_conf_item(conf, i);
// Copy constant/static fields
memcpy(copy_item, conf_item, sizeof(*conf_item));
// Make a type-dependent copy of the value
switch(conf_item->t)
{
case CONF_BOOL:
case CONF_INT:
case CONF_UINT:
case CONF_UINT16:
case CONF_LONG:
case CONF_ULONG:
case CONF_DOUBLE:
case CONF_STRING:
case CONF_ENUM_PTR_TYPE:
case CONF_ENUM_BUSY_TYPE:
case CONF_ENUM_BLOCKING_MODE:
case CONF_ENUM_REFRESH_HOSTNAMES:
case CONF_ENUM_PRIVACY_LEVEL:
case CONF_ENUM_LISTENING_MODE:
case CONF_STRUCT_IN_ADDR:
case CONF_STRUCT_IN6_ADDR:
// Nothing to do, the memcpy above has already covered this
break;
case CONF_STRING_ALLOCATED:
copy_item->v.s = strdup(conf_item->v.s);
break;
case CONF_JSON_STRING_ARRAY:
copy_item->v.json = cJSON_Duplicate(conf_item->v.json, true);
break;
}
}
}
void free_config(struct config *conf)
{
// Post-processing:
// Initialize and verify config data
for(unsigned int i = 0; i < CONFIG_ELEMENTS; i++)
{
// Get pointer to memory location of this conf_item (copy)
struct conf_item *copy_item = get_conf_item(conf, i);
// Make a type-dependent copy of the value
switch(copy_item->t)
{
case CONF_BOOL:
case CONF_INT:
case CONF_UINT:
case CONF_UINT16:
case CONF_LONG:
case CONF_ULONG:
case CONF_DOUBLE:
case CONF_STRING:
case CONF_ENUM_PTR_TYPE:
case CONF_ENUM_BUSY_TYPE:
case CONF_ENUM_BLOCKING_MODE:
case CONF_ENUM_REFRESH_HOSTNAMES:
case CONF_ENUM_PRIVACY_LEVEL:
case CONF_ENUM_LISTENING_MODE:
case CONF_STRUCT_IN_ADDR:
case CONF_STRUCT_IN6_ADDR:
// Nothing to do
break;
case CONF_STRING_ALLOCATED:
free(copy_item->v.s);
break;
case CONF_JSON_STRING_ARRAY:
cJSON_Delete(copy_item->v.json);
break;
}
}
}
void initConfig(void)
{
// struct dns
@ -862,7 +947,7 @@ void initConfig(void)
for(unsigned int i = 0; i < CONFIG_ELEMENTS; i++)
{
// Get pointer to memory location of this conf_item
struct conf_item *conf_item = get_conf_item(i);
struct conf_item *conf_item = get_conf_item(&config, i);
// Initialize config value with default one for all *except* the log file path
if(conf_item != &config.files.log.ftl)
@ -908,7 +993,7 @@ void readFTLconf(const bool rewrite)
if(rewrite)
{
writeFTLtoml(true);
write_dnsmasq_config(false);
write_dnsmasq_config(&config, false, NULL);
}
read_legacy_dhcp_static_config();
read_legacy_cnames_config();
@ -939,7 +1024,7 @@ void readFTLconf(const bool rewrite)
// Initialize the TOML config file
writeFTLtoml(true);
write_dnsmasq_config(false);
write_dnsmasq_config(&config, false, NULL);
}
bool getLogFilePath(void)

View File

@ -28,26 +28,6 @@
#define GLOBALTOMLPATH "/etc/pihole/pihole-FTL.toml"
// Defined in config.c
void set_all_debug(const bool status);
void initConfig(void);
void readFTLconf(const bool rewrite);
bool getLogFilePath(void);
struct conf_item *get_conf_item(unsigned int n);
struct conf_item *get_debug_item(const enum debug_flag debug);
unsigned int config_path_depth(char **paths) __attribute__ ((pure));
char **gen_config_path(const char *pathin, const char delim);
void free_config_path(char **paths);
bool check_paths_equal(char **paths1, char **paths2, unsigned int max_level) __attribute__ ((pure));
// Defined in toml_reader.c
bool getPrivacyLevel(void);
bool getBlockingMode(void);
bool readDebugSettings(void);
void init_config_mutex(void);
bool get_blockingstatus(void) __attribute__((pure));
void set_blockingstatus(bool enabled);
union conf_value {
bool b; // boolean value
int i; // integer value
@ -289,4 +269,26 @@ extern struct config config;
#define CONFIG_ELEMENTS (sizeof(config)/sizeof(struct conf_item))
#define DEBUG_ELEMENTS (sizeof(config.debug)/sizeof(struct conf_item))
// Defined in config.c
void set_all_debug(const bool status);
void initConfig(void);
void readFTLconf(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(const enum debug_flag debug);
unsigned int config_path_depth(char **paths) __attribute__ ((pure));
void duplicate_config(struct config *conf);
void free_config(struct config *conf);
char **gen_config_path(const char *pathin, const char delim);
void free_config_path(char **paths);
bool check_paths_equal(char **paths1, char **paths2, unsigned int max_level) __attribute__ ((pure));
// Defined in toml_reader.c
bool getPrivacyLevel(void);
bool getBlockingMode(void);
bool readDebugSettings(void);
void init_config_mutex(void);
bool get_blockingstatus(void) __attribute__((pure));
void set_blockingstatus(bool enabled);
#endif //CONFIG_H

View File

@ -24,16 +24,145 @@
#include "files.h"
// trim_whitespace()
#include "setupVars.h"
// run_dnsmasq_main()
#include "args.h"
// optind
#include <unistd.h>
// wait
#include <sys/wait.h>
static bool test_dnsmasq_config(void)
static bool test_dnsmasq_config(char errbuf[ERRBUF_SIZE])
{
FILE *pipe = popen("pihole-FTL dnsmasq-test-file "DNSMASQ_TEMP_CONF, "r");
if(!pipe)
// Create a pipe for communication with our child
int pipefd[2];
if(pipe(pipefd) !=0)
{
log_err("Cannot open pipe to pihole-FTL dnsmasq-test-file: %s", strerror(errno));
log_err("Cannot create pipe while testing new dnsmasq config: %s", strerror(errno));
return false;
}
return WEXITSTATUS(pclose(pipe)) == EXIT_SUCCESS;
// Fork!
pid_t cpid = fork();
int code = -1;
bool crashed = false;
if (cpid == 0)
{
/*** CHILD ***/
// Close the reading end of the pipe
close(pipefd[0]);
const char *argv[3];
argv[0] = "X";
argv[1] = "--conf-file="DNSMASQ_TEMP_CONF;
argv[2] = "--test";
// Disable logging
log_ctrl(false, false);
// Flush STDERR
fflush(stderr);
// Redirect STDERR into our pipe
dup2(pipefd[1], STDERR_FILENO);
dup2(pipefd[1], STDOUT_FILENO);
// Call dnsmasq's option parser
test_dnsmasq_options(3, argv);
// We'll never actually reach this point as test_dnsmasq_options() will
// exit. We still close the fork nicely in case other stumble upon this
// code and want to use it in their projects
// Close the writing end of the pipe, thus sending EOF to the reader
close(pipefd[1]);
// Exit the fork
exit(EXIT_SUCCESS);
}
else
{
/*** PARENT ***/
// Close the writing end of the pipe
close(pipefd[1]);
// Read readirected STDERR until EOF
if(errbuf != NULL)
{
// We are only interested in the last pipe line
while(read(pipefd[0], errbuf, ERRBUF_SIZE) > 0)
{
// Remove initial newline character (if present)
if(errbuf[0] == '\n')
memmove(errbuf, &errbuf[1], ERRBUF_SIZE-1);
// Strip newline character (if present)
if(errbuf[strlen(errbuf)-1] == '\n')
errbuf[strlen(errbuf)-1] = '\0';
log_debug(DEBUG_CONFIG, "dnsmasq pipe: %s", errbuf);
}
}
// Wait until child has exited to get its return code
int status;
waitpid(cpid, &status, 0);
code = WEXITSTATUS(status);
if(WIFSIGNALED(status))
{
crashed = true;
log_err("dnsmasq test failed with signal %d %s",
WTERMSIG(status),
WCOREDUMP(status) ? "(core dumped)" : "");
}
log_debug(DEBUG_CONFIG, "Code: %d", code);
// Close the reading end of the pipe
close(pipefd[0]);
}
return code == EXIT_SUCCESS && !crashed;
}
int get_lineno_from_string(const char *string)
{
int lineno = -1;
char *ptr = strstr(string, " at line ");
if(ptr == NULL)
return -1;
if(sscanf(ptr, " at line %d of ", &lineno) == 1)
return lineno;
else
return -1;
}
char *get_dnsmasq_line(const unsigned int lineno)
{
// Open temporary file
FILE *fp = fopen(DNSMASQ_TEMP_CONF, "r");
if (fp == NULL)
{
log_warn("Cannot read "DNSMASQ_TEMP_CONF);
return NULL;
}
// Read file line-by-line until we reach the requested line
char *linebuffer = NULL;
size_t size = 0u;
unsigned int count = 1;
while(getline(&linebuffer, &size, fp) != -1)
{
if (count == lineno)
{
fclose(fp);
if(linebuffer[strlen(linebuffer)] == '\n')
linebuffer[strlen(linebuffer)] = '\0';
return linebuffer;
}
else
count++;
}
fclose(fp);
return NULL;
}
static void write_config_header(FILE *fp)
@ -66,8 +195,9 @@ static void write_config_header(FILE *fp)
fputs("###############################################################################\n\n", fp);
}
bool __attribute__((const)) write_dnsmasq_config(bool test_config)
bool __attribute__((const)) write_dnsmasq_config(struct config *conf, bool test_config, char errbuf[ERRBUF_SIZE])
{
log_debug(DEBUG_CONFIG, "Opening "DNSMASQ_TEMP_CONF" for writing");
FILE *pihole_conf = fopen(DNSMASQ_TEMP_CONF, "w");
// Return early if opening failed
if(!pihole_conf)
@ -92,14 +222,14 @@ bool __attribute__((const)) write_dnsmasq_config(bool test_config)
fputs("no-resolv\n", pihole_conf);
fputs("\n", pihole_conf);
fputs("# DNS port to be used\n", pihole_conf);
fprintf(pihole_conf, "port=%u\n", config.dnsmasq.port.v.u16);
if(cJSON_GetArraySize(config.dnsmasq.upstreams.v.json) > 0)
fprintf(pihole_conf, "port=%u\n", conf->dnsmasq.port.v.u16);
if(cJSON_GetArraySize(conf->dnsmasq.upstreams.v.json) > 0)
{
fputs("# List of upstream DNS server\n", pihole_conf);
const int n = cJSON_GetArraySize(config.dnsmasq.upstreams.v.json);
const int n = cJSON_GetArraySize(conf->dnsmasq.upstreams.v.json);
for(int i = 0; i < n; i++)
{
cJSON *server = cJSON_GetArrayItem(config.dnsmasq.upstreams.v.json, i);
cJSON *server = cJSON_GetArrayItem(conf->dnsmasq.upstreams.v.json, i);
if(server != NULL && cJSON_IsString(server))
fprintf(pihole_conf, "server=%s\n", server->valuestring);
}
@ -107,7 +237,7 @@ bool __attribute__((const)) write_dnsmasq_config(bool test_config)
}
fputs("# Set the size of dnsmasq's cache. The default is 150 names. Setting the cache\n", pihole_conf);
fputs("# size to zero disables caching. Note: huge cache size impacts performance\n", pihole_conf);
fprintf(pihole_conf, "cache-size=%u\n", config.dnsmasq.cache_size.v.ui);
fprintf(pihole_conf, "cache-size=%u\n", conf->dnsmasq.cache_size.v.ui);
fputs("\n", pihole_conf);
fputs("# Return answers to DNS queries from /etc/hosts and interface-name and\n", pihole_conf);
@ -119,7 +249,7 @@ bool __attribute__((const)) write_dnsmasq_config(bool test_config)
fputs("localise-queries\n", pihole_conf);
fputs("\n", pihole_conf);
if(config.dnsmasq.logging.v.b)
if(conf->dnsmasq.logging.v.b)
{
fputs("# Enable query logging\n", pihole_conf);
fputs("log-queries\n", pihole_conf);
@ -132,16 +262,16 @@ bool __attribute__((const)) write_dnsmasq_config(bool test_config)
fputs("#log-async\n", pihole_conf);
}
if(strlen(config.files.log.dnsmasq.v.s) > 0)
if(strlen(conf->files.log.dnsmasq.v.s) > 0)
{
fputs("# Specify the log file to use\n", pihole_conf);
fputs("# We set this even if logging is disabled to store warnings\n", pihole_conf);
fputs("# and errors in this file. This is useful for debugging.\n", pihole_conf);
fprintf(pihole_conf, "log-facility=%s\n", config.files.log.dnsmasq.v.s);
fprintf(pihole_conf, "log-facility=%s\n", conf->files.log.dnsmasq.v.s);
fputs("\n", pihole_conf);
}
if(config.dnsmasq.bogus_priv.v.b)
if(conf->dnsmasq.bogus_priv.v.b)
{
fputs("# Bogus private reverse lookups. All reverse lookups for private IP\n", pihole_conf);
fputs("# ranges (ie 192.168.x.x, etc) which are not found in /etc/hosts or the\n", pihole_conf);
@ -150,7 +280,7 @@ bool __attribute__((const)) write_dnsmasq_config(bool test_config)
fputs("\n", pihole_conf);
}
if(config.dnsmasq.domain_needed.v.b)
if(conf->dnsmasq.domain_needed.v.b)
{
fputs("# Add the domain to simple names (without a period) in /etc/hosts in\n", pihole_conf);
fputs("# the same way as for DHCP-derived names\n", pihole_conf);
@ -158,7 +288,7 @@ bool __attribute__((const)) write_dnsmasq_config(bool test_config)
fputs("\n", pihole_conf);
}
if(config.dnsmasq.expand_hosts.v.b)
if(conf->dnsmasq.expand_hosts.v.b)
{
fputs("# Never forward A or AAAA queries for plain names, without dots or\n", pihole_conf);
fputs("# domain parts, to upstream nameservers\n", pihole_conf);
@ -166,7 +296,7 @@ bool __attribute__((const)) write_dnsmasq_config(bool test_config)
fputs("\n", pihole_conf);
}
if(config.dnsmasq.dnssec.v.b)
if(conf->dnsmasq.dnssec.v.b)
{
fputs("# Use DNNSEC\n", pihole_conf);
fputs("dnssec\n", pihole_conf);
@ -176,38 +306,38 @@ bool __attribute__((const)) write_dnsmasq_config(bool test_config)
fputs("\n", pihole_conf);
}
if(strlen(config.dnsmasq.domain.v.s) > 0 && strcasecmp("none", config.dnsmasq.domain.v.s) != 0)
if(strlen(conf->dnsmasq.domain.v.s) > 0 && strcasecmp("none", conf->dnsmasq.domain.v.s) != 0)
{
fputs("# DNS domain for the DNS server\n", pihole_conf);
fprintf(pihole_conf, "domain=%s\n", config.dnsmasq.domain.v.s);
fprintf(pihole_conf, "domain=%s\n", conf->dnsmasq.domain.v.s);
fputs("\n", pihole_conf);
// When there is a Pi-hole domain set and "Never forward non-FQDNs" is
// ticked, we add `local=/domain/` to signal that this domain is purely
// local and FTL may answer queries from /etc/hosts or DHCP but should
// never forward queries on that domain to any upstream servers
if(config.dnsmasq.domain_needed.v.b)
if(conf->dnsmasq.domain_needed.v.b)
{
fputs("# Never forward A or AAAA queries for plain names, without\n",pihole_conf);
fputs("# dots or domain parts, to upstream nameservers. If the name\n", pihole_conf);
fputs("# is not known from /etc/hosts or DHCP a NXDOMAIN is returned\n", pihole_conf);
fprintf(pihole_conf, "local=/%s/\n",
config.dnsmasq.domain.v.s);
conf->dnsmasq.domain.v.s);
fputs("\n", pihole_conf);
}
}
if(strlen(config.dnsmasq.host_record.v.s) > 0)
if(strlen(conf->dnsmasq.host_record.v.s) > 0)
{
fputs("# Add A, AAAA and PTR records to the DNS\n", pihole_conf);
fprintf(pihole_conf, "host-record=%s\n", config.dnsmasq.host_record.v.s);
fprintf(pihole_conf, "host-record=%s\n", conf->dnsmasq.host_record.v.s);
}
const char *interface = config.dnsmasq.interface.v.s;
const char *interface = conf->dnsmasq.interface.v.s;
// Use eth0 as fallback interface if the interface is missing
if(strlen(interface) == 0)
interface = "eth0";
switch(config.dnsmasq.listening_mode.v.listening_mode)
switch(conf->dnsmasq.listening_mode.v.listening_mode)
{
case LISTEN_LOCAL:
fputs("# Only respond to queries from devices that are at most one hop away (local devices)\n",
@ -230,54 +360,54 @@ bool __attribute__((const)) write_dnsmasq_config(bool test_config)
}
fputs("\n", pihole_conf);
if(config.dnsmasq.rev_server.active.v.b)
if(conf->dnsmasq.rev_server.active.v.b)
{
fputs("# Reverse server setting\n", pihole_conf);
fprintf(pihole_conf, "rev-server=%s,%s\n",
config.dnsmasq.rev_server.cidr.v.s, config.dnsmasq.rev_server.target.v.s);
conf->dnsmasq.rev_server.cidr.v.s, conf->dnsmasq.rev_server.target.v.s);
// If we have a reverse domain, we forward all queries to this domain to
// the same destination
if(strlen(config.dnsmasq.rev_server.domain.v.s) > 0)
if(strlen(conf->dnsmasq.rev_server.domain.v.s) > 0)
fprintf(pihole_conf, "server=/%s/%s\n",
config.dnsmasq.rev_server.domain.v.s, config.dnsmasq.rev_server.target.v.s);
conf->dnsmasq.rev_server.domain.v.s, conf->dnsmasq.rev_server.target.v.s);
// Forward unqualified names to the target only when the "never forward
// non-FQDN" option is NOT ticked
if(!config.dnsmasq.domain_needed.v.b)
if(!conf->dnsmasq.domain_needed.v.b)
fprintf(pihole_conf, "server=//%s\n",
config.dnsmasq.rev_server.target.v.s);
conf->dnsmasq.rev_server.target.v.s);
fputs("\n", pihole_conf);
}
if(config.dnsmasq.dhcp.active.v.b)
if(conf->dnsmasq.dhcp.active.v.b)
{
fputs("# DHCP server setting\n", pihole_conf);
fputs("dhcp-authoritative\n", pihole_conf);
fputs("dhcp-leasefile=/etc/pihole/dhcp.leases\n", pihole_conf);
fprintf(pihole_conf, "dhcp-range=%s,%s,%s\n",
config.dnsmasq.dhcp.start.v.s,
config.dnsmasq.dhcp.end.v.s,
config.dnsmasq.dhcp.leasetime.v.s);
conf->dnsmasq.dhcp.start.v.s,
conf->dnsmasq.dhcp.end.v.s,
conf->dnsmasq.dhcp.leasetime.v.s);
fprintf(pihole_conf, "dhcp-option=option:router,%s\n",
config.dnsmasq.dhcp.router.v.s);
conf->dnsmasq.dhcp.router.v.s);
if(config.dnsmasq.dhcp.rapid_commit.v.b)
if(conf->dnsmasq.dhcp.rapid_commit.v.b)
fputs("dhcp-rapid-commit\n", pihole_conf);
if(config.dnsmasq.dhcp.ipv6.v.b)
if(conf->dnsmasq.dhcp.ipv6.v.b)
{
fputs("dhcp-option=option6:dns-server,[::]\n", pihole_conf);
fprintf(pihole_conf, "dhcp-range=::,constructor:%s,ra-names,ra-stateless,64\n", interface);
}
fputs("\n", pihole_conf);
if(cJSON_GetArraySize(config.dnsmasq.dhcp.hosts.v.json) > 0)
if(cJSON_GetArraySize(conf->dnsmasq.dhcp.hosts.v.json) > 0)
{
fputs("# Per host parameters for the DHCP server\n", pihole_conf);
const int n = cJSON_GetArraySize(config.dnsmasq.dhcp.hosts.v.json);
const int n = cJSON_GetArraySize(conf->dnsmasq.dhcp.hosts.v.json);
for(int i = 0; i < n; i++)
{
cJSON *server = cJSON_GetArrayItem(config.dnsmasq.dhcp.hosts.v.json, i);
cJSON *server = cJSON_GetArrayItem(conf->dnsmasq.dhcp.hosts.v.json, i);
if(server != NULL && cJSON_IsString(server))
fprintf(pihole_conf, "dhcp-host=%s\n", server->valuestring);
}
@ -285,13 +415,13 @@ bool __attribute__((const)) write_dnsmasq_config(bool test_config)
}
}
if(cJSON_GetArraySize(config.dnsmasq.cnames.v.json) > 0)
if(cJSON_GetArraySize(conf->dnsmasq.cnames.v.json) > 0)
{
fputs("# User-defined custom CNAMEs\n", pihole_conf);
const int n = cJSON_GetArraySize(config.dnsmasq.cnames.v.json);
const int n = cJSON_GetArraySize(conf->dnsmasq.cnames.v.json);
for(int i = 0; i < n; i++)
{
cJSON *server = cJSON_GetArrayItem(config.dnsmasq.cnames.v.json, i);
cJSON *server = cJSON_GetArrayItem(conf->dnsmasq.cnames.v.json, i);
if(server != NULL && cJSON_IsString(server))
fprintf(pihole_conf, "cname=%s\n", server->valuestring);
}
@ -344,12 +474,14 @@ bool __attribute__((const)) write_dnsmasq_config(bool test_config)
return false;
}
if(test_config && !test_dnsmasq_config())
log_debug(DEBUG_CONFIG, "Testing "DNSMASQ_TEMP_CONF);
if(test_config && !test_dnsmasq_config(errbuf))
{
log_warn("New dnsmasq configuration is not valid, using previous one");
log_warn("New dnsmasq configuration is not valid (%s), config remains unchanged", errbuf);
return false;
}
log_debug(DEBUG_CONFIG, "Installing "DNSMASQ_TEMP_CONF" to "DNSMASQ_PH_CONFIG);
if(rename(DNSMASQ_TEMP_CONF, DNSMASQ_PH_CONFIG) != 0)
{
log_err("Cannot install dnsmasq config file: %s", strerror(errno));

View File

@ -10,7 +10,13 @@
#ifndef DNSMASQ_CONFIG_H
#define DNSMASQ_CONFIG_H
bool write_dnsmasq_config(bool test_config);
#include "config/config.h"
#define ERRBUF_SIZE 1024
bool write_dnsmasq_config(struct config *conf, bool test_config, char errbuf[ERRBUF_SIZE]);
int get_lineno_from_string(const char *string);
char *get_dnsmasq_line(const unsigned int lineno);
bool read_legacy_dhcp_static_config(void);
bool read_legacy_cnames_config(void);

View File

@ -52,7 +52,7 @@ bool readFTLtoml(void)
for(unsigned int i = 0; i < CONFIG_ELEMENTS; i++)
{
// Get pointer to memory location of this conf_item
struct conf_item *conf_item = get_conf_item(i);
struct conf_item *conf_item = get_conf_item(&config, i);
// Get config path depth
unsigned int level = config_path_depth(conf_item->p);

View File

@ -45,7 +45,7 @@ bool writeFTLtoml(const bool verbose)
for(unsigned int i = 0; i < CONFIG_ELEMENTS; i++)
{
// Get pointer to memory location of this conf_item
struct conf_item *conf_item = get_conf_item(i);
struct conf_item *conf_item = get_conf_item(&config, i);
// Get path depth
unsigned int level = config_path_depth(conf_item->p);

View File

@ -6067,3 +6067,13 @@ void read_opts(int argc, char **argv, char *compile_opts)
exit(0);
}
}
/******************** Pi-hole extension ********************/
void reset_usage_indicator(void)
{
for (unsigned int i = 0; usage[i].opt != 0; i++)
if(usage[i].rept == ARG_USED_CL ||
usage[i].rept == ARG_USED_FILE)
usage[i].rept = ARG_ONE;
}
/**********************************************************/

View File

@ -134,7 +134,7 @@ int main (int argc, char *argv[])
startup = false;
main_dnsmasq(argc_dnsmasq, argv_dnsmasq);
log_info("Shutting down...");
log_info("Shutting down... // exit code %d", exit_code);
// Extra grace time is needed as dnsmasq script-helpers and the API may not
// be terminating immediately
sleepms(250);

View File

@ -402,6 +402,9 @@ void _lock_shm(const char *func, const int line, const char *file)
log_debug(DEBUG_LOCKS, "Waiting for SHM lock in %s() (%s:%i)", func, file, line);
log_debug(DEBUG_LOCKS, "SHM lock: %p", shmLock);
if(shmLock == NULL)
return;
int result = pthread_mutex_lock(&shmLock->lock.outer);
if(result != 0)
@ -460,6 +463,9 @@ void _unlock_shm(const char* func, const int line, const char * file)
(long int)shmLock->owner.pid, (long int)shmLock->owner.tid);
}
if(shmLock == NULL)
return;
// Unlock mutex
int result = pthread_mutex_unlock(&shmLock->lock.inner);
shmLock->owner.pid = 0;
@ -861,6 +867,9 @@ static void delete_shm(SharedMemory *sharedMemory)
sharedMemory->ptr, sharedMemory->size, strerror(errno));
}
// Set unmapped pointer to NULL
sharedMemory->ptr = NULL;
// Now you can no longer `shm_open` the memory, and once all others
// unlink, it will be destroyed.
if(shm_unlink(sharedMemory->name) != 0)