From ef4e592ea9052cabc51a6fd72d89c14f481eabd2 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 21 Jan 2024 20:16:09 +0100 Subject: [PATCH] Verify that every config option has a validator. Otherwise, return a log error that will fail the CI tests. This will help ensuring we won't forget to add validators for PRs that are merged in parallel or new config options added in the future Signed-off-by: DL6ER --- src/api/config.c | 19 +++---- src/config/config.c | 124 ++++++++++++++++++++++++++++++++++++++++- src/config/validator.c | 7 +++ src/config/validator.h | 1 + 4 files changed, 139 insertions(+), 12 deletions(-) diff --git a/src/api/config.c b/src/api/config.c index 5d4e68c9..a9b9eafd 100644 --- a/src/api/config.c +++ b/src/api/config.c @@ -767,17 +767,14 @@ static int api_config_patch(struct ftl_conn *api) // If we reach this point, a valid setting was found and changed // Validate new value (if validation function is defined) - if(conf_item->c != NULL) + char errbuf[VALIDATOR_ERRBUF_LEN] = { 0 }; + if(!conf_item->c(&new_item->v, errbuf)) { - char errbuf[VALIDATOR_ERRBUF_LEN] = { 0 }; - if(!conf_item->c(&new_item->v, errbuf)) - { - free_config(&newconf); - return send_json_error(api, 400, - "bad_request", - "Invalid value", - errbuf); - } + free_config(&newconf); + return send_json_error(api, 400, + "bad_request", + "Invalid value", + errbuf); } // Check if this item requires a config-rewrite + restart of dnsmasq @@ -965,7 +962,7 @@ static int api_config_put_delete(struct ftl_conn *api) // If we reach this point, a valid setting was found and changed // Validate new value on PUT (if validation function is defined) - if(new_item->c != NULL && api->method == HTTP_PUT) + if(api->method == HTTP_PUT) { char errbuf[VALIDATOR_ERRBUF_LEN] = { 0 }; if(!new_item->c(&new_item->v, errbuf)) diff --git a/src/config/config.c b/src/config/config.c index 60c19e5a..7c87dd5e 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -388,42 +388,49 @@ void initConfig(struct config *conf) conf->dns.upstreams.t = CONF_JSON_STRING_ARRAY; conf->dns.upstreams.d.json = cJSON_CreateArray(); conf->dns.upstreams.f = FLAG_RESTART_FTL; + conf->dns.upstreams.c = validate_stub; // Type-based checking + dnsmasq syntax checking conf->dns.CNAMEdeepInspect.k = "dns.CNAMEdeepInspect"; conf->dns.CNAMEdeepInspect.h = "Use this option to control deep CNAME inspection. Disabling it might be beneficial for very low-end devices"; conf->dns.CNAMEdeepInspect.t = CONF_BOOL; conf->dns.CNAMEdeepInspect.f = FLAG_ADVANCED_SETTING; conf->dns.CNAMEdeepInspect.d.b = true; + conf->dns.CNAMEdeepInspect.c = validate_stub; // Only type-based checking conf->dns.blockESNI.k = "dns.blockESNI"; conf->dns.blockESNI.h = "Should _esni. subdomains be blocked by default? Encrypted Server Name Indication (ESNI) is certainly a good step into the right direction to enhance privacy on the web. It prevents on-path observers, including ISPs, coffee shop owners and firewalls, from intercepting the TLS Server Name Indication (SNI) extension by encrypting it. This prevents the SNI from being used to determine which websites users are visiting.\n ESNI will obviously cause issues for pixelserv-tls which will be unable to generate matching certificates on-the-fly when it cannot read the SNI. Cloudflare and Firefox are already enabling ESNI. According to the IEFT draft (link above), we can easily restore piselserv-tls's operation by replying NXDOMAIN to _esni. subdomains of blocked domains as this mimics a \"not configured for this domain\" behavior."; conf->dns.blockESNI.t = CONF_BOOL; conf->dns.blockESNI.f = FLAG_ADVANCED_SETTING; conf->dns.blockESNI.d.b = true; + conf->dns.blockESNI.c = validate_stub; // Only type-based checking conf->dns.EDNS0ECS.k = "dns.EDNS0ECS"; conf->dns.EDNS0ECS.h = "Should we overwrite the query source when client information is provided through EDNS0 client subnet (ECS) information? This allows Pi-hole to obtain client IPs even if they are hidden behind the NAT of a router. This feature has been requested and discussed on Discourse where further information how to use it can be found: https://discourse.pi-hole.net/t/support-for-add-subnet-option-from-dnsmasq-ecs-edns0-client-subnet/35940"; conf->dns.EDNS0ECS.t = CONF_BOOL; conf->dns.EDNS0ECS.f = FLAG_ADVANCED_SETTING; conf->dns.EDNS0ECS.d.b = true; + conf->dns.EDNS0ECS.c = validate_stub; // Only type-based checking conf->dns.ignoreLocalhost.k = "dns.ignoreLocalhost"; conf->dns.ignoreLocalhost.h = "Should FTL hide queries made by localhost?"; conf->dns.ignoreLocalhost.t = CONF_BOOL; conf->dns.ignoreLocalhost.f = FLAG_ADVANCED_SETTING; conf->dns.ignoreLocalhost.d.b = false; + conf->dns.ignoreLocalhost.c = validate_stub; // Only type-based checking conf->dns.showDNSSEC.k = "dns.showDNSSEC"; conf->dns.showDNSSEC.h = "Should FTL should analyze and show internally generated DNSSEC queries?"; conf->dns.showDNSSEC.t = CONF_BOOL; conf->dns.showDNSSEC.f = FLAG_ADVANCED_SETTING; conf->dns.showDNSSEC.d.b = true; + conf->dns.showDNSSEC.c = validate_stub; // Only type-based checking conf->dns.analyzeOnlyAandAAAA.k = "dns.analyzeOnlyAandAAAA"; conf->dns.analyzeOnlyAandAAAA.h = "Should FTL analyze *only* A and AAAA queries?"; conf->dns.analyzeOnlyAandAAAA.t = CONF_BOOL; conf->dns.analyzeOnlyAandAAAA.f = FLAG_ADVANCED_SETTING; conf->dns.analyzeOnlyAandAAAA.d.b = false; + conf->dns.analyzeOnlyAandAAAA.c = validate_stub; // Only type-based checking conf->dns.piholePTR.k = "dns.piholePTR"; conf->dns.piholePTR.h = "Controls whether and how FTL will reply with for address for which a local interface exists."; @@ -440,6 +447,7 @@ void initConfig(struct config *conf) conf->dns.piholePTR.t = CONF_ENUM_PTR_TYPE; conf->dns.piholePTR.f = FLAG_ADVANCED_SETTING; conf->dns.piholePTR.d.ptr_type = PTR_PIHOLE; + conf->dns.piholePTR.c = validate_stub; // Only type-based checking conf->dns.replyWhenBusy.k = "dns.replyWhenBusy"; conf->dns.replyWhenBusy.h = "How should FTL handle queries when the gravity database is not available?"; @@ -456,12 +464,14 @@ void initConfig(struct config *conf) conf->dns.replyWhenBusy.t = CONF_ENUM_BUSY_TYPE; conf->dns.replyWhenBusy.f = FLAG_ADVANCED_SETTING; conf->dns.replyWhenBusy.d.busy_reply = BUSY_ALLOW; + conf->dns.replyWhenBusy.c = validate_stub; // Only type-based checking conf->dns.blockTTL.k = "dns.blockTTL"; conf->dns.blockTTL.h = "FTL's internal TTL to be handed out for blocked queries in seconds. This settings allows users to select a value different from the dnsmasq config option local-ttl. This is useful in context of locally used hostnames that are known to stay constant over long times (printers, etc.).\n Note that large values may render whitelisting ineffective due to client-side caching of blocked queries."; conf->dns.blockTTL.t = CONF_UINT; conf->dns.blockTTL.f = FLAG_ADVANCED_SETTING; conf->dns.blockTTL.d.ui = 2; + conf->dns.blockTTL.c = validate_stub; // Only type-based checking conf->dns.hosts.k = "dns.hosts"; conf->dns.hosts.h = "Array of custom DNS records\n Example: hosts = [ \"127.0.0.1 mylocal\", \"192.168.0.1 therouter\" ]"; @@ -476,12 +486,14 @@ void initConfig(struct config *conf) conf->dns.domainNeeded.t = CONF_BOOL; conf->dns.domainNeeded.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING; conf->dns.domainNeeded.d.b = false; + conf->dns.domainNeeded.c = validate_stub; // Only type-based checking conf->dns.expandHosts.k = "dns.expandHosts"; conf->dns.expandHosts.h = "If set, the domain is added to simple names (without a period) in /etc/hosts in the same way as for DHCP-derived names"; conf->dns.expandHosts.t = CONF_BOOL; conf->dns.expandHosts.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING; conf->dns.expandHosts.d.b = false; + conf->dns.expandHosts.c = validate_stub; // Only type-based checking conf->dns.domain.k = "dns.domain"; conf->dns.domain.h = "The DNS domain used by your Pi-hole to expand hosts and for DHCP.\n\n Only if DHCP is enabled below: For DHCP, this has two effects; firstly it causes the DHCP server to return the domain to any hosts which request it, and secondly it sets the domain which it is legal for DHCP-configured hosts to claim. The intention is to constrain hostnames so that an untrusted host on the LAN cannot advertise its name via DHCP as e.g. \"google.com\" and capture traffic not meant for it. If no domain suffix is specified, then any DHCP hostname with a domain part (ie with a period) will be disallowed and logged. If a domain is specified, then hostnames with a domain part are allowed, provided the domain part matches the suffix. In addition, when a suffix is set then hostnames without a domain part have the suffix added as an optional domain part. For instance, we can set domain=mylab.com and have a machine whose DHCP hostname is \"laptop\". The IP address for that machine is available both as \"laptop\" and \"laptop.mylab.com\".\n\n You can disable setting a domain by setting this option to an empty string."; @@ -496,12 +508,14 @@ void initConfig(struct config *conf) conf->dns.bogusPriv.t = CONF_BOOL; conf->dns.bogusPriv.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING; conf->dns.bogusPriv.d.b = true; + conf->dns.bogusPriv.c = validate_stub; // Only type-based checking conf->dns.dnssec.k = "dns.dnssec"; conf->dns.dnssec.h = "Validate DNS replies using DNSSEC?"; conf->dns.dnssec.t = CONF_BOOL; conf->dns.dnssec.f = FLAG_RESTART_FTL; conf->dns.dnssec.d.b = true; + conf->dns.dnssec.c = validate_stub; // Only type-based checking conf->dns.interface.k = "dns.interface"; conf->dns.interface.h = "Interface to use for DNS (see also dnsmasq.listening.mode) and DHCP (if enabled)"; @@ -509,6 +523,7 @@ void initConfig(struct config *conf) conf->dns.interface.t = CONF_STRING; conf->dns.interface.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING; conf->dns.interface.d.s = (char*)""; + conf->dns.interface.c = validate_stub; // Type-based checking + dnsmasq syntax checking conf->dns.hostRecord.k = "dns.hostRecord"; conf->dns.hostRecord.h = "Add A, AAAA and PTR records to the DNS. This adds one or more names to the DNS with associated IPv4 (A) and IPv6 (AAAA) records"; @@ -516,6 +531,7 @@ void initConfig(struct config *conf) conf->dns.hostRecord.t = CONF_STRING; conf->dns.hostRecord.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING; conf->dns.hostRecord.d.s = (char*)""; + conf->dns.hostRecord.c = validate_stub; // Type-based checking + dnsmasq syntax checking conf->dns.listeningMode.k = "dns.listeningMode"; conf->dns.listeningMode.h = "Pi-hole interface listening modes"; @@ -533,12 +549,14 @@ void initConfig(struct config *conf) conf->dns.listeningMode.t = CONF_ENUM_LISTENING_MODE; conf->dns.listeningMode.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING; conf->dns.listeningMode.d.listeningMode = LISTEN_LOCAL; + conf->dns.listeningMode.c = validate_stub; // Only type-based checking conf->dns.queryLogging.k = "dns.queryLogging"; conf->dns.queryLogging.h = "Log DNS queries and replies to pihole.log"; conf->dns.queryLogging.t = CONF_BOOL; conf->dns.queryLogging.f = FLAG_RESTART_FTL; conf->dns.queryLogging.d.b = true; + conf->dns.queryLogging.c = validate_stub; // Only type-based checking conf->dns.cnameRecords.k = "dns.cnameRecords"; conf->dns.cnameRecords.h = "List of CNAME records which indicate that is really . If the is given, it overwrites the value of local-ttl"; @@ -553,6 +571,7 @@ void initConfig(struct config *conf) conf->dns.port.t = CONF_UINT16; conf->dns.port.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING; conf->dns.port.d.ui = 53u; + conf->dns.port.c = validate_stub; // Only type-based checking // sub-struct dns.cache conf->dns.cache.size.k = "dns.cache.size"; @@ -560,18 +579,21 @@ void initConfig(struct config *conf) conf->dns.cache.size.t = CONF_UINT; conf->dns.cache.size.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING; conf->dns.cache.size.d.ui = 10000u; + conf->dns.cache.size.c = validate_stub; // Only type-based checking conf->dns.cache.optimizer.k = "dns.cache.optimizer"; conf->dns.cache.optimizer.h = "Query cache optimizer: If a DNS name exists in the cache, but its time-to-live has expired only recently, the data will be used anyway (a refreshing from upstream is triggered). This can improve DNS query delays especially over unreliable Internet connections. This feature comes at the expense of possibly sometimes returning out-of-date data and less efficient cache utilization, since old data cannot be flushed when its TTL expires, so the cache becomes mostly least-recently-used. To mitigate issues caused by massively outdated DNS replies, the maximum overaging of cached records is limited. We strongly recommend staying below 86400 (1 day) with this option.\n Setting the TTL excess time to zero will serve stale cache data regardless how long it has expired. This is not recommended as it may lead to stale data being served for a long time. Setting this option to any negative value will disable this feature altogether."; conf->dns.cache.optimizer.t = CONF_INT; conf->dns.cache.optimizer.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING; conf->dns.cache.optimizer.d.i = 3600u; + conf->dns.cache.optimizer.c = validate_stub; // Only type-based checking // sub-struct dns.blocking conf->dns.blocking.active.k = "dns.blocking.active"; conf->dns.blocking.active.h = "Should FTL block queries?"; conf->dns.blocking.active.t = CONF_BOOL; conf->dns.blocking.active.d.b = true; + conf->dns.blocking.active.c = validate_stub; // Only type-based checking conf->dns.blocking.mode.k = "dns.blocking.mode"; conf->dns.blocking.mode.h = "How should FTL reply to blocked queries?"; @@ -588,28 +610,33 @@ void initConfig(struct config *conf) } conf->dns.blocking.mode.t = CONF_ENUM_BLOCKING_MODE; conf->dns.blocking.mode.d.blocking_mode = MODE_NULL; + conf->dns.blocking.mode.c = validate_stub; // Only type-based checking // sub-struct dns.rate_limit conf->dns.rateLimit.count.k = "dns.rateLimit.count"; conf->dns.rateLimit.count.h = "Rate-limited queries are answered with a REFUSED reply and not further processed by FTL.\n The default settings for FTL's rate-limiting are to permit no more than 1000 queries in 60 seconds. Both numbers can be customized independently. It is important to note that rate-limiting is happening on a per-client basis. Other clients can continue to use FTL while rate-limited clients are short-circuited at the same time.\n For this setting, both numbers, the maximum number of queries within a given time, and the length of the time interval (seconds) have to be specified. For instance, if you want to set a rate limit of 1 query per hour, the option should look like RATE_LIMIT=1/3600. The time interval is relative to when FTL has finished starting (start of the daemon + possible delay by DELAY_STARTUP) then it will advance in steps of the rate-limiting interval. If a client reaches the maximum number of queries it will be blocked until the end of the current interval. This will be logged to /var/log/pihole/FTL.log, e.g. Rate-limiting 10.0.1.39 for at least 44 seconds. If the client continues to send queries while being blocked already and this number of queries during the blocking exceeds the limit the client will continue to be blocked until the end of the next interval (FTL.log will contain lines like Still rate-limiting 10.0.1.39 as it made additional 5007 queries). As soon as the client requests less than the set limit, it will be unblocked (Ending rate-limitation of 10.0.1.39).\n Rate-limiting may be disabled altogether by setting both values to zero (this results in the same behavior as before FTL v5.7).\n How many queries are permitted..."; conf->dns.rateLimit.count.t = CONF_UINT; conf->dns.rateLimit.count.d.ui = 1000; + conf->dns.rateLimit.count.c = validate_stub; // Only type-based checking conf->dns.rateLimit.interval.k = "dns.rateLimit.interval"; conf->dns.rateLimit.interval.h = "... in the set interval before rate-limiting?"; conf->dns.rateLimit.interval.t = CONF_UINT; conf->dns.rateLimit.interval.d.ui = 60; + conf->dns.rateLimit.interval.c = validate_stub; // Only type-based checking // sub-struct dns.special_domains conf->dns.specialDomains.mozillaCanary.k = "dns.specialDomains.mozillaCanary"; conf->dns.specialDomains.mozillaCanary.h = "Should Pi-hole always replies with NXDOMAIN to A and AAAA queries of use-application-dns.net to disable Firefox automatic DNS-over-HTTP? This is following the recommendation on https://support.mozilla.org/en-US/kb/configuring-networks-disable-dns-over-https"; conf->dns.specialDomains.mozillaCanary.t = CONF_BOOL; conf->dns.specialDomains.mozillaCanary.d.b = true; + conf->dns.specialDomains.mozillaCanary.c = validate_stub; // Only type-based checking conf->dns.specialDomains.iCloudPrivateRelay.k = "dns.specialDomains.iCloudPrivateRelay"; conf->dns.specialDomains.iCloudPrivateRelay.h = "Should Pi-hole always replies with NXDOMAIN to A and AAAA queries of mask.icloud.com and mask-h2.icloud.com to disable Apple's iCloud Private Relay to prevent Apple devices from bypassing Pi-hole? This is following the recommendation on https://developer.apple.com/support/prepare-your-network-for-icloud-private-relay"; conf->dns.specialDomains.iCloudPrivateRelay.t = CONF_BOOL; conf->dns.specialDomains.iCloudPrivateRelay.d.b = true; + conf->dns.specialDomains.iCloudPrivateRelay.c = validate_stub; // Only type-based checking // sub-struct dns.reply_addr conf->dns.reply.host.force4.k = "dns.reply.host.force4"; @@ -617,6 +644,7 @@ void initConfig(struct config *conf) conf->dns.reply.host.force4.t = CONF_BOOL; conf->dns.reply.host.force4.f = FLAG_ADVANCED_SETTING; conf->dns.reply.host.force4.d.b = false; + conf->dns.reply.host.force4.c = validate_stub; // Only type-based checking conf->dns.reply.host.v4.k = "dns.reply.host.IPv4"; conf->dns.reply.host.v4.h = "Custom IPv4 address for the Pi-hole host"; @@ -624,12 +652,14 @@ void initConfig(struct config *conf) conf->dns.reply.host.v4.t = CONF_STRUCT_IN_ADDR; conf->dns.reply.host.v4.f = FLAG_ADVANCED_SETTING; memset(&conf->dns.reply.host.v4.d.in_addr, 0, sizeof(struct in_addr)); + conf->dns.reply.host.v4.c = validate_stub; // Only type-based checking conf->dns.reply.host.force6.k = "dns.reply.host.force6"; conf->dns.reply.host.force6.h = "Use a specific IPv6 address for the Pi-hole host? See description for the IPv4 variant above for further details."; conf->dns.reply.host.force6.t = CONF_BOOL; conf->dns.reply.host.force6.f = FLAG_ADVANCED_SETTING; conf->dns.reply.host.force6.d.b = false; + conf->dns.reply.host.force6.c = validate_stub; // Only type-based checking conf->dns.reply.host.v6.k = "dns.reply.host.IPv6"; conf->dns.reply.host.v6.h = "Custom IPv6 address for the Pi-hole host"; @@ -637,12 +667,14 @@ void initConfig(struct config *conf) conf->dns.reply.host.v6.t = CONF_STRUCT_IN6_ADDR; conf->dns.reply.host.v6.f = FLAG_ADVANCED_SETTING; memset(&conf->dns.reply.host.v6.d.in6_addr, 0, sizeof(struct in6_addr)); + conf->dns.reply.host.v6.c = validate_stub; // Only type-based checking conf->dns.reply.blocking.force4.k = "dns.reply.blocking.force4"; conf->dns.reply.blocking.force4.h = "Use a specific IPv4 address in IP blocking mode? By default, FTL determines the address of the interface a query arrived on and uses this address for replying to A queries with the most suitable address for the requesting client. This setting can be used to use a fixed, rather than the dynamically obtained, address when Pi-hole responds in the following cases: IP blocking mode is used and this query is to be blocked, regular expressions with the ;reply=IP regex extension."; conf->dns.reply.blocking.force4.t = CONF_BOOL; conf->dns.reply.blocking.force4.f = FLAG_ADVANCED_SETTING; conf->dns.reply.blocking.force4.d.b = false; + conf->dns.reply.blocking.force4.c = validate_stub; // Only type-based checking conf->dns.reply.blocking.v4.k = "dns.reply.blocking.IPv4"; conf->dns.reply.blocking.v4.h = "Custom IPv4 address for IP blocking mode"; @@ -650,12 +682,14 @@ void initConfig(struct config *conf) conf->dns.reply.blocking.v4.t = CONF_STRUCT_IN_ADDR; conf->dns.reply.blocking.v4.f = FLAG_ADVANCED_SETTING; memset(&conf->dns.reply.blocking.v4.d.in_addr, 0, sizeof(struct in_addr)); + conf->dns.reply.blocking.v4.c = validate_stub; // Only type-based checking conf->dns.reply.blocking.force6.k = "dns.reply.blocking.force6"; conf->dns.reply.blocking.force6.h = "Use a specific IPv6 address in IP blocking mode? See description for the IPv4 variant above for further details."; conf->dns.reply.blocking.force6.t = CONF_BOOL; conf->dns.reply.blocking.force6.f = FLAG_ADVANCED_SETTING; conf->dns.reply.blocking.force6.d.b = false; + conf->dns.reply.blocking.force6.c = validate_stub; // Only type-based checking conf->dns.reply.blocking.v6.k = "dns.reply.blocking.IPv6"; conf->dns.reply.blocking.v6.h = "Custom IPv6 address for IP blocking mode"; @@ -663,6 +697,7 @@ void initConfig(struct config *conf) conf->dns.reply.blocking.v6.t = CONF_STRUCT_IN6_ADDR; conf->dns.reply.blocking.v6.f = FLAG_ADVANCED_SETTING; memset(&conf->dns.reply.blocking.v6.d.in6_addr, 0, sizeof(struct in6_addr)); + conf->dns.reply.blocking.v6.c = validate_stub; // Only type-based checking // sub-struct revServer conf->dns.revServer.active.k = "dns.revServer.active"; @@ -670,6 +705,7 @@ void initConfig(struct config *conf) conf->dns.revServer.active.t = CONF_BOOL; conf->dns.revServer.active.d.b = false; conf->dns.revServer.active.f = FLAG_RESTART_FTL; + conf->dns.revServer.active.c = validate_stub; // Only type-based checking conf->dns.revServer.cidr.k = "dns.revServer.cidr"; conf->dns.revServer.cidr.h = "Address range for the reverse server feature in CIDR notation. If the prefix length is omitted, either 32 (IPv4) or 128 (IPv6) are substitutet (exact address match). This is almost certainly not what you want here."; @@ -701,6 +737,7 @@ void initConfig(struct config *conf) conf->dhcp.active.t = CONF_BOOL; conf->dhcp.active.f = FLAG_RESTART_FTL; conf->dhcp.active.d.b = false; + conf->dhcp.active.c = validate_stub; // Only type-based checking conf->dhcp.start.k = "dhcp.start"; conf->dhcp.start.h = "Start address of the DHCP address pool"; @@ -708,6 +745,7 @@ void initConfig(struct config *conf) conf->dhcp.start.t = CONF_STRUCT_IN_ADDR; conf->dhcp.start.f = FLAG_RESTART_FTL; memset(&conf->dhcp.start.d.in_addr, 0, sizeof(struct in_addr)); + conf->dhcp.start.c = validate_stub; // Only type-based checking conf->dhcp.end.k = "dhcp.end"; conf->dhcp.end.h = "End address of the DHCP address pool"; @@ -715,6 +753,7 @@ void initConfig(struct config *conf) conf->dhcp.end.t = CONF_STRUCT_IN_ADDR; conf->dhcp.end.f = FLAG_RESTART_FTL; memset(&conf->dhcp.end.d.in_addr, 0, sizeof(struct in_addr)); + conf->dhcp.end.c = validate_stub; // Only type-based checking conf->dhcp.router.k = "dhcp.router"; conf->dhcp.router.h = "Address of the gateway to be used (typically the address of your router in a home installation)"; @@ -722,6 +761,7 @@ void initConfig(struct config *conf) conf->dhcp.router.t = CONF_STRUCT_IN_ADDR; conf->dhcp.router.f = FLAG_RESTART_FTL; memset(&conf->dhcp.router.d.in_addr, 0, sizeof(struct in_addr)); + conf->dhcp.router.c = validate_stub; // Only type-based checking conf->dhcp.netmask.k = "dhcp.netmask"; conf->dhcp.netmask.h = "The netmask used by your Pi-hole. For directly connected networks (i.e., networks on which the machine running Pi-hole has an interface) the netmask is optional and may be set to an empty string (\"\"): it will then be determined from the interface configuration itself. For networks which receive DHCP service via a relay agent, we cannot determine the netmask itself, so it should explicitly be specified, otherwise Pi-hole guesses based on the class (A, B or C) of the network address."; @@ -729,6 +769,7 @@ void initConfig(struct config *conf) conf->dhcp.netmask.t = CONF_STRUCT_IN_ADDR; conf->dhcp.netmask.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING; memset(&conf->dhcp.netmask.d.in_addr, 0, sizeof(struct in_addr)); + conf->dhcp.netmask.c = validate_stub; // Only type-based checking conf->dhcp.leaseTime.k = "dhcp.leaseTime"; conf->dhcp.leaseTime.h = "If the lease time is given, then leases will be given for that length of time. If not given, the default lease time is one hour for IPv4 and one day for IPv6."; @@ -736,24 +777,28 @@ void initConfig(struct config *conf) conf->dhcp.leaseTime.t = CONF_STRING; conf->dhcp.leaseTime.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING; conf->dhcp.leaseTime.d.s = (char*)""; + conf->dhcp.leaseTime.c = validate_stub; // Type-based checking + dnsmasq syntax checking conf->dhcp.ipv6.k = "dhcp.ipv6"; conf->dhcp.ipv6.h = "Should Pi-hole make an attempt to also satisfy IPv6 address requests (be aware that IPv6 works a whole lot different than IPv4)"; conf->dhcp.ipv6.t = CONF_BOOL; conf->dhcp.ipv6.f = FLAG_RESTART_FTL; conf->dhcp.ipv6.d.b = false; + conf->dhcp.ipv6.c = validate_stub; // Only type-based checking conf->dhcp.multiDNS.k = "dhcp.multiDNS"; conf->dhcp.multiDNS.h = "Advertise DNS server multiple times to clients. Some devices will add their own proprietary DNS servers to the list of DNS servers, which can cause issues with Pi-hole. This option will advertise the Pi-hole DNS server multiple times to clients, which should prevent this from happening."; conf->dhcp.multiDNS.t = CONF_BOOL; conf->dhcp.multiDNS.f = FLAG_RESTART_FTL; conf->dhcp.multiDNS.d.b = false; + conf->dhcp.multiDNS.c = validate_stub; // Only type-based checking conf->dhcp.rapidCommit.k = "dhcp.rapidCommit"; conf->dhcp.rapidCommit.h = "Enable DHCPv4 Rapid Commit Option specified in RFC 4039. Should only be enabled if either the server is the only server for the subnet to avoid conflicts"; conf->dhcp.rapidCommit.t = CONF_BOOL; conf->dhcp.rapidCommit.f = FLAG_RESTART_FTL; conf->dhcp.rapidCommit.d.b = false; + conf->dhcp.rapidCommit.c = validate_stub; // Only type-based checking conf->dhcp.hosts.k = "dhcp.hosts"; conf->dhcp.hosts.h = "Per host parameters for the DHCP server. This allows a machine with a particular hardware address to be always allocated the same hostname, IP address and lease time or to specify static DHCP leases"; @@ -761,6 +806,7 @@ void initConfig(struct config *conf) conf->dhcp.hosts.t = CONF_JSON_STRING_ARRAY; conf->dhcp.hosts.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING; conf->dhcp.hosts.d.json = cJSON_CreateArray(); + conf->dhcp.hosts.c = validate_stub; // Type-based checking + dnsmasq syntax checking // struct resolver @@ -768,17 +814,20 @@ void initConfig(struct config *conf) conf->resolver.resolveIPv6.h = "Should FTL try to resolve IPv6 addresses to hostnames?"; conf->resolver.resolveIPv6.t = CONF_BOOL; conf->resolver.resolveIPv6.d.b = true; + conf->resolver.resolveIPv6.c = validate_stub; // Only type-based checking conf->resolver.resolveIPv4.k = "resolver.resolveIPv4"; conf->resolver.resolveIPv4.h = "Should FTL try to resolve IPv4 addresses to hostnames?"; conf->resolver.resolveIPv4.t = CONF_BOOL; conf->resolver.resolveIPv4.d.b = true; + conf->resolver.resolveIPv4.c = validate_stub; // Only type-based checking conf->resolver.networkNames.k = "resolver.networkNames"; conf->resolver.networkNames.h = "Control whether FTL should use the fallback option to try to obtain client names from checking the network table. This behavior can be disabled with this option.\n Assume an IPv6 client without a host names. However, the network table knows - though the client's MAC address - that this is the same device where we have a host name for another IP address (e.g., a DHCP server managed IPv4 address). In this case, we use the host name associated to the other address as this is the same device."; conf->resolver.networkNames.t = CONF_BOOL; conf->resolver.networkNames.f = FLAG_ADVANCED_SETTING; conf->resolver.networkNames.d.b = true; + conf->resolver.networkNames.c = validate_stub; // Only type-based checking conf->resolver.refreshNames.k = "resolver.refreshNames"; conf->resolver.refreshNames.h = "With this option, you can change how (and if) hourly PTR requests are made to check for changes in client and upstream server hostnames."; @@ -795,6 +844,7 @@ void initConfig(struct config *conf) conf->resolver.refreshNames.t = CONF_ENUM_REFRESH_HOSTNAMES; conf->resolver.refreshNames.f = FLAG_ADVANCED_SETTING; conf->resolver.refreshNames.d.refresh_hostnames = REFRESH_IPV4_ONLY; + conf->resolver.refreshNames.c = validate_stub; // Only type-based checking // struct database @@ -802,21 +852,25 @@ void initConfig(struct config *conf) conf->database.DBimport.h = "Should FTL load information from the database on startup to be aware of the most recent history?"; conf->database.DBimport.t = CONF_BOOL; conf->database.DBimport.d.b = true; + conf->database.DBimport.c = validate_stub; // Only type-based checking conf->database.DBexport.k = "database.DBexport"; conf->database.DBexport.h = "Should FTL store queries in the long-term database?"; conf->database.DBexport.t = CONF_BOOL; conf->database.DBexport.d.b = true; + conf->database.DBexport.c = validate_stub; // Only type-based checking conf->database.maxDBdays.k = "database.maxDBdays"; conf->database.maxDBdays.h = "How long should queries be stored in the database [days]?"; conf->database.maxDBdays.t = CONF_INT; conf->database.maxDBdays.d.i = (365/4); + conf->database.maxDBdays.c = validate_stub; // Only type-based checking conf->database.DBinterval.k = "database.DBinterval"; conf->database.DBinterval.h = "How often do we store queries in FTL's database [seconds]?"; conf->database.DBinterval.t = CONF_UINT; conf->database.DBinterval.d.ui = 60; + conf->database.DBinterval.c = validate_stub; // Only type-based checking // sub-struct database.network conf->database.network.parseARPcache.k = "database.network.parseARPcache"; @@ -824,12 +878,14 @@ void initConfig(struct config *conf) conf->database.network.parseARPcache.t = CONF_BOOL; conf->database.network.parseARPcache.f = FLAG_ADVANCED_SETTING; conf->database.network.parseARPcache.d.b = true; + conf->database.network.parseARPcache.c = validate_stub; // Only type-based checking conf->database.network.expire.k = "database.network.expire"; conf->database.network.expire.h = "How long should IP addresses be kept in the network_addresses table [days]? IP addresses (and associated host names) older than the specified number of days are removed to avoid dead entries in the network overview table."; conf->database.network.expire.t = CONF_UINT; conf->database.network.expire.f = FLAG_ADVANCED_SETTING; conf->database.network.expire.d.ui = conf->database.maxDBdays.d.ui; + conf->database.network.expire.c = validate_stub; // Only type-based checking // struct http @@ -847,6 +903,7 @@ void initConfig(struct config *conf) conf->webserver.acl.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL; conf->webserver.acl.t = CONF_STRING; conf->webserver.acl.d.s = (char*)""; + conf->webserver.acl.c = validate_stub; // Type-based checking + civetweb syntax checking conf->webserver.port.k = "webserver.port"; conf->webserver.port.h = "Ports to be used by the webserver.\n Comma-separated list of ports to listen on. It is possible to specify an IP address to bind to. In this case, an IP address and a colon must be prepended to the port number. For example, to bind to the loopback interface on port 80 (IPv4) and to all interfaces port 8080 (IPv4), use \"127.0.0.1:80,8080\". \"[::]:80\" can be used to listen to IPv6 connections to port 80. IPv6 addresses of network interfaces can be specified as well, e.g. \"[::1]:80\" for the IPv6 loopback interface. [::]:80 will bind to port 80 IPv6 only.\n In order to use port 80 for all interfaces, both IPv4 and IPv6, use either the configuration \"80,[::]:80\" (create one socket for IPv4 and one for IPv6 only), or \"+80\" (create one socket for both, IPv4 and IPv6). The + notation to use IPv4 and IPv6 will only work if no network interface is specified. Depending on your operating system version and IPv6 network environment, some configurations might not work as expected, so you have to test to find the configuration most suitable for your needs. In case \"+80\" does not work for your environment, you need to use \"80,[::]:80\".\n If the port is TLS/SSL, a letter 's' must be appended, for example, \"80,443s\" will open port 80 and port 443, and connections on port 443 will be encrypted. For non-encrypted ports, it is allowed to append letter 'r' (as in redirect). Redirected ports will redirect all their traffic to the first configured SSL port. For example, if webserver.port is \"80r,443s\", then all HTTP traffic coming at port 80 will be redirected to HTTPS port 443. If this value is not set (empty string), the web server will not be started and, hence, the API will not be available."; @@ -854,12 +911,14 @@ void initConfig(struct config *conf) conf->webserver.port.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL; conf->webserver.port.t = CONF_STRING; conf->webserver.port.d.s = (char*)"80,[::]:80,443s,[::]:443s"; + conf->webserver.port.c = validate_stub; // Type-based checking + civetweb syntax checking conf->webserver.tls.rev_proxy.k = "webserver.tls.rev_proxy"; conf->webserver.tls.rev_proxy.h = "Is Pi-hole running behind a reverse proxy? If yes, Pi-hole will not consider HTTP-only connections being insecure. This is useful if you are running Pi-hole in a trusted environment, for example, in a local network, and you are using a reverse proxy to provide TLS encryption, e.g., by using Traefik (docker). If you are using a reverse proxy, you can alternatively set webserver.tls.cert to the path of the TLS certificate file and let Pi-hole handle true end-to-end encryption."; conf->webserver.tls.rev_proxy.f = FLAG_ADVANCED_SETTING; conf->webserver.tls.rev_proxy.t = CONF_BOOL; conf->webserver.tls.rev_proxy.d.b = false; + conf->webserver.tls.rev_proxy.c = validate_stub; // Only type-based checking conf->webserver.tls.cert.k = "webserver.tls.cert"; conf->webserver.tls.cert.h = "Path to the TLS (SSL) certificate file. This option is only required when at least one of webserver.port is TLS. The file must be in PEM format, and it must have both, private key and certificate (the *.pem file created must contain a 'CERTIFICATE' section as well as a 'RSA PRIVATE KEY' section).\n The *.pem file can be created using\n cp server.crt server.pem\n cat server.key >> server.pem\n if you have these files instead"; @@ -873,11 +932,13 @@ void initConfig(struct config *conf) conf->webserver.session.timeout.h = "Session timeout in seconds. If a session is inactive for more than this time, it will be terminated. Sessions are continuously refreshed by the web interface, preventing sessions from timing out while the web interface is open.\n This option may also be used to make logins persistent for long times, e.g. 86400 seconds (24 hours), 604800 seconds (7 days) or 2592000 seconds (30 days). Note that the total number of concurrent sessions is limited so setting this value too high may result in users being rejected and unable to log in if there are already too many sessions active."; conf->webserver.session.timeout.t = CONF_UINT; conf->webserver.session.timeout.d.ui = 1800u; + conf->webserver.session.timeout.c = validate_stub; // Only type-based checking conf->webserver.session.restore.k = "webserver.session.restore"; conf->webserver.session.restore.h = "Should Pi-hole backup and restore sessions from the database? This is useful if you want to keep your sessions after a restart of the web interface."; conf->webserver.session.restore.t = CONF_BOOL; conf->webserver.session.restore.d.b = true; + conf->webserver.session.restore.c = validate_stub; // Only type-based checking // sub-struct paths conf->webserver.paths.webroot.k = "webserver.paths.webroot"; @@ -901,6 +962,7 @@ void initConfig(struct config *conf) conf->webserver.interface.boxed.h = "Should the web interface use the boxed layout?"; conf->webserver.interface.boxed.t = CONF_BOOL; conf->webserver.interface.boxed.d.b = true; + conf->webserver.interface.boxed.c = validate_stub; // Only type-based checking conf->webserver.interface.theme.k = "webserver.interface.theme"; conf->webserver.interface.theme.h = "Theme used by the Pi-hole web interface"; @@ -915,29 +977,34 @@ void initConfig(struct config *conf) } conf->webserver.interface.theme.t = CONF_ENUM_WEB_THEME; conf->webserver.interface.theme.d.web_theme = THEME_DEFAULT_AUTO; + conf->webserver.interface.theme.c = validate_stub; // Only type-based checking // sub-struct api conf->webserver.api.searchAPIauth.k = "webserver.api.searchAPIauth"; conf->webserver.api.searchAPIauth.h = "Do local clients need to authenticate to access the search API? This settings allows local clients to use pihole -q ... without authentication. Note that \"local\" in the sense of the option means only 127.0.0.1 and [::1]"; conf->webserver.api.searchAPIauth.t = CONF_BOOL; conf->webserver.api.searchAPIauth.d.b = false; + conf->webserver.api.searchAPIauth.c = validate_stub; // Only type-based checking conf->webserver.api.localAPIauth.k = "webserver.api.localAPIauth"; conf->webserver.api.localAPIauth.h = "Do local clients need to authenticate to access the API? This settings allows local clients to use the API without authentication."; conf->webserver.api.localAPIauth.t = CONF_BOOL; conf->webserver.api.localAPIauth.d.b = true; + conf->webserver.api.localAPIauth.c = validate_stub; // Only type-based checking conf->webserver.api.max_sessions.k = "webserver.api.max_sessions"; conf->webserver.api.max_sessions.h = "Number of concurrent sessions allowed for the API. If the number of sessions exceeds this value, no new sessions will be allowed until the number of sessions drops due to session expiration or logout. Note that the number of concurrent sessions is irrelevant if authentication is disabled as no sessions are used in this case."; conf->webserver.api.max_sessions.t = CONF_UINT16; conf->webserver.api.max_sessions.d.u16 = 16; conf->webserver.api.max_sessions.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL; + conf->webserver.api.max_sessions.c = validate_stub; // Only type-based checking conf->webserver.api.prettyJSON.k = "webserver.api.prettyJSON"; conf->webserver.api.prettyJSON.h = "Should FTL prettify the API output (add extra spaces, newlines and indentation)?"; conf->webserver.api.prettyJSON.t = CONF_BOOL; conf->webserver.api.prettyJSON.f = FLAG_ADVANCED_SETTING; conf->webserver.api.prettyJSON.d.b = false; + conf->webserver.api.prettyJSON.c = validate_stub; // Only type-based checking conf->webserver.api.pwhash.k = "webserver.api.pwhash"; conf->webserver.api.pwhash.h = "API password hash"; @@ -945,6 +1012,7 @@ void initConfig(struct config *conf) conf->webserver.api.pwhash.t = CONF_STRING; conf->webserver.api.pwhash.f = FLAG_INVALIDATE_SESSIONS; conf->webserver.api.pwhash.d.s = (char*)""; + conf->webserver.api.pwhash.c = validate_stub; // Only type-based checking conf->webserver.api.password.k = "webserver.api.password"; conf->webserver.api.password.h = "Pi-hole web interface and API password. When set to something different than \""PASSWORD_VALUE"\", this property will compute the corresponding password hash to set webserver.api.pwhash"; @@ -952,6 +1020,7 @@ void initConfig(struct config *conf) conf->webserver.api.password.t = CONF_PASSWORD; conf->webserver.api.password.f = FLAG_PSEUDO_ITEM | FLAG_INVALIDATE_SESSIONS; conf->webserver.api.password.d.s = (char*)""; + conf->webserver.api.password.c = validate_stub; // Only type-based checking conf->webserver.api.totp_secret.k = "webserver.api.totp_secret"; conf->webserver.api.totp_secret.h = "Pi-hole 2FA TOTP secret. When set to something different than \"""\", 2FA authentication will be enforced for the API and the web interface. This setting is write-only, you can not read the secret back."; @@ -959,6 +1028,7 @@ void initConfig(struct config *conf) conf->webserver.api.totp_secret.t = CONF_STRING; conf->webserver.api.totp_secret.f = FLAG_WRITE_ONLY | FLAG_INVALIDATE_SESSIONS; conf->webserver.api.totp_secret.d.s = (char*)""; + conf->webserver.api.totp_secret.c = validate_stub; // Only type-based checking conf->webserver.api.app_pwhash.k = "webserver.api.app_pwhash"; conf->webserver.api.app_pwhash.h = "Pi-hole application password.\n After you turn on two-factor (2FA) verification and set up an Authenticator app, you may run into issues if you use apps or other services that don't support two-step verification. In this case, you can create and use an app password to sign in. An app password is a long, randomly generated password that can be used instead of your regular password + TOTP token when signing in to the API. The app password can be generated through the API and will be shown only once. You can revoke the app password at any time. If you revoke the app password, be sure to generate a new one and update your app with the new password."; @@ -966,6 +1036,7 @@ void initConfig(struct config *conf) conf->webserver.api.app_pwhash.t = CONF_STRING; conf->webserver.api.app_pwhash.f = FLAG_INVALIDATE_SESSIONS; conf->webserver.api.app_pwhash.d.s = (char*)""; + conf->webserver.api.app_pwhash.c = validate_stub; // Only type-based checking conf->webserver.api.excludeClients.k = "webserver.api.excludeClients"; conf->webserver.api.excludeClients.h = "Array of clients to be excluded from certain API responses (regex):\n - Query Log (/api/queries)\n - Top Clients (/api/stats/top_clients)\n This setting accepts both IP addresses (IPv4 and IPv6) as well as hostnames.\n Note that backslashes \"\\\" need to be escaped, i.e. \"\\\\\" in this setting\n\n Example: [ \"^192\\\\.168\\\\.2\\\\.56$\", \"^fe80::341:[0-9a-f]*$\", \"^localhost$\" ]"; @@ -985,22 +1056,26 @@ void initConfig(struct config *conf) conf->webserver.api.maxHistory.h = "How much history should be imported from the database and returned by the API [seconds]? (max 24*60*60 = 86400)"; conf->webserver.api.maxHistory.t = CONF_UINT; conf->webserver.api.maxHistory.d.ui = MAXLOGAGE*3600; + conf->webserver.api.maxHistory.c = validate_stub; // Only type-based checking conf->webserver.api.maxClients.k = "webserver.api.maxClients"; conf->webserver.api.maxClients.h = "Up to how many clients should be returned in the activity graph endpoint (/api/history/clients)?\n This setting can be overwritten at run-time using the parameter N"; conf->webserver.api.maxClients.t = CONF_UINT16; conf->webserver.api.maxClients.d.u16 = 10; + conf->webserver.api.maxClients.c = validate_stub; // Only type-based checking conf->webserver.api.allow_destructive.k = "webserver.api.allow_destructive"; conf->webserver.api.allow_destructive.h = "Allow destructive API calls (e.g. deleting all queries, powering off the system, ...)"; conf->webserver.api.allow_destructive.t = CONF_BOOL; conf->webserver.api.allow_destructive.d.b = true; + conf->webserver.api.allow_destructive.c = validate_stub; // Only type-based checking // sub-struct webserver.api.temp conf->webserver.api.temp.limit.k = "webserver.api.temp.limit"; conf->webserver.api.temp.limit.h = "Which upper temperature limit should be used by Pi-hole? Temperatures above this limit will be shown as \"hot\". The number specified here is in the unit defined below"; conf->webserver.api.temp.limit.t = CONF_DOUBLE; conf->webserver.api.temp.limit.d.d = 60.0; // °C + conf->webserver.api.temp.limit.c = validate_stub; // Only type-based checking conf->webserver.api.temp.unit.k = "webserver.api.temp.unit"; conf->webserver.api.temp.unit.h = "Which temperature unit should be used for temperatures processed by FTL?"; @@ -1015,7 +1090,7 @@ void initConfig(struct config *conf) } conf->webserver.api.temp.unit.t = CONF_ENUM_TEMP_UNIT; conf->webserver.api.temp.unit.d.temp_unit = TEMP_UNIT_C; - + conf->webserver.api.temp.unit.c = validate_stub; // Only type-based checking // struct files conf->files.pid.k = "files.pid"; @@ -1048,6 +1123,7 @@ void initConfig(struct config *conf) conf->files.gravity_tmp.t = CONF_STRING; conf->files.gravity_tmp.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL; conf->files.gravity_tmp.d.s = (char*)"/tmp"; + conf->files.gravity_tmp.c = validate_stub; // Only type-based checking conf->files.macvendor.k = "files.macvendor"; conf->files.macvendor.h = "The database containing MAC -> Vendor information for the network table"; @@ -1108,29 +1184,34 @@ void initConfig(struct config *conf) } conf->misc.privacylevel.t = CONF_ENUM_PRIVACY_LEVEL; conf->misc.privacylevel.d.privacy_level = PRIVACY_SHOW_ALL; + conf->misc.privacylevel.c = validate_stub; // Only type-based checking conf->misc.delay_startup.k = "misc.delay_startup"; conf->misc.delay_startup.h = "During startup, in some configurations, network interfaces appear only late during system startup and are not ready when FTL tries to bind to them. Therefore, you may want FTL to wait a given amount of time before trying to start the DNS revolver. This setting takes any integer value between 0 and 300 seconds. To prevent delayed startup while the system is already running and FTL is restarted, the delay only takes place within the first 180 seconds (hard-coded) after booting."; conf->misc.delay_startup.t = CONF_UINT; conf->misc.delay_startup.d.ui = 0; + conf->misc.delay_startup.c = validate_stub; // Only type-based checking conf->misc.nice.k = "misc.nice"; conf->misc.nice.h = "Set niceness of pihole-FTL. Defaults to -10 and can be disabled altogether by setting a value of -999. The nice value is an attribute that can be used to influence the CPU scheduler to favor or disfavor a process in scheduling decisions. The range of the nice value varies across UNIX systems. On modern Linux, the range is -20 (high priority = not very nice to other processes) to +19 (low priority)."; conf->misc.nice.t = CONF_INT; conf->misc.nice.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL; conf->misc.nice.d.i = -10; + conf->misc.nice.c = validate_stub; // Only type-based checking conf->misc.addr2line.k = "misc.addr2line"; conf->misc.addr2line.h = "Should FTL translate its own stack addresses into code lines during the bug backtrace? This improves the analysis of crashed significantly. It is recommended to leave the option enabled. This option should only be disabled when addr2line is known to not be working correctly on the machine because, in this case, the malfunctioning addr2line can prevent from generating any backtrace at all."; conf->misc.addr2line.t = CONF_BOOL; conf->misc.addr2line.f = FLAG_ADVANCED_SETTING; conf->misc.addr2line.d.b = true; + conf->misc.addr2line.c = validate_stub; // Only type-based checking conf->misc.etc_dnsmasq_d.k = "misc.etc_dnsmasq_d"; conf->misc.etc_dnsmasq_d.h = "Should FTL load additional dnsmasq configuration files from /etc/dnsmasq.d/?"; conf->misc.etc_dnsmasq_d.t = CONF_BOOL; conf->misc.etc_dnsmasq_d.f = FLAG_RESTART_FTL | FLAG_ADVANCED_SETTING; conf->misc.etc_dnsmasq_d.d.b = false; + conf->misc.etc_dnsmasq_d.c = validate_stub; // Only type-based checking conf->misc.dnsmasq_lines.k = "misc.dnsmasq_lines"; conf->misc.dnsmasq_lines.h = "Additional lines to inject into the generated dnsmasq configuration.\n Warning: This is an advanced setting and should only be used with care. Incorrectly formatted or duplicated lines as well as lines conflicting with the automatic configuration of Pi-hole can break the embedded dnsmasq and will stop DNS resolution from working.\n Use this option with extra care."; @@ -1138,28 +1219,33 @@ void initConfig(struct config *conf) conf->misc.dnsmasq_lines.t = CONF_JSON_STRING_ARRAY; conf->misc.dnsmasq_lines.f = FLAG_ADVANCED_SETTING | FLAG_RESTART_FTL; conf->misc.dnsmasq_lines.d.json = cJSON_CreateArray(); + conf->misc.dnsmasq_lines.c = validate_stub; // Type-based checking + dnsmasq syntax checking conf->misc.extraLogging.k = "misc.extraLogging"; conf->misc.extraLogging.h = "Log additional information about queries and replies to pihole.log\n When this setting is enabled, the log has extra information at the start of each line. This consists of a serial number which ties together the log lines associated with an individual query, and the IP address of the requestor. This setting is only effective if dns.queryLogging is enabled, too. This option is only useful for debugging and is not recommended for normal use."; conf->misc.extraLogging.t = CONF_BOOL; conf->misc.extraLogging.f = FLAG_RESTART_FTL; conf->misc.extraLogging.d.b = false; + conf->misc.extraLogging.c = validate_stub; // Only type-based checking // sub-struct misc.check conf->misc.check.load.k = "misc.check.load"; conf->misc.check.load.h = "Pi-hole is very lightweight on resources. Nevertheless, this does not mean that you should run Pi-hole on a server that is otherwise extremely busy as queuing on the system can lead to unnecessary delays in DNS operation as the system becomes less and less usable as the system load increases because all resources are permanently in use. To account for this, FTL regularly checks the system load. To bring this to your attention, FTL warns about excessive load when the 15 minute system load average exceeds the number of cores.\n This check can be disabled with this setting."; conf->misc.check.load.t = CONF_BOOL; conf->misc.check.load.d.b = true; + conf->misc.check.load.c = validate_stub; // Only type-based checking conf->misc.check.disk.k = "misc.check.disk"; conf->misc.check.disk.h = "FTL stores its long-term history in a database file on disk. Furthermore, FTL stores log files. By default, FTL warns if usage of the disk holding any crucial file exceeds 90%. You can set any integer limit between 0 to 100 (interpreted as percentages) where 0 means that checking of disk usage is disabled."; conf->misc.check.disk.t = CONF_UINT; conf->misc.check.disk.d.ui = 90; + conf->misc.check.disk.c = validate_stub; // Only type-based checking conf->misc.check.shmem.k = "misc.check.shmem"; conf->misc.check.shmem.h = "FTL stores history in shared memory to allow inter-process communication with forked dedicated TCP workers. If FTL runs out of memory, it cannot continue to work as queries cannot be analyzed any further. Hence, FTL checks if enough shared memory is available on your system and warns you if this is not the case.\n By default, FTL warns if the shared-memory usage exceeds 90%. You can set any integer limit between 0 to 100 (interpreted as percentages) where 0 means that checking of shared-memory usage is disabled."; conf->misc.check.shmem.t = CONF_UINT; conf->misc.check.shmem.d.ui = 90; + conf->misc.check.shmem.c = validate_stub; // Only type-based checking // struct debug @@ -1168,168 +1254,196 @@ void initConfig(struct config *conf) conf->debug.database.t = CONF_BOOL; conf->debug.database.f = FLAG_ADVANCED_SETTING; conf->debug.database.d.b = false; + conf->debug.database.c = validate_stub; // Only type-based checking conf->debug.networking.k = "debug.networking"; conf->debug.networking.h = "Prints a list of the detected interfaces on the startup of pihole-FTL. Also, prints whether these interfaces are IPv4 or IPv6 interfaces."; conf->debug.networking.t = CONF_BOOL; conf->debug.networking.f = FLAG_ADVANCED_SETTING; conf->debug.networking.d.b = false; + conf->debug.networking.c = validate_stub; // Only type-based checking conf->debug.locks.k = "debug.locks"; conf->debug.locks.h = "Print information about shared memory locks. Messages will be generated when waiting, obtaining, and releasing a lock."; conf->debug.locks.t = CONF_BOOL; conf->debug.locks.f = FLAG_ADVANCED_SETTING; conf->debug.locks.d.b = false; + conf->debug.locks.c = validate_stub; // Only type-based checking conf->debug.queries.k = "debug.queries"; conf->debug.queries.h = "Print extensive query information (domains, types, replies, etc.). This has always been part of the legacy debug mode of pihole-FTL."; conf->debug.queries.t = CONF_BOOL; conf->debug.queries.f = FLAG_ADVANCED_SETTING; conf->debug.queries.d.b = false; + conf->debug.queries.c = validate_stub; // Only type-based checking conf->debug.flags.k = "debug.flags"; conf->debug.flags.h = "Print flags of queries received by the DNS hooks. Only effective when DEBUG_QUERIES is enabled as well."; conf->debug.flags.t = CONF_BOOL; conf->debug.flags.f = FLAG_ADVANCED_SETTING; conf->debug.flags.d.b = false; + conf->debug.flags.c = validate_stub; // Only type-based checking conf->debug.shmem.k = "debug.shmem"; conf->debug.shmem.h = "Print information about shared memory buffers. Messages are either about creating or enlarging shmem objects or string injections."; conf->debug.shmem.t = CONF_BOOL; conf->debug.shmem.f = FLAG_ADVANCED_SETTING; conf->debug.shmem.d.b = false; + conf->debug.shmem.c = validate_stub; // Only type-based checking conf->debug.gc.k = "debug.gc"; conf->debug.gc.h = "Print information about garbage collection (GC): What is to be removed, how many have been removed and how long did GC take."; conf->debug.gc.t = CONF_BOOL; conf->debug.gc.f = FLAG_ADVANCED_SETTING; conf->debug.gc.d.b = false; + conf->debug.gc.c = validate_stub; // Only type-based checking conf->debug.arp.k = "debug.arp"; conf->debug.arp.h = "Print information about ARP table processing: How long did parsing take, whether read MAC addresses are valid, and if the macvendor.db file exists."; conf->debug.arp.t = CONF_BOOL; conf->debug.arp.f = FLAG_ADVANCED_SETTING; conf->debug.arp.d.b = false; + conf->debug.arp.c = validate_stub; // Only type-based checking conf->debug.regex.k = "debug.regex"; conf->debug.regex.h = "Controls if FTLDNS should print extended details about regex matching into FTL.log."; conf->debug.regex.t = CONF_BOOL; conf->debug.regex.f = FLAG_ADVANCED_SETTING; conf->debug.regex.d.b = false; + conf->debug.regex.c = validate_stub; // Only type-based checking conf->debug.api.k = "debug.api"; conf->debug.api.h = "Print extra debugging information during telnet API calls. Currently only used to send extra information when getting all queries."; conf->debug.api.t = CONF_BOOL; conf->debug.api.f = FLAG_ADVANCED_SETTING; conf->debug.api.d.b = false; + conf->debug.api.c = validate_stub; // Only type-based checking conf->debug.tls.k = "debug.tls"; conf->debug.tls.h = "Print extra debugging information about TLS connections. This includes the TLS version, the cipher suite, the certificate chain and much more. This very verbose output should only be used when debugging specific TLS issues and can be helpful, e.g., when a client cannot connect due to an obscure TLS error as modern browsers do not provide much information about the underlying TLS connection and most often give only very generic error messages without much/any underlying technical information."; conf->debug.tls.t = CONF_BOOL; conf->debug.tls.f = FLAG_ADVANCED_SETTING; conf->debug.tls.d.b = false; + conf->debug.tls.c = validate_stub; // Only type-based checking conf->debug.overtime.k = "debug.overtime"; conf->debug.overtime.h = "Print information about overTime memory operations, such as initializing or moving overTime slots."; conf->debug.overtime.t = CONF_BOOL; conf->debug.overtime.f = FLAG_ADVANCED_SETTING; conf->debug.overtime.d.b = false; + conf->debug.overtime.c = validate_stub; // Only type-based checking conf->debug.status.k = "debug.status"; conf->debug.status.h = "Print information about status changes for individual queries. This can be useful to identify unexpected unknown queries."; conf->debug.status.t = CONF_BOOL; conf->debug.status.f = FLAG_ADVANCED_SETTING; conf->debug.status.d.b = false; + conf->debug.status.c = validate_stub; // Only type-based checking conf->debug.caps.k = "debug.caps"; conf->debug.caps.h = "Print information about capabilities granted to the pihole-FTL process. The current capabilities are printed on receipt of SIGHUP, i.e., the current set of capabilities can be queried without restarting pihole-FTL (by setting DEBUG_CAPS=true and thereafter sending killall -HUP pihole-FTL)."; conf->debug.caps.t = CONF_BOOL; conf->debug.caps.f = FLAG_ADVANCED_SETTING; conf->debug.caps.d.b = false; + conf->debug.caps.c = validate_stub; // Only type-based checking conf->debug.dnssec.k = "debug.dnssec"; conf->debug.dnssec.h = "Print information about DNSSEC activity"; conf->debug.dnssec.t = CONF_BOOL; conf->debug.dnssec.f = FLAG_ADVANCED_SETTING; conf->debug.dnssec.d.b = false; + conf->debug.dnssec.c = validate_stub; // Only type-based checking conf->debug.vectors.k = "debug.vectors"; conf->debug.vectors.h = "FTL uses dynamically allocated vectors for various tasks. This config option enables extensive debugging information such as information about allocation, referencing, deletion, and appending."; conf->debug.vectors.t = CONF_BOOL; conf->debug.vectors.f = FLAG_ADVANCED_SETTING; conf->debug.vectors.d.b = false; + conf->debug.vectors.c = validate_stub; // Only type-based checking conf->debug.resolver.k = "debug.resolver"; conf->debug.resolver.h = "Extensive information about hostname resolution like which DNS servers are used in the first and second hostname resolving tries (only affecting internally generated PTR queries)."; conf->debug.resolver.t = CONF_BOOL; conf->debug.resolver.f = FLAG_ADVANCED_SETTING; conf->debug.resolver.d.b = false; + conf->debug.resolver.c = validate_stub; // Only type-based checking conf->debug.edns0.k = "debug.edns0"; conf->debug.edns0.h = "Print debugging information about received EDNS(0) data."; conf->debug.edns0.t = CONF_BOOL; conf->debug.edns0.f = FLAG_ADVANCED_SETTING; conf->debug.edns0.d.b = false; + conf->debug.edns0.c = validate_stub; // Only type-based checking conf->debug.clients.k = "debug.clients"; conf->debug.clients.h = "Log various important client events such as change of interface (e.g., client switching from WiFi to wired or VPN connection), as well as extensive reporting about how clients were assigned to its groups."; conf->debug.clients.t = CONF_BOOL; conf->debug.clients.f = FLAG_ADVANCED_SETTING; conf->debug.clients.d.b = false; + conf->debug.clients.c = validate_stub; // Only type-based checking conf->debug.aliasclients.k = "debug.aliasclients"; conf->debug.aliasclients.h = "Log information related to alias-client processing."; conf->debug.aliasclients.t = CONF_BOOL; conf->debug.aliasclients.f = FLAG_ADVANCED_SETTING; conf->debug.aliasclients.d.b = false; + conf->debug.aliasclients.c = validate_stub; // Only type-based checking conf->debug.events.k = "debug.events"; conf->debug.events.h = "Log information regarding FTL's embedded event handling queue."; conf->debug.events.t = CONF_BOOL; conf->debug.events.f = FLAG_ADVANCED_SETTING; conf->debug.events.d.b = false; + conf->debug.events.c = validate_stub; // Only type-based checking conf->debug.helper.k = "debug.helper"; conf->debug.helper.h = "Log information about script helpers, e.g., due to dhcp-script."; conf->debug.helper.t = CONF_BOOL; conf->debug.helper.f = FLAG_ADVANCED_SETTING; conf->debug.helper.d.b = false; + conf->debug.helper.c = validate_stub; // Only type-based checking conf->debug.config.k = "debug.config"; conf->debug.config.h = "Print config parsing details"; conf->debug.config.t = CONF_BOOL; conf->debug.config.f = FLAG_ADVANCED_SETTING; conf->debug.config.d.b = false; + conf->debug.config.c = validate_stub; // Only type-based checking conf->debug.inotify.k = "debug.inotify"; conf->debug.inotify.h = "Debug monitoring of /etc/pihole filesystem events"; conf->debug.inotify.t = CONF_BOOL; conf->debug.inotify.f = FLAG_ADVANCED_SETTING; conf->debug.inotify.d.b = false; + conf->debug.inotify.c = validate_stub; // Only type-based checking conf->debug.webserver.k = "debug.webserver"; conf->debug.webserver.h = "Debug monitoring of the webserver (CivetWeb) events"; conf->debug.webserver.t = CONF_BOOL; conf->debug.webserver.f = FLAG_ADVANCED_SETTING; conf->debug.webserver.d.b = false; + conf->debug.webserver.c = validate_stub; // Only type-based checking conf->debug.extra.k = "debug.extra"; conf->debug.extra.h = "Temporary flag that may print additional information. This debug flag is meant to be used whenever needed for temporary investigations. The logged content may change without further notice at any time."; conf->debug.extra.t = CONF_BOOL; conf->debug.extra.f = FLAG_ADVANCED_SETTING; conf->debug.extra.d.b = false; + conf->debug.extra.c = validate_stub; // Only type-based checking conf->debug.reserved.k = "debug.reserved"; conf->debug.reserved.h = "Reserved debug flag"; conf->debug.reserved.t = CONF_BOOL; conf->debug.reserved.f = FLAG_ADVANCED_SETTING; conf->debug.reserved.d.b = false; + conf->debug.reserved.c = validate_stub; // Only type-based checking conf->debug.all.k = "debug.all"; conf->debug.all.h = "Set all debug flags at once. This is a convenience option to enable all debug flags at once. Note that this option is not persistent, setting it to true will enable all *remaining* debug flags but unsetting it will disable *all* debug flags."; conf->debug.all.t = CONF_ALL_DEBUG_BOOL; conf->debug.all.f = FLAG_ADVANCED_SETTING; conf->debug.all.d.b = false; + conf->debug.all.c = validate_stub; // Only type-based checking // Post-processing: // Initialize and verify config data @@ -1372,6 +1486,13 @@ void initConfig(struct config *conf) log_err("Config option %s has NULL default JSON array!", conf_item->k); continue; } + + // Verify that all config options have a validator function + if(conf_item->c == NULL) + { + log_err("Config option %s has no validator function!", conf_item->k); + continue; + } } } @@ -1517,6 +1638,7 @@ bool getLogFilePath(void) config.files.log.ftl.f = FLAG_ADVANCED_SETTING; config.files.log.ftl.d.s = (char*)"/var/log/pihole/FTL.log"; config.files.log.ftl.v.s = config.files.log.ftl.d.s; + config.files.log.ftl.c = validate_filepath; // Check if the config file contains a different path if(!getLogFilePathTOML()) diff --git a/src/config/validator.c b/src/config/validator.c index 0dca3530..9c921510 100644 --- a/src/config/validator.c +++ b/src/config/validator.c @@ -15,6 +15,13 @@ // regex #include "regex_r.h" +// Stub validator for config types that need to dedicated validation as they can +// be tested by their type only (e.g., integers, strings, booleans, enums, etc.) +bool __attribute__((const)) validate_stub(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) +{ + return true; +} + // Validate the dns.hosts array // Each entry needs to be a string in form "IP HOSTNAME" bool validate_dns_hosts(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) diff --git a/src/config/validator.h b/src/config/validator.h index 025af77f..dbe4e7bd 100644 --- a/src/config/validator.h +++ b/src/config/validator.h @@ -14,6 +14,7 @@ #include "FTL.h" #include "config/config.h" +bool validate_stub(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) __attribute__((const)); bool validate_dns_hosts(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]); bool validate_dns_cnames(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]); bool validate_cidr(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]);