From e16261aa6cc8fa30666dc01d4368b782b5439703 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 19 Nov 2021 08:42:47 -0800 Subject: [PATCH] [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. --- Android.mk | 1 + src/core/BUILD.gn | 1 + src/core/CMakeLists.txt | 1 + src/core/Makefile.am | 1 + src/core/common/data.cpp | 60 +++++++++++++++++++++ src/core/common/data.hpp | 48 ++++++++++++++++- src/core/common/message.cpp | 9 ++-- src/core/common/message.hpp | 12 ++++- src/core/net/dns_types.cpp | 46 ++++++++++++---- src/core/net/dns_types.hpp | 28 +++++----- tests/unit/test_dns.cpp | 101 +++++++++++++++++++++++++----------- 11 files changed, 251 insertions(+), 57 deletions(-) create mode 100644 src/core/common/data.cpp diff --git a/Android.mk b/Android.mk index b51f020c9..42560711d 100644 --- a/Android.mk +++ b/Android.mk @@ -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 \ diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index c44097c80..1090324a2 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -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", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3d1c4d791..826965f59 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -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 diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 31e0b01fe..33f2d33fa 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -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 \ diff --git a/src/core/common/data.cpp b/src/core/common/data.cpp new file mode 100644 index 000000000..f7bec4ec0 --- /dev/null +++ b/src/core/common/data.cpp @@ -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 diff --git a/src/core/common/data.hpp b/src/core/common/data.hpp index c8e57daf9..4522ee319 100644 --- a/src/core/common/data.hpp +++ b/src/core/common/data.hpp @@ -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 class MutableData; /** @@ -76,7 +105,7 @@ template class MutableData; * */ template -class Data : public Clearable>, public Unequatable> +class Data : public Clearable>, public Unequatable>, private DataUtils { friend class MutableData; @@ -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(aBuffer), mLength, aMatcher); + } + /** * This method overloads operator `==` to compare the `Data` content with the content from another one. * diff --git a/src/core/common/message.cpp b/src/core/common/message.cpp index 056395307..6121636c3 100644 --- a/src/core/common/message.cpp +++ b/src/core/common/message.cpp @@ -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(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); diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp index 8f20dc4eb..fca6a4764 100644 --- a/src/core/common/message.hpp +++ b/src/core/common/message.hpp @@ -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. diff --git a/src/core/net/dns_types.cpp b/src/core/net/dns_types.cpp index 46e316b2f..aa2ae54c2 100644 --- a/src/core/net/dns_types.cpp +++ b/src/core/net/dns_types.cpp @@ -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(aFirst)) == ToLowercase(static_cast(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; diff --git a/src/core/net/dns_types.hpp b/src/core/net/dns_types.hpp index 907055811..27a15a43f 100644 --- a/src/core/net/dns_types.hpp +++ b/src/core/net/dns_types.hpp @@ -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 "..", 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'; }; /** diff --git a/tests/unit/test_dns.cpp b/tests/unit/test_dns.cpp index 61b4f3927..e35da551e 100644 --- a/tests/unit/test_dns.cpp +++ b/tests/unit/test_dns.cpp @@ -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");