mirror of
https://github.com/espressif/openthread.git
synced 2026-09-04 16:20:05 +00:00
[dns-header] helper methods to parse/find/read record(s) in a message (#6097)
This commit adds new helper methods in `ResourceRecord` class. `ParseRecords()` parses and skips over a given number of resource records in a message. `FindRecord()` searches in a given message to find the first resource record matching a given record name. And `ReadRecord<RecordType>()` tries to read a matching resource record (of a given type) from a message. If the record type does not match the type, it skips over the record. This commit also adds methods in `PtrRecord`, `SrvRecord`, `SigRecord` and `TxtRecord` to parse and read the data fields such as PTR name, host name, signer name and TXT data. This commit updates the DNS unit test to add a detailed new test constructing and parsing a DNS message (covering the behavior of the newly added and some of the existing methods).
This commit is contained in:
@@ -531,6 +531,169 @@ bool Name::LabelIterator::CompareLabel(const LabelIterator &aOtherIterator) cons
|
||||
mLabelLength);
|
||||
}
|
||||
|
||||
otError ResourceRecord::ParseRecords(const Message &aMessage, uint16_t &aOffset, uint16_t aNumRecords)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
while (aNumRecords > 0)
|
||||
{
|
||||
ResourceRecord record;
|
||||
|
||||
SuccessOrExit(error = Name::ParseName(aMessage, aOffset));
|
||||
SuccessOrExit(error = record.ReadFrom(aMessage, aOffset));
|
||||
aOffset += static_cast<uint16_t>(record.GetSize());
|
||||
aNumRecords--;
|
||||
}
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
otError ResourceRecord::FindRecord(const Message &aMessage, uint16_t &aOffset, uint16_t &aNumRecords, const char *aName)
|
||||
{
|
||||
otError error;
|
||||
|
||||
while (aNumRecords > 0)
|
||||
{
|
||||
bool matches = true;
|
||||
ResourceRecord record;
|
||||
|
||||
error = Name::CompareName(aMessage, aOffset, aName);
|
||||
|
||||
switch (error)
|
||||
{
|
||||
case OT_ERROR_NONE:
|
||||
break;
|
||||
case OT_ERROR_NOT_FOUND:
|
||||
matches = false;
|
||||
break;
|
||||
default:
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
SuccessOrExit(error = record.ReadFrom(aMessage, aOffset));
|
||||
aNumRecords--;
|
||||
VerifyOrExit(!matches);
|
||||
aOffset += static_cast<uint16_t>(record.GetSize());
|
||||
}
|
||||
|
||||
error = OT_ERROR_NOT_FOUND;
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
otError ResourceRecord::ReadRecord(const Message & aMessage,
|
||||
uint16_t & aOffset,
|
||||
uint16_t aType,
|
||||
ResourceRecord &aRecord,
|
||||
uint16_t aMinRecordSize)
|
||||
{
|
||||
// This static method tries to read a matching resource record of a
|
||||
// given type and a minimum record size from a message. The `aType`
|
||||
// value of `kTypeAny` matches any type. If the record in the
|
||||
// message does not match, it skips over the record. Please see
|
||||
// `ReadRecord<RecordType>()` for more details.
|
||||
|
||||
otError error;
|
||||
ResourceRecord record;
|
||||
|
||||
SuccessOrExit(error = record.ReadFrom(aMessage, aOffset));
|
||||
|
||||
if (((aType == kTypeAny) || (record.GetType() == aType)) && (record.GetSize() >= aMinRecordSize))
|
||||
{
|
||||
IgnoreError(aMessage.Read(aOffset, &aRecord, aMinRecordSize));
|
||||
aOffset += aMinRecordSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Skip over the entire record.
|
||||
aOffset += static_cast<uint16_t>(record.GetSize());
|
||||
error = OT_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
otError ResourceRecord::ReadName(const Message &aMessage,
|
||||
uint16_t & aOffset,
|
||||
uint16_t aStartOffset,
|
||||
char * aNameBuffer,
|
||||
uint16_t aNameBufferSize,
|
||||
bool aSkipRecord) const
|
||||
{
|
||||
// This protected method parses and reads a name field in a record
|
||||
// from a message. It is intended only for sub-classes of
|
||||
// `ResourceRecord`.
|
||||
//
|
||||
// On input `aOffset` gives the offset in `aMessage` to the start of
|
||||
// name field. `aStartOffset` gives the offset to the start of the
|
||||
// `ResourceRecord`. `aSkipRecord` indicates whether to skip over
|
||||
// the entire resource record or just the read name. On exit, when
|
||||
// successfully read, `aOffset` is updated to either point after the
|
||||
// end of record or after the the name field.
|
||||
//
|
||||
// When read successfully, this method returns `OT_ERROR_NONE`. On a
|
||||
// parse error (invalid format) returns `OT_ERROR_PARSE`. If the
|
||||
// name does not fit in the given name buffer it returns
|
||||
// `OT_ERROR_NO_BUFS`
|
||||
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
SuccessOrExit(error = Name::ReadName(aMessage, aOffset, aNameBuffer, aNameBufferSize));
|
||||
VerifyOrExit(aOffset <= aStartOffset + GetSize(), error = OT_ERROR_PARSE);
|
||||
|
||||
VerifyOrExit(aSkipRecord);
|
||||
aOffset = aStartOffset;
|
||||
error = SkipRecord(aMessage, aOffset);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
otError ResourceRecord::SkipRecord(const Message &aMessage, uint16_t &aOffset) const
|
||||
{
|
||||
// This protected method parses and skips over a resource record
|
||||
// in a message.
|
||||
//
|
||||
// On input `aOffset` gives the offset in `aMessage` to the start of
|
||||
// the `ResourceRecord`. On exit, when successfully parsed, `aOffset`
|
||||
// is updated to point to byte after the entire record.
|
||||
|
||||
otError error;
|
||||
|
||||
SuccessOrExit(error = CheckRecord(aMessage, aOffset));
|
||||
aOffset += static_cast<uint16_t>(GetSize());
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
otError ResourceRecord::CheckRecord(const Message &aMessage, uint16_t aOffset) const
|
||||
{
|
||||
// This method checks that the entire record (including record data)
|
||||
// is present in `aMessage` at `aOffset` (pointing to the start of
|
||||
// the `ResourceRecord` in `aMessage`).
|
||||
|
||||
return (aOffset + GetSize() <= aMessage.GetLength()) ? OT_ERROR_NONE : OT_ERROR_PARSE;
|
||||
}
|
||||
|
||||
otError ResourceRecord::ReadFrom(const Message &aMessage, uint16_t aOffset)
|
||||
{
|
||||
// This method reads the `ResourceRecord` from `aMessage` at
|
||||
// `aOffset`. It verifies that the entire record (including record
|
||||
// data) is present in the message.
|
||||
|
||||
otError error;
|
||||
|
||||
SuccessOrExit(error = aMessage.Read(aOffset, *this));
|
||||
error = CheckRecord(aMessage, aOffset);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
bool AaaaRecord::IsValid(void) const
|
||||
{
|
||||
return GetType() == Dns::ResourceRecord::kTypeAaaa && GetSize() == sizeof(*this);
|
||||
@@ -565,5 +728,50 @@ bool LeaseOption::IsValid(void) const
|
||||
return GetLeaseInterval() <= GetKeyLeaseInterval();
|
||||
}
|
||||
|
||||
otError PtrRecord::ReadPtrName(const Message &aMessage,
|
||||
uint16_t & aOffset,
|
||||
char * aLabelBuffer,
|
||||
uint8_t aLabelBufferSize,
|
||||
char * aNameBuffer,
|
||||
uint16_t aNameBufferSize) const
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
uint16_t startOffset = aOffset - sizeof(PtrRecord); // start of `PtrRecord`.
|
||||
|
||||
// Verify that the name is within the record data length.
|
||||
SuccessOrExit(error = Name::ParseName(aMessage, aOffset));
|
||||
VerifyOrExit(aOffset <= startOffset + GetSize(), error = OT_ERROR_PARSE);
|
||||
|
||||
aOffset = startOffset + sizeof(PtrRecord);
|
||||
SuccessOrExit(error = Name::ReadLabel(aMessage, aOffset, aLabelBuffer, aLabelBufferSize));
|
||||
|
||||
if (aNameBuffer != nullptr)
|
||||
{
|
||||
SuccessOrExit(error = Name::ReadName(aMessage, aOffset, aNameBuffer, aNameBufferSize));
|
||||
}
|
||||
|
||||
aOffset = startOffset;
|
||||
error = SkipRecord(aMessage, aOffset);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
otError TxtRecord::ReadTxtData(const Message &aMessage,
|
||||
uint16_t & aOffset,
|
||||
uint8_t * aTxtBuffer,
|
||||
uint16_t & aTxtBufferSize) const
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
VerifyOrExit(GetLength() <= aTxtBufferSize, error = OT_ERROR_NO_BUFS);
|
||||
SuccessOrExit(error = aMessage.Read(aOffset, aTxtBuffer, GetLength()));
|
||||
aTxtBufferSize = GetLength();
|
||||
aOffset += GetLength();
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
} // namespace Dns
|
||||
} // namespace ot
|
||||
|
||||
@@ -233,6 +233,7 @@ public:
|
||||
* This method denotes whether recursive query support is available in the name server.
|
||||
*
|
||||
* @returns True if Recursion Available flag (RA) is set in the header, false otherwise.
|
||||
*
|
||||
*/
|
||||
bool IsRecursionAvailableFlagSet(void) const { return (mFlags[1] & kRaFlagMask) == kRaFlagMask; }
|
||||
|
||||
@@ -934,7 +935,108 @@ public:
|
||||
*/
|
||||
uint32_t GetSize(void) const { return sizeof(ResourceRecord) + GetLength(); }
|
||||
|
||||
/**
|
||||
* This static method parses and skips over a given number of resource records in a message from a given offset.
|
||||
*
|
||||
* @param[in] aMessage The message from which to parse/read the resource records. `aMessage.GetOffset()`
|
||||
* MUST point to the start of DNS header.
|
||||
* @param[inout] aOffset On input the offset in @p aMessage pointing to the start of the first record.
|
||||
* On exit (when parsed successfully), @p aOffset is updated to point to the byte after
|
||||
* the last parsed record.
|
||||
* @param[in] aNumRecords Number of resource records to parse.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Parsed records successfully. @p aOffset is updated.
|
||||
* @retval OT_ERROR_PARSE Could not parse the records from @p aMessage (e.g., ran out of bytes in @p aMessage).
|
||||
*
|
||||
*/
|
||||
static otError ParseRecords(const Message &aMessage, uint16_t &aOffset, uint16_t aNumRecords);
|
||||
|
||||
/**
|
||||
* This static method searches in a given message to find the first resource record matching a given record name.
|
||||
*
|
||||
* @param[in] aMessage The message in which to search for a matching resource record.
|
||||
* `aMessage.GetOffset()` MUST point to the start of DNS header.
|
||||
* @param[inout] aOffset On input, the offset in @p aMessage pointing to the start of the first record.
|
||||
* On exit, if a matching record is found, @p aOffset is updated to point to the byte
|
||||
* after the record name.
|
||||
* If a matching record could not be found, @p aOffset is updated to point to the byte
|
||||
* after the last record that was checked.
|
||||
* @param[inout] aNumRecords On input, the maximum number of records to check (starting from @p aOffset).
|
||||
* On exit and if a matching record is found, @p aNumRecords is updated to give the
|
||||
* number of remaining records after @p aOffset (excluding the matching record).
|
||||
* @param[in] aName The record name to match against (MUST be a null terminated string).
|
||||
*
|
||||
* @retval OT_ERROR_NONE A matching record was found. @p aOffset, @p aNumRecords are updated.
|
||||
* @retval OT_ERROR_NOT_FOUND A matching record could not be found. @p aOffset and @p aNumRecords are updated.
|
||||
* @retval OT_ERROR_PARSE Could not parse records from @p aMessage (e.g., ran out of bytes in @p aMessage).
|
||||
*
|
||||
*/
|
||||
static otError FindRecord(const Message &aMessage, uint16_t &aOffset, uint16_t &aNumRecords, const char *aName);
|
||||
|
||||
/**
|
||||
* This template static method tries to read a resource record of a given type from a message. If the record type
|
||||
* does not matches the type, it skips over the record.
|
||||
*
|
||||
* This method requires the record name to be already parsed/read from the message. On input, @p aOffset should
|
||||
* point to the start of the `ResourceRecord` fields (type, class, TTL, data length) in @p aMessage.
|
||||
*
|
||||
* This method verifies that the record is well-formed in the message. It then reads the record type and compares
|
||||
* it with `RecordType::kType` and ensures that the record size is at least `sizeof(RecordType)`. If it all matches,
|
||||
* the record is read into @p aRecord.
|
||||
*
|
||||
* On success (i.e., when a matching record is read from the message), the @p aOffset is updated to point to after
|
||||
* the last byte read from the message and copied into @p aRecord and not necessarily the end of the record.
|
||||
* Depending on the `RecordType` format, there may still be more data bytes left in the record to be read. For
|
||||
* example, when reading a SRV record using `SrvRecord` type, @p aOffset would point to after the last field in
|
||||
* `SrvRecord` which is the start of "target host domain name" field.
|
||||
*
|
||||
* @tparam RecordType The resource record type (i.e., a sub-class of `ResourceRecord`).
|
||||
*
|
||||
* @param[in] aMessage The message from which to read the record.
|
||||
* @param[inout] aOffset On input, the offset in @p aMessage pointing to the byte after the record name.
|
||||
* On exit, if a matching record is read, @p aOffset is updated to point to the last
|
||||
* read byte in the record.
|
||||
* If a matching record could not be read, @p aOffset is updated to point to the byte
|
||||
* after the entire record (skipping over the record).
|
||||
* @param[out] aRecord A reference to a record to read a matching record into.
|
||||
* If a matching record is found, `sizeof(RecordType)` bytes from @p aMessage are
|
||||
* read from @p aMessage and copied into @p aRecord.
|
||||
*
|
||||
* @retval OT_ERROR_NONE A matching record was read successfully. @p aOffset, and @p aRecord are updated.
|
||||
* @retval OT_ERROR_NOT_FOUND A matching record could not be found. @p aOffset is updated.
|
||||
* @retval OT_ERROR_PARSE Could not parse records from @p aMessage (e.g., ran out of bytes in @p aMessage).
|
||||
*
|
||||
*/
|
||||
template <class RecordType>
|
||||
static otError ReadRecord(const Message &aMessage, uint16_t &aOffset, RecordType &aRecord)
|
||||
{
|
||||
return ReadRecord(aMessage, aOffset, RecordType::kType, aRecord, sizeof(RecordType));
|
||||
}
|
||||
|
||||
protected:
|
||||
otError ReadName(const Message &aMessage,
|
||||
uint16_t & aOffset,
|
||||
uint16_t aStartOffset,
|
||||
char * aNameBuffer,
|
||||
uint16_t aNameBufferSize,
|
||||
bool aSkipRecord) const;
|
||||
otError SkipRecord(const Message &aMessage, uint16_t &aOffset) const;
|
||||
|
||||
private:
|
||||
enum : uint8_t
|
||||
{
|
||||
kType = kTypeAny, // This is intended for used by `ReadRecord()` only.
|
||||
};
|
||||
|
||||
static otError ReadRecord(const Message & aMessage,
|
||||
uint16_t & aOffset,
|
||||
uint16_t aType,
|
||||
ResourceRecord &aRecord,
|
||||
uint16_t aMinRecordSize);
|
||||
|
||||
otError CheckRecord(const Message &aMessage, uint16_t aOffset) const;
|
||||
otError ReadFrom(const Message &aMessage, uint16_t aOffset);
|
||||
|
||||
uint16_t mType; // The type of the data in RDATA section.
|
||||
uint16_t mClass; // The class of the data in RDATA section.
|
||||
uint32_t mTtl; // Specifies the maximum time that the resource record may be cached.
|
||||
@@ -950,6 +1052,11 @@ OT_TOOL_PACKED_BEGIN
|
||||
class PtrRecord : public ResourceRecord
|
||||
{
|
||||
public:
|
||||
enum : uint16_t
|
||||
{
|
||||
kType = kTypePtr, ///< The PTR record type.
|
||||
};
|
||||
|
||||
/**
|
||||
* This method initializes the PTR Resource Record by setting its type and class.
|
||||
*
|
||||
@@ -960,6 +1067,70 @@ public:
|
||||
*/
|
||||
void Init(uint16_t aClass = kClassInternet) { ResourceRecord::Init(kTypePtr, aClass); }
|
||||
|
||||
/**
|
||||
* This method parses and reads the PTR name from a message.
|
||||
*
|
||||
* This method also verifies that the PTR record is well-formed (e.g., the record data length `GetLength()` matches
|
||||
* the PTR encoded name).
|
||||
*
|
||||
* @param[in] aMessage The message to read from. `aMessage.GetOffset()` MUST point to the start of
|
||||
* DNS header.
|
||||
* @param[inout] aOffset On input, the offset in @p aMessage to start of PTR name field.
|
||||
* On exit when successfully read, @p aOffset is updated to point to the byte
|
||||
* after the entire PTR record (skipping over the record).
|
||||
* @param[out] aNameBuffer A pointer to a char array to output the read name as a null-terminated C string
|
||||
* (MUST NOT be nullptr).
|
||||
* @param[in] aNameBufferSize The size of @p aNameBuffer.
|
||||
*
|
||||
* @retval OT_ERROR_NONE The PTR name was read successfully. @p aOffset and @p aNameBuffer are updated.
|
||||
* @retval OT_ERROR_PARSE The PTR record in @p aMessage could not be parsed (invalid format).
|
||||
* @retval OT_ERROR_NO_BUFS Name could not fit in @p aNameBufferSize chars.
|
||||
*
|
||||
*/
|
||||
otError ReadPtrName(const Message &aMessage, uint16_t &aOffset, char *aNameBuffer, uint16_t aNameBufferSize) const
|
||||
{
|
||||
return ResourceRecord::ReadName(aMessage, aOffset, /* aStartOffset */ aOffset - sizeof(PtrRecord), aNameBuffer,
|
||||
aNameBufferSize,
|
||||
/* aSkipRecord */ true);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method parses and reads the PTR name from a message.
|
||||
*
|
||||
* This method also verifies that the PTR record is well-formed (e.g., the record data length `GetLength()` matches
|
||||
* the PTR encoded name).
|
||||
*
|
||||
* Unlike the previous method which reads the entire PTR name into a single char buffer, this method reads the
|
||||
* first label separately and into a different buffer @p aLabelBuffer and the rest of the name into @p aNameBuffer.
|
||||
* The @p aNameBuffer can be set to `nullptr` if the caller is only interested in the first label. This method is
|
||||
* intended for "Service Instance Name" where first label (`<Instance>` portion) can be a user-friendly string and
|
||||
* can contain dot character.
|
||||
*
|
||||
* @param[in] aMessage The message to read from. `aMessage.GetOffset()` MUST point to the start of
|
||||
* DNS header.
|
||||
* @param[inout] aOffset On input, the offset in @p aMessage to the start of PTR name field.
|
||||
* On exit, when successfully read, @p aOffset is updated to point to the byte
|
||||
* after the entire PTR record (skipping over the record).
|
||||
* @param[out] aLabelBuffer A pointer to a char array to output the first label as a null-terminated C
|
||||
* string (MUST NOT be nullptr).
|
||||
* @param[in] aLabelBufferSize The size of @p aLabelBuffer.
|
||||
* @param[out] aNameBuffer A pointer to a char array to output the rest of name (after first label). Can
|
||||
* be `nullptr` if caller is only interested in the first label.
|
||||
* @param[in] aNameBufferSize The size of @p aNameBuffer.
|
||||
*
|
||||
* @retval OT_ERROR_NONE The PTR name was read successfully. @p aOffset, @aLabelBuffer and @aNameBuffer
|
||||
* are updated.
|
||||
* @retval OT_ERROR_PARSE The PTR record in @p aMessage could not be parsed (invalid format).
|
||||
* @retval OT_ERROR_NO_BUFS Either label or name could not fit in the related char buffers.
|
||||
*
|
||||
*/
|
||||
otError ReadPtrName(const Message &aMessage,
|
||||
uint16_t & aOffset,
|
||||
char * aLabelBuffer,
|
||||
uint8_t aLabelBufferSize,
|
||||
char * aNameBuffer,
|
||||
uint16_t aNameBufferSize) const;
|
||||
|
||||
} OT_TOOL_PACKED_END;
|
||||
|
||||
/**
|
||||
@@ -970,6 +1141,11 @@ OT_TOOL_PACKED_BEGIN
|
||||
class TxtRecord : public ResourceRecord
|
||||
{
|
||||
public:
|
||||
enum : uint16_t
|
||||
{
|
||||
kType = kTypeTxt, ///< The TXT record type.
|
||||
};
|
||||
|
||||
/**
|
||||
* This method initializes the TXT Resource Record by setting its type and class.
|
||||
*
|
||||
@@ -980,6 +1156,28 @@ public:
|
||||
*/
|
||||
void Init(uint16_t aClass = kClassInternet) { ResourceRecord::Init(kTypeTxt, aClass); }
|
||||
|
||||
/**
|
||||
* This method parses and reads the TXT record data from a message.
|
||||
*
|
||||
* @param[in] aMessage The message to read from.
|
||||
* @param[inout] aOffset On input, the offset in @p aMessage to start of TXT record data.
|
||||
* On exit when successfully read, @p aOffset is updated to point to the byte
|
||||
* after the entire TXT record (skipping over the record).
|
||||
* @param[out] aTxtBuffer A pointer to a byte array to output the read TXT data.
|
||||
* @param[inout] aTxtBufferSize On input, the size of @p aTxtBuffer (max bytes that can be read).
|
||||
* On exit, @p aTxtBufferSize gives number of bytes written to @p aTxtBuffer.
|
||||
*
|
||||
* @retval OT_ERROR_NONE The TXT data was read successfully. @p aOffset, @p aTxtBuffer and
|
||||
* @p aTxtBufferSize are updated.
|
||||
* @retval OT_ERROR_PARSE The TXT record in @p aMessage could not be parsed (invalid format).
|
||||
* @retval OT_ERROR_NO_BUFS TXT data could not fit in @p aTxtBufferSize bytes.
|
||||
*
|
||||
*/
|
||||
otError ReadTxtData(const Message &aMessage,
|
||||
uint16_t & aOffset,
|
||||
uint8_t * aTxtBuffer,
|
||||
uint16_t & aTxtBufferSize) const;
|
||||
|
||||
} OT_TOOL_PACKED_END;
|
||||
|
||||
/**
|
||||
@@ -990,6 +1188,11 @@ OT_TOOL_PACKED_BEGIN
|
||||
class AaaaRecord : public ResourceRecord
|
||||
{
|
||||
public:
|
||||
enum : uint16_t
|
||||
{
|
||||
kType = kTypeAaaa, ///< The AAAA record type.
|
||||
};
|
||||
|
||||
/**
|
||||
* This method initializes the AAAA Resource Record by setting its type, class, and length.
|
||||
*
|
||||
@@ -1038,6 +1241,11 @@ OT_TOOL_PACKED_BEGIN
|
||||
class SrvRecord : public ResourceRecord
|
||||
{
|
||||
public:
|
||||
enum : uint16_t
|
||||
{
|
||||
kType = kTypeSrv, ///< The SRV record type.
|
||||
};
|
||||
|
||||
/**
|
||||
* This method initializes the SRV Resource Record by settings its type and class.
|
||||
*
|
||||
@@ -1096,6 +1304,36 @@ public:
|
||||
*/
|
||||
void SetPort(uint16_t aPort) { mPort = HostSwap16(aPort); }
|
||||
|
||||
/**
|
||||
* This method parses and reads the SRV target host name from a message.
|
||||
*
|
||||
* This method also verifies that the SRV record is well-formed (e.g., the record data length `GetLength()` matches
|
||||
* the SRV encoded name).
|
||||
*
|
||||
* @param[in] aMessage The message to read from. `aMessage.GetOffset()` MUST point to the start of
|
||||
* DNS header.
|
||||
* @param[inout] aOffset On input, the offset in @p aMessage to start of target host name field.
|
||||
* On exit when successfully read, @p aOffset is updated to point to the byte
|
||||
* after the entire SRV record (skipping over the record).
|
||||
* @param[out] aNameBuffer A pointer to a char array to output the read name as a null-terminated C string
|
||||
* (MUST NOT be nullptr).
|
||||
* @param[in] aNameBufferSize The size of @p aNameBuffer.
|
||||
*
|
||||
* @retval OT_ERROR_NONE The host name was read successfully. @p aOffset and @p aNameBuffer are updated.
|
||||
* @retval OT_ERROR_PARSE The SRV record in @p aMessage could not be parsed (invalid format).
|
||||
* @retval OT_ERROR_NO_BUFS Name could not fit in @p aNameBufferSize chars.
|
||||
*
|
||||
*/
|
||||
otError ReadTargetHostName(const Message &aMessage,
|
||||
uint16_t & aOffset,
|
||||
char * aNameBuffer,
|
||||
uint16_t aNameBufferSize) const
|
||||
{
|
||||
return ResourceRecord::ReadName(aMessage, aOffset, /* aStartOffset */ aOffset - sizeof(SrvRecord), aNameBuffer,
|
||||
aNameBufferSize,
|
||||
/* aSkipRecord */ true);
|
||||
}
|
||||
|
||||
private:
|
||||
uint16_t mPriority;
|
||||
uint16_t mWeight;
|
||||
@@ -1112,6 +1350,11 @@ OT_TOOL_PACKED_BEGIN
|
||||
class KeyRecord : public ResourceRecord
|
||||
{
|
||||
public:
|
||||
enum : uint16_t
|
||||
{
|
||||
kType = kTypeKey, ///< The KEY record type.
|
||||
};
|
||||
|
||||
/**
|
||||
* This enumeration defines protocol field values (RFC 2535 - section 3.1.3).
|
||||
*
|
||||
@@ -1347,6 +1590,11 @@ OT_TOOL_PACKED_BEGIN
|
||||
class SigRecord : public ResourceRecord, public Clearable<SigRecord>
|
||||
{
|
||||
public:
|
||||
enum : uint16_t
|
||||
{
|
||||
kType = kTypeSig, ///< The SIG record type.
|
||||
};
|
||||
|
||||
/**
|
||||
* This method initializes the SIG Resource Record by setting its type and class.
|
||||
*
|
||||
@@ -1489,6 +1737,33 @@ public:
|
||||
*/
|
||||
const uint8_t *GetRecordData(void) const { return reinterpret_cast<const uint8_t *>(&mTypeCovered); }
|
||||
|
||||
/**
|
||||
* This method parses and reads the SIG signer name from a message.
|
||||
*
|
||||
* @param[in] aMessage The message to read from. `aMessage.GetOffset()` MUST point to the start of DNS
|
||||
* header.
|
||||
* @param[inout] aOffset On input, the offset in @p aMessage to start of signer name field.
|
||||
* On exit when successfully read, @p aOffset is updated to point to the byte
|
||||
* after the name field (i.e., start of signature field).
|
||||
* @param[out] aNameBuffer A pointer to a char array to output the read name as a null-terminated C string
|
||||
* (MUST NOT be nullptr).
|
||||
* @param[in] aNameBufferSize The size of @p aNameBuffer.
|
||||
*
|
||||
* @retval OT_ERROR_NONE The name was read successfully. @p aOffset and @p aNameBuffer are updated.
|
||||
* @retval OT_ERROR_PARSE The SIG record in @p aMessage could not be parsed (invalid format).
|
||||
* @retval OT_ERROR_NO_BUFS Name could not fit in @p aNameBufferSize chars.
|
||||
*
|
||||
*/
|
||||
otError ReadSignerName(const Message &aMessage,
|
||||
uint16_t & aOffset,
|
||||
char * aNameBuffer,
|
||||
uint16_t aNameBufferSize) const
|
||||
{
|
||||
return ResourceRecord::ReadName(aMessage, aOffset, /* aStartOffset */ aOffset - sizeof(SigRecord), aNameBuffer,
|
||||
aNameBufferSize,
|
||||
/* aSkipRecord */ false);
|
||||
}
|
||||
|
||||
private:
|
||||
uint16_t mTypeCovered; // type of the other RRs covered by this SIG. set to zero for SIG(0).
|
||||
uint8_t mAlgorithm; // Algorithm number (see `KeyRecord` enumeration).
|
||||
@@ -1508,6 +1783,11 @@ OT_TOOL_PACKED_BEGIN
|
||||
class OptRecord : public ResourceRecord
|
||||
{
|
||||
public:
|
||||
enum : uint16_t
|
||||
{
|
||||
kType = kTypeOpt, ///< The OPT record type.
|
||||
};
|
||||
|
||||
/**
|
||||
* This method initializes the OPT Resource Record by setting its type and clearing extended Response Code, version
|
||||
* and all flags.
|
||||
|
||||
@@ -653,12 +653,339 @@ void TestDnsCompressedName(void)
|
||||
testFreeInstance(instance);
|
||||
}
|
||||
|
||||
void TestHeaderAndResourceRecords(void)
|
||||
{
|
||||
enum
|
||||
{
|
||||
kHeaderOffset = 0,
|
||||
kQuestionCount = 1,
|
||||
kAnswerCount = 2,
|
||||
kAdditionalCount = 5,
|
||||
kTtl = 7200,
|
||||
kTxtTtl = 7300,
|
||||
kSrvPort = 1234,
|
||||
kSrvPriority = 1,
|
||||
kSrvWeight = 2,
|
||||
kMaxSize = 600,
|
||||
};
|
||||
|
||||
const char kMessageString[] = "DnsMessage";
|
||||
const char kDomainName[] = "example.com.";
|
||||
const char kServiceLabels[] = "_service._udp";
|
||||
const char kServiceName[] = "_service._udp.example.com.";
|
||||
const char kInstance1Label[] = "inst1";
|
||||
const char kInstance2Label[] = "instance2";
|
||||
const char kInstance1Name[] = "inst1._service._udp.example.com.";
|
||||
const char kInstance2Name[] = "instance2._service._udp.example.com.";
|
||||
const char kHostName[] = "host.example.com.";
|
||||
const uint8_t kTxtData[] = {9, 'k', 'e', 'y', '=', 'v', 'a', 'l', 'u', 'e', 0};
|
||||
const char kHostAddress[] = "fd00::abcd:";
|
||||
|
||||
const char *kInstanceLabels[] = {kInstance1Label, kInstance2Label};
|
||||
const char *kInstanceNames[] = {kInstance1Name, kInstance2Name};
|
||||
|
||||
Instance * instance;
|
||||
MessagePool * messagePool;
|
||||
Message * message;
|
||||
Dns::Header header;
|
||||
uint16_t messageId;
|
||||
uint16_t headerOffset;
|
||||
uint16_t offset;
|
||||
uint16_t numRecords;
|
||||
uint16_t len;
|
||||
uint16_t serviceNameOffset;
|
||||
uint16_t hostNameOffset;
|
||||
uint16_t answerSectionOffset;
|
||||
uint16_t additionalSectionOffset;
|
||||
Dns::PtrRecord ptrRecord;
|
||||
Dns::SrvRecord srvRecord;
|
||||
Dns::TxtRecord txtRecord;
|
||||
Dns::AaaaRecord aaaaRecord;
|
||||
Dns::ResourceRecord record;
|
||||
Ip6::Address hostAddress;
|
||||
|
||||
char label[Dns::Name::kMaxLabelLength + 1];
|
||||
char name[Dns::Name::kMaxLength];
|
||||
uint8_t buffer[kMaxSize];
|
||||
|
||||
printf("================================================================\n");
|
||||
printf("TestHeaderAndResourceRecords()\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("Preparing the message\n");
|
||||
|
||||
SuccessOrQuit(message->Append(kMessageString), "Message::Append() failed");
|
||||
|
||||
// Header
|
||||
|
||||
headerOffset = message->GetLength();
|
||||
SuccessOrQuit(header.SetRandomMessageId(), "Header::SetRandomMessageId() failed");
|
||||
messageId = header.GetMessageId();
|
||||
header.SetType(Dns::Header::kTypeResponse);
|
||||
header.SetQuestionCount(kQuestionCount);
|
||||
header.SetAnswerCount(kAnswerCount);
|
||||
header.SetAdditionalRecordCount(kAdditionalCount);
|
||||
SuccessOrQuit(message->Append(header), "Message::Append() failed");
|
||||
message->SetOffset(headerOffset);
|
||||
|
||||
// Question section
|
||||
|
||||
serviceNameOffset = message->GetLength() - headerOffset;
|
||||
SuccessOrQuit(Dns::Name::AppendMultipleLabels(kServiceLabels, *message), "AppendMultipleLabels() failed");
|
||||
SuccessOrQuit(Dns::Name::AppendName(kDomainName, *message), "AppendName() failed");
|
||||
SuccessOrQuit(message->Append(Dns::Question(Dns::ResourceRecord::kTypePtr)), "Message::Append() failed");
|
||||
|
||||
// Answer section
|
||||
|
||||
answerSectionOffset = message->GetLength();
|
||||
|
||||
for (const char *instanceLabel : kInstanceLabels)
|
||||
{
|
||||
SuccessOrQuit(Dns::Name::AppendPointerLabel(serviceNameOffset, *message), "AppendPointerLabel() failed");
|
||||
ptrRecord.Init();
|
||||
ptrRecord.SetTtl(kTtl);
|
||||
offset = message->GetLength();
|
||||
SuccessOrQuit(message->Append(ptrRecord), "Message::Append() failed");
|
||||
SuccessOrQuit(Dns::Name::AppendLabel(instanceLabel, *message), "AppendLabel failed");
|
||||
SuccessOrQuit(Dns::Name::AppendPointerLabel(serviceNameOffset, *message), "AppendPointerLabel() failed");
|
||||
ptrRecord.SetLength(message->GetLength() - offset - sizeof(Dns::ResourceRecord));
|
||||
message->Write(offset, ptrRecord);
|
||||
}
|
||||
|
||||
// Additional section
|
||||
|
||||
additionalSectionOffset = message->GetLength();
|
||||
|
||||
for (const char *instanceName : kInstanceNames)
|
||||
{
|
||||
uint16_t instanceNameOffset = message->GetLength() - headerOffset;
|
||||
|
||||
// SRV record
|
||||
SuccessOrQuit(Dns::Name::AppendName(instanceName, *message), "AppendName() failed");
|
||||
srvRecord.Init();
|
||||
srvRecord.SetTtl(kTtl);
|
||||
srvRecord.SetPort(kSrvPort);
|
||||
srvRecord.SetWeight(kSrvWeight);
|
||||
srvRecord.SetPriority(kSrvPriority);
|
||||
offset = message->GetLength();
|
||||
SuccessOrQuit(message->Append(srvRecord), "Message::Append() failed");
|
||||
hostNameOffset = message->GetLength() - headerOffset;
|
||||
SuccessOrQuit(Dns::Name::AppendName(kHostName, *message), "AppendName() failed");
|
||||
srvRecord.SetLength(message->GetLength() - offset - sizeof(Dns::ResourceRecord));
|
||||
message->Write(offset, srvRecord);
|
||||
|
||||
// TXT record
|
||||
SuccessOrQuit(Dns::Name::AppendPointerLabel(instanceNameOffset, *message), "AppendPointerLabel() failed");
|
||||
txtRecord.Init();
|
||||
txtRecord.SetTtl(kTxtTtl);
|
||||
txtRecord.SetLength(sizeof(kTxtData));
|
||||
SuccessOrQuit(message->Append(txtRecord), "Message::Append() failed");
|
||||
SuccessOrQuit(message->Append(kTxtData), "Message::Append() failed");
|
||||
}
|
||||
|
||||
SuccessOrQuit(hostAddress.FromString(kHostAddress), "Address::FromString() failed");
|
||||
SuccessOrQuit(Dns::Name::AppendPointerLabel(hostNameOffset, *message), "AppendPointerLabel() failed");
|
||||
aaaaRecord.Init();
|
||||
aaaaRecord.SetTtl(kTtl);
|
||||
aaaaRecord.SetAddress(hostAddress);
|
||||
SuccessOrQuit(message->Append(aaaaRecord), "Message::Append()");
|
||||
|
||||
// Dump the entire message
|
||||
|
||||
VerifyOrQuit(message->GetLength() < kMaxSize, "Message is too long");
|
||||
SuccessOrQuit(message->Read(0, buffer, message->GetLength()), "Message::Read() failed");
|
||||
DumpBuffer("message", buffer, message->GetLength());
|
||||
|
||||
printf("----------------------------------------------------------------\n");
|
||||
printf("Parse and verify the message\n");
|
||||
|
||||
offset = 0;
|
||||
VerifyOrQuit(message->Compare(offset, kMessageString), "Message header does not match");
|
||||
offset += sizeof(kMessageString);
|
||||
|
||||
// Header
|
||||
|
||||
VerifyOrQuit(offset == headerOffset, "headerOffset is incorrect");
|
||||
SuccessOrQuit(message->Read(offset, header), "Message::Read() failed");
|
||||
offset += sizeof(header);
|
||||
|
||||
VerifyOrQuit(header.GetMessageId() == messageId, "Header::GetMessageId() failed");
|
||||
VerifyOrQuit(header.GetType() == Dns::Header::kTypeResponse, "Header::GetType() failed");
|
||||
VerifyOrQuit(header.GetQuestionCount() == kQuestionCount, "Header::GetQuestionCount() failed");
|
||||
VerifyOrQuit(header.GetAnswerCount() == kAnswerCount, "Header::GetAnswerCount() failed");
|
||||
VerifyOrQuit(header.GetAdditionalRecordCount() == kAdditionalCount, "Header::GetAdditionalRecordCount() failed");
|
||||
|
||||
printf("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \n");
|
||||
printf("Question Section\n");
|
||||
|
||||
SuccessOrQuit(Dns::Name::CompareName(*message, offset, kServiceName), "Question name does not match");
|
||||
VerifyOrQuit(message->Compare(offset, Dns::Question(Dns::ResourceRecord::kTypePtr)), "Question does not match");
|
||||
offset += sizeof(Dns::Question);
|
||||
|
||||
printf("PTR for \"%s\"\n", kServiceName);
|
||||
|
||||
printf("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \n");
|
||||
printf("Answer Section\n");
|
||||
|
||||
VerifyOrQuit(offset == answerSectionOffset, "answer section offset is incorrect");
|
||||
|
||||
for (const char *instanceName : kInstanceNames)
|
||||
{
|
||||
SuccessOrQuit(Dns::Name::CompareName(*message, offset, kServiceName), "ServiceName is incorrect");
|
||||
SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, ptrRecord), "ReadRecord() failed");
|
||||
VerifyOrQuit(ptrRecord.GetTtl() == kTtl, "Read PTR is incorrect");
|
||||
|
||||
SuccessOrQuit(ptrRecord.ReadPtrName(*message, offset, name, sizeof(name)), "ReadName() failed");
|
||||
VerifyOrQuit(strcmp(name, instanceName) == 0, "Inst1 name is incorrect");
|
||||
|
||||
printf(" \"%s\" PTR %u %d \"%s\"\n", kServiceName, ptrRecord.GetTtl(), ptrRecord.GetLength(), name);
|
||||
}
|
||||
|
||||
VerifyOrQuit(offset == additionalSectionOffset, "offset is incorrect after answer section parse");
|
||||
|
||||
offset = answerSectionOffset;
|
||||
SuccessOrQuit(Dns::ResourceRecord::ParseRecords(*message, offset, kAnswerCount), "ParseRecords() failed");
|
||||
VerifyOrQuit(offset == additionalSectionOffset, "offset is incorrect after answer section parse");
|
||||
|
||||
printf("Use FindRecord() to find and iterate through all the records:\n");
|
||||
|
||||
offset = answerSectionOffset;
|
||||
numRecords = kAnswerCount;
|
||||
|
||||
while (numRecords > 0)
|
||||
{
|
||||
uint16_t prevNumRecords = numRecords;
|
||||
|
||||
SuccessOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, numRecords, kServiceName), "FindRecord failed");
|
||||
VerifyOrQuit(numRecords == prevNumRecords - 1, "Incorrect num records");
|
||||
SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, ptrRecord), "ReadRecord() failed");
|
||||
VerifyOrQuit(ptrRecord.GetTtl() == kTtl, "Read PTR is incorrect");
|
||||
SuccessOrQuit(ptrRecord.ReadPtrName(*message, offset, label, sizeof(label), name, sizeof(name)),
|
||||
"ReadName() failed");
|
||||
printf(" \"%s\" PTR %u %d inst:\"%s\" at \"%s\"\n", kServiceName, ptrRecord.GetTtl(), ptrRecord.GetLength(),
|
||||
label, name);
|
||||
}
|
||||
|
||||
VerifyOrQuit(offset == additionalSectionOffset, "offset is incorrect after answer section parse");
|
||||
VerifyOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, numRecords, kServiceName) == OT_ERROR_NOT_FOUND,
|
||||
"FindRecord did not fail with no records");
|
||||
|
||||
// Use `ReadRecord()` with a non-matching record type. Verify that it correct skips over the record.
|
||||
|
||||
offset = answerSectionOffset;
|
||||
numRecords = kAnswerCount;
|
||||
|
||||
while (numRecords > 0)
|
||||
{
|
||||
SuccessOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, numRecords, kServiceName), "FindRecord failed");
|
||||
VerifyOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, srvRecord) == OT_ERROR_NOT_FOUND,
|
||||
"ReadRecord() did not fail with non-matching type");
|
||||
}
|
||||
|
||||
VerifyOrQuit(offset == additionalSectionOffset, "offset is incorrect after answer section parse");
|
||||
|
||||
// Use `FindRecord` with a non-matching name. Verify that it correctly skips over all records.
|
||||
|
||||
offset = answerSectionOffset;
|
||||
numRecords = kAnswerCount;
|
||||
VerifyOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, numRecords, kInstance1Name) == OT_ERROR_NOT_FOUND,
|
||||
"FindRecord did not fail with non-matching name");
|
||||
VerifyOrQuit(numRecords == 0, "Incorrect num records");
|
||||
VerifyOrQuit(offset == additionalSectionOffset, "offset is incorrect after answer section parse");
|
||||
|
||||
printf("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \n");
|
||||
printf("Additional Section\n");
|
||||
|
||||
for (const char *instanceName : kInstanceNames)
|
||||
{
|
||||
// SRV record
|
||||
SuccessOrQuit(Dns::Name::CompareName(*message, offset, instanceName), "Instance is incorrect");
|
||||
SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, srvRecord), "ReadRecord() failed");
|
||||
VerifyOrQuit(srvRecord.GetTtl() == kTtl, "Read SRV is incorrect");
|
||||
VerifyOrQuit(srvRecord.GetPort() == kSrvPort, "Read SRV port is incorrect");
|
||||
VerifyOrQuit(srvRecord.GetWeight() == kSrvWeight, "Read SRV weight is incorrect");
|
||||
VerifyOrQuit(srvRecord.GetPriority() == kSrvPriority, "Read SRV priority is incorrect");
|
||||
SuccessOrQuit(srvRecord.ReadTargetHostName(*message, offset, name, sizeof(name)),
|
||||
"ReadTargetHostName() failed");
|
||||
VerifyOrQuit(strcmp(name, kHostName) == 0, "Inst1 name is incorrect");
|
||||
printf(" \"%s\" SRV %u %d %d %d %d \"%s\"\n", instanceName, srvRecord.GetTtl(), srvRecord.GetLength(),
|
||||
srvRecord.GetPort(), srvRecord.GetWeight(), srvRecord.GetPriority(), name);
|
||||
|
||||
// TXT record
|
||||
SuccessOrQuit(Dns::Name::CompareName(*message, offset, instanceName), "Instance is incorrect");
|
||||
SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, txtRecord), "ReadRecord() failed");
|
||||
VerifyOrQuit(txtRecord.GetTtl() == kTxtTtl, "Read TXT is incorrect");
|
||||
len = sizeof(buffer);
|
||||
SuccessOrQuit(txtRecord.ReadTxtData(*message, offset, buffer, len), "ReadTxtData() failed");
|
||||
VerifyOrQuit(len == sizeof(kTxtData), "TXT data length is not valid");
|
||||
VerifyOrQuit(memcmp(buffer, kTxtData, len) == 0, "TXT data is not valid");
|
||||
printf(" \"%s\" TXT %u %d \"%s\"\n", instanceName, txtRecord.GetTtl(), txtRecord.GetLength(),
|
||||
reinterpret_cast<const char *>(buffer));
|
||||
}
|
||||
|
||||
SuccessOrQuit(Dns::Name::CompareName(*message, offset, kHostName), "HostName is incorrect");
|
||||
SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, aaaaRecord), "ReadRecord() failed");
|
||||
VerifyOrQuit(aaaaRecord.GetTtl() == kTtl, "Read AAAA is incorrect");
|
||||
VerifyOrQuit(aaaaRecord.GetAddress() == hostAddress, "Read host address is incorrect");
|
||||
printf(" \"%s\" AAAA %u %d \"%s\"\n", kHostName, aaaaRecord.GetTtl(), aaaaRecord.GetLength(),
|
||||
aaaaRecord.GetAddress().ToString().AsCString());
|
||||
|
||||
VerifyOrQuit(offset == message->GetLength(), "offset is incorrect after additional section parse");
|
||||
|
||||
// Use `ParseRecords()` to parse all records
|
||||
offset = additionalSectionOffset;
|
||||
SuccessOrQuit(Dns::ResourceRecord::ParseRecords(*message, offset, kAdditionalCount), "ParseRecords() failed");
|
||||
VerifyOrQuit(offset == message->GetLength(), "offset is incorrect after additional section parse");
|
||||
|
||||
printf("Use FindRecord() to search for specific name:\n");
|
||||
|
||||
for (const char *instanceName : kInstanceNames)
|
||||
{
|
||||
offset = additionalSectionOffset;
|
||||
numRecords = kAdditionalCount;
|
||||
|
||||
SuccessOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, numRecords, instanceName), "FindRecord failed");
|
||||
SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, srvRecord), "ReadRecord() failed");
|
||||
SuccessOrQuit(Dns::Name::ParseName(*message, offset), "ParseName() failed");
|
||||
printf(" \"%s\" SRV %u %d %d %d %d\n", instanceName, srvRecord.GetTtl(), srvRecord.GetLength(),
|
||||
srvRecord.GetPort(), srvRecord.GetWeight(), srvRecord.GetPriority());
|
||||
|
||||
SuccessOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, numRecords, instanceName), "FindRecord failed");
|
||||
SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, txtRecord), "ReadRecord() failed");
|
||||
offset += txtRecord.GetLength();
|
||||
printf(" \"%s\" TXT %u %d\n", instanceName, txtRecord.GetTtl(), txtRecord.GetLength());
|
||||
|
||||
VerifyOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, numRecords, instanceName) == OT_ERROR_NOT_FOUND,
|
||||
"FindRecord() did not fail with no more records");
|
||||
|
||||
VerifyOrQuit(offset == message->GetLength(), "offset is incorrect after additional section parse");
|
||||
}
|
||||
|
||||
offset = additionalSectionOffset;
|
||||
numRecords = kAdditionalCount;
|
||||
SuccessOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, numRecords, kHostName), "FindRecord() failed");
|
||||
SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, record), "ReadRecord() failed");
|
||||
VerifyOrQuit(record.GetType() == Dns::ResourceRecord::kTypeAaaa, "Read record has incorrect type");
|
||||
offset += record.GetLength();
|
||||
VerifyOrQuit(offset == message->GetLength(), "offset is incorrect after additional section parse");
|
||||
|
||||
message->Free();
|
||||
testFreeInstance(instance);
|
||||
}
|
||||
|
||||
} // namespace ot
|
||||
|
||||
int main(void)
|
||||
{
|
||||
ot::TestDnsName();
|
||||
ot::TestDnsCompressedName();
|
||||
ot::TestHeaderAndResourceRecords();
|
||||
|
||||
printf("All tests passed\n");
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user