mirror of
https://github.com/espressif/openthread.git
synced 2026-08-28 21:09:54 +00:00
[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:
@@ -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()`.
|
||||
|
||||
@@ -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
@@ -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)));
|
||||
|
||||
@@ -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 *);
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user