diff --git a/include/openthread/instance.h b/include/openthread/instance.h index d53c2015c..a89d1b324 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -52,7 +52,7 @@ extern "C" { * * @note This number versions both OpenThread platform and user APIs. */ -#define OPENTHREAD_API_VERSION (541) +#define OPENTHREAD_API_VERSION (542) /** * @addtogroup api-instance diff --git a/include/openthread/mdns.h b/include/openthread/mdns.h index fddf9ee91..f276b9ae0 100644 --- a/include/openthread/mdns.h +++ b/include/openthread/mdns.h @@ -309,6 +309,7 @@ otError otMdnsSetLocalHostName(otInstance *aInstance, const char *aName); * * @retval OT_ERROR_NONE Successfully started registration. @p aCallback will report the outcome. * @retval OT_ERROR_INVALID_STATE mDNS module is not enabled. + * @retval OT_ERROR_INVALID_ARGS The name in @p aHost is invalid. */ otError otMdnsRegisterHost(otInstance *aInstance, const otMdnsHost *aHost, @@ -333,6 +334,7 @@ otError otMdnsRegisterHost(otInstance *aInstance, * * @retval OT_ERROR_NONE Successfully unregistered host. * @retval OT_ERROR_INVALID_STATE mDNS module is not enabled. + * @retval OT_ERROR_INVALID_ARGS The name in @p aHost is invalid. */ otError otMdnsUnregisterHost(otInstance *aInstance, const otMdnsHost *aHost); @@ -372,6 +374,7 @@ otError otMdnsUnregisterHost(otInstance *aInstance, const otMdnsHost *aHost); * * @retval OT_ERROR_NONE Successfully started registration. @p aCallback will report the outcome. * @retval OT_ERROR_INVALID_STATE mDNS module is not enabled. + * @retval OT_ERROR_INVALID_ARGS A name in @p aService (instance, service type, sub-types, or host) is not valid. */ otError otMdnsRegisterService(otInstance *aInstance, const otMdnsService *aService, @@ -399,6 +402,7 @@ otError otMdnsRegisterService(otInstance *aInstance, * * @retval OT_ERROR_NONE Successfully unregistered service. * @retval OT_ERROR_INVALID_STATE mDNS module is not enabled. + * @retval OT_ERROR_INVALID_ARGS A name in @p aService (instance or service type) is not valid. */ otError otMdnsUnregisterService(otInstance *aInstance, const otMdnsService *aService); @@ -429,6 +433,7 @@ otError otMdnsUnregisterService(otInstance *aInstance, const otMdnsService *aSer * * @retval OT_ERROR_NONE Successfully started registration. @p aCallback will report the outcome. * @retval OT_ERROR_INVALID_STATE mDNS module is not enabled. + * @retval OT_ERROR_INVALID_ARGS A name in @p aKey is not valid. */ otError otMdnsRegisterKey(otInstance *aInstance, const otMdnsKey *aKey, @@ -457,6 +462,7 @@ otError otMdnsRegisterKey(otInstance *aInstance, * * @retval OT_ERROR_NONE Successfully unregistered key * @retval OT_ERROR_INVALID_STATE mDNS module is not enabled. + * @retval OT_ERROR_INVALID_ARGS A name in @p aKey is not valid. */ otError otMdnsUnregisterKey(otInstance *aInstance, const otMdnsKey *aKey); diff --git a/src/core/net/dns_types.cpp b/src/core/net/dns_types.cpp index 8db30eff1..c8d1c5bab 100644 --- a/src/core/net/dns_types.cpp +++ b/src/core/net/dns_types.cpp @@ -746,6 +746,69 @@ exit: return error; } +Error Name::ValidateLabel(const char *aLabel) +{ + Error error = kErrorInvalidArgs; + uint16_t length; + + VerifyOrExit(aLabel != nullptr); + + length = StringLength(aLabel, kMaxLabelSize); + VerifyOrExit((0 < length) && (length <= kMaxLabelLength)); + error = kErrorNone; + +exit: + return error; +} + +Error Name::ValidateName(const char *aName) +{ + Error error = kErrorNone; + uint16_t index = 0; + uint16_t labelStartIndex = 0; + char ch; + uint16_t length; + + VerifyOrExit(aName != nullptr, error = kErrorInvalidArgs); + length = StringLength(aName, kMaxNameSize); + + VerifyOrExit(length > 0, error = kErrorInvalidArgs); + VerifyOrExit(length <= kMaxNameLength, error = kErrorInvalidArgs); + + do + { + ch = aName[index]; + + if ((ch == kNullChar) || (ch == kLabelSeparatorChar)) + { + length = index - labelStartIndex; + + VerifyOrExit(length <= kMaxLabelLength, error = kErrorInvalidArgs); + + if (length == 0) + { + // Empty label (e.g., consecutive dots) is invalid, but we + // allow for two cases: (1) where `aName` ends with a dot + // (`length` is zero but we are at end of `aName` string + // and `ch` is null char. (2) if `aName` is just "." (we + // see a dot at index 0, and index 1 is null char). + + VerifyOrExit((ch == kNullChar) || ((index == 0) && (aName[1] == kNullChar)), error = kErrorInvalidArgs); + error = kErrorNone; + ExitNow(); + } + + labelStartIndex = index + 1; + } + + index++; + + } while (ch != kNullChar); + +exit: + return error; +} + bool Name::IsSubDomainOf(const char *aName, const char *aDomain) { bool match = false; diff --git a/src/core/net/dns_types.hpp b/src/core/net/dns_types.hpp index 0eacf5fff..9e47cc4a9 100644 --- a/src/core/net/dns_types.hpp +++ b/src/core/net/dns_types.hpp @@ -1000,6 +1000,37 @@ public: return ExtractLabels(aName, aSuffixName, aName, kNameBufferSize); } + /** + * Validates a DNS label. + * + * A label must be between 1 and 63 (`kMaxLabelLength`) characters long. + * + * @param[in] aLabel The string containing the label. + * + * @retval kErrorNone The label is valid. + * @retval kErrorInvalidArgs The label is not valid (e.g., is `nullptr`, empty, or too long). + */ + static Error ValidateLabel(const char *aLabel); + + /** + * Validates a DNS name. + * + * A DNS name is a sequence of labels separated by dots. + * + * This method validates the following rules: + * - The name string is not `nullptr`. + * - The total length of the name is between 1 and 254 (`kMaxNameLength`) characters. + * - Each label is at most 63 (`kMaxLabelLength`) characters long. + * - Empty labels (e.g., consecutive dots `..`) are disallowed, except for a single trailing dot `.` at the end of + * the name, or if the name is just ".". + * + * @param[in] aName The string containing the name. + * + * @retval kErrorNone The name is valid. + * @retval kErrorInvalidArgs The name is not valid. + */ + static Error ValidateName(const char *aName); + /** * Tests if a DNS name is a sub-domain of a given domain. * diff --git a/src/core/net/mdns.cpp b/src/core/net/mdns.cpp index bbece3317..69ecf10d4 100644 --- a/src/core/net/mdns.cpp +++ b/src/core/net/mdns.cpp @@ -209,6 +209,52 @@ exit: #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +Error Core::ValidateHostName(const Host &aHost) const { return Name::ValidateName(aHost.mHostName); } + +Error Core::ValidateServiceNames(const Service &aService, bool aCheckHostAndSubTypeLabels) const +{ + Error error; + + SuccessOrExit(error = Name::ValidateLabel(aService.mServiceInstance)); + SuccessOrExit(error = Name::ValidateName(aService.mServiceType)); + + if (aCheckHostAndSubTypeLabels && aService.mHostName != nullptr) + { + SuccessOrExit(error = Name::ValidateName(aService.mHostName)); + } + + if (aCheckHostAndSubTypeLabels && (aService.mSubTypeLabelsLength > 0)) + { + VerifyOrExit(aService.mSubTypeLabels != nullptr, error = kErrorInvalidArgs); + + for (uint16_t index = 0; index < aService.mSubTypeLabelsLength; index++) + { + SuccessOrExit(error = Name::ValidateLabel(aService.mSubTypeLabels[index])); + } + } + +exit: + return error; +} + +Error Core::ValidateKeyName(const Key &aKey) const +{ + Error error; + + if (IsKeyForService(aKey)) + { + SuccessOrExit(error = Name::ValidateName(aKey.mServiceType)); + error = Name::ValidateLabel(aKey.mName); + } + else + { + error = Name::ValidateName(aKey.mName); + } + +exit: + return error; +} + template Error Core::Register(const ItemInfo &aItemInfo, RequestId aRequestId, RegisterCallback aCallback) { @@ -252,27 +298,70 @@ exit: Error Core::RegisterHost(const Host &aHost, RequestId aRequestId, RegisterCallback aCallback) { - return Register(aHost, aRequestId, aCallback); + Error error; + + SuccessOrExit(error = ValidateHostName(aHost)); + error = Register(aHost, aRequestId, aCallback); + +exit: + return error; } -Error Core::UnregisterHost(const Host &aHost) { return Unregister(aHost); } +Error Core::UnregisterHost(const Host &aHost) +{ + Error error; + + SuccessOrExit(error = ValidateHostName(aHost)); + error = Unregister(aHost); + +exit: + return error; +} Error Core::RegisterService(const Service &aService, RequestId aRequestId, RegisterCallback aCallback) { - return Register(aService, aRequestId, aCallback); + Error error; + + SuccessOrExit(error = ValidateServiceNames(aService, /* aCheckHostAndSubTypeLabels */ true)); + error = Register(aService, aRequestId, aCallback); + +exit: + return error; } -Error Core::UnregisterService(const Service &aService) { return Unregister(aService); } +Error Core::UnregisterService(const Service &aService) +{ + Error error; + + SuccessOrExit(error = ValidateServiceNames(aService, /* aCheckHostAndSubTypeLabels */ false)); + error = Unregister(aService); + +exit: + return error; +} Error Core::RegisterKey(const Key &aKey, RequestId aRequestId, RegisterCallback aCallback) { - return IsKeyForService(aKey) ? Register(aKey, aRequestId, aCallback) - : Register(aKey, aRequestId, aCallback); + Error error; + + SuccessOrExit(error = ValidateKeyName(aKey)); + + error = IsKeyForService(aKey) ? Register(aKey, aRequestId, aCallback) + : Register(aKey, aRequestId, aCallback); + +exit: + return error; } Error Core::UnregisterKey(const Key &aKey) { - return IsKeyForService(aKey) ? Unregister(aKey) : Unregister(aKey); + Error error; + + SuccessOrExit(error = ValidateKeyName(aKey)); + error = IsKeyForService(aKey) ? Unregister(aKey) : Unregister(aKey); + +exit: + return error; } #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE diff --git a/src/core/net/mdns.hpp b/src/core/net/mdns.hpp index eae329dab..bd499add8 100644 --- a/src/core/net/mdns.hpp +++ b/src/core/net/mdns.hpp @@ -313,6 +313,7 @@ public: * * @retval kErrorNone Successfully started registration. @p aCallback will report the outcome. * @retval kErrorInvalidState mDNS module is not enabled. + * @retval kErrorInvalidArgs The host name in @p aHost is not valid. */ Error RegisterHost(const Host &aHost, RequestId aRequestId, RegisterCallback aCallback); @@ -333,6 +334,7 @@ public: * * @retval kErrorNone Successfully unregistered host. * @retval kErrorInvalidState mDNS module is not enabled. + * @retval kErrorInvalidArgs The host name in @p aHost is not valid. */ Error UnregisterHost(const Host &aHost); @@ -370,6 +372,7 @@ public: * * @retval kErrorNone Successfully started registration. @p aCallback will report the outcome. * @retval kErrorInvalidState mDNS module is not enabled. + * @retval kErrorInvalidArgs A name in @p aService (instance, service type, sub-types, or host) is not valid. */ Error RegisterService(const Service &aService, RequestId aRequestId, RegisterCallback aCallback); @@ -393,6 +396,7 @@ public: * * @retval kErrorNone Successfully unregistered service. * @retval kErrorInvalidState mDNS module is not enabled. + * @retval kErrorInvalidArgs A name in @p aService (instance, service type) is not valid. */ Error UnregisterService(const Service &aService); @@ -421,6 +425,7 @@ public: * * @retval kErrorNone Successfully started registration. @p aCallback will report the outcome. * @retval kErrorInvalidState mDNS module is not enabled. + * @retval kErrorInvalidArgs A name in @p aKey is not valid. */ Error RegisterKey(const Key &aKey, RequestId aRequestId, RegisterCallback aCallback); @@ -445,6 +450,7 @@ public: * * @retval kErrorNone Successfully unregistered key * @retval kErrorInvalidState mDNS module is not enabled. + * @retval kErrorInvalidArgs A name in @p aKey is not valid. */ Error UnregisterKey(const Key &aKey); @@ -2313,6 +2319,9 @@ private: void AfterInstanceInit(void); Error SetEnabled(bool aEnable, uint32_t aInfraIfIndex, Requester aRequester); + Error ValidateHostName(const Host &aHost) const; + Error ValidateServiceNames(const Service &aService, bool aCheckHostAndSubTypeLabels) const; + Error ValidateKeyName(const Key &aKey) const; void HandleInfraIfStateChanged(void); void HandleHostAddressEvent(const Ip6::Address &aAddress, bool aAdded, uint32_t aInfraIfIndex); void HandleHostAddressRemoveAll(uint32_t aInfraIfIndex); diff --git a/tests/unit/test_dns.cpp b/tests/unit/test_dns.cpp index 0bd856706..860a4469d 100644 --- a/tests/unit/test_dns.cpp +++ b/tests/unit/test_dns.cpp @@ -591,6 +591,37 @@ void TestDnsName(void) VerifyOrQuit(!dnsName.Matches("Name.With.Dot", "_srv._tcp", "local.")); VerifyOrQuit(!dnsName.Matches("Name.With.Dot", "_srv._udp", "arpa.")); + printf("----------------------------------------------------------------\n"); + printf("Name::ValidateLabel\n"); + + SuccessOrQuit(Dns::Name::ValidateLabel("a")); + SuccessOrQuit(Dns::Name::ValidateLabel("hello")); + SuccessOrQuit(Dns::Name::ValidateLabel("012345678901234567890123456789012345678901234567890123456789012")); // 63 + VerifyOrQuit(Dns::Name::ValidateLabel("0123456789012345678901234567890123456789012345678901234567890123") == + kErrorInvalidArgs); + VerifyOrQuit(Dns::Name::ValidateLabel("") == kErrorInvalidArgs); + + SuccessOrQuit(Dns::Name::ValidateName("a")); + SuccessOrQuit(Dns::Name::ValidateName("a.b.c")); + SuccessOrQuit(Dns::Name::ValidateName("a.b.c.")); + SuccessOrQuit(Dns::Name::ValidateName("a.b.012345678901234567890123456789012345678901234567890123456789012.")); + SuccessOrQuit(Dns::Name::ValidateName(".")); + + // Empty labels + VerifyOrQuit(Dns::Name::ValidateName("") == kErrorInvalidArgs); + VerifyOrQuit(Dns::Name::ValidateName("a..b") == kErrorInvalidArgs); + VerifyOrQuit(Dns::Name::ValidateName(".a.b") == kErrorInvalidArgs); + VerifyOrQuit(Dns::Name::ValidateName("a.b..") == kErrorInvalidArgs); + + // Long labels or names + VerifyOrQuit(Dns::Name::ValidateName("a.b.0123456789012345678901234567890123456789012345678901234567890123.") == + kErrorInvalidArgs); + VerifyOrQuit(Dns::Name::ValidateName("012345678901234567890123456789012345678901234567890123456789012." + "012345678901234567890123456789012345678901234567890123456789012." + "012345678901234567890123456789012345678901234567890123456789012." + "012345678901234567890123456789012345678901234567890123456789012") == + kErrorInvalidArgs); + message->Free(); testFreeInstance(instance); }