[srp] get parsed TXT entry from SRP server (#6113)

This commit returns parsed TXT entries for service from the SRP
server, it enables prettier CLI output and easier Advertising Proxy
processing. We moved the otSrpTxtEntry to include/openthread/dns.h
because this structure can ( and will) also be used by DNS-SD
client. We also added one more mKeyLength field in otDnsTxtEntry for
saving length of the TXT key so that we can iterate over the encoded
TXT data without dynamically allocating memory for each entry.
This commit is contained in:
kangping
2021-01-28 21:12:56 -08:00
committed by GitHub
parent c03458433e
commit 46f796d4f0
17 changed files with 382 additions and 197 deletions
+39
View File
@@ -57,6 +57,45 @@ extern "C" {
#define OT_DNS_DEFAULT_SERVER_IP "2001:4860:4860::8888" ///< Defines default DNS Server address - Google DNS.
#define OT_DNS_DEFAULT_SERVER_PORT 53 ///< Defines default DNS Server port.
/**
* Initializer for otDnsTxtIterator.
*/
#define OT_DNS_TXT_ITERATOR_INIT 0
typedef uint16_t otDnsTxtIterator; ///< Used to iterate through the TXT entries.
/**
* This structure represents a TXT record entry representing a key/value pair (RFC 6763 - section 6.3).
*
* The string buffers pointed to by `mKey` and `mValue` MUST persist and remain unchanged after an instance of such
* structure is passed to OpenThread (as part of `otSrpClientService` instance).
*
* An array of `otDnsTxtEntry` entries are used in `otSrpClientService` to specify the full TXT record (a list of
* entries).
*
*/
typedef struct otDnsTxtEntry
{
/**
* The TXT record key string. It doesn't need to be a null-terminated string and `mKeyLength` gives its length.
*
* If `mKey` is not NULL, then the entry is treated as key/value pair with `mValue` buffer providing the value.
* - The entry is encoded as follows:
* - A single string length byte followed by "key=value" format (without the quotation marks).
- In this case, the overall encoded length must be 255 bytes or less.
* - If `mValue` is NULL, then key is treated as a boolean attribute and encoded as "key" (with no `=`).
* - If `mValue` is not NULL but `mValueLength` is zero, then it is treated as empty value and encoded as "key=".
*
* If `mKey` is NULL, then `mValue` buffer is treated as an already encoded TXT-DATA and is appended as is in the
* DNS message.
*
*/
const char * mKey;
const uint8_t *mValue; ///< The TXT record value or already encoded TXT-DATA (depending on `mKey`).
uint16_t mValueLength; ///< Number of bytes in `mValue` buffer.
uint8_t mKeyLength; ///< Number of bytes in `mKey` buffer. MUST be set even if `mKey` is a null-terminated string.
} otDnsTxtEntry;
/**
* This structure implements DNS Query parameters.
*
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (66)
#define OPENTHREAD_API_VERSION (67)
/**
* @addtogroup api-instance
+4 -34
View File
@@ -35,6 +35,7 @@
#ifndef OPENTHREAD_SRP_CLIENT_H_
#define OPENTHREAD_SRP_CLIENT_H_
#include <openthread/dns.h>
#include <openthread/ip6.h>
#ifdef __cplusplus
@@ -51,37 +52,6 @@ extern "C" {
*
*/
/**
* This structure represents a TXT record entry representing a key/value pair (RFC 6763 - section 6.3).
*
* The strings buffers pointed to by `mKey` and `mValue` MUST persist and remain unchanged after an instance of such a
* structure is passed to OpenThread (as part of `otSrpClientService` instance).
*
* An array of `otSrpTxtEntry` entries is used in `otSrpClientService` to specify the full TXT record (a list of
* entries).
*
*/
typedef struct otSrpTxtEntry
{
/**
* The TXT record key string.
*
* If `mKey` is not NULL, then the entry is treated as key/value pair with `mValue` buffer providing the value.
* - The entry is encoded as follows:
* - A single string length byte followed by "key=value" format (without the quotation marks).
- In this case, the overall encoded length must be 255 bytes or less.
* - If `mValue` is NULL, then key is treated as a boolean attribute and encoded as "key" (with no `=`).
* - If `mValue` is not NULL but `mValueLength` is zero, then it is treated as empty value and encoded as "key=".
*
* If `mKey` is NULL, then `mValue` buffer is treated as an already encoded TXT-DATA and is appended as is in the
* DNS message.
*
*/
const char * mKey;
const uint8_t *mValue; ///< The TXT record value or already encoded TXT-DATA (depending on `mKey`).
uint16_t mValueLength; ///< Number of bytes in `mValue` buffer.
} otSrpTxtEntry;
/**
* This enumeration specifies an SRP client item (service or host info) state.
*
@@ -122,7 +92,7 @@ typedef struct otSrpClientService
{
const char * mName; ///< The service name labels (e.g., "_chip._udp", not the full domain name).
const char * mInstanceName; ///< The service instance name label (not the full name).
const otSrpTxtEntry *mTxtEntries; ///< Array of TXT entries (number of entries is given by `mNumTxtEntries`).
const otDnsTxtEntry *mTxtEntries; ///< Array of TXT entries (number of entries is given by `mNumTxtEntries`).
uint16_t mPort; ///< The service port number.
uint16_t mPriority; ///< The service priority.
uint16_t mWeight; ///< The service weight.
@@ -179,7 +149,7 @@ typedef struct otSrpClientService
* The following errors are also possible:
*
* OT_ERROR_RESPONSE_TIMEOUT : Timed out waiting for response from server (client would continue to retry).
* OT_ERROR_INVALID_ARGS : The provided service structure is invalid (e.g., bad service name or `otSrpTxtEntry`).
* OT_ERROR_INVALID_ARGS : The provided service structure is invalid (e.g., bad service name or `otDnsTxtEntry`).
* OT_ERROR_NO_BUFS : Insufficient buffer to prepare or send the update message.
*
* Note that in case of any failure, the client continues the operation, i.e. it prepares and (re)transmits the SRP
@@ -377,7 +347,7 @@ otError otSrpClientSetHostAddresses(otInstance *aInstance, const otIp6Address *a
* @retval OT_ERROR_NONE The addition of service started successfully. The `otSrpClientCallback` will be
* called to report the status.
* @retval OT_ERROR_ALREADY The same service is already in the list.
* @retval OT_ERROR_INVALID_ARGS The service structure is invalid (e.g., bad service name or `otSrpTxtEntry`).
* @retval OT_ERROR_INVALID_ARGS The service structure is invalid (e.g., bad service name or `otDnsTxtEntry`).
*
*/
otError otSrpClientAddService(otInstance *aInstance, otSrpClientService *aService);
+11 -5
View File
@@ -37,6 +37,7 @@
#include <stdint.h>
#include <openthread/dns.h>
#include <openthread/instance.h>
#include <openthread/ip6.h>
@@ -303,15 +304,20 @@ uint16_t otSrpServerServiceGetWeight(const otSrpServerService *aService);
uint16_t otSrpServerServiceGetPriority(const otSrpServerService *aService);
/**
* This method returns the TXT data of the service instance.
* This method returns the next TXT entry of the service instance.
*
* @param[in] aService A pointer to the SRP service.
* @param[out] aTxtLength A pointer to the output of the TXT data length.
* @param[in] aService A pointer to the SRP service.
* @param[inout] aIterator A pointer to the TXT iterator context. To get the first
* TXT entry, it should be set to OT_DNS_TXT_ITERATOR_INIT.
* @param[out] aTxtEntry A pointer to where the TXT entry will be placed.
*
* @returns A pointer to the standard TXT data with format described by RFC 6763.
* @retval OT_ERROR_NONE Successfully found the next TXT entry.
* @retval OT_ERROR_NOT_FOUND No subsequent TXT entry exists in the service.
*
*/
const uint8_t *otSrpServerServiceGetTxtData(const otSrpServerService *aService, uint16_t *aTxtLength);
otError otSrpServerServiceGetNextTxtEntry(const otSrpServerService *aService,
otDnsTxtIterator * aIterator,
otDnsTxtEntry * aTxtEntry);
/**
* This method returns the host which the service instance reside on.
+1
View File
@@ -323,6 +323,7 @@ otError SrpClient::ProcessService(uint8_t aArgsLength, char *aArgs[])
entry->mService.mNumTxtEntries = 1;
entry->mService.mTxtEntries = &entry->mTxtEntry;
entry->mTxtEntry.mKey = nullptr; // Treat`mValue` as an already encoded TXT-DATA
entry->mTxtEntry.mKeyLength = 0;
entry->mTxtEntry.mValue = entry->mTxtBuffer;
entry->mTxtEntry.mValueLength = sizeof(entry->mTxtBuffer);
+1 -1
View File
@@ -88,7 +88,7 @@ private:
bool IsInUse(void) const { return (mService.mNext != &mService); }
otSrpClientService mService;
otSrpTxtEntry mTxtEntry;
otDnsTxtEntry mTxtEntry;
char mInstanceName[kNameSize];
char mServiceName[kNameSize];
uint8_t mTxtBuffer[kTxtSize];
+56 -26
View File
@@ -163,6 +163,53 @@ exit:
return error;
}
void SrpServer::OutputServiceTxtEntries(const otSrpServerService *aService)
{
uint16_t count = 0;
otDnsTxtEntry entry;
otDnsTxtIterator iterator = OT_DNS_TXT_ITERATOR_INIT;
mInterpreter.OutputFormat("[");
while (otSrpServerServiceGetNextTxtEntry(aService, &iterator, &entry) == OT_ERROR_NONE)
{
if (count != 0)
{
mInterpreter.OutputFormat(", ");
}
mInterpreter.Output(entry.mKey, entry.mKeyLength);
if (entry.mValue != nullptr)
{
mInterpreter.OutputFormat("=");
mInterpreter.OutputBytes(entry.mValue, entry.mValueLength);
}
++count;
}
mInterpreter.OutputFormat("]");
}
void SrpServer::OutputHostAddresses(const otSrpServerHost *aHost)
{
const otIp6Address *addresses;
uint8_t addressesNum;
addresses = otSrpServerHostGetAddresses(aHost, &addressesNum);
mInterpreter.OutputFormat("[");
for (uint8_t i = 0; i < addressesNum; ++i)
{
if (i != 0)
{
mInterpreter.OutputFormat(", ");
}
mInterpreter.OutputIp6Address(addresses[i]);
}
mInterpreter.OutputFormat("]");
}
otError SrpServer::ProcessService(uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aArgs);
@@ -179,11 +226,7 @@ otError SrpServer::ProcessService(uint8_t aArgsLength, char *aArgs[])
while ((service = otSrpServerHostGetNextService(host, service)) != nullptr)
{
const otIp6Address *addresses;
uint8_t addressesNum;
const uint8_t * txtData;
uint16_t txtLength;
bool isDeleted = otSrpServerServiceIsDeleted(service);
bool isDeleted = otSrpServerServiceIsDeleted(service);
mInterpreter.OutputLine(otSrpServerServiceGetFullName(service));
mInterpreter.OutputLine(Interpreter::kIndentSize, "deleted: %s", isDeleted ? "true" : "false");
@@ -196,30 +239,17 @@ otError SrpServer::ProcessService(uint8_t aArgsLength, char *aArgs[])
mInterpreter.OutputLine(Interpreter::kIndentSize, "priority: %hu", otSrpServerServiceGetPriority(service));
mInterpreter.OutputLine(Interpreter::kIndentSize, "weight: %hu", otSrpServerServiceGetWeight(service));
txtData = otSrpServerServiceGetTxtData(service, &txtLength);
if (txtLength > 0)
{
mInterpreter.OutputSpaces(Interpreter::kIndentSize);
mInterpreter.OutputFormat("TXT: ");
mInterpreter.OutputBytes(txtData, txtLength);
mInterpreter.OutputFormat("\r\n");
}
mInterpreter.OutputSpaces(Interpreter::kIndentSize);
mInterpreter.OutputFormat("TXT: ");
OutputServiceTxtEntries(service);
mInterpreter.OutputFormat("\r\n");
mInterpreter.OutputLine(Interpreter::kIndentSize, "host: %s", otSrpServerHostGetFullName(host));
mInterpreter.OutputSpaces(Interpreter::kIndentSize);
mInterpreter.OutputFormat("addresses: [");
addresses = otSrpServerHostGetAddresses(host, &addressesNum);
for (uint8_t i = 0; i < addressesNum; ++i)
{
mInterpreter.OutputIp6Address(addresses[i]);
if (i < addressesNum - 1)
{
mInterpreter.OutputFormat(", ");
}
}
mInterpreter.OutputFormat("]\r\n");
mInterpreter.OutputSpaces(Interpreter::kIndentSize);
mInterpreter.OutputFormat("addresses: ");
OutputHostAddresses(host);
mInterpreter.OutputFormat("\r\n");
}
}
+3
View File
@@ -92,6 +92,9 @@ private:
otError ProcessService(uint8_t aArgsLength, char *aArgs[]);
otError ProcessHelp(uint8_t aArgsLength, char *aArgs[]);
void OutputServiceTxtEntries(const otSrpServerService *aService);
void OutputHostAddresses(const otSrpServerHost *aHost);
static constexpr Command sCommands[] = {
{"disable", &SrpServer::ProcessDisable}, {"domain", &SrpServer::ProcessDomain},
{"enable", &SrpServer::ProcessEnable}, {"help", &SrpServer::ProcessHelp},
+5 -2
View File
@@ -147,9 +147,12 @@ uint16_t otSrpServerServiceGetPriority(const otSrpServerService *aService)
return static_cast<const Srp::Server::Service *>(aService)->GetPriority();
}
const uint8_t *otSrpServerServiceGetTxtData(const otSrpServerService *aService, uint16_t *aTxtLength)
otError otSrpServerServiceGetNextTxtEntry(const otSrpServerService *aService,
otDnsTxtIterator * aIterator,
otDnsTxtEntry * aTxtEntry)
{
return static_cast<const Srp::Server::Service *>(aService)->GetTxtData(*aTxtLength);
return static_cast<const Srp::Server::Service *>(aService)->GetNextTxtEntry(
*aIterator, static_cast<Dns::TxtEntry &>(*aTxtEntry));
}
const otSrpServerHost *otSrpServerServiceGetHost(const otSrpServerService *aService)
+131
View File
@@ -702,6 +702,64 @@ exit:
return error;
}
otError TxtEntry::AppendTo(Message &aMessage) const
{
otError error = OT_ERROR_NONE;
uint8_t length;
if (mKey == nullptr)
{
VerifyOrExit(mValue != nullptr);
error = aMessage.AppendBytes(mValue, mValueLength);
ExitNow();
}
length = mKeyLength;
VerifyOrExit(length <= kMaxKeyLength, error = OT_ERROR_INVALID_ARGS);
if (mValue == nullptr)
{
// Treat as a boolean attribute and encoded as "key" (with no `=`).
SuccessOrExit(error = aMessage.Append(length));
error = aMessage.AppendBytes(mKey, length);
ExitNow();
}
// Treat as key/value and encode as "key=value", value may be empty.
VerifyOrExit(mValueLength + length + sizeof(char) <= kMaxKeyValueEncodedSize, error = OT_ERROR_INVALID_ARGS);
length += static_cast<uint8_t>(mValueLength + sizeof(char));
SuccessOrExit(error = aMessage.Append(length));
SuccessOrExit(error = aMessage.AppendBytes(mKey, length));
SuccessOrExit(error = aMessage.Append<char>(kKeyValueSeparator));
error = aMessage.AppendBytes(mValue, mValueLength);
exit:
return error;
}
otError TxtEntry::AppendEntries(const TxtEntry *aEntries, uint8_t aNumEntries, Message &aMessage)
{
otError error = OT_ERROR_NONE;
uint16_t startOffset = aMessage.GetLength();
for (uint8_t index = 0; index < aNumEntries; index++)
{
SuccessOrExit(error = aEntries[index].AppendTo(aMessage));
}
if (aMessage.GetLength() == startOffset)
{
error = aMessage.Append<uint8_t>(0);
}
exit:
return error;
}
bool AaaaRecord::IsValid(void) const
{
return GetType() == Dns::ResourceRecord::kTypeAaaa && GetSize() == sizeof(*this);
@@ -774,6 +832,7 @@ otError TxtRecord::ReadTxtData(const Message &aMessage,
VerifyOrExit(GetLength() <= aTxtBufferSize, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = aMessage.Read(aOffset, aTxtBuffer, GetLength()));
VerifyOrExit(VerifyTxtData(aTxtBuffer, GetLength()), error = OT_ERROR_PARSE);
aTxtBufferSize = GetLength();
aOffset += GetLength();
@@ -781,5 +840,77 @@ exit:
return error;
}
bool TxtRecord::VerifyTxtData(const uint8_t *aTxtData, uint16_t aTxtLength)
{
bool valid = false;
uint8_t curEntryLength = 0;
// Per RFC 1035, TXT-DATA MUST have one or more <character-string>s.
VerifyOrExit(aTxtLength > 0);
for (uint16_t i = 0; i < aTxtLength; ++i)
{
if (curEntryLength == 0)
{
curEntryLength = aTxtData[i];
}
else
{
--curEntryLength;
}
}
valid = (curEntryLength == 0);
exit:
return valid;
}
otError TxtRecord::GetNextTxtEntry(const uint8_t *aTxtData,
uint16_t aTxtLength,
TxtIterator & aIterator,
TxtEntry & aTxtEntry)
{
otError error = OT_ERROR_NONE;
for (uint16_t i = aIterator; i < aTxtLength;)
{
uint8_t length = aTxtData[i++];
OT_ASSERT(i + length <= aTxtLength);
aTxtEntry.mKey = reinterpret_cast<const char *>(aTxtData + i);
aTxtEntry.mKeyLength = length;
aTxtEntry.mValue = nullptr;
aTxtEntry.mValueLength = 0;
for (uint8_t j = 0; j < length; ++j)
{
if (aTxtData[i + j] == TxtEntry::kKeyValueSeparator)
{
aTxtEntry.mKeyLength = j;
aTxtEntry.mValue = aTxtData + i + j + 1;
aTxtEntry.mValueLength = length - j - 1;
break;
}
}
i += length;
// Per RFC 6763, a TXT entry with empty key MUST be silently ignored.
if (aTxtEntry.mKeyLength == 0)
{
continue;
}
aIterator = i;
ExitNow();
}
error = OT_ERROR_NOT_FOUND;
exit:
return error;
}
} // namespace Dns
} // namespace ot
+87
View File
@@ -36,6 +36,8 @@
#include "openthread-core-config.h"
#include <openthread/dns.h>
#include "common/clearable.hpp"
#include "common/encoding.hpp"
#include "common/message.hpp"
@@ -955,6 +957,56 @@ private:
uint16_t mOffset; // Offset in `mMessage` to the start of name (used when name is from `mMessage`).
};
/**
* This type represents a TXT record entry representing a key/value pair (RFC 6763 - section 6.3).
*
*/
class TxtEntry : public otDnsTxtEntry
{
friend class TxtRecord;
public:
/**
* This method encodes and appends the `TxtEntry` to a message.
*
* @param[in] aMessage The message to append to.
*
* @retval OT_ERROR_NONE Entry was appended successfully to @p aMessage.
* @retval OT_ERROR_INVALID_ARGS The `TxTEntry` info is not valid.
* @retval OT_ERROR_NO_BUFS Insufficient available buffers to grow the message.
*
*/
otError AppendTo(Message &aMessage) const;
/**
* This static method appends an array of `TxtEntry` items to a message.
*
* @param[in] aEntries A pointer to array of `TxtEntry` items.
* @param[in] aNumEntries The number of entries in @p aEntries array.
* @param[in] aMessage The message to append to.
*
*
* @retval OT_ERROR_NONE Entries appended successfully to @p aMessage.
* @retval OT_ERROR_INVALID_ARGS The `TxTEntry` info is not valid.
* @retval OT_ERROR_NO_BUFS Insufficient available buffers to grow the message.
*
*/
static otError AppendEntries(const TxtEntry *aEntries, uint8_t aNumEntries, Message &aMessage);
private:
enum : char
{
kKeyValueSeparator = '=',
};
enum : uint8_t
{
kMinKeyLength = 1,
kMaxKeyLength = 9,
kMaxKeyValueEncodedSize = 255,
};
};
/**
* This class implements Resource Record (RR) body format.
*
@@ -1308,6 +1360,8 @@ public:
kType = kTypeTxt, ///< The TXT record type.
};
typedef otDnsTxtIterator TxtIterator;
/**
* This method initializes the TXT Resource Record by setting its type and class.
*
@@ -1321,6 +1375,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()`.
*
* @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
@@ -1340,6 +1396,37 @@ public:
uint8_t * aTxtBuffer,
uint16_t & aTxtBufferSize) const;
/**
* This static method tests if a buffer contains valid encoded TXT data.
*
* @param[in] aTxtData The TXT data buffer.
* @param[in] aTxtLength The length of the TXT data buffer.
*
* @returns TRUE if @p aTxtData contains valid encoded TXT data, FALSE if not.
*
*/
static bool VerifyTxtData(const uint8_t *aTxtData, uint16_t aTxtLength);
/**
* This static method returns the next TXT entry in the encoded TXT data buffer.
*
* This method assumes that @p aTxtData has already been verified by `VerifyTxtData()`.
*
* @param[in] aTxtData The encoded TXT data buffer.
* @param[in] aTxtLength The length of the encoded TXT data.
* @param[inout] aIterator A reference to the TXT iterator context. To get the first
* TXT entry, it should be set to OT_DNS_TXT_ITERATOR_INIT.
* @param[out] aTxtEntry A reference to where the TXT entry will be placed.
*
* @retval OT_ERROR_NONE Successfully found the next TXT entry.
* @retval OT_ERROR_NOT_FOUND No subsequent TXT entry exists in the service.
*
*/
static otError GetNextTxtEntry(const uint8_t *aTxtData,
uint16_t aTxtLength,
TxtIterator & aIterator,
TxtEntry & aTxtEntry);
} OT_TOOL_PACKED_END;
/**
+2 -62
View File
@@ -46,67 +46,6 @@
namespace ot {
namespace Srp {
//---------------------------------------------------------------------
// Client::TxtEntry
otError Client::TxtEntry::AppendTo(Message &aMessage) const
{
otError error = OT_ERROR_NONE;
uint8_t length;
if (mKey == nullptr)
{
VerifyOrExit(mValue != nullptr);
error = aMessage.AppendBytes(mValue, mValueLength);
ExitNow();
}
length = static_cast<uint8_t>(StringLength(mKey, kMaxKeyLength + 1));
VerifyOrExit(length <= kMaxKeyLength, error = OT_ERROR_INVALID_ARGS);
if (mValue == nullptr)
{
// Treat as a boolean attribute and encoded as "key" (with no `=`).
SuccessOrExit(error = aMessage.Append(length));
error = aMessage.AppendBytes(mKey, length);
ExitNow();
}
// Treat as key/value and encode as "key=value", value may be empty.
VerifyOrExit(mValueLength + length + sizeof(char) <= kMaxKeyValueEncodedSize, error = OT_ERROR_INVALID_ARGS);
length += static_cast<uint8_t>(mValueLength + sizeof(char));
SuccessOrExit(error = aMessage.Append(length));
SuccessOrExit(error = aMessage.AppendBytes(mKey, length));
SuccessOrExit(error = aMessage.Append<char>(kKeyValueSeparator));
error = aMessage.AppendBytes(mValue, mValueLength);
exit:
return error;
}
otError Client::TxtEntry::AppendEntries(const TxtEntry *aEntries, uint8_t aNumEntries, Message &aMessage)
{
otError error = OT_ERROR_NONE;
uint16_t startOffset = aMessage.GetLength();
for (uint8_t index = 0; index < aNumEntries; index++)
{
SuccessOrExit(error = aEntries[index].AppendTo(aMessage));
}
if (aMessage.GetLength() == startOffset)
{
error = aMessage.Append<uint8_t>(0);
}
exit:
return error;
}
//---------------------------------------------------------------------
// Client::HostInfo
@@ -834,7 +773,8 @@ otError Client::AppendServiceInstructions(Service &aService, Message &aMessage,
rr.Init(Dns::ResourceRecord::kTypeTxt);
offset = aMessage.GetLength();
SuccessOrExit(error = aMessage.Append(rr));
SuccessOrExit(error = TxtEntry::AppendEntries(aService.GetTxtEntries(), aService.GetNumTxtEntries(), aMessage));
SuccessOrExit(error =
Dns::TxtEntry::AppendEntries(aService.GetTxtEntries(), aService.GetNumTxtEntries(), aMessage));
UpdateRecordLengthInMessage(rr, offset, aMessage);
aInfo.mRecordCount++;
+1 -49
View File
@@ -92,54 +92,6 @@ public:
*/
typedef otSrpClientCallback Callback;
/**
* This type represents a TXT record entry representing a key/value pair (RFC 6763 - section 6.3).
*
*/
class TxtEntry : public otSrpTxtEntry
{
public:
/**
* This method encodes and appends the `TxtEntry` to a message.
*
* @param[in] aMessage The message to append to.
*
* @retval OT_ERROR_NONE Entry was appended successfully to @p aMessage.
* @retval OT_ERROR_INVALID_ARGS The `TxEntry` info is not valid.
* @retval OT_ERROR_NO_BUFS Insufficient available buffers to grow the message.
*
*/
otError AppendTo(Message &aMessage) const;
/**
* This static method appends an array of `TxtEntry` items to a message.
*
* @param[in] aEntries A pointer to array of `TxtEntry` items.
* @param[in] aNumEntries The number of entries in @p aEntries array.
* @param[in] aMessage The message to append to.
*
*
* @retval OT_ERROR_NONE Entries appended successfully to @p aMessage.
* @retval OT_ERROR_INVALID_ARGS The `TxEntry` info is not valid.
* @retval OT_ERROR_NO_BUFS Insufficient available buffers to grow the message.
*
*/
static otError AppendEntries(const TxtEntry *aEntries, uint8_t aNumEntries, Message &aMessage);
private:
enum : char
{
kKeyValueSeparator = '=',
};
enum : uint8_t
{
kMinKeyLength = 1,
kMaxKeyLength = 9,
kMaxKeyValueEncodedSize = 255,
};
};
/**
* This type represents an SRP client host info.
*
@@ -268,7 +220,7 @@ public:
* @returns A pointer to an array of service TXT entries.
*
*/
const TxtEntry *GetTxtEntries(void) const { return static_cast<const TxtEntry *>(mTxtEntries); }
const Dns::TxtEntry *GetTxtEntries(void) const { return static_cast<const Dns::TxtEntry *>(mTxtEntries); }
/**
* This method gets the number of entries in the service TXT entry array.
+6
View File
@@ -1324,6 +1324,11 @@ exit:
return error;
}
otError Server::Service::GetNextTxtEntry(Dns::TxtRecord::TxtIterator &aIterator, Dns::TxtEntry &aTxtEntry) const
{
return Dns::TxtRecord::GetNextTxtEntry(mTxtData, mTxtLength, aIterator, aTxtEntry);
}
TimeMilli Server::Service::GetExpireTime(void) const
{
OT_ASSERT(!mIsDeleted);
@@ -1366,6 +1371,7 @@ otError Server::Service::SetTxtDataFromMessage(const Message &aMessage, uint16_t
txtData = static_cast<uint8_t *>(GetInstance().HeapCAlloc(1, aLength));
VerifyOrExit(txtData != nullptr, error = OT_ERROR_NO_BUFS);
VerifyOrExit(aMessage.ReadBytes(aOffset, txtData, aLength) == aLength, error = OT_ERROR_PARSE);
VerifyOrExit(Dns::TxtRecord::VerifyTxtData(txtData, aLength), error = OT_ERROR_PARSE);
GetInstance().HeapFree(mTxtData);
mTxtData = txtData;
+7 -8
View File
@@ -151,18 +151,17 @@ public:
uint16_t GetPriority(void) const { return mPriority; }
/**
* This method returns the TXT data of the service instance.
* This method returns the next TXT entry of the service instance.
*
* @param[out] aTxtLength A pointer to the output of the TXT data length.
* @param[inout] aIterator A pointer to the TXT iterator context. To get the first
* TXT entry, it should be set to OT_DNS_TXT_ITERATOR_INIT.
* @param[out] aTxtEntry A pointer to where the TXT entry will be placed.
*
* @returns A pointer to the standard TXT data with format described by RFC 6763.
* @retval OT_ERROR_NONE Successfully found the next TXT entry.
* @retval OT_ERROR_NOT_FOUND No subsequent TXT entry exists in the service.
*
*/
const uint8_t *GetTxtData(uint16_t &aTxtLength) const
{
aTxtLength = mTxtLength;
return mTxtData;
}
otError GetNextTxtEntry(Dns::TxtRecord::TxtIterator &aIterator, Dns::TxtEntry &aTxtEntry) const;
/**
* This method returns the host which the service instance reside on.
+22 -6
View File
@@ -757,7 +757,7 @@ class NodeImpl:
'port': '12345',
'priority': '0',
'weight': '0',
'TXT': '00',
'TXT': ['abc=010203'],
'host_fullname': 'my-host.default.service.arpa.',
'host': 'my-host',
'addresses': ['2001::1', '2001::2']
@@ -783,16 +783,20 @@ class NodeImpl:
service_list.append(service)
continue
# 'port', 'priority', 'weight', 'TXT'
for i in range(0, 4):
# 'port', 'priority', 'weight'
for i in range(0, 3):
key_value = lines.pop(0).strip().split(':')
service[key_value[0].strip()] = key_value[1].strip()
txt_entries = lines.pop(0).strip().split('[')[1].strip(' ]').split(',')
txt_entries = map(str.strip, txt_entries)
service['TXT'] = [txt for txt in txt_entries if txt]
service['host_fullname'] = lines.pop(0).strip().split(':')[1].strip()
service['host'] = service['host_fullname'].split('.')[0]
addresses = lines.pop(0).strip().split('[')[1].strip(' ]').split(',')
map(str.strip, addresses)
addresses = map(str.strip, addresses)
service['addresses'] = [addr for addr in addresses if addr]
service_list.append(service)
@@ -860,8 +864,10 @@ class NodeImpl:
self.send_command(f'srp client host address')
self._expect_done()
def srp_client_add_service(self, instance_name, service_name, port):
self.send_command(f'srp client service add {instance_name} {service_name} {port}')
def srp_client_add_service(self, instance_name, service_name, port, priority=0, weight=0, txt_entries=[]):
txt_record = "".join(self._encode_txt_entry(entry) for entry in txt_entries)
self.send_command(
f'srp client service add {instance_name} {service_name} {port} {priority} {weight} {txt_record}')
self._expect_done()
def srp_client_remove_service(self, instance_name, service_name):
@@ -874,6 +880,16 @@ class NodeImpl:
service_lines = self._expect_command_output(cmd)
return [self._parse_srp_client_service(line) for line in service_lines]
def _encode_txt_entry(self, entry):
"""Encodes the TXT entry to the DNS-SD TXT record format as a HEX string.
Example usage:
self._encode_txt_entries(['abc']) -> '03616263'
self._encode_txt_entries(['def=']) -> '046465663d'
self._encode_txt_entries(['xyz=XYZ']) -> '0778797a3d58595a'
"""
return '{:02x}'.format(len(entry)) + "".join("{:02x}".format(ord(c)) for c in entry)
def _parse_srp_client_service(self, line: str):
"""Parse one line of srp service list into a dictionary which
maps string keys to string values.
@@ -94,7 +94,7 @@ class SrpRegisterSingleService(thread_cert.TestCase):
client.srp_client_set_host_name('my-host')
client.srp_client_set_host_address('2001::1')
client.srp_client_start(server.get_addrs()[0], client.get_srp_server_port())
client.srp_client_add_service('my-service', '_ipps._tcp', 12345)
client.srp_client_add_service('my-service', '_ipps._tcp', 12345, 0, 0, ['abc', 'def=', 'xyz=XYZ'])
self.simulator.go(2)
self.check_host_and_service(server, client)
@@ -135,7 +135,7 @@ class SrpRegisterSingleService(thread_cert.TestCase):
# reused.
#
client.srp_client_add_service('my-service', '_ipps._tcp', 12345)
client.srp_client_add_service('my-service', '_ipps._tcp', 12345, 0, 0, ['abc', 'def=', 'xyz=XYZ'])
self.simulator.go(2)
self.check_host_and_service(server, client)
@@ -181,7 +181,6 @@ class SrpRegisterSingleService(thread_cert.TestCase):
self.assertEqual(client_service['state'], 'Registered')
server_services = server.srp_server_get_services()
print(server_services)
self.assertEqual(len(server_services), 1)
server_service = server_services[0]
@@ -193,6 +192,9 @@ class SrpRegisterSingleService(thread_cert.TestCase):
self.assertEqual(int(server_service['port']), int(client_service['port']))
self.assertEqual(int(server_service['priority']), int(client_service['priority']))
self.assertEqual(int(server_service['weight']), int(client_service['weight']))
# We output value of TXT entry as HEX string.
print(server_service['TXT'])
self.assertEqual(server_service['TXT'], ['abc', 'def=', 'xyz=58595a'])
self.assertEqual(server_service['host'], 'my-host')
server_hosts = server.srp_server_get_hosts()