[dns] allow partial read of TXT data and add config for max size in CLI (#8232)

This commit updates `Dns::Client` so that when reading service info
`otDnsServiceInfo` if the received TXT data does not fit in the given
buffer, it is read partially. A new field `mTxtDataTruncated` is added
in `otDnsServiceInfo` to indicate that TXT data was not fully read.

This commit also updates `dns` related commands in CLI so to output
the partially read TXT data and add a new config specifying the max
TXT data size used by CLI.
This commit is contained in:
Abtin Keshavarzian
2022-10-05 15:10:28 -07:00
committed by GitHub
parent c5b4cd7bbc
commit d3e00fbd28
9 changed files with 72 additions and 18 deletions
+4 -1
View File
@@ -323,7 +323,8 @@ typedef struct otDnsServiceInfo
otIp6Address mHostAddress; ///< The host IPv6 address. Set to all zero if not available.
uint32_t mHostAddressTtl; ///< The host address TTL.
uint8_t * mTxtData; ///< Buffer to output TXT data (can be NULL if not needed).
uint16_t mTxtDataSize; ///< On input, size of `mTxtData` buffer. On output `mTxtData` length.
uint16_t mTxtDataSize; ///< On input, size of `mTxtData` buffer. On output number bytes written.
bool mTxtDataTruncated; ///< Indicates if TXT data could not fit in `mTxtDataSize` and was truncated.
uint32_t mTxtDataTtl; ///< The TXT data TTL.
} otDnsServiceInfo;
@@ -408,6 +409,7 @@ otError otDnsBrowseResponseGetServiceInstance(const otDnsBrowseResponse *aRespon
* - If no matching SRV record is found in @p aResponse, `OT_ERROR_NOT_FOUND` is returned.
* - If a matching SRV record is found in @p aResponse, @p aServiceInfo is updated and `OT_ERROR_NONE` is returned.
* - If no matching TXT record is found in @p aResponse, `mTxtDataSize` in @p aServiceInfo is set to zero.
* - If TXT data length is greater than `mTxtDataSize`, it is read partially and `mTxtDataTruncated` is set to true.
* - If no matching AAAA record is found in @p aResponse, `mHostAddress is set to all zero or unspecified address.
* - If there are multiple AAAA records for the host name in @p aResponse, `mHostAddress` is set to the first one. The
* other addresses can be retrieved using `otDnsBrowseResponseGetHostAddress()`.
@@ -537,6 +539,7 @@ otError otDnsServiceResponseGetServiceName(const otDnsServiceResponse *aResponse
* - If no matching SRV record is found in @p aResponse, `OT_ERROR_NOT_FOUND` is returned.
* - If a matching SRV record is found in @p aResponse, @p aServiceInfo is updated and `OT_ERROR_NONE` is returned.
* - If no matching TXT record is found in @p aResponse, `mTxtDataSize` in @p aServiceInfo is set to zero.
* - If TXT data length is greater than `mTxtDataSize`, it is read partially and `mTxtDataTruncated` is set to true.
* - If no matching AAAA record is found in @p aResponse, `mHostAddress is set to all zero or unspecified address.
* - If there are multiple AAAA records for the host name in @p aResponse, `mHostAddress` is set to the first one. The
* other addresses can be retrieved using `otDnsServiceResponseGetHostAddress()`.
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (247)
#define OPENTHREAD_API_VERSION (248)
/**
* @addtogroup api-instance
+14 -3
View File
@@ -3023,7 +3023,18 @@ void Interpreter::OutputDnsServiceInfo(uint8_t aIndentSize, const otDnsServiceIn
OutputIp6Address(aServiceInfo.mHostAddress);
OutputLine(" TTL:%u", aServiceInfo.mHostAddressTtl);
OutputFormat(aIndentSize, "TXT:");
OutputDnsTxtData(aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize);
if (!aServiceInfo.mTxtDataTruncated)
{
OutputDnsTxtData(aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize);
}
else
{
OutputFormat("[");
OutputBytes(aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize);
OutputFormat("...]");
}
OutputLine(" TTL:%u", aServiceInfo.mTxtDataTtl);
}
@@ -3036,7 +3047,7 @@ void Interpreter::HandleDnsBrowseResponse(otError aError, const otDnsBrowseRespo
{
char name[OT_DNS_MAX_NAME_SIZE];
char label[OT_DNS_MAX_LABEL_SIZE];
uint8_t txtBuffer[255];
uint8_t txtBuffer[kMaxTxtDataSize];
otDnsServiceInfo serviceInfo;
IgnoreError(otDnsBrowseResponseGetServiceName(aResponse, name, sizeof(name)));
@@ -3078,7 +3089,7 @@ void Interpreter::HandleDnsServiceResponse(otError aError, const otDnsServiceRes
{
char name[OT_DNS_MAX_NAME_SIZE];
char label[OT_DNS_MAX_LABEL_SIZE];
uint8_t txtBuffer[255];
uint8_t txtBuffer[kMaxTxtDataSize];
otDnsServiceInfo serviceInfo;
IgnoreError(otDnsServiceResponseGetServiceName(aResponse, label, sizeof(label), name, sizeof(name)));
+2
View File
@@ -271,6 +271,8 @@ private:
static constexpr uint32_t kNetworkDiagnosticTimeoutMsecs = 5000;
static constexpr uint32_t kLocateTimeoutMsecs = 2500;
static constexpr uint16_t kMaxTxtDataSize = OPENTHREAD_CONFIG_CLI_TXT_RECORD_MAX_SIZE;
using Command = CommandEntry<Interpreter>;
template <typename ValueType> using GetHandler = ValueType (&)(otInstance *);
+13
View File
@@ -126,4 +126,17 @@
#define OPENTHREAD_CONFIG_CLI_PROMPT_ENABLE 1
#endif
/**
* @def OPENTHREAD_CONFIG_CLI_TXT_RECORD_MAX_SIZE
*
* Specifies the max TXT record data length to use when performing DNS queries.
*
* If the service TXT record data length is greater than the specified value, it will be read partially (up to the given
* size) and output as a sequence of raw hex bytes `[{hex-bytes}...]`
*
*/
#ifndef OPENTHREAD_CONFIG_CLI_TXT_RECORD_MAX_SIZE
#define OPENTHREAD_CONFIG_CLI_TXT_RECORD_MAX_SIZE 512
#endif
#endif // CONFIG_CLI_H_
+16 -6
View File
@@ -269,11 +269,10 @@ Error Client::Response::FindServiceInfo(Section aSection, const Name &aName, Ser
{
AsCoreType(&aServiceInfo.mHostAddress).Clear();
aServiceInfo.mHostAddressTtl = 0;
error = kErrorNone;
}
else
{
SuccessOrExit(error);
}
SuccessOrExit(error);
// A null `mTxtData` indicates that caller does not want to retrieve TXT data.
VerifyOrExit(aServiceInfo.mTxtData != nullptr);
@@ -282,19 +281,30 @@ Error Client::Response::FindServiceInfo(Section aSection, const Name &aName, Ser
// setting `aServiceInfo.mTxtDataSize` to zero.
SelectSection(aSection, offset, numRecords);
aServiceInfo.mTxtDataTruncated = false;
error = ResourceRecord::FindRecord(*mMessage, offset, numRecords, /* aIndex */ 0, aName, txtRecord);
switch (error)
{
case kErrorNone:
SuccessOrExit(error =
txtRecord.ReadTxtData(*mMessage, offset, aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize));
error = txtRecord.ReadTxtData(*mMessage, offset, aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize);
if (error == kErrorNoBufs)
{
error = kErrorNone;
aServiceInfo.mTxtDataTruncated = true;
}
SuccessOrExit(error);
aServiceInfo.mTxtDataTtl = txtRecord.GetTtl();
break;
case kErrorNotFound:
aServiceInfo.mTxtDataSize = 0;
aServiceInfo.mTxtDataTtl = 0;
error = kErrorNone;
break;
default:
+5 -4
View File
@@ -1136,12 +1136,13 @@ Error TxtRecord::ReadTxtData(const Message &aMessage,
{
Error error = kErrorNone;
VerifyOrExit(GetLength() <= aTxtBufferSize, error = kErrorNoBufs);
SuccessOrExit(error = aMessage.Read(aOffset, aTxtBuffer, GetLength()));
VerifyOrExit(VerifyTxtData(aTxtBuffer, GetLength(), /* aAllowEmpty */ true), error = kErrorParse);
aTxtBufferSize = GetLength();
SuccessOrExit(error = aMessage.Read(aOffset, aTxtBuffer, Min(GetLength(), aTxtBufferSize)));
aOffset += GetLength();
VerifyOrExit(GetLength() <= aTxtBufferSize, error = kErrorNoBufs);
aTxtBufferSize = GetLength();
VerifyOrExit(VerifyTxtData(aTxtBuffer, aTxtBufferSize, /* aAllowEmpty */ true), error = kErrorParse);
exit:
return error;
}
+5 -2
View File
@@ -1692,7 +1692,8 @@ public:
/**
* This method parses and reads the TXT record data from a message.
*
* This method also checks if the TXT data is well-formed by calling `VerifyTxtData()`.
* This method also checks if the TXT data is well-formed by calling `VerifyTxtData()` when it is successfully
* read.
*
* @param[in] aMessage The message to read from.
* @param[in,out] aOffset On input, the offset in @p aMessage to start of TXT record data.
@@ -1705,7 +1706,9 @@ public:
* @retval kErrorNone The TXT data was read successfully. @p aOffset, @p aTxtBuffer and @p aTxtBufferSize
* are updated.
* @retval kErrorParse The TXT record in @p aMessage could not be parsed (invalid format).
* @retval kErrorNoBufs TXT data could not fit in @p aTxtBufferSize bytes.
* @retval kErrorNoBufs TXT data could not fit in @p aTxtBufferSize bytes. TXT data is still partially read
* into @p aTxtBuffer up to its size and @p aOffset is updated to skip over the full
* TXT record.
*
*/
Error ReadTxtData(const Message &aMessage, uint16_t &aOffset, uint8_t *aTxtBuffer, uint16_t &aTxtBufferSize) const;
+12 -1
View File
@@ -1021,6 +1021,8 @@ void TestHeaderAndResourceRecords(void)
for (const char *instanceName : kInstanceNames)
{
uint16_t savedOffset;
// SRV record
SuccessOrQuit(Dns::Name::CompareName(*message, offset, instanceName));
SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, srvRecord));
@@ -1037,12 +1039,21 @@ void TestHeaderAndResourceRecords(void)
SuccessOrQuit(Dns::Name::CompareName(*message, offset, instanceName));
SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, txtRecord));
VerifyOrQuit(txtRecord.GetTtl() == kTxtTtl);
len = sizeof(buffer);
savedOffset = offset;
len = sizeof(buffer);
SuccessOrQuit(txtRecord.ReadTxtData(*message, offset, buffer, len));
VerifyOrQuit(len == sizeof(kTxtData));
VerifyOrQuit(memcmp(buffer, kTxtData, len) == 0);
printf(" \"%s\" TXT %u %d \"%s\"\n", instanceName, txtRecord.GetTtl(), txtRecord.GetLength(),
reinterpret_cast<const char *>(buffer));
// Partial read of TXT data
len = sizeof(kTxtData) - 1;
memset(buffer, 0, sizeof(buffer));
VerifyOrQuit(txtRecord.ReadTxtData(*message, savedOffset, buffer, len) == kErrorNoBufs);
VerifyOrQuit(len == sizeof(kTxtData) - 1);
VerifyOrQuit(memcmp(buffer, kTxtData, len) == 0);
VerifyOrQuit(savedOffset == offset);
}
SuccessOrQuit(Dns::Name::CompareName(*message, offset, kHostName));