diff --git a/src/api/config.c b/src/api/config.c index a9b9eafd..c2c7a71e 100644 --- a/src/api/config.c +++ b/src/api/config.c @@ -768,7 +768,7 @@ static int api_config_patch(struct ftl_conn *api) // Validate new value (if validation function is defined) char errbuf[VALIDATOR_ERRBUF_LEN] = { 0 }; - if(!conf_item->c(&new_item->v, errbuf)) + if(!conf_item->c(&new_item->v, new_item->k, errbuf)) { free_config(&newconf); return send_json_error(api, 400, @@ -965,7 +965,7 @@ static int api_config_put_delete(struct ftl_conn *api) if(api->method == HTTP_PUT) { char errbuf[VALIDATOR_ERRBUF_LEN] = { 0 }; - if(!new_item->c(&new_item->v, errbuf)) + if(!new_item->c(&new_item->v, new_item->k, errbuf)) { free_config(&newconf); return send_json_error(api, 400, diff --git a/src/config/cli.c b/src/config/cli.c index 0385d8d6..37289641 100644 --- a/src/config/cli.c +++ b/src/config/cli.c @@ -442,7 +442,7 @@ int set_config_from_CLI(const char *key, const char *value) if(new_item->c != NULL) { char errbuf[VALIDATOR_ERRBUF_LEN] = { 0 }; - if(!new_item->c(&new_item->v, errbuf)) + if(!new_item->c(&new_item->v, new_item->k, errbuf)) { free_config(&newconf); log_err("Invalid value: %s", errbuf); diff --git a/src/config/config.h b/src/config/config.h index 180bc907..3f4a0e19 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -111,7 +111,7 @@ struct conf_item { uint8_t f; // additional Flags union conf_value v; // current Value union conf_value d; // Default value - bool (*c)(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]); // Function pointer to validate the value + bool (*c)(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]); // Function pointer to validate the value }; struct enum_options { diff --git a/src/config/validator.c b/src/config/validator.c index 9c921510..5b6a929b 100644 --- a/src/config/validator.c +++ b/src/config/validator.c @@ -17,31 +17,31 @@ // 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]) +bool __attribute__((const)) validate_stub(union conf_value *val, const char *key, 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]) +bool validate_dns_hosts(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]) { if(!cJSON_IsArray(val->json)) { - strncat(err, "Not an array", VALIDATOR_ERRBUF_LEN); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not an array", key); return false; } - for(int i = 1; i <= cJSON_GetArraySize(val->json); i++) + for(int i = 0; i < cJSON_GetArraySize(val->json); i++) { // Get array item - cJSON *item = cJSON_GetArrayItem(val->json, i-1); + cJSON *item = cJSON_GetArrayItem(val->json, i); // Check if it's a string if(!cJSON_IsString(item)) { - snprintf(err, VALIDATOR_ERRBUF_LEN, "%d%s element is not a string", - i, get_ordinal_suffix(i)); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: not a string", + key, i); return false; } @@ -52,8 +52,8 @@ bool validate_dns_hosts(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) if(!ip || !*ip) { - snprintf(err, VALIDATOR_ERRBUF_LEN, "%d%s element does not have an IP address (\"%s\")", - i, get_ordinal_suffix(i), item->valuestring); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: not an IP address (\"%s\")", + key, i, item->valuestring); free(str); return false; } @@ -63,8 +63,8 @@ bool validate_dns_hosts(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) struct in6_addr addr6; if(inet_pton(AF_INET, ip, &addr) != 1 && inet_pton(AF_INET6, ip, &addr6) != 1) { - snprintf(err, VALIDATOR_ERRBUF_LEN, "%d%s element is neither a valid IPv4 nor IPv6 address (\"%s\")", - i, get_ordinal_suffix(i), ip); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: neither a valid IPv4 nor IPv6 address (\"%s\")", + key, i, ip); free(str); return false; } @@ -78,8 +78,8 @@ bool validate_dns_hosts(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) { if(!valid_domain(host, strlen(host), false)) { - snprintf(err, VALIDATOR_ERRBUF_LEN, "%d%s element has an invalid hostname (\"%s\")", - i, get_ordinal_suffix(i), host); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: invalid hostname (\"%s\")", + key, i, host); free(str); return false; } @@ -89,8 +89,8 @@ bool validate_dns_hosts(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) // Check if there is at least one hostname in this record if(hosts < 1) { - snprintf(err, VALIDATOR_ERRBUF_LEN, "%d%s element does not have at least one hostname (\"%s\")", - i, get_ordinal_suffix(i), item->valuestring); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: entry does not have at least one hostname (\"%s\")", + key, i, item->valuestring); free(str); return false; } @@ -103,23 +103,23 @@ bool validate_dns_hosts(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) // Validate the dns.cnames array // Each entry needs to be a string in form ",[,][,]" -bool validate_dns_cnames(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) +bool validate_dns_cnames(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]) { if(!cJSON_IsArray(val->json)) { - strncat(err, "Not an array", VALIDATOR_ERRBUF_LEN); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not an array", key); return false; } - for(int i = 1; i <= cJSON_GetArraySize(val->json); i++) + for(int i = 0; i < cJSON_GetArraySize(val->json); i++) { // Get array item - cJSON *item = cJSON_GetArrayItem(val->json, i-1); + cJSON *item = cJSON_GetArrayItem(val->json, i); // Check if it's a string if(!cJSON_IsString(item)) { - snprintf(err, VALIDATOR_ERRBUF_LEN, "%d%s element is not a string", i, get_ordinal_suffix(i)); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: not a string", key, i); return false; } @@ -141,7 +141,7 @@ bool validate_dns_cnames(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) if(strlen(s) == 0) { // Contains an empty string - snprintf(err, VALIDATOR_ERRBUF_LEN, "%d%s element contains an empty string at position %u", i, get_ordinal_suffix(i), j); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: contains an empty string at position %u", key, i, j); free(str); return false; } @@ -153,7 +153,7 @@ bool validate_dns_cnames(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) // Check if there are at least one cname and a target if(j < 2) { - snprintf(err, VALIDATOR_ERRBUF_LEN, "%d%s element is not a valid CNAME definition", i, get_ordinal_suffix(i)); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d] element is not a valid CNAME definition", key, i); return false; } } @@ -162,7 +162,7 @@ bool validate_dns_cnames(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) } // Validate IPs in CIDR notation -bool validate_cidr(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) +bool validate_cidr(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]) { // Check if it's a valid CIDR char *str = strdup(val->s); @@ -174,7 +174,7 @@ bool validate_cidr(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) // Check if there is an IP and no tail if(!ip || !*ip || tail) { - snprintf(err, VALIDATOR_ERRBUF_LEN, "Not a valid IP in CIDR notation (\"%s\")", val->s); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not a valid IP in CIDR notation (\"%s\")", key, val->s); free(str); return false; } @@ -185,7 +185,7 @@ bool validate_cidr(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) int ip4 = 0, ip6 = 0; if((ip4 = inet_pton(AF_INET, ip, &addr) != 1) && (ip6 = inet_pton(AF_INET6, ip, &addr6)) != 1) { - snprintf(err, VALIDATOR_ERRBUF_LEN, "Not a valid IPv4 nor IPv6 address (\"%s\")", ip); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not a valid IPv4 nor IPv6 address (\"%s\")", key, ip); free(str); return false; } @@ -195,20 +195,20 @@ bool validate_cidr(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) { if(strlen(cidr) == 0) { - strncat(err, "Empty CIDR value", VALIDATOR_ERRBUF_LEN); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: empty CIDR value", key); free(str); return false; } int cidr_int = atoi(cidr); if(ip4 && (cidr_int < 0 || cidr_int > 32)) { - snprintf(err, VALIDATOR_ERRBUF_LEN, "Not a valid IPv4 CIDR (\"%s\")", cidr); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not a valid IPv4 CIDR (\"%s\")", key, cidr); free(str); return false; } else if(ip6 && (cidr_int < 0 || cidr_int > 128)) { - snprintf(err, VALIDATOR_ERRBUF_LEN, "Not a valid IPv6 CIDR (\"%s\")", cidr); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not a valid IPv6 CIDR (\"%s\")", key, cidr); free(str); return false; } @@ -219,7 +219,7 @@ bool validate_cidr(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) } // Validate IP address optionally followed by a port (separator is "#") -bool validate_ip_port(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) +bool validate_ip_port(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]) { // Check if it's a valid IP char *str = strdup(val->s); @@ -231,7 +231,7 @@ bool validate_ip_port(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) // Check if there is an IP and no tail if(!ip || !*ip || tail) { - snprintf(err, VALIDATOR_ERRBUF_LEN, "Not a valid IP (\"%s\")", val->s); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not a valid IP (\"%s\")", key, val->s); free(str); return false; } @@ -242,7 +242,7 @@ bool validate_ip_port(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) int ip4 = 0, ip6 = 0; if((ip4 = inet_pton(AF_INET, ip, &addr) != 1) && (ip6 = inet_pton(AF_INET6, ip, &addr6)) != 1) { - snprintf(err, VALIDATOR_ERRBUF_LEN, "Not a valid IPv4 nor IPv6 address (\"%s\")", ip); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not a valid IPv4 nor IPv6 address (\"%s\")", key, ip); free(str); return false; } @@ -252,14 +252,14 @@ bool validate_ip_port(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) { if(strlen(port) == 0) { - strncat(err, "Empty port value", VALIDATOR_ERRBUF_LEN); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: empty port value", key); free(str); return false; } int port_int = atoi(port); if(port_int < 0 || port_int > 65535) { - snprintf(err, VALIDATOR_ERRBUF_LEN, "Not a valid port (\"%s\")", port); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not a valid port (\"%s\")", key, port); free(str); return false; } @@ -270,12 +270,12 @@ bool validate_ip_port(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) } // Validate domain -bool validate_domain(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) +bool validate_domain(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]) { // Check if domain is valid if(!valid_domain(val->s, strlen(val->s), false)) { - snprintf(err, VALIDATOR_ERRBUF_LEN, "Not a valid domain (\"%s\")", val->s); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not a valid domain (\"%s\")", key, val->s); return false; } @@ -283,14 +283,14 @@ bool validate_domain(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) } // Validate file path -bool validate_filepath(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) +bool validate_filepath(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]) { // Check if the path contains only valid characters for(unsigned int i = 0; i < strlen(val->s); i++) { if(!isalnum(val->s[i]) && val->s[i] != '/' && val->s[i] != '.' && val->s[i] != '-' && val->s[i] != '_' && val->s[i] != ' ') { - snprintf(err, VALIDATOR_ERRBUF_LEN, "Not a valid file path (\"%s\")", val->s); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s: not a valid file path (\"%s\")", key, val->s); return false; } } @@ -299,14 +299,14 @@ bool validate_filepath(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) } // Validate file path (empty allowed) -bool validate_filepath_empty(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) +bool validate_filepath_empty(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]) { // Empty paths are allowed, e.g., to disable a feature like PCAP if(strlen(val->s) == 0) return true; // else: - return validate_filepath(val, err); + return validate_filepath(val, key, err); } // Validate a single regular expression @@ -329,24 +329,24 @@ static bool validate_regex(const char *regex, char err[VALIDATOR_ERRBUF_LEN]) } // Validate array of regexes -bool validate_regex_array(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) +bool validate_regex_array(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]) { if(val == NULL || !cJSON_IsArray(val->json)) { - strncat(err, "Not an array", VALIDATOR_ERRBUF_LEN); + strncat(err, "%s: not an array", VALIDATOR_ERRBUF_LEN); return false; } - for(int i = 1; i <= cJSON_GetArraySize(val->json); i++) + for(int i = 0; i < cJSON_GetArraySize(val->json); i++) { // Get array item - cJSON *item = cJSON_GetArrayItem(val->json, i-1); + cJSON *item = cJSON_GetArrayItem(val->json, i); // Check if it's a string if(!cJSON_IsString(item)) { - snprintf(err, VALIDATOR_ERRBUF_LEN, "%d%s element is not a string", - i, get_ordinal_suffix(i)); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: not a string", + key, i); return false; } @@ -354,8 +354,8 @@ bool validate_regex_array(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]) char errbuf[VALIDATOR_ERRBUF_LEN] = { 0 }; if(!validate_regex(item->valuestring, errbuf)) { - snprintf(err, VALIDATOR_ERRBUF_LEN, "%d%s element is not a valid regex (\"%s\"): %s", - i, get_ordinal_suffix(i), item->valuestring, errbuf); + snprintf(err, VALIDATOR_ERRBUF_LEN, "%s[%d]: not a valid regex (\"%s\"): %s", + key, i, item->valuestring, errbuf); return false; } } diff --git a/src/config/validator.h b/src/config/validator.h index dbe4e7bd..27249f5f 100644 --- a/src/config/validator.h +++ b/src/config/validator.h @@ -14,14 +14,14 @@ #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]); -bool validate_ip_port(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]); -bool validate_domain(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]); -bool validate_filepath(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]); -bool validate_filepath_empty(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]); -bool validate_regex_array(union conf_value *val, char err[VALIDATOR_ERRBUF_LEN]); +bool validate_stub(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]) __attribute__((const)); +bool validate_dns_hosts(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]); +bool validate_dns_cnames(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]); +bool validate_cidr(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]); +bool validate_ip_port(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]); +bool validate_domain(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]); +bool validate_filepath(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN]); +bool validate_filepath_empty(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]); #endif // CONFIG_VALIDATOR_H