mirror of
https://github.com/espressif/openthread.git
synced 2026-08-09 04:07:47 +00:00
[dns] introduce DecompressRecordData() and use it in mDNS (#11408)
This commit updates the mDNS `RecordQuerier` to handle record types where the RDATA contains one or more potentially compressed DNS names. For these types, the reported record data is now decompressed to include the full DNS names. This enhancement applies to the following record types: NS, CNAME, SOA, PTR, MX, RP, AFSDB, RT, PX, SRV, KX, DNAME, and NSEC. To achieve this, a helper `ResourceRecord::DecompressRecordData()` method is introduced. This method uses a "recipe" formula specific to each supported record type. The recipe defines the number of prefix bytes before the first embedded name, the number of DNS names, and the minimum number of suffix bytes after the names. A common implementation then uses this recipe to parse and decompress the RDATA. This approach makes the implementation flexible and allows for easier addition of new record types and formats in the future. Unit test `test_dns` is updated to validate the newly added method.
This commit is contained in:
@@ -52,7 +52,7 @@ extern "C" {
|
||||
*
|
||||
* @note This number versions both OpenThread platform and user APIs.
|
||||
*/
|
||||
#define OPENTHREAD_API_VERSION (497)
|
||||
#define OPENTHREAD_API_VERSION (498)
|
||||
|
||||
/**
|
||||
* @addtogroup api-instance
|
||||
|
||||
@@ -836,10 +836,16 @@ otError otMdnsStopIp4AddressResolver(otInstance *aInstance, const otMdnsAddressR
|
||||
* MUST NOT include the domain name. The reason for a separate first label is to allow it to include a dot `.`
|
||||
* character (as allowed for service instance labels).
|
||||
*
|
||||
* Discovered results are reported through the `mCallback` function in @p aQuerier, providing the raw record
|
||||
* data bytes. A removed record data is indicated with a TTL value of zero. The callback may be invoked immediately
|
||||
* with cached information (if available) and potentially before this function returns. When cached results are used,
|
||||
* the reported TTL value will reflect the original TTL from the last received response.
|
||||
* Discovered results are reported through the `mCallback` function in @p aQuerier, providing the record data bytes
|
||||
* (RDATA). For NS, CNAME, SOA, PTR, MX, RP, AFSDB, RT, PX, SRV, KX, DNAME, and NSEC record types, the RDATA format
|
||||
* contains one or more DNS names (which may use DNS name compression). For the above list, the reported record data
|
||||
* bytes via @p mCallback will be decompressed to contain the full DNS name(s). For all other record types, the record
|
||||
* data bytes are provided exactly as they appear in the received mDNS response. This aligns the implementation with
|
||||
* RFC 6762 (section 18.14) regarding the use of name compression.
|
||||
*
|
||||
* A removed record data is indicated with a TTL value of zero. The callback may be invoked immediately with cached
|
||||
* information (if available) and potentially before this function returns. When cached results are used, the reported
|
||||
* TTL value will reflect the original TTL from the last received response.
|
||||
*
|
||||
* Multiple querier instances can be started for the same name, provided they use different callback functions.
|
||||
*
|
||||
|
||||
@@ -1036,6 +1036,101 @@ exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
Error ResourceRecord::DecompressRecordData(const Message &aMessage, uint16_t aOffset, OwnedPtr<Message> &aDataMsg)
|
||||
{
|
||||
// Reads the `ResourceRecord` header to identify the record type
|
||||
// and uses a predefined recipe to parse the record data.
|
||||
|
||||
struct DataRecipe
|
||||
{
|
||||
int Compare(uint16_t aRecordType) const { return (aRecordType - mRecordType); }
|
||||
|
||||
constexpr static bool AreInOrder(const DataRecipe &aFirst, const DataRecipe &aSecond)
|
||||
{
|
||||
return (aFirst.mRecordType < aSecond.mRecordType);
|
||||
}
|
||||
|
||||
uint16_t mRecordType; // The record type.
|
||||
uint8_t mNumPrefixBytes; // Number of bytes in RDATA before the first name.
|
||||
uint8_t mNumNames; // Number of DNS names embedded in the RDATA.
|
||||
uint16_t mMinNumSuffixBytes; // Minimum number of expected bytes in RDATA after the last name.
|
||||
};
|
||||
|
||||
static constexpr DataRecipe kRecipes[] = {
|
||||
{kTypeNs, 0, 1, 0},
|
||||
{kTypeCname, 0, 1, 0},
|
||||
{kTypeSoa, 0, 2, 5 * sizeof(uint32_t)}, // mname, rname, followed by five 32-bit values.
|
||||
{kTypePtr, 0, 1, 0},
|
||||
{kTypeMx, sizeof(uint16_t), 1, 0}, // `preference` 16-bit field, exchange name [RFC 1035]
|
||||
{kTypeRp, 0, 2, 0}, /// `mbox-dname` `txt-dname` [RFC 1183]
|
||||
{kTypeAfsdb, sizeof(uint16_t), 1, 0}, // `sub-type` 16-bit field, host name [RFC 1183]
|
||||
{kTypeRt, sizeof(uint16_t), 1, 0}, // `preference` 16-bit field, host name [RFC 1183]
|
||||
{kTypePx, sizeof(uint16_t), 2, 0}, // `preference` 16-bit field, two names [RFC 2163]
|
||||
{kTypeSrv, sizeof(SrvRecord) - sizeof(ResourceRecord), 1, 0},
|
||||
{kTypeKx, sizeof(uint16_t), 1, 0}, // `preference` 16-bit field, name [RFC 2230]
|
||||
{kTypeDname, 0, 1, 0},
|
||||
{kTypeNsec, 0, 1, NsecRecord::TypeBitMap::kMinSize},
|
||||
};
|
||||
|
||||
static_assert(BinarySearch::IsSorted(kRecipes), "kRecipes is not sorted");
|
||||
|
||||
Error error;
|
||||
ResourceRecord record;
|
||||
const DataRecipe *recipe;
|
||||
uint16_t startOffset;
|
||||
uint16_t remainingLength;
|
||||
|
||||
SuccessOrExit(error = record.ReadFrom(aMessage, aOffset));
|
||||
aOffset += sizeof(ResourceRecord);
|
||||
|
||||
recipe = BinarySearch::Find(record.GetType(), kRecipes);
|
||||
|
||||
if (recipe == nullptr)
|
||||
{
|
||||
aDataMsg.Free();
|
||||
error = kErrorNone;
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
aDataMsg.Reset(aMessage.Get<MessagePool>().Allocate(Message::kTypeOther));
|
||||
VerifyOrExit(!aDataMsg.IsNull(), error = kErrorNoBufs);
|
||||
|
||||
startOffset = aOffset;
|
||||
|
||||
// Check and copy the prefix bytes in the record data.
|
||||
|
||||
VerifyOrExit(record.GetLength() >= recipe->mNumPrefixBytes, error = kErrorParse);
|
||||
SuccessOrExit(error = aDataMsg->AppendBytesFromMessage(aMessage, aOffset, recipe->mNumPrefixBytes));
|
||||
aOffset += recipe->mNumPrefixBytes;
|
||||
|
||||
// Read and decompress embedded DNS names in the record data.
|
||||
|
||||
for (uint8_t numNames = 0; numNames < recipe->mNumNames; numNames++)
|
||||
{
|
||||
Name name(aMessage, aOffset);
|
||||
|
||||
// ParseName() updates `aOffset` to point to the byte after
|
||||
// the end of name field.
|
||||
|
||||
SuccessOrExit(error = Name::ParseName(aMessage, aOffset));
|
||||
SuccessOrExit(error = name.AppendTo(*aDataMsg));
|
||||
}
|
||||
|
||||
// Determine the remaining length after the names in the record
|
||||
// data. Ensure we have at least `mMinNumSuffixBytes` and copy
|
||||
// them into `aDataMsg`.
|
||||
|
||||
VerifyOrExit(aOffset - startOffset <= record.GetLength(), error = kErrorParse);
|
||||
remainingLength = record.GetLength() - (aOffset - startOffset);
|
||||
|
||||
VerifyOrExit(remainingLength >= recipe->mMinNumSuffixBytes, error = kErrorParse);
|
||||
|
||||
SuccessOrExit(error = aDataMsg->AppendBytesFromMessage(aMessage, aOffset, remainingLength));
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
void TxtEntry::Iterator::Init(const uint8_t *aTxtData, uint16_t aTxtDataLength)
|
||||
{
|
||||
SetTxtData(aTxtData);
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "common/encoding.hpp"
|
||||
#include "common/equatable.hpp"
|
||||
#include "common/message.hpp"
|
||||
#include "common/owned_ptr.hpp"
|
||||
#include "crypto/ecdsa.hpp"
|
||||
#include "net/ip4_types.hpp"
|
||||
#include "net/ip6_address.hpp"
|
||||
@@ -1268,14 +1269,20 @@ public:
|
||||
static constexpr uint16_t kTypeZero = 0; ///< Zero as special indicator for the SIG RR (SIG(0) from RFC 2931).
|
||||
static constexpr uint16_t kTypeA = 1; ///< Address record (IPv4).
|
||||
static constexpr uint16_t kTypeNs = 2; ///< NS record (an authoritative name server).
|
||||
static constexpr uint16_t kTypeSoa = 6; ///< Start of (zone of) authority.
|
||||
static constexpr uint16_t kTypeCname = 5; ///< CNAME record.
|
||||
static constexpr uint16_t kTypeSoa = 6; ///< SOA record (start of (zone of) authority).
|
||||
static constexpr uint16_t kTypePtr = 12; ///< PTR record.
|
||||
static constexpr uint16_t kTypeMx = 15; ///< MAX record (mail exchange).
|
||||
static constexpr uint16_t kTypeTxt = 16; ///< TXT record.
|
||||
static constexpr uint16_t kTypeRp = 17; ///< RP record (Responsible Person).
|
||||
static constexpr uint16_t kTypeAfsdb = 18; ///< AFSDB record (AFS Data Base location).
|
||||
static constexpr uint16_t kTypeRt = 21; ///< RT record (Route Through).
|
||||
static constexpr uint16_t kTypeSig = 24; ///< SIG record.
|
||||
static constexpr uint16_t kTypeKey = 25; ///< KEY record.
|
||||
static constexpr uint16_t kTypePx = 26; ///< PX record (X.400 mail mapping information).
|
||||
static constexpr uint16_t kTypeAaaa = 28; ///< IPv6 address record.
|
||||
static constexpr uint16_t kTypeSrv = 33; ///< SRV locator record.
|
||||
static constexpr uint16_t kTypeKx = 36; ///< KX record (Key Exchanger).
|
||||
static constexpr uint16_t kTypeDname = 39; ///< DNAME record.
|
||||
static constexpr uint16_t kTypeOpt = 41; ///< Option record.
|
||||
static constexpr uint16_t kTypeNsec = 47; ///< NSEC record.
|
||||
@@ -1506,6 +1513,38 @@ public:
|
||||
return ReadRecord(aMessage, aOffset, RecordType::kType, aRecord, sizeof(RecordType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses and decompresses record data for specific record types where the record data format can contain one or
|
||||
* more compressed DNS names.
|
||||
*
|
||||
* The following record types are handled: NS, CNAME, SOA, PTR, MX, RP, AFSDB, RT, PX, SRV, KX, DNAME, and NSEC.
|
||||
*
|
||||
* If the record type is not in the list above, this method returns `kErrorNone`, with @p aDataMsg remaining
|
||||
* as `nullptr`.
|
||||
*
|
||||
* If the record type is in the above list (requires decompression) and is processed successfully, a new message
|
||||
* is allocated where the decompressed record data is placed. The allocated message is returned via @p aDataMsg
|
||||
* (its ownership is passed to the caller as indicated by the use of `OwnedPtr`). The allocated message contains
|
||||
* the decompressed record data. Importantly it does not include the `ResourceRecord` header.
|
||||
*
|
||||
* When decompressing the record data, this method ensures any embedded DNS names are well-formed and parsable.
|
||||
* It also verifies the record data meets the expected data format (e.g., minimum length based on the record type).
|
||||
* Any additional bytes beyond the expected record data format are also copied to `aDataMsg`. No deep semantic
|
||||
* validation of the record data content is performed.
|
||||
*
|
||||
* @param[in] aMessage The message to read from. `aMessage.GetOffset()` MUST point to the start of DNS header
|
||||
* (this is used to handle compressed names).
|
||||
* @param[in] aOffset The offset in @p aMessage pointing to the byte after the record name and the start of
|
||||
* `ResourceRecord` fields.
|
||||
* @param[out] aDataMsg A reference to an `OwnedPtr<Message>` to output the allocated message containing the
|
||||
* decompressed record data. On input, it should be set to `nullptr`.
|
||||
*
|
||||
* @retval kErrorNone The record type did not require decompression, or decompression was successful.
|
||||
* @retval kErrorNoBufs Failed to allocate a buffer to return the decompressed data.
|
||||
* @retval kErrorParse The record data format is invalid.
|
||||
*/
|
||||
static Error DecompressRecordData(const Message &aMessage, uint16_t aOffset, OwnedPtr<Message> &aDataMsg);
|
||||
|
||||
protected:
|
||||
Error ReadName(const Message &aMessage,
|
||||
uint16_t &aOffset,
|
||||
|
||||
+25
-7
@@ -7025,15 +7025,30 @@ void Core::RecordCache::ProcessResponseRecord(const Message &aMessage,
|
||||
{
|
||||
// Name and record type in `aMessage` are already matched.
|
||||
|
||||
// Adds a new record data to `mNewEntries` list. This called as
|
||||
// the records in a received response are processed one by one.
|
||||
// Once all records are processed `CommitNewResponseEntries()` is
|
||||
// called to update the list.
|
||||
// First, checks if the record data needs to be decompressed
|
||||
// (the record data format can contain one or more compressed DNS
|
||||
// names). This check applies to records: NS, CNAME, SOA, PTR,
|
||||
// MX, RP, AFSDB, RT, PX, SRV, KX, DNAME, and NSEC.
|
||||
//
|
||||
// Then, adds the new record data to the `mNewEntries` list. This
|
||||
// step occurs as the records in a received response are
|
||||
// processed one by one. Once all records are processed,
|
||||
// `CommitNewResponseEntries()` is called to update the list.
|
||||
|
||||
Heap::Data data;
|
||||
NewRecordEntry *entry;
|
||||
OwnedPtr<Message> dataMsg;
|
||||
Heap::Data data;
|
||||
NewRecordEntry *entry;
|
||||
|
||||
SuccessOrAssert(data.SetFrom(aMessage, aRecordOffset + sizeof(ResourceRecord), aRecord.GetLength()));
|
||||
SuccessOrExit(ResourceRecord::DecompressRecordData(aMessage, aRecordOffset, dataMsg));
|
||||
|
||||
if (dataMsg != nullptr)
|
||||
{
|
||||
SuccessOrAssert(data.SetFrom(*dataMsg));
|
||||
}
|
||||
else
|
||||
{
|
||||
SuccessOrAssert(data.SetFrom(aMessage, aRecordOffset + sizeof(ResourceRecord), aRecord.GetLength()));
|
||||
}
|
||||
|
||||
// Check for duplicates in the same response. If there
|
||||
// are exact duplicates, we remember the last one in the
|
||||
@@ -7053,6 +7068,9 @@ void Core::RecordCache::ProcessResponseRecord(const Message &aMessage,
|
||||
|
||||
mNewEntries.Push(*entry);
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void Core::RecordCache::CommitNewResponseEntries(void)
|
||||
|
||||
+93
-25
@@ -1122,7 +1122,7 @@ void TestHeaderAndResourceRecords(void)
|
||||
kHeaderOffset = 0,
|
||||
kQuestionCount = 1,
|
||||
kAnswerCount = 2,
|
||||
kAdditionalCount = 5,
|
||||
kAdditionalCount = 6,
|
||||
kTtl = 7200,
|
||||
kTxtTtl = 7300,
|
||||
kSrvPort = 1234,
|
||||
@@ -1146,30 +1146,34 @@ void TestHeaderAndResourceRecords(void)
|
||||
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;
|
||||
uint16_t index;
|
||||
Dns::PtrRecord ptrRecord;
|
||||
Dns::SrvRecord srvRecord;
|
||||
Dns::TxtRecord txtRecord;
|
||||
Dns::AaaaRecord aaaaRecord;
|
||||
Dns::ResourceRecord record;
|
||||
Ip6::Address hostAddress;
|
||||
|
||||
Dns::Name::LabelBuffer label;
|
||||
Dns::Name::Buffer name;
|
||||
uint8_t buffer[kMaxSize];
|
||||
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;
|
||||
uint16_t index;
|
||||
Dns::PtrRecord ptrRecord;
|
||||
Dns::SrvRecord srvRecord;
|
||||
Dns::TxtRecord txtRecord;
|
||||
Dns::AaaaRecord aaaaRecord;
|
||||
Dns::NsecRecord nsecRecord;
|
||||
Dns::NsecRecord::TypeBitMap nsecBitmap;
|
||||
Dns::ResourceRecord record;
|
||||
Ip6::Address hostAddress;
|
||||
Dns::Name::LabelBuffer label;
|
||||
Dns::Name::Buffer name;
|
||||
uint8_t buffer[kMaxSize];
|
||||
uint8_t *bytes;
|
||||
OwnedPtr<Message> dataMsg;
|
||||
uint16_t dataOffset;
|
||||
|
||||
printf("================================================================\n");
|
||||
printf("TestHeaderAndResourceRecords()\n");
|
||||
@@ -1259,6 +1263,19 @@ void TestHeaderAndResourceRecords(void)
|
||||
aaaaRecord.SetAddress(hostAddress);
|
||||
SuccessOrQuit(message->Append(aaaaRecord));
|
||||
|
||||
nsecRecord.Init();
|
||||
nsecRecord.SetTtl(kTtl);
|
||||
nsecBitmap.Clear();
|
||||
nsecBitmap.AddType(Dns::ResourceRecord::kTypeAaaa);
|
||||
|
||||
SuccessOrQuit(Dns::Name::AppendPointerLabel(hostNameOffset, *message));
|
||||
offset = message->GetLength();
|
||||
SuccessOrQuit(message->Append(nsecRecord));
|
||||
SuccessOrQuit(Dns::Name::AppendPointerLabel(hostNameOffset, *message));
|
||||
SuccessOrQuit(message->AppendBytes(&nsecBitmap, nsecBitmap.GetSize()));
|
||||
nsecRecord.SetLength(message->GetLength() - offset - sizeof(Dns::ResourceRecord));
|
||||
message->Write(offset, nsecRecord);
|
||||
|
||||
// Dump the entire message
|
||||
|
||||
VerifyOrQuit(message->GetLength() < kMaxSize, "Message is too long");
|
||||
@@ -1301,6 +1318,13 @@ void TestHeaderAndResourceRecords(void)
|
||||
for (const char *instanceLabel : kInstanceLabels)
|
||||
{
|
||||
SuccessOrQuit(Dns::Name::CompareName(*message, offset, kServiceName));
|
||||
|
||||
SuccessOrQuit(Dns::ResourceRecord::DecompressRecordData(*message, offset, dataMsg));
|
||||
VerifyOrQuit(dataMsg != nullptr);
|
||||
dataOffset = 0;
|
||||
VerifyOrQuit(Dns::Name::CompareName(*dataMsg, dataOffset, kServiceName));
|
||||
VerifyOrQuit(dataOffset == dataMsg->GetLength());
|
||||
|
||||
SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, ptrRecord));
|
||||
VerifyOrQuit(ptrRecord.GetTtl() == kTtl, "Read PTR is incorrect");
|
||||
|
||||
@@ -1374,6 +1398,7 @@ void TestHeaderAndResourceRecords(void)
|
||||
|
||||
// SRV record
|
||||
SuccessOrQuit(Dns::Name::CompareName(*message, offset, instanceName));
|
||||
savedOffset = offset;
|
||||
SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, srvRecord));
|
||||
VerifyOrQuit(srvRecord.GetTtl() == kTtl);
|
||||
VerifyOrQuit(srvRecord.GetPort() == kSrvPort);
|
||||
@@ -1384,8 +1409,24 @@ void TestHeaderAndResourceRecords(void)
|
||||
printf(" \"%s\" SRV %u %d %d %d %d \"%s\"\n", instanceName, srvRecord.GetTtl(), srvRecord.GetLength(),
|
||||
srvRecord.GetPort(), srvRecord.GetWeight(), srvRecord.GetPriority(), name);
|
||||
|
||||
// SRV record again using `DecompressRecordData()`
|
||||
dataMsg.Free();
|
||||
SuccessOrQuit(Dns::ResourceRecord::DecompressRecordData(*message, savedOffset, dataMsg));
|
||||
VerifyOrQuit(dataMsg != nullptr);
|
||||
dataOffset = 0;
|
||||
len = sizeof(Dns::SrvRecord) - sizeof(Dns::ResourceRecord);
|
||||
bytes = reinterpret_cast<uint8_t *>(&srvRecord);
|
||||
bytes += sizeof(Dns::ResourceRecord);
|
||||
VerifyOrQuit(dataMsg->CompareBytes(dataOffset, bytes, len));
|
||||
dataOffset += len;
|
||||
SuccessOrQuit(Dns::Name::CompareName(*dataMsg, dataOffset, kHostName));
|
||||
VerifyOrQuit(dataOffset == dataMsg->GetLength());
|
||||
|
||||
// TXT record
|
||||
SuccessOrQuit(Dns::Name::CompareName(*message, offset, instanceName));
|
||||
dataMsg.Free();
|
||||
SuccessOrQuit(Dns::ResourceRecord::DecompressRecordData(*message, offset, dataMsg));
|
||||
VerifyOrQuit(dataMsg == nullptr);
|
||||
SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, txtRecord));
|
||||
VerifyOrQuit(txtRecord.GetTtl() == kTxtTtl);
|
||||
savedOffset = offset;
|
||||
@@ -1405,13 +1446,34 @@ void TestHeaderAndResourceRecords(void)
|
||||
VerifyOrQuit(savedOffset == offset);
|
||||
}
|
||||
|
||||
// AAAA record
|
||||
SuccessOrQuit(Dns::Name::CompareName(*message, offset, kHostName));
|
||||
dataMsg.Free();
|
||||
SuccessOrQuit(Dns::ResourceRecord::DecompressRecordData(*message, offset, dataMsg));
|
||||
VerifyOrQuit(dataMsg == nullptr);
|
||||
SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, aaaaRecord));
|
||||
VerifyOrQuit(aaaaRecord.GetTtl() == kTtl);
|
||||
VerifyOrQuit(aaaaRecord.GetAddress() == hostAddress);
|
||||
printf(" \"%s\" AAAA %u %d \"%s\"\n", kHostName, aaaaRecord.GetTtl(), aaaaRecord.GetLength(),
|
||||
aaaaRecord.GetAddress().ToString().AsCString());
|
||||
|
||||
// NSEC record
|
||||
SuccessOrQuit(Dns::Name::CompareName(*message, offset, kHostName));
|
||||
dataMsg.Free();
|
||||
SuccessOrQuit(Dns::ResourceRecord::DecompressRecordData(*message, offset, dataMsg));
|
||||
VerifyOrQuit(dataMsg != nullptr);
|
||||
dataOffset = 0;
|
||||
SuccessOrQuit(Dns::Name::CompareName(*dataMsg, dataOffset, kHostName));
|
||||
VerifyOrQuit(dataMsg->CompareBytes(dataOffset, &nsecBitmap, nsecBitmap.GetSize()));
|
||||
dataOffset += nsecBitmap.GetSize();
|
||||
VerifyOrQuit(dataOffset == dataMsg->GetLength());
|
||||
SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, record));
|
||||
VerifyOrQuit(record.GetType() == Dns::ResourceRecord::kTypeNsec);
|
||||
VerifyOrQuit(nsecBitmap.ContainsType(Dns::ResourceRecord::kTypeAaaa));
|
||||
printf(" \"%s\" NSEC %u %d bitmap-size:%d\n", kHostName, record.GetTtl(), record.GetLength(),
|
||||
nsecBitmap.GetSize());
|
||||
offset += record.GetLength();
|
||||
|
||||
VerifyOrQuit(offset == message->GetLength(), "offset is incorrect after additional section parse");
|
||||
|
||||
// Use `ParseRecords()` to parse all records
|
||||
@@ -1451,6 +1513,12 @@ void TestHeaderAndResourceRecords(void)
|
||||
SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, record));
|
||||
VerifyOrQuit(record.GetType() == Dns::ResourceRecord::kTypeAaaa);
|
||||
offset += record.GetLength();
|
||||
|
||||
SuccessOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, numRecords, Dns::Name(kHostName)));
|
||||
SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, record));
|
||||
VerifyOrQuit(record.GetType() == Dns::ResourceRecord::kTypeNsec);
|
||||
offset += record.GetLength();
|
||||
|
||||
VerifyOrQuit(offset == message->GetLength(), "offset is incorrect after additional section parse");
|
||||
|
||||
printf("Use FindRecord() to search for specific records:\n");
|
||||
|
||||
Reference in New Issue
Block a user