[dns] add helper methods to append/parse DNS names (#5922)

This commit adds `Dns::Name` type which provides a set of helper
methods to encode/decode DNS names. `AppendName()` method encodes and
appends a full name (e.g., "test.example.com") to a message. Other
helper methods enable appending labels (or groups of labels) and/or
constructing a compressed name (using pointer labels). `ParseName()`
method parses and skips over a full name in a message. `ReadLabel()`
method reads labels one by one and works independently of whether the
encoded name is compressed or not. `ReadName()` method read an entire
name from message. The new methods are used in `Dns::Client`.

This commit also add a unit test `test-dns` covering the behavior of
the newly added helper methods.

Finally, this commit adds few helper methods in `ResourceRecords` to
get the size and check the type of a DNS record.
This commit is contained in:
Abtin Keshavarzian
2020-12-08 12:20:19 -08:00
committed by GitHub
parent c70e47d16c
commit 73126f7099
15 changed files with 1196 additions and 128 deletions
+1
View File
@@ -237,6 +237,7 @@ LOCAL_SRC_FILES := \
src/core/net/dhcp6_client.cpp \
src/core/net/dhcp6_server.cpp \
src/core/net/dns_client.cpp \
src/core/net/dns_headers.cpp \
src/core/net/icmp6.cpp \
src/core/net/ip6.cpp \
src/core/net/ip6_address.cpp \
+1
View File
@@ -466,6 +466,7 @@ openthread_core_files = [
"net/dhcp6_server.hpp",
"net/dns_client.cpp",
"net/dns_client.hpp",
"net/dns_headers.cpp",
"net/dns_headers.hpp",
"net/icmp6.cpp",
"net/icmp6.hpp",
+1
View File
@@ -129,6 +129,7 @@ set(COMMON_SOURCES
net/dhcp6_client.cpp
net/dhcp6_server.cpp
net/dns_client.cpp
net/dns_headers.cpp
net/icmp6.cpp
net/ip6.cpp
net/ip6_address.cpp
+1
View File
@@ -206,6 +206,7 @@ SOURCES_COMMON = \
net/dhcp6_client.cpp \
net/dhcp6_server.cpp \
net/dns_client.cpp \
net/dns_headers.cpp \
net/icmp6.cpp \
net/ip6.cpp \
net/ip6_address.cpp \
+16
View File
@@ -49,6 +49,22 @@ uint16_t StringLength(const char *aString, uint16_t aMaxLength)
return ret;
}
const char *StringFind(const char *aString, char aChar)
{
const char *ret = nullptr;
for (; *aString != '\0'; aString++)
{
if (*aString == aChar)
{
ret = aString;
break;
}
}
return ret;
}
otError StringBase::Write(char *aBuffer, uint16_t aSize, uint16_t &aLength, const char *aFormat, va_list aArgs)
{
otError error = OT_ERROR_NONE;
+11
View File
@@ -68,6 +68,17 @@ namespace ot {
*/
uint16_t StringLength(const char *aString, uint16_t aMaxLength);
/**
* This function finds the first occurrence of a given character in a null-terminated string.
*
* @param[in] aString A pointer to the string.
* @param[in] aChar A char to search for in the string.
*
* @returns The pointer to first occurrence of the @p aChar in @p aString, or nullptr if cannot be found.
*
*/
const char *StringFind(const char *aString, char aChar);
/**
* This class defines the base class for `String`.
*
+4 -84
View File
@@ -105,7 +105,7 @@ otError Client::Query(const QueryInfo &aQuery, ResponseHandler aHandler, void *a
VerifyOrExit((message = NewMessage(header)) != nullptr, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = AppendCompressedHostname(*message, aQuery.GetHostname()));
SuccessOrExit(error = Name::AppendName(aQuery.GetHostname(), *message));
SuccessOrExit(error = question.AppendTo(*message));
queryMetadata.mHostname = aQuery.GetHostname();
@@ -199,46 +199,6 @@ exit:
}
}
otError Client::AppendCompressedHostname(Message &aMessage, const char *aHostname)
{
otError error = OT_ERROR_NONE;
uint8_t index = 0;
uint8_t labelPosition = 0;
uint8_t labelSize = 0;
while (true)
{
// Look for string separator.
if (aHostname[index] == kLabelSeparator || aHostname[index] == kLabelTerminator)
{
VerifyOrExit(labelSize > 0, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = aMessage.Append(labelSize));
SuccessOrExit(error = aMessage.AppendBytes(&aHostname[labelPosition], labelSize));
labelPosition += labelSize + 1;
labelSize = 0;
if (aHostname[index] == kLabelTerminator)
{
break;
}
}
else
{
labelSize++;
}
index++;
}
// Add termination character at the end.
labelSize = kLabelTerminator;
SuccessOrExit(error = aMessage.Append(labelSize));
exit:
return error;
}
otError Client::CompareQuestions(Message &aMessageResponse, Message &aMessageQuery, uint16_t &aOffset)
{
otError error = OT_ERROR_NONE;
@@ -268,46 +228,6 @@ exit:
return error;
}
otError Client::SkipHostname(Message &aMessage, uint16_t &aOffset)
{
otError error = OT_ERROR_NONE;
uint8_t buf[kBufSize];
uint16_t index;
uint16_t read = 0;
uint16_t offset = aOffset;
uint16_t length = aMessage.GetLength() - aOffset;
while (length > 0)
{
VerifyOrExit((read = aMessage.ReadBytes(offset, buf, sizeof(buf))) > 0, error = OT_ERROR_PARSE);
index = 0;
while (index < read)
{
if (buf[index] == kLabelTerminator)
{
ExitNow(aOffset = offset + 1);
}
if ((buf[index] & kCompressionOffsetMask) == kCompressionOffsetMask)
{
ExitNow(aOffset = offset + 2);
}
index++;
offset++;
}
length -= read;
}
ExitNow(error = OT_ERROR_PARSE);
exit:
return error;
}
Message *Client::FindRelatedQuery(const Header &aResponseHeader, QueryMetadata &aQueryMetadata)
{
uint16_t messageId;
@@ -438,18 +358,18 @@ void Client::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessag
{
uint32_t newOffset;
SuccessOrExit(error = SkipHostname(aMessage, offset));
SuccessOrExit(error = Name::ParseName(aMessage, offset));
SuccessOrExit(error = aMessage.Read(offset, record));
if (ResourceRecordAaaa::IsAaaa(record))
if (record.Matches(ResourceRecord::kTypeAaaa))
{
// Return the first found IPv6 address.
FinalizeDnsTransaction(*message, queryMetadata, &record.GetAddress(), record.GetTtl(), OT_ERROR_NONE);
ExitNow(error = OT_ERROR_NONE);
}
newOffset = offset + sizeof(ResourceRecord) + record.GetLength();
newOffset = offset + record.GetSize();
VerifyOrExit(newOffset <= aMessage.GetLength(), error = OT_ERROR_PARSE);
offset = static_cast<uint16_t>(newOffset);
}
-15
View File
@@ -154,19 +154,6 @@ private:
kMaxRetransmit = OPENTHREAD_CONFIG_DNS_MAX_RETRANSMIT,
};
/**
* Special DNS symbols.
*/
enum
{
kLabelTerminator = 0,
kLabelSeparator = '.',
kCompressionOffsetMask = 0xc0
};
/**
* Operating on message buffers.
*/
enum
{
kBufSize = 16
@@ -194,9 +181,7 @@ private:
otError SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
void SendCopy(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
otError AppendCompressedHostname(Message &aMessage, const char *aHostname);
otError CompareQuestions(Message &aMessageResponse, Message &aMessageQuery, uint16_t &aOffset);
otError SkipHostname(Message &aMessage, uint16_t &aOffset);
Message *FindRelatedQuery(const Header &aResponseHeader, QueryMetadata &aQueryMetadata);
void FinalizeDnsTransaction(Message & aQuery,
+335
View File
@@ -0,0 +1,335 @@
/*
* Copyright (c) 2020, 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 generating and processing of DNS headers and helper functions/methods.
*/
#include "dns_headers.hpp"
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/string.hpp"
namespace ot {
namespace Dns {
using ot::Encoding::BigEndian::HostSwap16;
otError Name::AppendLabel(const char *aLabel, Message &aMessage)
{
return AppendLabel(aLabel, static_cast<uint8_t>(StringLength(aLabel, kMaxLabelLength + 1)), aMessage);
}
otError Name::AppendLabel(const char *aLabel, uint8_t aLabelLength, Message &aMessage)
{
otError error = OT_ERROR_NONE;
VerifyOrExit((0 < aLabelLength) && (aLabelLength <= kMaxLabelLength), error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = aMessage.Append(aLabelLength));
error = aMessage.AppendBytes(aLabel, aLabelLength);
exit:
return error;
}
otError Name::AppendMultipleLabels(const char *aLabels, Message &aMessage)
{
otError error = OT_ERROR_NONE;
uint16_t index = 0;
uint16_t labelStartIndex = 0;
char ch;
VerifyOrExit(aLabels != nullptr);
do
{
VerifyOrExit(index < kMaxLength, error = OT_ERROR_INVALID_ARGS);
ch = aLabels[index];
if ((ch == kNullChar) || (ch == kLabelSeperatorChar))
{
uint8_t labelLength = static_cast<uint8_t>(index - labelStartIndex);
if (labelLength == 0)
{
// Empty label (e.g., consecutive dots) is invalid, but we
// allow for two cases: (1) where `aLabels` ends with a dot
// (`labelLength` is zero but we are at end of `aLabels` string
// and `ch` is null char. (2) if `aLabels` is just "." (we
// see a dot at index 0, and index 1 is null char).
error = ((ch == kNullChar) || ((index == 0) && (aLabels[1] == kNullChar))) ? OT_ERROR_NONE
: OT_ERROR_INVALID_ARGS;
ExitNow();
}
SuccessOrExit(error = AppendLabel(&aLabels[labelStartIndex], labelLength, aMessage));
labelStartIndex = index + 1;
}
index++;
} while (ch != kNullChar);
exit:
return error;
}
otError Name::AppendTerminator(Message &aMessage)
{
uint8_t terminator = 0;
return aMessage.Append(terminator);
}
otError Name::AppendPointerLabel(uint16_t aOffset, Message &aMessage)
{
// A pointer label takes the form of a two byte sequence as a
// `uint16_t` value. The first two bits are ones. This allows a
// pointer to be distinguished from a text label, since the text
// label must begin with two zero bits (note that labels are
// restricted to 63 octets or less). The next 14-bits specify
// an offset value relative to start of DNS header.
uint16_t value;
OT_ASSERT(aOffset < kPointerLabelTypeUint16);
value = HostSwap16(aOffset | kPointerLabelTypeUint16);
return aMessage.Append(value);
}
otError Name::AppendName(const char *aName, Message &aMessage)
{
otError error;
SuccessOrExit(error = AppendMultipleLabels(aName, aMessage));
error = AppendTerminator(aMessage);
exit:
return error;
}
otError Name::ParseName(const Message &aMessage, uint16_t &aOffset)
{
otError error;
LabelIterator iterator(aMessage, aOffset);
while (true)
{
error = iterator.GetNextLabel();
VerifyOrExit((error == OT_ERROR_NONE) || (error == OT_ERROR_NOT_FOUND));
if (iterator.IsEndOffsetSet())
{
aOffset = iterator.mNameEndOffset;
ExitNow(error = OT_ERROR_NONE);
}
}
exit:
return error;
}
otError Name::ReadLabel(const Message &aMessage,
uint16_t & aOffset,
uint16_t aHeaderOffset,
char * aLabelBuffer,
uint8_t & aLabelLength)
{
otError error;
LabelIterator iterator(aMessage, aOffset, aHeaderOffset);
SuccessOrExit(error = iterator.GetNextLabel());
SuccessOrExit(error = iterator.ReadLabel(aLabelBuffer, aLabelLength, /* aAllowDotCharInLabel */ true));
aOffset = iterator.mNextLabelOffset;
exit:
return error;
}
otError Name::ReadName(const Message &aMessage,
uint16_t & aOffset,
uint16_t aHeaderOffset,
char * aNameBuffer,
uint16_t aNameBufferSize)
{
otError error;
LabelIterator iterator(aMessage, aOffset, aHeaderOffset);
bool firstLabel = true;
uint8_t labelLength;
while (true)
{
error = iterator.GetNextLabel();
switch (error)
{
case OT_ERROR_NONE:
if (!firstLabel)
{
*aNameBuffer++ = kLabelSeperatorChar;
aNameBufferSize--;
// No need to check if we have reached end of the name buffer
// here since `iterator.ReadLabel()` would verify it.
}
labelLength = static_cast<uint8_t>(OT_MIN(kMaxLabelLength + 1, aNameBufferSize));
SuccessOrExit(error = iterator.ReadLabel(aNameBuffer, labelLength, /* aAllowDotCharInLabel */ false));
aNameBuffer += labelLength;
aNameBufferSize -= labelLength;
firstLabel = false;
break;
case OT_ERROR_NOT_FOUND:
// We reach the end of name successfully. Always add a terminating dot
// at the end.
*aNameBuffer++ = kLabelSeperatorChar;
aNameBufferSize--;
VerifyOrExit(aNameBufferSize >= sizeof(uint8_t), error = OT_ERROR_NO_BUFS);
*aNameBuffer = kNullChar;
aOffset = iterator.mNameEndOffset;
error = OT_ERROR_NONE;
// Fall through
default:
ExitNow();
}
}
exit:
return error;
}
otError Name::LabelIterator::GetNextLabel(void)
{
otError error;
while (true)
{
uint8_t labelLength;
uint8_t labelType;
SuccessOrExit(error = mMessage.Read(mNextLabelOffset, labelLength));
labelType = labelLength & kLabelTypeMask;
if (labelType == kTextLabelType)
{
if (labelLength == 0)
{
// Zero label length indicates end of a name.
if (!IsEndOffsetSet())
{
mNameEndOffset = mNextLabelOffset + sizeof(uint8_t);
}
ExitNow(error = OT_ERROR_NOT_FOUND);
}
mLabelStartOffset = mNextLabelOffset + sizeof(uint8_t);
mLabelLength = labelLength;
mNextLabelOffset = mLabelStartOffset + labelLength;
ExitNow();
}
else if (labelType == kPointerLabelType)
{
// A pointer label takes the form of a two byte sequence as a
// `uint16_t` value. The first two bits are ones. The next 14 bits
// specify an offset value from the start of the DNS header.
uint16_t pointerValue;
SuccessOrExit(error = mMessage.Read(mNextLabelOffset, pointerValue));
if (!IsEndOffsetSet())
{
mNameEndOffset = mNextLabelOffset + sizeof(uint16_t);
}
mNextLabelOffset = mHeaderOffset + (HostSwap16(pointerValue) & kPointerLabelOffsetMask);
// Go back through the `while(true)` loop to get the next label.
}
else
{
ExitNow(error = OT_ERROR_PARSE);
}
}
exit:
return error;
}
otError Name::LabelIterator::ReadLabel(char *aLabelBuffer, uint8_t &aLabelLength, bool aAllowDotCharInLabel) const
{
otError error;
VerifyOrExit(mLabelLength < aLabelLength, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = mMessage.Read(mLabelStartOffset, aLabelBuffer, mLabelLength));
aLabelBuffer[mLabelLength] = kNullChar;
aLabelLength = mLabelLength;
if (!aAllowDotCharInLabel)
{
VerifyOrExit(StringFind(aLabelBuffer, kLabelSeperatorChar) == nullptr, error = OT_ERROR_PARSE);
}
exit:
return error;
}
void ResourceRecord::Init(uint16_t aType, uint16_t aClass, uint32_t aTtl)
{
SetType(aType);
SetClass(aClass);
SetTtl(aTtl);
SetLength(0);
}
void ResourceRecordAaaa::Init(void)
{
ResourceRecord::Init(kTypeAaaa);
SetLength(sizeof(mAddress));
mAddress.Clear();
}
} // namespace Dns
} // namespace ot
+294 -29
View File
@@ -374,6 +374,242 @@ private:
} OT_TOOL_PACKED_END;
/**
* This class implement helper methods for encoding/decoding of DNS Names.
*
*/
class Name
{
public:
enum : uint8_t
{
kMaxLabelLength = 63, ///< Max number of characters in a label.
kMaxLength = 255, ///< Max number of characters in a name.
};
/**
* This static method encodes and appends a single name label to a message.
*
* The @p aLabel is assumed to contain a single name label as a C string (null-terminated). Unlike
* `AppendMultipleLabels()` which parses the label string and treats it as sequence of multiple (dot-separated)
* labels, this method always appends @p aLabel as a single whole label. This allows the label string to even
* contain dot '.' character, which, for example, is useful for "Service Instance Names" where <Instance> portion
* is a user-friendly name and can contain dot characters.
*
* @param[in] aLabel The label string to append. MUST NOT be nullptr.
* @param[in] aMessage The message to append to.
*
* @retval OT_ERROR_NONE Successfully encoded and appended the name label to @p aMessage.
* @retval OT_ERROR_INVALID_ARGS @p aLabel is not valid (e.g., label length is not within valid range).
* @retval OT_ERROR_NO_BUFS Insufficient available buffers to grow the message.
*
*/
static otError AppendLabel(const char *aLabel, Message &aMessage);
/**
* This static method encodes and appends a sequence of name labels to a given message.
*
* The @p aLabels must follow "<label1>.<label2>.<label3>", i.e., a sequence of labels separated by dot '.' char.
* E.g., "_http._tcp", "_http._tcp." (same as previous one), "host-1.test".
*
* This method validates that the @p aLabels is a valid name format, i.e., no empty label, and labels are
* `kMaxLabelLength` (63) characters or less.
*
* @note This method NEVER adds a label terminator (empty label) to the message, even in the case where @p aLabels
* ends with a dot character, e.g., "host-1.test." is treated same as "host-1.test".
*
* @param[in] aLabels A name label string. Can be nullptr (then treated as "").
* @param[in] aMessage The message to which to append the encoded name.
*
* @retval OT_ERROR_NONE Successfully encoded and appended the name label(s) to @p aMessage.
* @retval OT_ERROR_INVALID_ARGS Name label @p aLabels is not valid.
* @retval OT_ERROR_NO_BUFS Insufficient available buffers to grow the message.
*
*/
static otError AppendMultipleLabels(const char *aLabels, Message &aMessage);
/**
* This static method appends a name label terminator to a message.
*
* An encoded name is terminated by an empty label (a zero byte).
*
* @param[in] aMessage The message to append to.
*
* @retval OT_ERROR_NONE Successfully encoded and appended the terminator label to @p aMessage.
* @retval OT_ERROR_NO_BUFS Insufficient available buffers to grow the message.
*
*/
static otError AppendTerminator(Message &aMessage);
/**
* This static method appends a pointer type name label to a message.
*
* Pointer label is used for name compression. It allows an entire name or a list of labels at the end of an
* encoded name to be replaced with a pointer to a prior occurrence of the same name within the message.
*
* @param[in] aOffset The offset from the start of DNS header to use for pointer value.
* @param[in] aMessage The message to append to.
*
* @retval OT_ERROR_NONE Successfully encoded and appended the pointer label to @p aMessage.
* @retval OT_ERROR_NO_BUFS Insufficient available buffers to grow the message.
*
*/
static otError AppendPointerLabel(uint16_t aOffset, Message &aMessage);
/**
* This static method encodes and appends a full name to a message.
*
* 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).
*
* This method validates that the @p aName is a valid name format, i.e. no empty labels, and labels are
* `kMaxLabelLength` (63) characters or less, and the name is `kMaxLength` (255) characters or less.
*
* @param[in] aName A name string. Can be nullptr (then treated as "." or root).
* @param[in] aMessage The message to append to.
*
* @retval OT_ERROR_NONE Successfully encoded and appended the name to @p aMessage.
* @retval OT_ERROR_INVALID_ARGS Name @p aName is not valid.
* @retval OT_ERROR_NO_BUFS Insufficient available buffers to grow the message.
*
*/
static otError AppendName(const char *aName, Message &aMessage);
/**
* This static method parses and skips over a full name in a message.
*
* @param[in] aMessage The message to parse the name from.
* @param[inout] aOffset On input the offset in @p aMessage pointing to the start of the name field.
* On exit (when parsed successfully), @p aOffset is updated to point to the byte
* after the end of name field.
*
* @retval OT_ERROR_NONE Successfully parsed and skipped over name, @p Offset is updated.
* @retval OT_ERROR_PARSE Name could not be parsed (invalid format).
*
*/
static otError ParseName(const Message &aMessage, uint16_t &aOffset);
/**
* This static method reads a name label from a message.
*
* This method can be used to read labels one by one in a name. After a successful label read, @p aOffset is
* updated to point to the start of the next label. When we reach the end of the name, OT_ERROR_NOT_FOUND is
* returned. This method handles compressed names which use pointer labels. So as the labels in a name are read,
* the @p aOffset may jump back in the message and at the end the @p aOffset does not necessarily point to the end
* of the original name field.
*
* Unlike `ReadName()` which requires and verifies that the read label to contain no dot '.' character, this method
* allows the read label to include any character.
*
* @param[in] aMessage The message to read the label from.
* @param[inout] aOffset On input, the offset in @p aMessage pointing to the start of the label to read.
* On exit, when successfully read, @p aOffset is updated to point to the start of
* the next label.
* @param[in] aHeaderOffset The offset in @p aMessage to the start of the DNS header.
* @param[out] aLabelBuffer A pointer to a char array to output the read label as a null-terminated C string.
* @param[inout] aLabelLength On input, the maximum number chars in @p aLabelBuffer array.
* On output, when label is successfully read, @aLabelLength is updated to return
* the label's length (number of chars in the label string, excluding the null char).
*
* @retval OT_ERROR_NONE Successfully read the label and updated @p aLabelBuffer, @p aLabelLength, and
* @p aOffset.
* @retval OT_ERROR_NOT_FOUND Reached the end of name and no more label to read.
* @retval OT_ERROR_PARSE Name could not be parsed (invalid format).
* @retval OT_ERROR_NO_BUFS Label could not fit in @p aLabelLength chars.
*
*/
static otError ReadLabel(const Message &aMessage,
uint16_t & aOffset,
uint16_t aHeaderOffset,
char * aLabelBuffer,
uint8_t & aLabelLength);
/**
* This static method reads a full name from a message.
*
* On successful read, the read name follows "<label1>.<label2>.<label3>.", i.e., a sequence of labels separated by
* dot '.' character. The read name will ALWAYS end with a dot.
*
* This method verifies that the read labels in message do not contain any dot character, otherwise it returns
* `OT_ERROR_PARSE`).
*
* @param[in] aMessage The message to read the name from.
* @param[inout] aOffset On input, the offset in @p aMessage pointing to the start of the name field.
* On exit (when parsed successfully), @p aOffset is updated to point to the byte
* after the end of name field.
* @param[in] aHeaderOffset The offset in @p aMessage to the start of the DNS header.
* @param[out] aNameBuffer A pointer to a char array to output the read name as a null-terminated C string.
* @param[inout] aNameBufferSize The maximum number chars in @p aNameBuffer array.
*
* @retval OT_ERROR_NONE Successfully read the name, @p aNameBuffer and @p Offset are updated.
* @retval OT_ERROR_PARSE Name could not be parsed (invalid format).
* @retval OT_ERROR_NO_BUFS Name could not fit in @p aNameBufferSize chars.
*
*/
static otError ReadName(const Message &aMessage,
uint16_t & aOffset,
uint16_t aHeaderOffset,
char * aNameBuffer,
uint16_t aNameBufferSize);
private:
enum : char
{
kNullChar = '\0',
kLabelSeperatorChar = '.',
};
enum : uint8_t
{
// The first 2 bits of the encoded label specifies label type.
//
// - Value 00 indicates normal text label (lower 6-bits indicates the label length).
// - Value 11 indicates pointer label type (lower 14-bits indicates the pointer offset).
// - Values 01,10 are reserved (RFC 6891 recommends to not use)
kLabelTypeMask = 0xc0, // 0b1100_0000 (first two bits)
kTextLabelType = 0x00, // Text label type (00)
kPointerLabelType = 0xc0, // Pointer label type - compressed name (11)
};
enum : uint16_t
{
kPointerLabelTypeUint16 = 0xc000, // Pointer label type as `uint16_t` mask (first 2 bits).
kPointerLabelOffsetMask = 0x3fff, // Mask to get the offset field in a pointer label (lower 14 bits).
};
struct LabelIterator
{
enum : uint16_t
{
kUnsetNameEndOffset = 0, // Special value indicating `mNameEndOffset` is not yet set.
};
LabelIterator(const Message &aMessage, uint16_t aLabelOffset, uint16_t aHeaderOffset = 0)
: mMessage(aMessage)
, mHeaderOffset(aHeaderOffset)
, mNextLabelOffset(aLabelOffset)
, mNameEndOffset(kUnsetNameEndOffset)
{
}
bool IsEndOffsetSet(void) const { return (mNameEndOffset != kUnsetNameEndOffset); }
otError GetNextLabel(void);
otError ReadLabel(char *aLabelBuffer, uint8_t &aLabelLength, bool aAllowDotCharInLabel) const;
const Message &mMessage; // Message to read labels from.
const uint16_t mHeaderOffset; // Offset in `mMessage` to the start of DNS header.
uint16_t mLabelStartOffset; // Offset in `mMessage` to the first char of current label text.
uint8_t mLabelLength; // Length of current label (number of chars).
uint16_t mNextLabelOffset; // Offset in `mMessage` to the start of the next label.
uint16_t mNameEndOffset; // Offset in `mMessage` to the byte after the end of domain name field.
};
Name(void) = default;
static otError AppendLabel(const char *aLabel, uint8_t aLabelLength, Message &aMessage);
};
/**
* This class implements Resource Record body format (RR).
*
@@ -382,6 +618,54 @@ OT_TOOL_PACKED_BEGIN
class ResourceRecord
{
public:
/**
* Resource Record Types.
*
*/
enum : uint16_t
{
kTypeA = 1, ///< Address record (IPv4).
kTypePtr = 12, ///< PTR record.
kTypeTxt = 16, ///< TXT record.
kTypeSrv = 33, ///< SRV locator record.
kTypeAaaa = 28, ///< IPv6 address record.
kTypeKey = 25, ///< Key record.
kTypeOpt = 41, ///< Option record.
};
/**
* Resource Record Class Codes.
*
*/
enum : uint16_t
{
kClassInternet = 1, ///< Class code Internet (IN).
};
/**
* This method initializes the resource record.
*
* @param[in] aType The type of the resource record.
* @param[in] aClass The class of the resource record (default is `kClassInternet`).
* @param[in] aTtl The time to live field of the resource record (default is zero).
*
*/
void Init(uint16_t aType, uint16_t aClass = kClassInternet, uint32_t aTtl = 0);
/**
* This method indicates whether the resources records matches a given type and class code.
*
* @param[in] aType The resource record type to compare with.
* @param[in] aClass The resource record class code to compare with (default is `kClassInternet`).
*
* @returns TRUE if the resources records matches @p aType and @p aClass, FALSE otherwise.
*
*/
bool Matches(uint16_t aType, uint16_t aClass = kClassInternet)
{
return (mType == HostSwap16(aType)) && (mClass == HostSwap16(aClass));
}
/**
* This method returns the type of the resource record.
*
@@ -444,6 +728,15 @@ public:
*/
void SetLength(uint16_t aLength) { mLength = HostSwap16(aLength); }
/**
* This method returns the size of (number of bytes) in resource record and its data RDATA section (excluding the
* name field).
*
* @returns Size (number of bytes) of resource record and its data section (excluding the name field)
*
*/
uint32_t GetSize(void) const { return sizeof(ResourceRecord) + GetLength(); }
private:
uint16_t mType; // The type of the data in RDATA section.
uint16_t mClass; // The class of the data in RDATA section.
@@ -460,31 +753,11 @@ OT_TOOL_PACKED_BEGIN
class ResourceRecordAaaa : public ResourceRecord
{
public:
/**
* This static method indicated whether or not a given Resource Record is of AAAA type.
*
* @param[in] aRecord A Resource Record.
*
* @returns TRUE if @p aRecord is of AAAA type, FALSE otherwise.
*
*/
static bool IsAaaa(const ResourceRecord &aRecord)
{
return (aRecord.GetType() == kType) && (aRecord.GetClass() == kClass);
}
/**
* This method initializes the AAAA Resource Record.
*
*/
void Init(void)
{
ResourceRecord::SetType(kType);
ResourceRecord::SetClass(kClass);
ResourceRecord::SetTtl(0);
ResourceRecord::SetLength(kLength);
mAddress.Clear();
}
void Init(void);
/**
* This method sets the IPv6 address of the resource record.
@@ -503,15 +776,7 @@ public:
Ip6::Address &GetAddress(void) { return mAddress; }
private:
enum
{
kType = 0x1C, // AAAA Resource Record type.
kClass = 0x01, // The value of the Internet class.
kLength = 16, // Size of the AAAA Resource Record type.
};
Ip6::Address mAddress; // IPv6 Address of AAAA Resource Record.
} OT_TOOL_PACKED_END;
/**
@@ -454,6 +454,14 @@
*/
#define OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE 1
/**
* @def OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
*
* Define to 1 to enable DNS Client support.
*
*/
#define OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE 1
#if OPENTHREAD_RADIO
/**
* @def OPENTHREAD_CONFIG_MAC_SOFTWARE_ACK_TIMEOUT_ENABLE
+22
View File
@@ -173,6 +173,27 @@ target_link_libraries(test-cmd-line-parser
add_test(NAME test-cmd-line-parser COMMAND test-cmd-line-parser)
add_executable(test-dns
test_dns.cpp
)
target_include_directories(test-dns
PRIVATE
${COMMON_INCLUDES}
)
target_compile_options(test-dns
PRIVATE
${COMMON_COMPILE_OPTIONS}
)
target_link_libraries(test-dns
PRIVATE
${COMMON_LIBS}
)
add_test(NAME test-dns COMMAND test-dns)
add_executable(test-flash
test_flash.cpp
)
@@ -644,6 +665,7 @@ set_target_properties(
test-child
test-child-table
test-cmd-line-parser
test-dns
test-flash
test-heap
test-hmac-sha256
+4
View File
@@ -111,6 +111,7 @@ check_PROGRAMS += \
test-child \
test-child-table \
test-cmd-line-parser \
test-dns \
test-flash \
test-heap \
test-hmac-sha256 \
@@ -188,6 +189,9 @@ test_child_table_SOURCES = $(COMMON_SOURCES) test_child_table.cpp
test_cmd_line_parser_LDADD = $(COMMON_LDADD)
test_cmd_line_parser_SOURCES = $(COMMON_SOURCES) test_cmd_line_parser.cpp
test_dns_LDADD = $(COMMON_LDADD)
test_dns_SOURCES = $(COMMON_SOURCES) test_dns.cpp
test_flash_LDADD = $(COMMON_LDADD)
test_flash_SOURCES = $(COMMON_SOURCES) test_flash.cpp
+476
View File
@@ -0,0 +1,476 @@
/*
* Copyright (c) 2020, 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.
*/
#include <string.h>
#include <openthread/config.h>
#include "test_platform.h"
#include "test_util.hpp"
#include "common/instance.hpp"
#include "net/dns_headers.hpp"
namespace ot {
void TestDnsName(void)
{
enum
{
kMaxSize = 300,
kLabelSize = 64,
kNameSize = 256,
};
struct TestName
{
const char * mName;
uint16_t mEncodedLength;
const uint8_t *mEncodedData;
const char ** mLabels;
const char * mExpectedReadName;
};
Instance * instance;
MessagePool *messagePool;
Message * message;
uint8_t buffer[kMaxSize];
uint16_t len;
uint16_t offset;
char label[kLabelSize];
uint8_t labelLength;
char name[kNameSize];
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};
static const uint8_t kEncodedName3[] = {10, 'f', 'o', 'u', 'n', 'd', 'a', 't', 'i', 'o', 'n', 0};
static const uint8_t kEncodedName4[] = {0};
static const char *kLabels1[] = {"example", "com", nullptr};
static const char *kLabels2[] = {"foo", "a", "bb", "edu", nullptr};
static const char *kLabels3[] = {"foundation", nullptr};
static const char *kLabels4[] = {nullptr};
static const TestName kTestNames[] = {
{"example.com", sizeof(kEncodedName1), kEncodedName1, kLabels1, "example.com."},
{"example.com.", sizeof(kEncodedName1), kEncodedName1, kLabels1, "example.com."},
{"foo.a.bb.edu", sizeof(kEncodedName2), kEncodedName2, kLabels2, "foo.a.bb.edu."},
{"foo.a.bb.edu.", sizeof(kEncodedName2), kEncodedName2, kLabels2, "foo.a.bb.edu."},
{"foundation", sizeof(kEncodedName3), kEncodedName3, kLabels3, "foundation."},
{"foundation.", sizeof(kEncodedName3), kEncodedName3, kLabels3, "foundation."},
{"", sizeof(kEncodedName4), kEncodedName4, kLabels4, "."},
{".", sizeof(kEncodedName4), kEncodedName4, kLabels4, "."},
{nullptr, sizeof(kEncodedName4), kEncodedName4, kLabels4, "."},
};
static const char *kInvalidNames[] = {
"foo..bar",
"..",
"a..",
"..b",
// Long label
"a.an-invalid-very-long-label-string-with-more-than-sixty-four-characters.com",
// Long name (more than 255 characters)
"HereIsSomeoneHidden.MyHoldFromMeTaken.FromSelfHasMeDriven.MyLeadFromMeTaken."
"HereIsSomeoneHidden.AsLifeSweeterThanLife.TakesMeToGardenOfSoul.MyFortFromMeTaken."
"HereIsSomeoneHidden.LikeSugarInSugarCane.ASweetSugarTrader.MyShopFromMeTaken."
"SorcererAndMagician.NoEyesCanEverSee.AnArtfulConjurer.MySenseFromMeTaken."
"MyEyesWillNeverSee.BeautiesOfTheWholeWorld.BeholdWhoseVisionFine.MySightFromMeTaken"
"PoemByRumiMolana",
};
printf("================================================================\n");
printf("TestDnsName()\n");
instance = static_cast<Instance *>(testInitInstance());
VerifyOrQuit(instance != nullptr, "Null OpenThread instance");
messagePool = &instance->Get<MessagePool>();
VerifyOrQuit((message = messagePool->New(Message::kTypeIp6, 0)) != nullptr, "Message::New failed");
printf("----------------------------------------------------------------\n");
printf("Append names, check encoded bytes, parse name and read labels:\n");
for (const TestName &test : kTestNames)
{
IgnoreError(message->SetLength(0));
SuccessOrQuit(Dns::Name::AppendName(test.mName, *message), "Name::AppendName() failed");
len = message->GetLength();
SuccessOrQuit(message->Read(0, buffer, len), "Message::Read() failed");
DumpBuffer(test.mName, buffer, len);
VerifyOrQuit(len == test.mEncodedLength, "Encoded length does not match expected value");
VerifyOrQuit(memcmp(buffer, test.mEncodedData, len) == 0, "Encoded name data does not match expected data");
// Parse and skip over the name
offset = 0;
SuccessOrQuit(Dns::Name::ParseName(*message, offset), "Name::ParseName() failed");
VerifyOrQuit(offset == len, "Name::ParseName() returned incorrect offset");
// Read labels one by one.
offset = 0;
for (uint8_t index = 0; test.mLabels[index] != nullptr; index++)
{
labelLength = sizeof(label);
SuccessOrQuit(Dns::Name::ReadLabel(*message, offset, 0, label, labelLength), "Name::ReadLabel() failed");
printf("Label[%d] = \"%s\"\n", index, label);
VerifyOrQuit(strcmp(label, test.mLabels[index]) == 0, "Name::ReadLabel() did not get expected label");
VerifyOrQuit(labelLength == strlen(label), "Name::ReadLabel() returned incorrect label length");
}
labelLength = sizeof(label);
VerifyOrQuit(Dns::Name::ReadLabel(*message, offset, 0, label, labelLength) == OT_ERROR_NOT_FOUND,
"Name::ReadLabel() failed at end of the name");
// Read entire name
offset = 0;
SuccessOrQuit(Dns::Name::ReadName(*message, offset, 0, name, sizeof(name)), "Name::ReadName() failed");
printf("Read name =\"%s\"\n", name);
VerifyOrQuit(strcmp(name, test.mExpectedReadName) == 0, "Name::ReadName() did not get expected name");
VerifyOrQuit(offset == len, "Name::ReadName() returned incorrect offset");
// Read entire name with different name buffer sizes (just right and one byte off the expected size)
offset = 0;
SuccessOrQuit(
Dns::Name::ReadName(*message, offset, 0, name, static_cast<uint16_t>(strlen(test.mExpectedReadName) + 1)),
"Name::ReadName() failed with exact name buffer size");
offset = 0;
VerifyOrQuit(Dns::Name::ReadName(*message, offset, 0, name,
static_cast<uint16_t>(strlen(test.mExpectedReadName))) == OT_ERROR_NO_BUFS,
"Name::ReadName() did not fail with too small name buffer size");
}
printf("----------------------------------------------------------------\n");
printf("Invalid names:\n");
for (const char *&invalidName : kInvalidNames)
{
IgnoreError(message->SetLength(0));
printf("\"%s\"\n", invalidName);
VerifyOrQuit(Dns::Name::AppendName(invalidName, *message) == OT_ERROR_INVALID_ARGS,
"Name::AppendName() did not fail with an invalid name");
}
printf("----------------------------------------------------------------\n");
printf("Append as multiple labels and terminator instead of full name:\n");
for (const TestName &test : kTestNames)
{
IgnoreError(message->SetLength(0));
SuccessOrQuit(Dns::Name::AppendMultipleLabels(test.mName, *message), "Name::Append() failed");
SuccessOrQuit(Dns::Name::AppendTerminator(*message), "Name::AppendTerminator() failed");
len = message->GetLength();
SuccessOrQuit(message->Read(0, buffer, len), "Message::Read() failed");
DumpBuffer(test.mName, buffer, len);
VerifyOrQuit(len == test.mEncodedLength, "Encoded length does not match expected value");
VerifyOrQuit(memcmp(buffer, test.mEncodedData, len) == 0, "Encoded name data does not match expected data");
}
printf("----------------------------------------------------------------\n");
printf("Append labels one by one:\n");
for (const TestName &test : kTestNames)
{
IgnoreError(message->SetLength(0));
for (uint8_t index = 0; test.mLabels[index] != nullptr; index++)
{
SuccessOrQuit(Dns::Name::AppendLabel(test.mLabels[index], *message), "Name::AppendLabel() failed");
}
SuccessOrQuit(Dns::Name::AppendTerminator(*message), "Name::AppendTerminator() failed");
len = message->GetLength();
SuccessOrQuit(message->Read(0, buffer, len), "Message::Read() failed");
DumpBuffer(test.mName, buffer, len);
VerifyOrQuit(len == test.mEncodedLength, "Encoded length does not match expected value");
VerifyOrQuit(memcmp(buffer, test.mEncodedData, len) == 0, "Encoded name data does not match expected data");
}
message->Free();
testFreeInstance(instance);
}
void TestDnsCompressedName(void)
{
enum
{
kHeaderOffset = 10,
kGuardBlockSize = 20,
kMaxBufferSize = 100,
kLabelSize = 64,
kNameSize = 256,
kName2EncodedSize = 4 + 2, // encoded "FOO" + pointer label (2 bytes)
kName3EncodedSize = 2, // pointer label (2 bytes)
kName4EncodedSize = 15 + 2, // encoded "Human.Readable" + pointer label (2 bytes).
};
const char kName[] = "F.ISI.ARPA";
const char kLabel1[] = "FOO";
const char kInstanceLabel[] = "Human.Readable";
static const uint8_t kEncodedName[] = {1, 'F', 3, 'I', 'S', 'I', 4, 'A', 'R', 'P', 'A', 0};
static const uint8_t kIsiRelativeIndex = 2; // Index in kEncodedName to the start of "ISI.ARPA" portion.
static const char *kName1Labels[] = {"F", "ISI", "ARPA"};
static const char *kName2Labels[] = {"FOO", "F", "ISI", "ARPA"};
static const char *kName3Labels[] = {"ISI", "ARPA"};
static const char *kName4Labels[] = {"Human.Readable", "F", "ISI", "ARPA"};
static const char kExpectedReadName1[] = "F.ISI.ARPA.";
static const char kExpectedReadName2[] = "FOO.F.ISI.ARPA.";
static const char kExpectedReadName3[] = "ISI.ARPA.";
Instance * instance;
MessagePool *messagePool;
Message * message;
uint16_t offset;
uint16_t name1Offset;
uint16_t name2Offset;
uint16_t name3Offset;
uint16_t name4Offset;
uint8_t buffer[kMaxBufferSize];
char label[kLabelSize];
uint8_t labelLength;
char name[kNameSize];
printf("================================================================\n");
printf("TestDnsCompressedName()\n");
instance = static_cast<Instance *>(testInitInstance());
VerifyOrQuit(instance != nullptr, "Null OpenThread instance");
messagePool = &instance->Get<MessagePool>();
VerifyOrQuit((message = messagePool->New(Message::kTypeIp6, 0)) != nullptr, "Message::New failed");
// Append name1 "F.ISI.ARPA"
for (uint8_t index = 0; index < kHeaderOffset + kGuardBlockSize; index++)
{
SuccessOrQuit(message->Append(index), "Message::Append() failed");
}
name1Offset = message->GetLength();
SuccessOrQuit(Dns::Name::AppendName(kName, *message), "Name::AppendName() failed");
// Append name2 "FOO.F.ISI.ARPA" as a compressed name after some guard/extra bytes
for (uint8_t index = 0; index < kGuardBlockSize; index++)
{
uint8_t value = 0xff;
SuccessOrQuit(message->Append(value), "Message::Append() failed");
}
name2Offset = message->GetLength();
SuccessOrQuit(Dns::Name::AppendLabel(kLabel1, *message), "Name::AppendLabel() failed");
SuccessOrQuit(Dns::Name::AppendPointerLabel(name1Offset - kHeaderOffset, *message),
"Name::AppendPointerLabel() failed");
// Append name3 "ISI.ARPA" as a compressed name after some guard/extra bytes
for (uint8_t index = 0; index < kGuardBlockSize; index++)
{
uint8_t value = 0xaa;
SuccessOrQuit(message->Append(value), "Message::Append() failed");
}
name3Offset = message->GetLength();
SuccessOrQuit(Dns::Name::AppendPointerLabel(name1Offset + kIsiRelativeIndex - kHeaderOffset, *message),
"Name::AppendPointerLabel() failed");
name4Offset = message->GetLength();
SuccessOrQuit(Dns::Name::AppendLabel(kInstanceLabel, *message), "Name::AppendLabel() failed");
SuccessOrQuit(Dns::Name::AppendPointerLabel(name1Offset - kHeaderOffset, *message),
"Name::AppendPointerLabel() failed");
printf("----------------------------------------------------------------\n");
printf("Read and parse the uncompressed name-1 \"F.ISI.ARPA\"\n");
SuccessOrQuit(message->Read(name1Offset, buffer, sizeof(kEncodedName)), "Message::Read() failed");
DumpBuffer(kName, buffer, sizeof(kEncodedName));
VerifyOrQuit(memcmp(buffer, kEncodedName, sizeof(kEncodedName)) == 0,
"Encoded name data does not match expected data");
offset = name1Offset;
SuccessOrQuit(Dns::Name::ParseName(*message, offset), "Name::ParseName() failed");
VerifyOrQuit(offset == name1Offset + sizeof(kEncodedName), "Name::ParseName() returned incorrect offset");
offset = name1Offset;
for (const char *nameLabel : kName1Labels)
{
labelLength = sizeof(label);
SuccessOrQuit(Dns::Name::ReadLabel(*message, offset, kHeaderOffset, label, labelLength),
"Name::ReadLabel() failed");
printf("label: \"%s\"\n", label);
VerifyOrQuit(strcmp(label, nameLabel) == 0, "Name::ReadLabel() did not get expected label");
VerifyOrQuit(labelLength == strlen(label), "Name::ReadLabel() returned incorrect label length");
}
labelLength = sizeof(label);
VerifyOrQuit(Dns::Name::ReadLabel(*message, offset, kHeaderOffset, label, labelLength) == OT_ERROR_NOT_FOUND,
"Name::ReadLabel() failed at end of the name");
offset = name1Offset;
SuccessOrQuit(Dns::Name::ReadName(*message, offset, kHeaderOffset, name, sizeof(name)), "Name::ReadName() failed");
printf("Read name =\"%s\"\n", name);
VerifyOrQuit(strcmp(name, kExpectedReadName1) == 0, "Name::ReadName() did not return expected name");
VerifyOrQuit(offset == name1Offset + sizeof(kEncodedName), "Name::ReadName() returned incorrect offset");
printf("----------------------------------------------------------------\n");
printf("Read and parse compressed name-2 \"FOO.F.ISI.ARPA\"\n");
SuccessOrQuit(message->Read(name2Offset, buffer, kName2EncodedSize), "Message::Read() failed");
DumpBuffer("name2(compressed)", buffer, kName2EncodedSize);
offset = name2Offset;
SuccessOrQuit(Dns::Name::ParseName(*message, offset), "Name::ParseName() failed");
VerifyOrQuit(offset == name2Offset + kName2EncodedSize, "Name::ParseName() returned incorrect offset");
offset = name2Offset;
for (const char *nameLabel : kName2Labels)
{
labelLength = sizeof(label);
SuccessOrQuit(Dns::Name::ReadLabel(*message, offset, kHeaderOffset, label, labelLength),
"Name::ReadLabel() failed");
printf("label: \"%s\"\n", label);
VerifyOrQuit(strcmp(label, nameLabel) == 0, "Name::ReadLabel() did not get expected label");
VerifyOrQuit(labelLength == strlen(label), "Name::ReadLabel() returned incorrect label length");
}
labelLength = sizeof(label);
VerifyOrQuit(Dns::Name::ReadLabel(*message, offset, kHeaderOffset, label, labelLength) == OT_ERROR_NOT_FOUND,
"Name::ReadLabel() failed at end of the name");
offset = name2Offset;
SuccessOrQuit(Dns::Name::ReadName(*message, offset, kHeaderOffset, name, sizeof(name)), "Name::ReadName() failed");
printf("Read name =\"%s\"\n", name);
VerifyOrQuit(strcmp(name, kExpectedReadName2) == 0, "Name::ReadName() did not return expected name");
VerifyOrQuit(offset == name2Offset + kName2EncodedSize, "Name::ReadName() returned incorrect offset");
printf("----------------------------------------------------------------\n");
printf("Read and parse compressed name-3 \"ISI.ARPA\"\n");
SuccessOrQuit(message->Read(name3Offset, buffer, kName3EncodedSize), "Message::Read() failed");
DumpBuffer("name2(compressed)", buffer, kName3EncodedSize);
offset = name3Offset;
SuccessOrQuit(Dns::Name::ParseName(*message, offset), "Name::ParseName() failed");
VerifyOrQuit(offset == name3Offset + kName3EncodedSize, "Name::ParseName() returned incorrect offset");
offset = name3Offset;
for (const char *nameLabel : kName3Labels)
{
labelLength = sizeof(label);
SuccessOrQuit(Dns::Name::ReadLabel(*message, offset, kHeaderOffset, label, labelLength),
"Name::ReadLabel() failed");
printf("label: \"%s\"\n", label);
VerifyOrQuit(strcmp(label, nameLabel) == 0, "Name::ReadLabel() did not get expected label");
VerifyOrQuit(labelLength == strlen(label), "Name::ReadLabel() returned incorrect label length");
}
labelLength = sizeof(label);
VerifyOrQuit(Dns::Name::ReadLabel(*message, offset, kHeaderOffset, label, labelLength) == OT_ERROR_NOT_FOUND,
"Name::ReadLabel() failed at end of the name");
offset = name3Offset;
SuccessOrQuit(Dns::Name::ReadName(*message, offset, kHeaderOffset, name, sizeof(name)), "Name::ReadName() failed");
printf("Read name =\"%s\"\n", name);
VerifyOrQuit(strcmp(name, kExpectedReadName3) == 0, "Name::ReadName() did not return expected name");
VerifyOrQuit(offset == name3Offset + kName3EncodedSize, "Name::ReadName() returned incorrect offset");
printf("----------------------------------------------------------------\n");
printf("Read and parse the uncompressed name-4 \"Human\\.Readable.F.ISI.ARPA\"\n");
SuccessOrQuit(message->Read(name4Offset, buffer, kName4EncodedSize), "Message::Read() failed");
DumpBuffer("name4(compressed)", buffer, kName4EncodedSize);
offset = name4Offset;
SuccessOrQuit(Dns::Name::ParseName(*message, offset), "Name::ParseName() failed");
VerifyOrQuit(offset == name4Offset + kName4EncodedSize, "Name::ParseName() returned incorrect offset");
offset = name4Offset;
for (const char *nameLabel : kName4Labels)
{
labelLength = sizeof(label);
SuccessOrQuit(Dns::Name::ReadLabel(*message, offset, kHeaderOffset, label, labelLength),
"Name::ReadLabel() failed");
printf("label: \"%s\"\n", label);
VerifyOrQuit(strcmp(label, nameLabel) == 0, "Name::ReadLabel() did not get expected label");
VerifyOrQuit(labelLength == strlen(label), "Name::ReadLabel() returned incorrect label length");
}
// `ReadName()` for name-4 should fails due to first label containing dot char.
offset = name4Offset;
VerifyOrQuit(Dns::Name::ReadName(*message, offset, kHeaderOffset, name, sizeof(name)) == OT_ERROR_PARSE,
"Name::ReadName() did not fail with invalid label");
message->Free();
testFreeInstance(instance);
}
} // namespace ot
int main(void)
{
ot::TestDnsName();
ot::TestDnsCompressedName();
printf("All tests passed\n");
return 0;
}
+22
View File
@@ -154,6 +154,27 @@ void TestUtf8(void)
printf(" -- PASS\n");
}
void TestStringFind(void)
{
char emptyString[1] = {'\0'};
char testString[] = "foo.bar\\.";
printf("\nTest 6: StringFind() function\n");
VerifyOrQuit(StringFind(testString, 'f') == testString, "StringFind() failed");
VerifyOrQuit(StringFind(testString, 'o') == &testString[1], "StringFind() failed");
VerifyOrQuit(StringFind(testString, '.') == &testString[3], "StringFind() failed");
VerifyOrQuit(StringFind(testString, 'r') == &testString[6], "StringFind() failed");
VerifyOrQuit(StringFind(testString, '\\') == &testString[7], "StringFind() failed");
VerifyOrQuit(StringFind(testString, 'x') == nullptr, "StringFind() failed");
VerifyOrQuit(StringFind(testString, ',') == nullptr, "StringFind() failed");
VerifyOrQuit(StringFind(emptyString, 'f') == nullptr, "StringFind() failed");
VerifyOrQuit(StringFind(emptyString, '.') == nullptr, "StringFind() failed");
printf(" -- PASS\n");
}
} // namespace ot
int main(void)
@@ -161,6 +182,7 @@ int main(void)
ot::TestString();
ot::TestStringLength();
ot::TestUtf8();
ot::TestStringFind();
printf("\nAll tests passed.\n");
return 0;
}