mirror of
https://github.com/espressif/openthread.git
synced 2026-08-13 06:07:48 +00:00
[dns-header] new helper method to find i-th record matching name and type (#6124)
This commit adds a new template helper method in `ResoureRecord` to search in a given message starting from a given offset and up to a maximum given number of records, for the i-th occurrence of a specific record (of given type with a matching record name). If found, it reads the record from the message into a given record object. This commit also updates the unit test `test_dns` to cover the behavior of newly added helper method.
This commit is contained in:
@@ -622,6 +622,65 @@ exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
otError ResourceRecord::FindRecord(const Message & aMessage,
|
||||
uint16_t & aOffset,
|
||||
uint16_t aNumRecords,
|
||||
uint16_t aIndex,
|
||||
const Name & aName,
|
||||
uint16_t aType,
|
||||
ResourceRecord &aRecord,
|
||||
uint16_t aMinRecordSize)
|
||||
{
|
||||
// This static method searches in `aMessage` starting from `aOffset`
|
||||
// up to maximum of `aNumRecords`, for the `(aIndex+1)`th
|
||||
// occurrence of a resource record of type `aType` with record name
|
||||
// matching `aName`. It also verifies that the record size is larger
|
||||
// than `aMinRecordSize`. If found, `aMinRecordSize` bytes from the
|
||||
// record are read and copied into `aRecord`. In this case `aOffset`
|
||||
// is updated to point to the last record byte read from the message
|
||||
// (so that the caller can read any remaining fields in the record
|
||||
// data).
|
||||
|
||||
otError error;
|
||||
uint16_t offset = aOffset;
|
||||
uint16_t recordOffset;
|
||||
|
||||
while (aNumRecords > 0)
|
||||
{
|
||||
SuccessOrExit(error = FindRecord(aMessage, offset, aNumRecords, aName));
|
||||
|
||||
// Save the offset to start of `ResourceRecord` fields.
|
||||
recordOffset = offset;
|
||||
|
||||
error = ReadRecord(aMessage, offset, aType, aRecord, aMinRecordSize);
|
||||
|
||||
if (error == OT_ERROR_NOT_FOUND)
|
||||
{
|
||||
// `ReadRecord()` already updates the `offset` to skip
|
||||
// over a non-matching record.
|
||||
continue;
|
||||
}
|
||||
|
||||
SuccessOrExit(error);
|
||||
|
||||
if (aIndex == 0)
|
||||
{
|
||||
aOffset = offset;
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
aIndex--;
|
||||
|
||||
// Skip over the record.
|
||||
offset = static_cast<uint16_t>(recordOffset + aRecord.GetSize());
|
||||
}
|
||||
|
||||
error = OT_ERROR_NOT_FOUND;
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
otError ResourceRecord::ReadRecord(const Message & aMessage,
|
||||
uint16_t & aOffset,
|
||||
uint16_t aType,
|
||||
|
||||
@@ -1200,6 +1200,50 @@ public:
|
||||
*/
|
||||
static otError FindRecord(const Message &aMessage, uint16_t &aOffset, uint16_t &aNumRecords, const Name &aName);
|
||||
|
||||
/**
|
||||
* This template static method searches in a message to find the i-th occurrence of resource records of specific
|
||||
* type with a given record name and if found, reads the record from the message.
|
||||
*
|
||||
* This method searches in @p aMessage starting from @p aOffset up to maximum of @p aNumRecords, for the
|
||||
* `(aIndex+1)`th occurrence of a resource record of `RecordType` with record name @p aName.
|
||||
*
|
||||
* On success (i.e., when a matching record is found and read from the message), @p aOffset is updated to point
|
||||
* to after the last byte read from the message and copied into @p aRecord. This allows the caller to read any
|
||||
* remaining fields in the record data.
|
||||
*
|
||||
* @tparam RecordType The resource record type (i.e., a sub-class of `ResourceRecord`).
|
||||
*
|
||||
* @param[in] aMessage The message to search within for matching 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 and only if a matching record is found, @p aOffset is updated to point to
|
||||
* the last read byte in the record (allowing caller to read any remaining fields in
|
||||
* the record data from the message).
|
||||
* @param[in] aNumRecords The maximum number of records to check (starting from @p aOffset).
|
||||
* @param[in] aIndex The matching record index to find. @p aIndex value of zero returns the first
|
||||
* matching record.
|
||||
* @param[in] aName The record name to match against.
|
||||
* @param[in] aRecord A reference to a record object to read a matching record into.
|
||||
* If a matching record is found, `sizeof(RecordType)` bytes from @p aMessage are
|
||||
* read and copied into @p aRecord.
|
||||
*
|
||||
* @retval OT_ERROR_NONE A matching record was found. @p aOffset is updated.
|
||||
* @retval OT_ERROR_NOT_FOUND A matching record could not be found.
|
||||
* @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 FindRecord(const Message &aMessage,
|
||||
uint16_t & aOffset,
|
||||
uint16_t aNumRecords,
|
||||
uint16_t aIndex,
|
||||
const Name & aName,
|
||||
RecordType & aRecord)
|
||||
{
|
||||
return FindRecord(aMessage, aOffset, aNumRecords, aIndex, aName, RecordType::kType, aRecord,
|
||||
sizeof(RecordType));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -1227,7 +1271,7 @@ public:
|
||||
* 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.
|
||||
* read 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.
|
||||
@@ -1255,6 +1299,15 @@ private:
|
||||
kType = kTypeAny, // This is intended for used by `ReadRecord()` only.
|
||||
};
|
||||
|
||||
static otError FindRecord(const Message & aMessage,
|
||||
uint16_t & aOffset,
|
||||
uint16_t aNumRecords,
|
||||
uint16_t aIndex,
|
||||
const Name & aName,
|
||||
uint16_t aType,
|
||||
ResourceRecord &aRecord,
|
||||
uint16_t aMinRecordSize);
|
||||
|
||||
static otError ReadRecord(const Message & aMessage,
|
||||
uint16_t & aOffset,
|
||||
uint16_t aType,
|
||||
|
||||
@@ -734,6 +734,7 @@ void TestHeaderAndResourceRecords(void)
|
||||
uint16_t hostNameOffset;
|
||||
uint16_t answerSectionOffset;
|
||||
uint16_t additionalSectionOffset;
|
||||
uint16_t index;
|
||||
Dns::PtrRecord ptrRecord;
|
||||
Dns::SrvRecord srvRecord;
|
||||
Dns::TxtRecord txtRecord;
|
||||
@@ -1020,6 +1021,81 @@ void TestHeaderAndResourceRecords(void)
|
||||
offset += record.GetLength();
|
||||
VerifyOrQuit(offset == message->GetLength(), "offset is incorrect after additional section parse");
|
||||
|
||||
printf("Use FindRecord() to search for specific records:\n");
|
||||
printf(" Answer Section\n");
|
||||
|
||||
for (index = 0; index < OT_ARRAY_LENGTH(kInstanceNames); index++)
|
||||
{
|
||||
offset = answerSectionOffset;
|
||||
SuccessOrQuit(
|
||||
Dns::ResourceRecord::FindRecord(*message, offset, kAnswerCount, index, Dns::Name(kServiceName), ptrRecord),
|
||||
"FindRecord() failed");
|
||||
|
||||
printf(" index:%d -> \"%s\" PTR %u %d\n", index, kServiceName, ptrRecord.GetTtl(), ptrRecord.GetLength());
|
||||
}
|
||||
|
||||
// Check `FindRecord()` failure with non-matching name, record type, or bad index.
|
||||
|
||||
offset = answerSectionOffset;
|
||||
VerifyOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, kAnswerCount, index, Dns::Name(kServiceName),
|
||||
ptrRecord) == OT_ERROR_NOT_FOUND,
|
||||
"FindRecord() did not fail with bad index");
|
||||
VerifyOrQuit(offset == answerSectionOffset, "FindRecord() changed offset on failure");
|
||||
|
||||
offset = answerSectionOffset;
|
||||
VerifyOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, kAnswerCount, index, Dns::Name(kInstance1Name),
|
||||
ptrRecord) == OT_ERROR_NOT_FOUND,
|
||||
"FindRecord() did not fail with bad index");
|
||||
VerifyOrQuit(offset == answerSectionOffset, "FindRecord() changed offset on failure");
|
||||
|
||||
offset = answerSectionOffset;
|
||||
VerifyOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, kAnswerCount, index, Dns::Name(kServiceName),
|
||||
txtRecord) == OT_ERROR_NOT_FOUND,
|
||||
"FindRecord() did not fail with bad index");
|
||||
VerifyOrQuit(offset == answerSectionOffset, "FindRecord() changed offset on failure");
|
||||
|
||||
printf(" Additional Section\n");
|
||||
|
||||
for (const char *instanceName : kInstanceNames)
|
||||
{
|
||||
// There is a single SRV and TXT entry for each instance
|
||||
offset = additionalSectionOffset;
|
||||
SuccessOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, kAdditionalCount, /* aIndex */ 0,
|
||||
Dns::Name(instanceName), srvRecord),
|
||||
"FindRecord() failed");
|
||||
printf(" \"%s\" SRV %u %d %d %d %d \n", instanceName, srvRecord.GetTtl(), srvRecord.GetLength(),
|
||||
srvRecord.GetPort(), srvRecord.GetWeight(), srvRecord.GetPriority());
|
||||
|
||||
offset = additionalSectionOffset;
|
||||
SuccessOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, kAdditionalCount, /* aIndex */ 0,
|
||||
Dns::Name(instanceName), txtRecord),
|
||||
"FindRecord() failed");
|
||||
printf(" \"%s\" TXT %u %d\n", instanceName, txtRecord.GetTtl(), txtRecord.GetLength());
|
||||
|
||||
offset = additionalSectionOffset;
|
||||
VerifyOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, kAdditionalCount, /* aIndex */ 1,
|
||||
Dns::Name(instanceName), srvRecord) == OT_ERROR_NOT_FOUND,
|
||||
"FindRecord() did not fail with bad index");
|
||||
|
||||
offset = additionalSectionOffset;
|
||||
VerifyOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, kAdditionalCount, /* aIndex */ 1,
|
||||
Dns::Name(instanceName), txtRecord) == OT_ERROR_NOT_FOUND,
|
||||
"FindRecord() did not fail with bad index");
|
||||
}
|
||||
|
||||
for (index = 0; index < kAdditionalCount; index++)
|
||||
{
|
||||
offset = additionalSectionOffset;
|
||||
// Find record with empty name (matching any) and any type.
|
||||
SuccessOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, kAdditionalCount, index, Dns::Name(), record),
|
||||
"FindRecord() failed");
|
||||
}
|
||||
|
||||
offset = additionalSectionOffset;
|
||||
VerifyOrQuit(Dns::ResourceRecord::FindRecord(*message, offset, kAdditionalCount, index, Dns::Name(), record) ==
|
||||
OT_ERROR_NOT_FOUND,
|
||||
"FindRecord() did not fail with bad index");
|
||||
|
||||
message->Free();
|
||||
testFreeInstance(instance);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user