From d0237f5e918ac5a679896b967aa838c609541cbb Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 27 Feb 2026 16:55:35 -0800 Subject: [PATCH] [dns] add `MatchesKey()` to `Dns::TxtEntry` (#12579) This commit adds `MatchesKey()` method to `Dns::TxtEntry` class to check if the entry's key matches a given key string using a case-insensitive string comparison. The new method properly handles the case where the entry's key is `nullptr` (which can happen when the key is longer than the recommended max length). It simplifies the TXT entry parsing logic in `BorderAgent` and `Trel::PeerDiscoverer`. The unit tests for `Dns::TxtEntry` are also updated to verify the new method. --- src/core/meshcop/border_agent_txt_data.cpp | 37 +++++++++------------- src/core/net/dns_types.cpp | 5 +++ src/core/net/dns_types.hpp | 10 ++++++ src/core/radio/trel_peer_discoverer.cpp | 13 ++------ tests/unit/test_dns.cpp | 16 +++++++--- 5 files changed, 44 insertions(+), 37 deletions(-) diff --git a/src/core/meshcop/border_agent_txt_data.cpp b/src/core/meshcop/border_agent_txt_data.cpp index 73785cff5..4ea20406c 100644 --- a/src/core/meshcop/border_agent_txt_data.cpp +++ b/src/core/meshcop/border_agent_txt_data.cpp @@ -303,28 +303,21 @@ Error TxtData::Info::ParseFrom(const uint8_t *aTxtData, uint16_t aTxtDataLength) void TxtData::Info::ProcessTxtEntry(const Dns::TxtEntry &aEntry) { - if (aEntry.mKey == nullptr) - { - // If the TXT data happens to have entries with key longer - // than `kMaxIterKeyLength`, `mKey` would be `nullptr` and full - // entry would be placed in `mValue`. We skip over such - // entries. - } - else if (StringMatch(aEntry.mKey, Key::kRecordVersion)) + if (aEntry.MatchesKey(Key::kRecordVersion)) { ReadStringValue(aEntry, mRecordVersion); mHasRecordVersion = true; } - else if (StringMatch(aEntry.mKey, Key::kAgentId)) + else if (aEntry.MatchesKey(Key::kAgentId)) { mHasAgentId = ReadValue(aEntry, mAgentId); } - else if (StringMatch(aEntry.mKey, Key::kThreadVersion)) + else if (aEntry.MatchesKey(Key::kThreadVersion)) { ReadStringValue(aEntry, mThreadVersion); mHasThreadVersion = true; } - else if (StringMatch(aEntry.mKey, Key::kStateBitmap)) + else if (aEntry.MatchesKey(Key::kStateBitmap)) { uint32_t bitmap; @@ -334,16 +327,16 @@ void TxtData::Info::ProcessTxtEntry(const Dns::TxtEntry &aEntry) mHasStateBitmap = true; } } - else if (StringMatch(aEntry.mKey, Key::kNetworkName)) + else if (aEntry.MatchesKey(Key::kNetworkName)) { ReadStringValue(aEntry, mNetworkName.m8); mHasNetworkName = true; } - else if (StringMatch(aEntry.mKey, Key::kExtendedPanId)) + else if (aEntry.MatchesKey(Key::kExtendedPanId)) { mHasExtendedPanId = ReadValue(aEntry, mExtendedPanId); } - else if (StringMatch(aEntry.mKey, Key::kActiveTimestamp)) + else if (aEntry.MatchesKey(Key::kActiveTimestamp)) { Timestamp timestamp; @@ -353,37 +346,37 @@ void TxtData::Info::ProcessTxtEntry(const Dns::TxtEntry &aEntry) mHasActiveTimestamp = true; } } - else if (StringMatch(aEntry.mKey, Key::kPartitionId)) + else if (aEntry.MatchesKey(Key::kPartitionId)) { mHasPartitionId = ReadBigEndianUintValue(aEntry, mPartitionId); } - else if (StringMatch(aEntry.mKey, Key::kDomainName)) + else if (aEntry.MatchesKey(Key::kDomainName)) { ReadStringValue(aEntry, mDomainName.m8); mHasDomainName = true; } - else if (StringMatch(aEntry.mKey, Key::kBbrSeqNum)) + else if (aEntry.MatchesKey(Key::kBbrSeqNum)) { mHasBbrSeqNum = ReadBigEndianUintValue(aEntry, mBbrSeqNum); } - else if (StringMatch(aEntry.mKey, Key::kBbrPort)) + else if (aEntry.MatchesKey(Key::kBbrPort)) { mHasBbrPort = ReadBigEndianUintValue(aEntry, mBbrPort); } - else if (StringMatch(aEntry.mKey, Key::kOmrPrefix)) + else if (aEntry.MatchesKey(Key::kOmrPrefix)) { mHasOmrPrefix = ReadOmrPrefix(aEntry, AsCoreType(&mOmrPrefix)); } - else if (StringMatch(aEntry.mKey, Key::kExtAddress)) + else if (aEntry.MatchesKey(Key::kExtAddress)) { mHasExtAddress = ReadValue(aEntry, mExtAddress); } - else if (StringMatch(aEntry.mKey, Key::kVendorName)) + else if (aEntry.MatchesKey(Key::kVendorName)) { ReadStringValue(aEntry, mVendorName); mHasVendorName = true; } - else if (StringMatch(aEntry.mKey, Key::kModelName)) + else if (aEntry.MatchesKey(Key::kModelName)) { ReadStringValue(aEntry, mModelName); mHasModelName = true; diff --git a/src/core/net/dns_types.cpp b/src/core/net/dns_types.cpp index fa47fa207..66f7247bc 100644 --- a/src/core/net/dns_types.cpp +++ b/src/core/net/dns_types.cpp @@ -1380,6 +1380,11 @@ exit: return error; } +bool TxtEntry::MatchesKey(const char *aKey) const +{ + return (mKey != nullptr) && StringMatch(mKey, aKey, kStringCaseInsensitiveMatch); +} + Error TxtEntry::AppendTo(Message &aMessage) const { Appender appender(aMessage); diff --git a/src/core/net/dns_types.hpp b/src/core/net/dns_types.hpp index a2d8539c1..a9c98989a 100644 --- a/src/core/net/dns_types.hpp +++ b/src/core/net/dns_types.hpp @@ -1223,6 +1223,16 @@ public: mValueLength = aValueLength; } + /** + * Indicates whether the entry's key (`mKey`) matches a given key string using a case-insensitive comparison. + * + * @param[in] aKey A pointer to a key string to compare with. + * + * @retval TRUE The entry's key matches the given @p aKey. + * @retval FALSE The entry's key does not match or `mKey` is `nullptr`. + */ + bool MatchesKey(const char *aKey) const; + /** * Encodes and appends the `TxtEntry` to a message. * diff --git a/src/core/radio/trel_peer_discoverer.cpp b/src/core/radio/trel_peer_discoverer.cpp index 3d1cefdcf..447c55ddc 100644 --- a/src/core/radio/trel_peer_discoverer.cpp +++ b/src/core/radio/trel_peer_discoverer.cpp @@ -750,23 +750,14 @@ Error PeerDiscoverer::TxtData::Decode(Info &aInfo) while ((error = iterator.GetNextEntry(entry)) == kErrorNone) { - // If the TXT data happens to have entries with key longer - // than `kMaxIterKeyLength`, `mKey` would be `nullptr` and full - // entry would be placed in `mValue`. We skip over such - // entries. - if (entry.mKey == nullptr) - { - continue; - } - - if (StringMatch(entry.mKey, kExtAddressKey)) + if (entry.MatchesKey(kExtAddressKey)) { VerifyOrExit(!parsedExtAddress, error = kErrorParse); VerifyOrExit(entry.mValueLength >= sizeof(Mac::ExtAddress), error = kErrorParse); aInfo.mExtAddress.Set(entry.mValue); parsedExtAddress = true; } - else if (StringMatch(entry.mKey, kExtPanIdKey)) + else if (entry.MatchesKey(kExtPanIdKey)) { VerifyOrExit(!parsedExtPanId, error = kErrorParse); VerifyOrExit(entry.mValueLength >= sizeof(MeshCoP::ExtendedPanId), error = kErrorParse); diff --git a/tests/unit/test_dns.cpp b/tests/unit/test_dns.cpp index 38410da46..7889d1add 100644 --- a/tests/unit/test_dns.cpp +++ b/tests/unit/test_dns.cpp @@ -34,6 +34,7 @@ #include "test_util.hpp" #include "common/array.hpp" +#include "common/string.hpp" #include "instance/instance.hpp" #include "net/dns_types.hpp" @@ -1632,10 +1633,8 @@ void TestHeaderAndResourceRecords(void) void TestDnsTxtEntry(void) { - enum - { - kMaxTxtDataSize = 255, - }; + static constexpr uint16_t kMaxTxtDataSize = 255; + static constexpr uint16_t kMaxKeyStringSize = Dns::TxtEntry::kMaxIterKeyLength; struct EncodedTxtData { @@ -1718,6 +1717,7 @@ void TestDnsTxtEntry(void) Dns::TxtEntry txtEntry; Dns::TxtEntry::Iterator iterator; MutableData data; + char keyString[kMaxKeyStringSize]; printf("================================================================\n"); printf("TestDnsTxtEntry()\n"); @@ -1772,6 +1772,14 @@ void TestDnsTxtEntry(void) } VerifyOrQuit(strcmp(txtEntry.mKey, expectedTxtEntry.mKey) == 0); + + SuccessOrQuit(StringCopy(keyString, expectedTxtEntry.mKey)); + VerifyOrQuit(txtEntry.MatchesKey(keyString)); + StringConvertToLowercase(keyString); + VerifyOrQuit(txtEntry.MatchesKey(keyString)); + StringConvertToUppercase(keyString); + VerifyOrQuit(txtEntry.MatchesKey(keyString)); + VerifyOrQuit(txtEntry.mValueLength == expectedTxtEntry.mValueLength); if (txtEntry.mValueLength != 0)