[dns] change Dns::Name::CompareName/Label() to be case-insensitive (#7189)

This commit changes the `Dns::Name::CompareName/Label()` methods to
perform case-insensitive string comparison. This is realized by
adding a new flavor of `Data::MatchesBytesIn()` which accepts a
`ByteMatcher` function pointer. This allows the caller to relax the
definition of a match and how the bytes are compared. `Message` class
methods that compare bytes are also updated to allow `ByteMatcher` as
an input parameter. This commit also updates the unit test `test_dns`
to cover the new method and behaviors.
This commit is contained in:
Abtin Keshavarzian
2021-11-23 16:31:18 -08:00
committed by Jonathan Hui
parent 07031e1651
commit e16261aa6c
11 changed files with 251 additions and 57 deletions
+1
View File
@@ -221,6 +221,7 @@ LOCAL_SRC_FILES := \
src/core/coap/coap_secure.cpp \
src/core/common/appender.cpp \
src/core/common/crc16.cpp \
src/core/common/data.cpp \
src/core/common/error.cpp \
src/core/common/heap.cpp \
src/core/common/heap_data.cpp \
+1
View File
@@ -382,6 +382,7 @@ openthread_core_files = [
"common/const_cast.hpp",
"common/crc16.cpp",
"common/crc16.hpp",
"common/data.cpp",
"common/data.hpp",
"common/debug.hpp",
"common/encoding.hpp",
+1
View File
@@ -94,6 +94,7 @@ set(COMMON_SOURCES
coap/coap_secure.cpp
common/appender.cpp
common/crc16.cpp
common/data.cpp
common/error.cpp
common/heap.cpp
common/heap_data.cpp
+1
View File
@@ -184,6 +184,7 @@ SOURCES_COMMON = \
coap/coap_secure.cpp \
common/appender.cpp \
common/crc16.cpp \
common/data.cpp \
common/error.cpp \
common/heap.cpp \
common/heap_data.cpp \
+60
View File
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2021, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements `Data` related function
*/
#include "data.hpp"
namespace ot {
bool DataUtils::MatchBytes(const uint8_t *aFirstBuffer,
const uint8_t *aSecondBuffer,
uint16_t aLength,
ByteMatcher aMatcher)
{
bool matches = true;
if (aMatcher == nullptr)
{
matches = (memcmp(aFirstBuffer, aSecondBuffer, aLength) == 0);
ExitNow();
}
while (aLength-- != 0)
{
VerifyOrExit(aMatcher(*aFirstBuffer++, *aSecondBuffer++), matches = false);
}
exit:
return matches;
}
} // namespace ot
+47 -1
View File
@@ -59,6 +59,35 @@ enum DataLengthType : uint8_t
kWithUint16Length, ///< Use `uint16_t` for data length
};
/**
* This type specifies a function pointer which matches two given bytes.
*
* Such a function is used as a parameter in `Data::MatchesByteIn()` method. This can be used to relax the definition
* of a match when comparing data bytes, e.g., can be used for case-insensitive string comparison.
*
* @param[in] aFirst A first byte.
* @param[in] aSecond A second byte.
*
* @retval TRUE if @p aFirst matches @p aSecond.
* @retval FLASE if @p aFirst does not match @p aSecond.
*
*/
typedef bool (*ByteMatcher)(uint8_t aFirst, uint8_t aSecond);
/**
* This class implements common utility methods used by `Data` and `MutableData`.
*
*/
class DataUtils
{
protected:
DataUtils(void) = default;
static bool MatchBytes(const uint8_t *aFirstBuffer,
const uint8_t *aSecondBuffer,
uint16_t aLength,
ByteMatcher aMatcher);
};
template <DataLengthType kDataLengthType> class MutableData;
/**
@@ -76,7 +105,7 @@ template <DataLengthType kDataLengthType> class MutableData;
*
*/
template <DataLengthType kDataLengthType>
class Data : public Clearable<Data<kDataLengthType>>, public Unequatable<Data<kDataLengthType>>
class Data : public Clearable<Data<kDataLengthType>>, public Unequatable<Data<kDataLengthType>>, private DataUtils
{
friend class MutableData<kDataLengthType>;
@@ -176,6 +205,23 @@ public:
*/
bool MatchesBytesIn(const void *aBuffer) const { return memcmp(mBuffer, aBuffer, mLength) == 0; }
/**
* This method compares the `Data` content with the bytes from a given buffer using a given `Matcher` function.
*
* It is up to the caller to ensure that @p aBuffer has enough bytes to compare with the current data length.
*
* @param[in] aBuffer A pointer to a buffer to compare with the data.
* @param[in] aMatcher A `ByteMatcher` function to match the bytes. If `nullptr`, bytes are compared directly.
*
* @retval TRUE The `Data` content matches the bytes in @p aBuffer.
* @retval FALSE The `Data` content does not match the byes in @p aBuffer.
*
*/
bool MatchesBytesIn(const void *aBuffer, ByteMatcher aMatcher)
{
return MatchBytes(mBuffer, static_cast<const uint8_t *>(aBuffer), mLength, aMatcher);
}
/**
* This method overloads operator `==` to compare the `Data` content with the content from another one.
*
+5 -4
View File
@@ -578,7 +578,7 @@ Error Message::Read(uint16_t aOffset, void *aBuf, uint16_t aLength) const
return (ReadBytes(aOffset, aBuf, aLength) == aLength) ? kErrorNone : kErrorParse;
}
bool Message::CompareBytes(uint16_t aOffset, const void *aBuf, uint16_t aLength) const
bool Message::CompareBytes(uint16_t aOffset, const void *aBuf, uint16_t aLength, ByteMatcher aMatcher) const
{
uint16_t bytesToCompare = aLength;
const uint8_t *bufPtr = reinterpret_cast<const uint8_t *>(aBuf);
@@ -588,7 +588,7 @@ bool Message::CompareBytes(uint16_t aOffset, const void *aBuf, uint16_t aLength)
while (chunk.GetLength() > 0)
{
VerifyOrExit(chunk.MatchesBytesIn(bufPtr));
VerifyOrExit(chunk.MatchesBytesIn(bufPtr, aMatcher));
bufPtr += chunk.GetLength();
bytesToCompare -= chunk.GetLength();
GetNextChunk(aLength, chunk);
@@ -601,7 +601,8 @@ exit:
bool Message::CompareBytes(uint16_t aOffset,
const Message &aOtherMessage,
uint16_t aOtherOffset,
uint16_t aLength) const
uint16_t aLength,
ByteMatcher aMatcher) const
{
uint16_t bytesToCompare = aLength;
Chunk chunk;
@@ -610,7 +611,7 @@ bool Message::CompareBytes(uint16_t aOffset,
while (chunk.GetLength() > 0)
{
VerifyOrExit(aOtherMessage.CompareBytes(aOtherOffset, chunk.GetBytes(), chunk.GetLength()));
VerifyOrExit(aOtherMessage.CompareBytes(aOtherOffset, chunk.GetBytes(), chunk.GetLength(), aMatcher));
aOtherOffset += chunk.GetLength();
bytesToCompare -= chunk.GetLength();
GetNextChunk(aLength, chunk);
+10 -2
View File
@@ -697,12 +697,14 @@ public:
* @param[in] aOffset Byte offset within the message to read from for the comparison.
* @param[in] aBuf A pointer to a data buffer to compare with the bytes from message.
* @param[in] aLength Number of bytes in @p aBuf.
* @param[in] aMatcher A `ByteMatcher` function pointer to match the bytes. If `nullptr` then bytes are directly
* compared.
*
* @returns TRUE if there are enough bytes available in @p aMessage and they match the bytes from @p aBuf,
* FALSE otherwise.
*
*/
bool CompareBytes(uint16_t aOffset, const void *aBuf, uint16_t aLength) const;
bool CompareBytes(uint16_t aOffset, const void *aBuf, uint16_t aLength, ByteMatcher aMatcher = nullptr) const;
/**
* This method compares the bytes in the message at a given offset with bytes read from another message.
@@ -714,11 +716,17 @@ public:
* @param[in] aOtherMessage The other message to compare with.
* @param[in] aOtherOffset Byte offset within @p aOtherMessage to read from for the comparison.
* @param[in] aLength Number of bytes to compare.
* @param[in] aMatcher A `ByteMatcher` function pointer to match the bytes. If `nullptr` then bytes are
* directly compared.
*
* @returns TRUE if there are enough bytes available in both messages and they all match. FALSE otherwise.
*
*/
bool CompareBytes(uint16_t aOffset, const Message &aOtherMessage, uint16_t aOtherOffset, uint16_t aLength) const;
bool CompareBytes(uint16_t aOffset,
const Message &aOtherMessage,
uint16_t aOtherOffset,
uint16_t aLength,
ByteMatcher aMatcher = nullptr) const;
/**
* This method compares the bytes in the message at a given offset with an object.
+37 -9
View File
@@ -368,7 +368,7 @@ Error Name::CompareLabel(const Message &aMessage, uint16_t &aOffset, const char
LabelIterator iterator(aMessage, aOffset);
SuccessOrExit(error = iterator.GetNextLabel());
VerifyOrExit(iterator.CompareLabel(aLabel, /* aIsSingleLabel */ true), error = kErrorNotFound);
VerifyOrExit(iterator.CompareLabel(aLabel, kIsSingleLabel), error = kErrorNotFound);
aOffset = iterator.mNextLabelOffset;
exit:
@@ -394,7 +394,7 @@ Error Name::CompareName(const Message &aMessage, uint16_t &aOffset, const char *
switch (error)
{
case kErrorNone:
if (matches && !iterator.CompareLabel(aName, /* aIsSingleLabel */ false))
if (matches && !iterator.CompareLabel(aName, !kIsSingleLabel))
{
matches = false;
}
@@ -564,6 +564,11 @@ exit:
return error;
}
bool Name::LabelIterator::CaseInsensitiveMatch(uint8_t aFirst, uint8_t aSecond)
{
return ToLowercase(static_cast<char>(aFirst)) == ToLowercase(static_cast<char>(aSecond));
}
bool Name::LabelIterator::CompareLabel(const char *&aName, bool aIsSingleLabel) const
{
// This method compares the current label in the iterator with the
@@ -577,7 +582,7 @@ bool Name::LabelIterator::CompareLabel(const char *&aName, bool aIsSingleLabel)
bool matches = false;
VerifyOrExit(StringLength(aName, mLabelLength) == mLabelLength);
matches = mMessage.CompareBytes(mLabelStartOffset, aName, mLabelLength);
matches = mMessage.CompareBytes(mLabelStartOffset, aName, mLabelLength, CaseInsensitiveMatch);
VerifyOrExit(matches);
@@ -606,7 +611,7 @@ bool Name::LabelIterator::CompareLabel(const LabelIterator &aOtherIterator) cons
return (mLabelLength == aOtherIterator.mLabelLength) &&
mMessage.CompareBytes(mLabelStartOffset, aOtherIterator.mMessage, aOtherIterator.mLabelStartOffset,
mLabelLength);
mLabelLength, CaseInsensitiveMatch);
}
Error Name::LabelIterator::AppendLabel(Message &aMessage) const
@@ -626,30 +631,53 @@ exit:
bool Name::IsSubDomainOf(const char *aName, const char *aDomain)
{
bool match = false;
uint16_t nameLength = StringLength(aName, kMaxNameLength);
uint16_t domainLength = StringLength(aDomain, kMaxNameLength);
bool match = false;
bool nameEndsWithDot = false;
bool domainEndsWithDot = false;
uint16_t nameLength = StringLength(aName, kMaxNameLength);
uint16_t domainLength = StringLength(aDomain, kMaxNameLength);
if (nameLength > 0 && aName[nameLength - 1] == kLabelSeperatorChar)
{
nameEndsWithDot = true;
--nameLength;
}
if (domainLength > 0 && aDomain[domainLength - 1] == kLabelSeperatorChar)
{
domainEndsWithDot = true;
--domainLength;
}
VerifyOrExit(nameLength >= domainLength);
aName += nameLength - domainLength;
if (nameLength > domainLength)
{
VerifyOrExit(aName[-1] == kLabelSeperatorChar);
}
VerifyOrExit(memcmp(aName, aDomain, domainLength) == 0);
match = true;
// This method allows either `aName` or `aDomain` to include or
// exclude the last `.` character. If both include it or if both
// do not, we do a full comparison using `StringMatch()`.
// Otherwise (i.e., when one includes and the other one does not)
// we use `StringStartWith()` to allow the extra `.` character.
if (nameEndsWithDot == domainEndsWithDot)
{
match = StringMatch(aName, aDomain, kStringCaseInsensitiveMatch);
}
else if (nameEndsWithDot)
{
// `aName` ends with dot, but `aDomain` does not.
match = StringStartsWith(aName, aDomain, kStringCaseInsensitiveMatch);
}
else
{
// `aDomain` ends with dot, but `aName` does not.
match = StringStartsWith(aDomain, aName, kStringCaseInsensitiveMatch);
}
exit:
return match;
+16 -12
View File
@@ -872,7 +872,7 @@ public:
* This static method compares a single name label from a message with a given label string.
*
* This method can be used to compare labels one by one. It checks whether the label read from @p aMessage matches
* @p aLabel string.
* @p aLabel string (case-insensitive comparison).
*
* Unlike `CompareName()` which requires the labels in the the name string to contain no dot '.' character, this
* method allows @p aLabel to include any character.
@@ -883,7 +883,7 @@ public:
* On exit and only when label is successfully read and does match @p aLabel,
* @p aOffset is updated to point to the start of the next label.
* @param[in] aLabel A pointer to a null terminated string containing the label to compare with.
*
* @retval kErrorNone The label from @p aMessage matches @p aLabel. @p aOffset is updated.
* @retval kErrorNotFound The label from @p aMessage does not match @p aLabel (note that @p aOffset is not
* updated in this case).
@@ -895,9 +895,10 @@ public:
/**
* This static method parses and compares a full name from a message with a given name.
*
* This method checks whether the encoded name in a message matches a given name string. It checks the name in
* the message in place and handles compressed names. If the name read from the message does not match @p aName, it
* returns `kErrorNotFound`. `kErrorNone` indicates that the name matches @p aName.
* This method checks whether the encoded name in a message matches a given name string (using case-insensitive
* comparison). It checks the name in the message in place and handles compressed names. If the name read from the
* message does not match @p aName, it returns `kErrorNotFound`. `kErrorNone` indicates that the name matches
* @p aName.
*
* The @p aName must follow "<label1>.<label2>.<label3>", i.e., a sequence of labels separated by dot '.' char.
* E.g., "example.com", "example.com." (same as previous one), "local.", "default.service.arpa", "." or "" (root).
@@ -922,9 +923,10 @@ public:
/**
* This static method parses and compares a full name from a message with a name from another message.
*
* This method checks whether the encoded name in @p aMessage matches the name from @p aMessage2. It compares the
* names in both messages in place and handles compressed names. Note that this method works correctly even when
* the same message instance is used for both @p aMessage and @p aMessage2 (e.g., at different offsets).
* This method checks whether the encoded name in @p aMessage matches the name from @p aMessage2 (using
* case-insensitive comparison). It compares the names in both messages in place and handles compressed names. Note
* that this method works correctly even when the same message instance is used for both @p aMessage and
* @p aMessage2 (e.g., at different offsets).
*
* Only the name in @p aMessage is fully parsed and checked for parse errors. This method assumes that the name in
* @p aMessage2 was previously parsed and validated before calling this method (if there is a parse error in
@@ -952,7 +954,8 @@ public:
static Error CompareName(const Message &aMessage, uint16_t &aOffset, const Message &aMessage2, uint16_t aOffset2);
/**
* This static method parses and compares a full name from a message with a given name.
* This static method parses and compares a full name from a message with a given name (using case-insensitive
* comparison).
*
* If @p aName is empty (not specified), then any name in @p aMessage is considered a match to it.
*
@@ -985,8 +988,6 @@ public:
static bool IsSubDomainOf(const char *aName, const char *aDomain);
private:
static constexpr char kNullChar = '\0';
// The first 2 bits of the encoded label specifies label type.
//
// - Value 00 indicates normal text label (lower 6-bits indicates the label length).
@@ -1002,6 +1003,8 @@ private:
static constexpr uint16_t kPointerLabelTypeUint16 = 0xc000; // Pointer label type mask (first 2 bits).
static constexpr uint16_t kPointerLabelOffsetMask = 0x3fff; // Mask for offset in a pointer label (lower 14 bits).
static constexpr bool kIsSingleLabel = true; // Used in `LabelIterator::CompareLable()`.
struct LabelIterator
{
static constexpr uint16_t kUnsetNameEndOffset = 0; // Special value indicating `mNameEndOffset` is not yet set.
@@ -1020,6 +1023,8 @@ private:
bool CompareLabel(const LabelIterator &aOtherIterator) const;
Error AppendLabel(Message &aMessage) const;
static bool CaseInsensitiveMatch(uint8_t aFirst, uint8_t aSecond);
const Message &mMessage; // Message to read labels from.
uint16_t mLabelStartOffset; // Offset in `mMessage` to the first char of current label text.
uint8_t mLabelLength; // Length of current label (number of chars).
@@ -1182,7 +1187,6 @@ private:
static constexpr uint8_t kMaxKeyValueEncodedSize = 255;
static constexpr char kKeyValueSeparator = '=';
static constexpr char kNullChar = '\0';
};
/**
+72 -29
View File
@@ -64,6 +64,8 @@ void TestDnsName(void)
char label[Dns::Name::kMaxLabelSize];
uint8_t labelLength;
char name[Dns::Name::kMaxNameSize];
const char * subDomain;
const char * domain;
static const uint8_t kEncodedName1[] = {7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0};
static const uint8_t kEncodedName2[] = {3, 'f', 'o', 'o', 1, 'a', 2, 'b', 'b', 3, 'e', 'd', 'u', 0};
@@ -147,39 +149,65 @@ void TestDnsName(void)
printf("----------------------------------------------------------------\n");
printf("Verify domain name match:\n");
{
const char *subDomain;
const char *domain;
subDomain = "my-service._ipps._tcp.local.";
domain = "local.";
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
subDomain = "my-service._ipps._tcp.local.";
domain = "local.";
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
subDomain = "my-service._ipps._tcp.local";
domain = "local.";
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
subDomain = "my-service._ipps._tcp.local";
domain = "local.";
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
subDomain = "my-service._ipps._tcp.local.";
domain = "local";
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
subDomain = "my-service._ipps._tcp.local.";
domain = "local";
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
subDomain = "my-service._ipps._tcp.local";
domain = "local";
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
subDomain = "my-service._ipps._tcp.local";
domain = "local";
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
subDomain = "my-service._ipps._tcp.default.service.arpa.";
domain = "default.service.arpa.";
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
subDomain = "my-service._ipps._tcp.default.service.arpa.";
domain = "default.service.arpa.";
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
subDomain = "my-service._ipps._tcp.default.service.arpa.";
domain = "service.arpa.";
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
subDomain = "my-service._ipps._tcp.default.service.arpa.";
domain = "service.arpa.";
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
// Verify it doesn't match a portion of a label.
subDomain = "my-service._ipps._tcp.default.service.arpa.";
domain = "vice.arpa.";
VerifyOrQuit(!Dns::Name::IsSubDomainOf(subDomain, domain));
// Verify it doesn't match a portion of a label.
subDomain = "my-service._ipps._tcp.default.service.arpa.";
domain = "vice.arpa.";
VerifyOrQuit(!Dns::Name::IsSubDomainOf(subDomain, domain));
}
// Validate case does not matter
subDomain = "my-service._ipps._tcp.local.";
domain = "LOCAL.";
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
subDomain = "my-service._ipps._tcp.local";
domain = "LOCAL.";
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
subDomain = "my-service._ipps._tcp.local.";
domain = "LOCAL";
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
subDomain = "my-service._ipps._tcp.local";
domain = "LOCAL";
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
subDomain = "my-service._ipps._tcp.Default.Service.ARPA.";
domain = "dEFAULT.Service.arpa.";
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
subDomain = "my-service._ipps._tcp.default.service.ARpa.";
domain = "SeRvIcE.arPA.";
VerifyOrQuit(Dns::Name::IsSubDomainOf(subDomain, domain));
// Verify it doesn't match a portion of a label.
subDomain = "my-service._ipps._tcp.default.service.arpa.";
domain = "Vice.arpa.";
VerifyOrQuit(!Dns::Name::IsSubDomainOf(subDomain, domain));
printf("----------------------------------------------------------------\n");
printf("Append names, check encoded bytes, parse name and read labels:\n");
@@ -247,18 +275,33 @@ void TestDnsName(void)
{
uint16_t startOffset = offset;
SuccessOrQuit(Dns::Name::CompareLabel(*message, offset, test.mLabels[index]));
strcpy(label, test.mLabels[index]);
SuccessOrQuit(Dns::Name::CompareLabel(*message, offset, label));
VerifyOrQuit(offset != startOffset, "Name::CompareLabel() did not change offset");
VerifyOrQuit(Dns::Name::CompareLabel(*message, startOffset, kBadLabel) == kErrorNotFound,
offset = startOffset;
VerifyOrQuit(Dns::Name::CompareLabel(*message, offset, kBadLabel) == kErrorNotFound,
"Name::CompareLabel() did not fail with incorrect label");
StringConvertToUppercase(label);
offset = startOffset;
SuccessOrQuit(Dns::Name::CompareLabel(*message, offset, label));
}
// Compare the whole name.
strcpy(name, test.mExpectedReadName);
offset = 0;
SuccessOrQuit(Dns::Name::CompareName(*message, offset, test.mExpectedReadName));
SuccessOrQuit(Dns::Name::CompareName(*message, offset, name));
VerifyOrQuit(offset == len, "Name::CompareName() returned incorrect offset");
StringConvertToUppercase(name);
offset = 0;
SuccessOrQuit(Dns::Name::CompareName(*message, offset, name));
offset = 0;
VerifyOrQuit(Dns::Name::CompareName(*message, offset, kBadName) == kErrorNotFound,
"Name::CompareName() did not fail with incorrect name");