mirror of
https://github.com/espressif/openthread.git
synced 2026-08-21 18:09:52 +00:00
[dns] add Name::Matches() to compare DNS names (#9734)
This commit adds `Name::Matches()` method which compares a `Name` instance (which can be from a C string or encoded in a `Message`) with a given set of labels and domain name strings. This method allows the caller to specify name components separately, enabling scenarios like comparing "service instance name" with separate instance label, service type, and domain strings. Unit test `test_dns` is also updated to validate the behavior of the newly added method.
This commit is contained in:
@@ -100,6 +100,68 @@ Error Header::ResponseCodeToError(Response aResponse)
|
||||
return error;
|
||||
}
|
||||
|
||||
bool Name::Matches(const char *aFirstLabels, const char *aSecondLabels, const char *aDomain) const
|
||||
{
|
||||
bool matches = false;
|
||||
const char *namePtr;
|
||||
Buffer nameBuffer;
|
||||
|
||||
VerifyOrExit(!IsEmpty());
|
||||
|
||||
if (IsFromCString())
|
||||
{
|
||||
namePtr = mString;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t offset = mOffset;
|
||||
|
||||
SuccessOrExit(ReadName(*mMessage, offset, nameBuffer));
|
||||
namePtr = nameBuffer;
|
||||
}
|
||||
|
||||
if (aFirstLabels != nullptr)
|
||||
{
|
||||
matches = CompareAndSkipLabels(namePtr, aFirstLabels, kLabelSeparatorChar);
|
||||
VerifyOrExit(matches);
|
||||
}
|
||||
|
||||
if (aSecondLabels != nullptr)
|
||||
{
|
||||
matches = CompareAndSkipLabels(namePtr, aSecondLabels, kLabelSeparatorChar);
|
||||
VerifyOrExit(matches);
|
||||
}
|
||||
|
||||
matches = CompareAndSkipLabels(namePtr, aDomain, kNullChar);
|
||||
|
||||
exit:
|
||||
return matches;
|
||||
}
|
||||
|
||||
bool Name::CompareAndSkipLabels(const char *&aNamePtr, const char *aLabels, char aExpectedNextChar)
|
||||
{
|
||||
// Compares `aNamePtr` to the label string `aLabels` followed by
|
||||
// the `aExpectedNextChar`(using case-insensitive match). Upon
|
||||
// successful comparison, `aNamePtr` is advanced to point after
|
||||
// the matched portion.
|
||||
|
||||
bool matches = false;
|
||||
uint16_t len = StringLength(aLabels, kMaxNameSize);
|
||||
|
||||
VerifyOrExit(len < kMaxNameSize);
|
||||
|
||||
VerifyOrExit(StringStartsWith(aNamePtr, aLabels, kStringCaseInsensitiveMatch));
|
||||
aNamePtr += len;
|
||||
|
||||
VerifyOrExit(*aNamePtr == aExpectedNextChar);
|
||||
aNamePtr++;
|
||||
|
||||
matches = true;
|
||||
|
||||
exit:
|
||||
return matches;
|
||||
}
|
||||
|
||||
Error Name::AppendTo(Message &aMessage) const
|
||||
{
|
||||
Error error;
|
||||
|
||||
@@ -662,6 +662,25 @@ public:
|
||||
return *mMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the `Name` with a given set of labels and domain name.
|
||||
*
|
||||
* This method allows the caller to specify name components separately, enabling scenarios like comparing "service
|
||||
* instance name" with separate instance label, service type, and domain strings.
|
||||
*
|
||||
* @p aFirstLabels or @p aSecondLabels can be `nullptr` if not needed. But if non-null, these strings MUST NOT
|
||||
* end with dot. @p aDomain MUST NOT be `nullptr` and MUST always end with a dot `.` character.
|
||||
*
|
||||
* @param[in] aFirstLabels A string of dot separated labels, MUST NOT end with dot. Can be `nullptr`.
|
||||
* @param[in] aSecondLabels A string of dot separated labels, MUST NOT end with dot. Can be `nullptr`.
|
||||
* @param[in] aDomain Domain name. MUST end with dot.
|
||||
*
|
||||
* @retval TRUE The name matches the given labels.
|
||||
* @retval FALSE The name does not match the given labels.
|
||||
*
|
||||
*/
|
||||
bool Matches(const char *aFirstLabels, const char *aSecondLabels, const char *aDomain) const;
|
||||
|
||||
/**
|
||||
* Encodes and appends the name to a message.
|
||||
*
|
||||
@@ -1094,6 +1113,7 @@ private:
|
||||
{
|
||||
}
|
||||
|
||||
static bool CompareAndSkipLabels(const char *&aNamePtr, const char *aLabels, char aExpectedNextChar);
|
||||
static Error AppendLabel(const char *aLabel, uint8_t aLength, Message &aMessage);
|
||||
|
||||
const char *mString; // String containing the name or `nullptr` if name is not from string.
|
||||
|
||||
@@ -56,6 +56,15 @@ void TestDnsName(void)
|
||||
const char *mExpectedReadName;
|
||||
};
|
||||
|
||||
struct TestMatches
|
||||
{
|
||||
const char *mFullName;
|
||||
const char *mFirstLabels;
|
||||
const char *mSecondLabels;
|
||||
const char *mDomain;
|
||||
bool mShouldMatch;
|
||||
};
|
||||
|
||||
Instance *instance;
|
||||
MessagePool *messagePool;
|
||||
Message *message;
|
||||
@@ -139,6 +148,25 @@ void TestDnsName(void)
|
||||
static const char kBadLabel[] = "badlabel";
|
||||
static const char kBadName[] = "bad.name";
|
||||
|
||||
static const TestMatches kTestMatches[] = {
|
||||
{"foo.bar.local.", "foo", "bar", "local.", true},
|
||||
{"foo.bar.local.", "foo.bar", nullptr, "local.", true},
|
||||
{"foo.bar.local.", nullptr, "foo.bar", "local.", true},
|
||||
{"foo.bar.local.", nullptr, nullptr, "foo.bar.local.", true},
|
||||
{"foo.bar.local.", "foo", "ba", "local.", false},
|
||||
{"foo.bar.local.", "fooooo", "bar", "local.", false},
|
||||
{"foo.bar.local.", "foo", "bar", "locall.", false},
|
||||
{"foo.bar.local.", "f", "bar", "local.", false},
|
||||
{"foo.bar.local.", "foo", "barr", "local.", false},
|
||||
{"foo.bar.local.", "foo", "bar", ".local.", false},
|
||||
{"My Lovely Instance._mt._udp.local.", "mY lovely instancE", "_mt._udp", "local.", true},
|
||||
{"My Lovely Instance._mt._udp.local.", "mY lovely instancE._mt", "_udp", "local.", true},
|
||||
{"_s1._sub._srv._udp.default.service.arpa.", "_s1._sub", "_srv._udp", "default.service.arpa.", true},
|
||||
{"_s1._sub._srv._udp.default.service.arpa.", "_s1._sub", "_srv._udp", "default.service.arpa", false},
|
||||
{"_s1._sub._srv._udp.default.service.arpa.", "_s1._sub", "_srv._udp.", "default.service.arpa.", false},
|
||||
{"_s1._sub._srv._udp.default.service.arpa.", "_s1._sub.", "_srv._udp", "default.service.arpa.", false},
|
||||
};
|
||||
|
||||
printf("================================================================\n");
|
||||
printf("TestDnsName()\n");
|
||||
|
||||
@@ -496,6 +524,25 @@ void TestDnsName(void)
|
||||
VerifyOrQuit(memcmp(buffer, test.mEncodedData, len) == 0, "Encoded name data does not match expected data");
|
||||
}
|
||||
|
||||
printf("----------------------------------------------------------------\n");
|
||||
printf("Name::Matches() variations\n");
|
||||
|
||||
for (const TestMatches &test : kTestMatches)
|
||||
{
|
||||
Dns::Name name;
|
||||
|
||||
printf(" \"%s\"\n", test.mFullName);
|
||||
|
||||
name.Set(test.mFullName);
|
||||
VerifyOrQuit(name.Matches(test.mFirstLabels, test.mSecondLabels, test.mDomain) == test.mShouldMatch);
|
||||
|
||||
IgnoreError(message->SetLength(0));
|
||||
SuccessOrQuit(name.AppendTo(*message));
|
||||
|
||||
name.SetFromMessage(*message, 0);
|
||||
VerifyOrQuit(name.Matches(test.mFirstLabels, test.mSecondLabels, test.mDomain) == test.mShouldMatch);
|
||||
}
|
||||
|
||||
message->Free();
|
||||
testFreeInstance(instance);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user