diff --git a/src/config/config.c b/src/config/config.c index 6291dcdd0..834412e02 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -619,7 +619,7 @@ void initConfig(struct config *conf) conf->dns.revServers.a = cJSON_CreateStringReference("array of reverse servers each one in one of the following forms: \",[/],[#],\", e.g., \"true,192.168.0.0/24,192.168.0.1,fritz.box\""); conf->dns.revServers.t = CONF_JSON_STRING_ARRAY; conf->dns.revServers.d.json = cJSON_CreateArray(); - conf->dns.revServers.c = validate_stub; // Only type-based checking + conf->dns.revServers.c = validate_dns_revServers; // Only type-based checking conf->dns.revServers.f = FLAG_RESTART_FTL; // sub-struct dns.rate_limit diff --git a/src/config/env.c b/src/config/env.c index d332a3483..5ca440d61 100644 --- a/src/config/env.c +++ b/src/config/env.c @@ -48,6 +48,13 @@ void getEnvVars(void) char *key = strtok(*env, "="); char *value = strtok(NULL, "="); + // Log warning if value is missing + if(value == NULL) + { + log_warn("Environment variable %s has no value, substituting with empty string", key); + value = (char*)""; + } + // Add to list struct env_item *new_item = calloc(1, sizeof(struct env_item)); new_item->used = false; diff --git a/src/config/validator.c b/src/config/validator.c index 5268c4f94..1da806529 100644 --- a/src/config/validator.c +++ b/src/config/validator.c @@ -373,3 +373,173 @@ bool validate_regex_array(union conf_value *val, const char *key, char err[VALID return true; } + +// Validate dns.revServers array +// Each entry has to be of form ",[/],[#]," +bool validate_dns_revServers(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]) +{ + if(!cJSON_IsArray(val->json)) + { + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not an array", key); + return false; + } + + for(int i = 0; i < cJSON_GetArraySize(val->json); i++) + { + // Get array item + cJSON *item = cJSON_GetArrayItem(val->json, i); + + // Check if it's a string + if(!cJSON_IsString(item)) + { + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: not a string", key, i); + return false; + } + + // Count the number of elements in the string + unsigned int elements = 1; + for(unsigned int j = 0; j < strlen(item->valuestring); j++) + if(item->valuestring[j] == ',') + elements++; + + // Check if it's in the form ",[/],[#]," + // Mandatory elements are: , , , and + // Optional elements are: [/] and [#] + char *str = strdup(item->valuestring); + char *tmp = str, *s = NULL; + unsigned int e = 0; + + while((s = strsep(&tmp, ",")) != NULL) + { + // Check if it's a valid element + if(strlen(s) == 0) + { + // Contains an empty string + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: contains two commas following each other immediately", key, i); + free(str); + return false; + } + // Check if the zeroth element is a boolean + if(e == 0) + { + if(strcmp(s, "true") != 0 && strcmp(s, "false") != 0) + { + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: not a boolean (\"%s\")", key, i, s); + free(str); + return false; + } + } + // Check if the first element is an IP address + else if(e == 1) + { + // Extract IP and prefix length (if present) + char *ip = strsep(&s, "/"); + char *prefix = strsep(&s, "/"); + if(strlen(ip) == 0) + { + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: empty", key, i); + free(str); + return false; + } + + // Check if IP is valid + struct in_addr addr = { 0 }; + struct in6_addr addr6 = { 0 }; + const bool ipv4 = inet_pton(AF_INET, ip, &addr) == 1; + const bool ipv6 = inet_pton(AF_INET6, ip, &addr6) == 1; + if(!ipv4 && !ipv6) + { + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: neither a valid IPv4 nor IPv6 address (\"%s\")", key, i, ip); + free(str); + return false; + } + + // Check if prefix length is valid (if present) + if(prefix != NULL) + { + if(strlen(prefix) == 0) + { + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: empty", key, i); + free(str); + return false; + } + const int prefix_int = atoi(prefix); + if(prefix_int < 0 || (ipv4 && prefix_int > 32) || (ipv6 && prefix_int > 128)) + { + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: not a valid prefix length (\"%s\")", key, i, prefix); + free(str); + return false; + } + } + } + // Check if the second element is a valid server (either an IP address or a domain, optionally with a port) + else if(e == 2) + { + // Extract server and port (if present) + char *server = strsep(&s, "#"); + char *port = strsep(&s, "#"); + if(strlen(server) == 0) + { + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: empty", key, i); + free(str); + return false; + } + + struct in_addr addr = { 0 }; + struct in6_addr addr6 = { 0 }; + const bool server_ipv4 = inet_pton(AF_INET, server, &addr) == 1; + const bool server_ipv6 = inet_pton(AF_INET6, server, &addr6) == 1; + const bool server_domain = valid_domain(server, strlen(server), false); + + // Check if server is valid + if(!server_ipv4 && !server_ipv6 && !server_domain) + { + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: neither a valid domain nor an IPv4 or IPv6 address (\"%s\")", key, i, server); + free(str); + return false; + } + + // Check if port is valid (if present) + if(port != NULL) + { + if(strlen(port) == 0) + { + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: specified server empty", key, i); + free(str); + return false; + } + const int port_int = atoi(port); + if(port_int < 0 || port_int > 65535) + { + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: server not a valid port (\"%s\")", key, i, port); + free(str); + return false; + } + } + } + // Check if the third element is a valid domain + else if(e == 3) + { + if(!valid_domain(s, strlen(s), false)) + { + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: not a valid domain (\"%s\")", key, i, s); + free(str); + return false; + } + } + // Check if there are too many elements + else + { + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: too many elements", key, i); + free(str); + return false; + } + + // Increment element counter + e++; + } + } + + // Return success + return true; +} diff --git a/src/config/validator.h b/src/config/validator.h index 1e58a1844..f8c7541d1 100644 --- a/src/config/validator.h +++ b/src/config/validator.h @@ -24,5 +24,6 @@ bool validate_filepath(union conf_value *val, const char *key, char err[VALIDATO bool validate_filepath_empty(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]); bool validate_filepath_dash(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]); bool validate_regex_array(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]); +bool validate_dns_revServers(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]); #endif // CONFIG_VALIDATOR_H