diff --git a/src/api/config.c b/src/api/config.c index 453052dac..ee7b87ff5 100644 --- a/src/api/config.c +++ b/src/api/config.c @@ -128,6 +128,11 @@ static cJSON *addJSONvalue(const enum conf_type conf_type, union conf_value *val return cJSON_CreateStringReference(get_temp_unit_str(val->temp_unit)); case CONF_STRUCT_IN_ADDR: { + // Special case 0.0.0.0 -> return empty string + if(val->in_addr.s_addr == INADDR_ANY) + return cJSON_CreateStringReference(""); + + // else: normal address char addr4[INET_ADDRSTRLEN] = { 0 }; inet_ntop(AF_INET, &val->in_addr, addr4, INET_ADDRSTRLEN); return cJSON_CreateString(addr4); // Performs a copy @@ -400,10 +405,18 @@ static const char *getJSONvalue(struct conf_item *conf_item, cJSON *elem, struct struct in_addr addr4 = { 0 }; if(!cJSON_IsString(elem)) return "not of type string"; - if(!inet_pton(AF_INET, elem->valuestring, &addr4)) + if(strlen(elem->valuestring) == 0) + { + // Special case: empty string -> 0.0.0.0 + addr4.s_addr = INADDR_ANY; + } + else if(inet_pton(AF_INET, elem->valuestring, &addr4)) + { + // Set item + memcpy(&conf_item->v.in_addr, &addr4, sizeof(addr4)); + } + else return "not a valid IPv4 address"; - // Set item - memcpy(&conf_item->v.in_addr, &addr4, sizeof(addr4)); log_debug(DEBUG_CONFIG, "%s = %s", conf_item->k, elem->valuestring); break; } diff --git a/src/config/cli.c b/src/config/cli.c index ddc23db98..e5b2ce31a 100644 --- a/src/config/cli.c +++ b/src/config/cli.c @@ -295,7 +295,12 @@ static bool readStringValue(struct conf_item *conf_item, const char *value, stru case CONF_STRUCT_IN_ADDR: { struct in_addr addr4 = { 0 }; - if(inet_pton(AF_INET, value, &addr4)) + if(strlen(value) == 0) + { + // Special case: empty string -> 0.0.0.0 + conf_item->v.in_addr.s_addr = INADDR_ANY; + } + else if(inet_pton(AF_INET, value, &addr4)) memcpy(&conf_item->v.in_addr, &addr4, sizeof(addr4)); else { @@ -307,7 +312,12 @@ static bool readStringValue(struct conf_item *conf_item, const char *value, stru case CONF_STRUCT_IN6_ADDR: { struct in6_addr addr6 = { 0 }; - if(inet_pton(AF_INET6, value, &addr6)) + if(strlen(value) == 0) + { + // Special case: empty string -> :: + conf_item->v.in6_addr = in6addr_any; + } + else if(inet_pton(AF_INET6, value, &addr6)) memcpy(&conf_item->v.in6_addr, &addr6, sizeof(addr6)); else { diff --git a/src/config/toml_helper.c b/src/config/toml_helper.c index 7f40b683f..90dab4880 100644 --- a/src/config/toml_helper.c +++ b/src/config/toml_helper.c @@ -364,6 +364,13 @@ void writeTOMLvalue(FILE * fp, const int indent, const enum conf_type t, union c break; case CONF_STRUCT_IN_ADDR: { + // Special case: 0.0.0.0 -> return empty string + if(v->in_addr.s_addr == INADDR_ANY) + { + printTOMLstring(fp, "", toml); + break; + } + // else: normal address char addr4[INET_ADDRSTRLEN] = { 0 }; inet_ntop(AF_INET, &v->in_addr, addr4, INET_ADDRSTRLEN); printTOMLstring(fp, addr4, toml); @@ -371,6 +378,13 @@ void writeTOMLvalue(FILE * fp, const int indent, const enum conf_type t, union c } case CONF_STRUCT_IN6_ADDR: { + // Special case: :: -> return empty string + if(memcmp(&v->in6_addr, &in6addr_any, sizeof(in6addr_any)) == 0) + { + printTOMLstring(fp, "", toml); + break; + } + // else: normal address char addr6[INET6_ADDRSTRLEN] = { 0 }; inet_ntop(AF_INET6, &v->in6_addr, addr6, INET6_ADDRSTRLEN); printTOMLstring(fp, addr6, toml); @@ -654,7 +668,12 @@ void readTOMLvalue(struct conf_item *conf_item, const char* key, toml_table_t *t const toml_datum_t val = toml_string_in(toml, key); if(val.ok) { - if(inet_pton(AF_INET, val.u.s, &addr4)) + if(strlen(val.u.s) == 0) + { + // Special case: empty string -> 0.0.0.0 + conf_item->v.in_addr.s_addr = INADDR_ANY; + } + else if(inet_pton(AF_INET, val.u.s, &addr4)) memcpy(&conf_item->v.in_addr, &addr4, sizeof(addr4)); else log_warn("Config %s is invalid (not of type IPv4 address)", conf_item->k); @@ -670,7 +689,12 @@ void readTOMLvalue(struct conf_item *conf_item, const char* key, toml_table_t *t const toml_datum_t val = toml_string_in(toml, key); if(val.ok) { - if(inet_pton(AF_INET6, val.u.s, &addr6)) + if(strlen(val.u.s) == 0) + { + // Special case: empty string -> :: + conf_item->v.in6_addr = in6addr_any; + } + else if(inet_pton(AF_INET6, val.u.s, &addr6)) memcpy(&conf_item->v.in6_addr, &addr6, sizeof(addr6)); else log_warn("Config %s is invalid (not of type IPv6 address)", conf_item->k); @@ -910,7 +934,12 @@ bool readEnvValue(struct conf_item *conf_item, struct config *newconf) case CONF_STRUCT_IN_ADDR: { struct in_addr addr4 = { 0 }; - if(inet_pton(AF_INET, envvar, &addr4)) + if(strlen(envvar) == 0) + { + // Special case: empty string -> 0.0.0.0 + conf_item->v.in_addr.s_addr = INADDR_ANY; + } + else if(inet_pton(AF_INET, envvar, &addr4)) memcpy(&conf_item->v.in_addr, &addr4, sizeof(addr4)); else log_warn("ENV %s is invalid (not of type IPv4 address)", envkey); @@ -919,7 +948,12 @@ bool readEnvValue(struct conf_item *conf_item, struct config *newconf) case CONF_STRUCT_IN6_ADDR: { struct in6_addr addr6 = { 0 }; - if(inet_pton(AF_INET6, envvar, &addr6)) + if(strlen(envvar) == 0) + { + // Special case: empty string -> :: + conf_item->v.in6_addr = in6addr_any; + } + else if(inet_pton(AF_INET6, envvar, &addr6)) memcpy(&conf_item->v.in6_addr, &addr6, sizeof(addr6)); else log_warn("ENV %s is invalid (not of type IPv6 address)", envkey); diff --git a/test/api/libs/responseVerifyer.py b/test/api/libs/responseVerifyer.py index dcfc8f6f6..812c1511e 100644 --- a/test/api/libs/responseVerifyer.py +++ b/test/api/libs/responseVerifyer.py @@ -223,6 +223,9 @@ def verify_teleporter_zip(self, teleporter_archive: bytes): # Check if a string is a valid IPv4 address def valid_ipv4(self, addr: str) -> bool: + # Empty string is valid (0.0.0.0) + if len(addr) == 0: + return true try: if type(ipaddress.ip_address(addr)) is ipaddress.IPv4Address: return True @@ -232,6 +235,9 @@ def valid_ipv4(self, addr: str) -> bool: # Check if a string is a valid IPv6 address def valid_ipv6(self, addr: str) -> bool: + # Empty string is valid (::) + if len(addr) == 0: + return true try: if type(ipaddress.ip_address(addr)) is ipaddress.IPv6Address: return True @@ -253,10 +259,10 @@ def verify_type(self, prop: any, yaml_type: str, yaml_nullable: bool, yaml_forma return False if yaml_format is not None: # Check if the format is correct - if yaml_format == "ipv4" and not type(ipaddress.ip_address(prop)) is ipaddress.IPv4Address: + if yaml_format == "ipv4" and not (len(prop) == 0 or type(ipaddress.ip_address(prop)) is ipaddress.IPv4Address): self.errors.append("Property \"" + str(prop) + "\" is not a valid IPv4 address") return False - elif yaml_format == "ipv6" and not type(ipaddress.ip_address(prop)) is ipaddress.IPv6Address: + elif yaml_format == "ipv6" and not (len(prop) == 0 or type(ipaddress.ip_address(prop)) is ipaddress.IPv6Address): self.errors.append("Property \"" + str(prop) + "\" is not a valid IPv6 address") return False return prop_type in self.YAML_TYPES[yaml_type]