mirror of
https://github.com/espressif/openthread.git
synced 2026-10-06 07:47:41 +00:00
[srp-client] add support for service subtypes (#6724)
This commit adds support for service subtype in `Srp::Client`. The `otSrpClientService` now includes a pointer to an array of service subtype labels (which can be set to `NULL` if there is no subtype). Each subtype is appended as a PTR record (i.e., a Service Discovery Instruction) in the SRP update message. This commit also updates The `Utils::SrpClientBuffers` to provide an array for the subtype labels (pointers). The default size of the service name buffer is also incremented so that it can be used for saving subtype label strings. The CLI `srp client` implementation is also updated to support service subtypes where the `<servicename>` can optionally include a list of subtype labels separated by comma (e.g., `_ser._udp,_s1,_s2`).
This commit is contained in:
@@ -53,7 +53,7 @@ extern "C" {
|
||||
* @note This number versions both OpenThread platform and user APIs.
|
||||
*
|
||||
*/
|
||||
#define OPENTHREAD_API_VERSION (131)
|
||||
#define OPENTHREAD_API_VERSION (132)
|
||||
|
||||
/**
|
||||
* @addtogroup api-instance
|
||||
|
||||
@@ -92,6 +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 char *const * mSubTypeLabels; ///< Array of service sub-type labels (must end with `NULL` or can be `NULL`).
|
||||
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.
|
||||
|
||||
@@ -97,6 +97,7 @@ otIp6Address *otSrpClientBuffersGetHostAddressesArray(otInstance *aInstance, uin
|
||||
* `otSrpClientBuffersGetServiceEntryServiceNameString()`.
|
||||
* - `mService.mInstanceName` will point to an allocated string buffer which can be retrieved using the function
|
||||
* `otSrpClientBuffersGetServiceEntryInstanceNameString()`.
|
||||
* - `mService.mSubTypeLabels` points to an array that is returned from `otSrpClientBuffersGetSubTypeLabelsArray()`.
|
||||
* - `mService.mTxtEntries` will point to `mTxtEntry`.
|
||||
* - `mService.mNumTxtEntries` will be set to one.
|
||||
* - Other `mService` fields (port, priority, weight) are set to zero.
|
||||
@@ -104,6 +105,7 @@ otIp6Address *otSrpClientBuffersGetHostAddressesArray(otInstance *aInstance, uin
|
||||
* - `mTxtEntry.mValue` will point to an allocated buffer which can be retrieved using the function
|
||||
* `otSrpClientBuffersGetServiceEntryTxtBuffer()`.
|
||||
* - `mTxtEntry.mValueLength` is set to zero.
|
||||
* - All related data/string buffers and arrays are cleared to all zero.
|
||||
*
|
||||
* @param[in] aInstance A pointer to the OpenThread instance.
|
||||
*
|
||||
@@ -167,6 +169,17 @@ char *otSrpClientBuffersGetServiceEntryInstanceNameString(otSrpClientBuffersServ
|
||||
*/
|
||||
uint8_t *otSrpClientBuffersGetServiceEntryTxtBuffer(otSrpClientBuffersServiceEntry *aEntry, uint16_t *aSize);
|
||||
|
||||
/**
|
||||
* This function gets the array for service subtype labels from the service entry.
|
||||
*
|
||||
* @param[in] aEntry A pointer to a previously allocated service entry (MUST NOT be NULL).
|
||||
* @param[out] aArrayLength A pointer to a variable to return the array length (MUST NOT be NULL).
|
||||
*
|
||||
* @returns A pointer to the array.
|
||||
*
|
||||
*/
|
||||
const char **otSrpClientBuffersGetSubTypeLabelsArray(otSrpClientBuffersServiceEntry *aEntry, uint16_t *aArrayLength);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*
|
||||
|
||||
@@ -99,7 +99,7 @@ Host info:
|
||||
name:"dev4312", state:Registered, addrs:[fd00:0:0:0:0:0:0:1]
|
||||
Service list:
|
||||
instance:"ins2", name:"_test2._udp", state:Registered, port:111, priority:1, weight:1
|
||||
instance:"ins1", name:"_test1._udp", state:Registered, port:777, priority:0, weight:0
|
||||
instance:"ins1", name:"_test1._udp,_sub1,_sub2", state:Registered, port:777, priority:0, weight:0
|
||||
```
|
||||
|
||||
When service `ins2` is removed:
|
||||
@@ -111,7 +111,7 @@ Host info:
|
||||
Service list:
|
||||
instance:"ins1", name:"_test1._udp", state:Registered, port:777, priority:0, weight:0
|
||||
Removed service list:
|
||||
instance:"ins2", name:"_test2._udp", state:Removed, port:111, priority:1, weight:1
|
||||
instance:"ins2", name:"_test2._udp,_sub1,_sub2", state:Removed, port:111, priority:1, weight:1
|
||||
```
|
||||
|
||||
When host info (and all services) is removed:
|
||||
@@ -301,7 +301,7 @@ Print the list of services.
|
||||
|
||||
```bash
|
||||
> srp client service
|
||||
instance:"ins2", name:"_test2._udp", state:Registered, port:111, priority:1, weight:1
|
||||
instance:"ins2", name:"_test2._udp,_sub1,_sub2", state:Registered, port:111, priority:1, weight:1
|
||||
instance:"ins1", name:"_test1._udp", state:Registered, port:777, priority:0, weight:0
|
||||
Done
|
||||
```
|
||||
@@ -310,10 +310,17 @@ Done
|
||||
|
||||
Usage: `srp client service add <instancename> <servicename> <port> [priority] [weight] [txt]`
|
||||
|
||||
Add a service with a given instance name, service name, port number, priority, weight and txt values. The priority and weight are optional and if not provided zero will be used. The txt should follow hex-string format and is treated as an already encoded TXT data byte sequence. It is also optional and if not provided it is considered empty.
|
||||
Add a service with a given instance name, service name, port number, priority, weight and txt values.
|
||||
|
||||
The `<servicename>` can optionally include a list of service subtype labels separated by comma.
|
||||
|
||||
The priority and weight are optional and if not provided zero will be used. The txt should follow hex-string format and is treated as an already encoded TXT data byte sequence. It is also optional and if not provided it is considered empty.
|
||||
|
||||
```bash
|
||||
> srp client service add ins2 _test2._udp 111 1 1
|
||||
> srp client service add ins1 _test1._udp 777
|
||||
Done
|
||||
|
||||
> srp client service add ins2 _test2._udp,_sub1,_sub2 111 1 1
|
||||
Done
|
||||
```
|
||||
|
||||
|
||||
@@ -388,6 +388,7 @@ otError SrpClient::ProcessServiceAdd(Arg aArgs[])
|
||||
uint16_t size;
|
||||
char * string;
|
||||
otError error;
|
||||
char * label;
|
||||
|
||||
entry = otSrpClientBuffersAllocateService(mInterpreter.mInstance);
|
||||
|
||||
@@ -404,6 +405,35 @@ otError SrpClient::ProcessServiceAdd(Arg aArgs[])
|
||||
string = otSrpClientBuffersGetServiceEntryServiceNameString(entry, &size);
|
||||
SuccessOrExit(error = CopyString(string, size, aArgs[2].GetCString()));
|
||||
|
||||
// Service subtypes are added as part of service name as a comma separated list
|
||||
// e.g., "_service._udp,_sub1,_sub2"
|
||||
|
||||
label = strchr(string, ',');
|
||||
|
||||
if (label != nullptr)
|
||||
{
|
||||
uint16_t arrayLength;
|
||||
const char **subTypeLabels = otSrpClientBuffersGetSubTypeLabelsArray(entry, &arrayLength);
|
||||
|
||||
// Leave the last array element as `nullptr` to indicate end of array.
|
||||
for (uint16_t index = 0; index + 1 < arrayLength; index++)
|
||||
{
|
||||
*label++ = '\0';
|
||||
subTypeLabels[index] = label;
|
||||
|
||||
label = strchr(label, ',');
|
||||
|
||||
if (label == nullptr)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
VerifyOrExit(label == nullptr, error = OT_ERROR_NO_BUFS);
|
||||
}
|
||||
|
||||
SuccessOrExit(error = aArgs[3].ParseAsUint16(entry->mService.mPort));
|
||||
|
||||
if (!aArgs[4].IsEmpty())
|
||||
{
|
||||
SuccessOrExit(error = aArgs[4].ParseAsUint16(entry->mService.mPriority));
|
||||
@@ -481,9 +511,19 @@ void SrpClient::OutputServiceList(uint8_t aIndentSize, const otSrpClientService
|
||||
|
||||
void SrpClient::OutputService(uint8_t aIndentSize, const otSrpClientService &aService)
|
||||
{
|
||||
mInterpreter.OutputLine(aIndentSize, "instance:\"%s\", name:\"%s\", state:%s, port:%d, priority:%d, weight:%d",
|
||||
aService.mInstanceName, aService.mName, otSrpClientItemStateToString(aService.mState),
|
||||
aService.mPort, aService.mPriority, aService.mWeight);
|
||||
mInterpreter.OutputFormat(aIndentSize, "instance:\"%s\", name:\"%s", aService.mInstanceName, aService.mName);
|
||||
|
||||
if (aService.mSubTypeLabels != nullptr)
|
||||
{
|
||||
for (uint16_t index = 0; aService.mSubTypeLabels[index] != nullptr; index++)
|
||||
{
|
||||
mInterpreter.OutputFormat(",%s", aService.mSubTypeLabels[index]);
|
||||
}
|
||||
}
|
||||
|
||||
mInterpreter.OutputLine("\", state:%s, port:%d, priority:%d, weight:%d",
|
||||
otSrpClientItemStateToString(aService.mState), aService.mPort, aService.mPriority,
|
||||
aService.mWeight);
|
||||
}
|
||||
|
||||
otError SrpClient::ProcessStart(Arg aArgs[])
|
||||
|
||||
@@ -94,4 +94,9 @@ uint8_t *otSrpClientBuffersGetServiceEntryTxtBuffer(otSrpClientBuffersServiceEnt
|
||||
return static_cast<Utils::SrpClientBuffers::ServiceEntry *>(aEntry)->GetTxtBuffer(*aSize);
|
||||
}
|
||||
|
||||
const char **otSrpClientBuffersGetSubTypeLabelsArray(otSrpClientBuffersServiceEntry *aEntry, uint16_t *aArrayLength)
|
||||
{
|
||||
return static_cast<Utils::SrpClientBuffers::ServiceEntry *>(aEntry)->GetSubTypeLabelsArray(*aArrayLength);
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_ENABLE
|
||||
|
||||
@@ -316,7 +316,19 @@
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_SERVICE_NAME_SIZE
|
||||
#define OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_SERVICE_NAME_SIZE 64
|
||||
#define OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_SERVICE_NAME_SIZE 96
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_SERVICE_MAX_SUB_TYPES
|
||||
*
|
||||
* Specifies the maximum number of service subtype labels (array length) in the SRP client buffers and service pool.
|
||||
*
|
||||
* This config is applicable only when `OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_ENABLE` is enabled.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_SERVICE_MAX_SUB_TYPES
|
||||
#define OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_SERVICE_MAX_SUB_TYPES 6
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
||||
@@ -116,10 +116,24 @@ void Client::Service::SetState(ItemState aState)
|
||||
|
||||
if (aState == kToAdd)
|
||||
{
|
||||
constexpr uint16_t kSubTypeLabelStringSize = 80;
|
||||
|
||||
String<kSubTypeLabelStringSize> string;
|
||||
|
||||
// Log more details only when entering `kToAdd` state.
|
||||
|
||||
otLogInfoSrp("[client] port:%d weight:%d prio:%d txts:%d", GetPort(), GetWeight(), GetPriority(),
|
||||
GetNumTxtEntries());
|
||||
if (HasSubType())
|
||||
{
|
||||
const char *label;
|
||||
|
||||
for (uint16_t index = 0; (label = GetSubTypeLabelAt(index)) != nullptr; index++)
|
||||
{
|
||||
string.Append("%s\"%s\"", (index != 0) ? ", " : "", label);
|
||||
}
|
||||
}
|
||||
|
||||
otLogInfoSrp("[client] subtypes:[%s] port:%d weight:%d prio:%d txts:%d", string.AsCString(), GetPort(),
|
||||
GetWeight(), GetPriority(), GetNumTxtEntries());
|
||||
}
|
||||
|
||||
mState = static_cast<otSrpClientItemState>(aState);
|
||||
@@ -843,6 +857,38 @@ Error Client::AppendServiceInstructions(Service &aService, Message &aMessage, In
|
||||
UpdateRecordLengthInMessage(rr, offset, aMessage);
|
||||
aInfo.mRecordCount++;
|
||||
|
||||
if (aService.HasSubType())
|
||||
{
|
||||
const char *subTypeLabel;
|
||||
uint16_t subServiceNameOffset = 0;
|
||||
|
||||
for (uint16_t index = 0; (subTypeLabel = aService.GetSubTypeLabelAt(index)) != nullptr; ++index)
|
||||
{
|
||||
// subtype label + "_sub" label + (pointer to) service name.
|
||||
|
||||
SuccessOrExit(error = Dns::Name::AppendLabel(subTypeLabel, aMessage));
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
subServiceNameOffset = aMessage.GetLength();
|
||||
SuccessOrExit(error = Dns::Name::AppendLabel("_sub", aMessage));
|
||||
SuccessOrExit(error = Dns::Name::AppendPointerLabel(serviceNameOffset, aMessage));
|
||||
}
|
||||
else
|
||||
{
|
||||
SuccessOrExit(error = Dns::Name::AppendPointerLabel(subServiceNameOffset, aMessage));
|
||||
}
|
||||
|
||||
// `rr` is already initialized as PTR (add or remove).
|
||||
offset = aMessage.GetLength();
|
||||
SuccessOrExit(error = aMessage.Append(rr));
|
||||
|
||||
SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, aMessage));
|
||||
UpdateRecordLengthInMessage(rr, offset, aMessage);
|
||||
aInfo.mRecordCount++;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------
|
||||
// Service Description Instruction
|
||||
|
||||
|
||||
@@ -191,6 +191,28 @@ public:
|
||||
*/
|
||||
const char *GetInstanceName(void) const { return mInstanceName; }
|
||||
|
||||
/**
|
||||
* This method indicates whether or not the service has any subtypes.
|
||||
*
|
||||
* @retval TRUE The service has at least one subtype.
|
||||
* @retval FALSE The service does not have any subtype.
|
||||
*
|
||||
*/
|
||||
bool HasSubType(void) const { return (mSubTypeLabels != nullptr); }
|
||||
|
||||
/**
|
||||
* This method gets the subtype label at a given index.
|
||||
*
|
||||
* This method MUST be used only after `HasSubType()` indicates that service has a subtype.
|
||||
*
|
||||
* @param[in] aIndex The index into list of subtype labels.
|
||||
*
|
||||
* @returns A pointer to subtype label at @p aIndex, or `nullptr` if there is no label (@p aIndex is after the
|
||||
* end of the subtype list).
|
||||
*
|
||||
*/
|
||||
const char *GetSubTypeLabelAt(uint16_t aIndex) const { return mSubTypeLabels[aIndex]; }
|
||||
|
||||
/**
|
||||
* This method gets the service port number.
|
||||
*
|
||||
|
||||
@@ -58,6 +58,7 @@ SrpClientBuffers::ServiceEntry *SrpClientBuffers::AllocateService(void)
|
||||
entry->Clear();
|
||||
entry->mService.mName = entry->mServiceName;
|
||||
entry->mService.mInstanceName = entry->mInstanceName;
|
||||
entry->mService.mSubTypeLabels = entry->mSubTypeLabels;
|
||||
entry->mService.mTxtEntries = &entry->mTxtEntry;
|
||||
entry->mService.mNumTxtEntries = 1;
|
||||
entry->mTxtEntry.mValue = entry->mTxtBuffer;
|
||||
|
||||
@@ -86,6 +86,12 @@ public:
|
||||
*/
|
||||
kServiceNameSize = OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_SERVICE_NAME_SIZE,
|
||||
|
||||
/**
|
||||
* Array length for service subtype label.
|
||||
*
|
||||
*/
|
||||
kServiceMaxSubTypes = OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_SERVICE_MAX_SUB_TYPES,
|
||||
|
||||
/**
|
||||
* Size (number of char) of service instance name string (includes null `\0` termination char).
|
||||
*
|
||||
@@ -151,14 +157,29 @@ public:
|
||||
return mTxtBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method gets the array for service subtype labels from the service entry.
|
||||
*
|
||||
* @param[out] aArrayLength Reference to a variable to return the array length.
|
||||
*
|
||||
* @returns A pointer to the array.
|
||||
*
|
||||
*/
|
||||
const char **GetSubTypeLabelsArray(uint16_t &aArrayLength)
|
||||
{
|
||||
aArrayLength = OT_ARRAY_LENGTH(mSubTypeLabels);
|
||||
return mSubTypeLabels;
|
||||
}
|
||||
|
||||
private:
|
||||
ServiceEntry * GetNext(void) { return reinterpret_cast<ServiceEntry *>(mService.mNext); }
|
||||
const ServiceEntry *GetNext(void) const { return reinterpret_cast<const ServiceEntry *>(mService.mNext); }
|
||||
void SetNext(ServiceEntry *aEntry) { mService.mNext = reinterpret_cast<Srp::Client::Service *>(aEntry); }
|
||||
|
||||
char mServiceName[kServiceNameSize];
|
||||
char mInstanceName[kInstanceNameSize];
|
||||
uint8_t mTxtBuffer[kTxtBufferSize];
|
||||
char mServiceName[kServiceNameSize];
|
||||
char mInstanceName[kInstanceNameSize];
|
||||
uint8_t mTxtBuffer[kTxtBufferSize];
|
||||
const char *mSubTypeLabels[kServiceMaxSubTypes + 1];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -204,13 +225,15 @@ public:
|
||||
* The returned service entry instance will be initialized as follows:
|
||||
*
|
||||
* - `mService.mName` points to a string buffer which can be retrieved using `GetServiceNameString()`.
|
||||
* - `mService.mInstanceName` points to a string buffer which can be retrieved `GetInstanceNameString()`.
|
||||
* - `mService.mInstanceName` points to a string buffer which can be retrieved using `GetInstanceNameString()`.
|
||||
* - `mService.mSubTypeLabels` points to array which can be retrieved using `GetSubTypeLabelsArray()`.
|
||||
* - `mService.mTxtEntries` points to `mTxtEntry`.
|
||||
* - `mService.mNumTxtEntries` is set to one (one entry in the list).
|
||||
* - Other `mService` fields (port, priority, weight) are set to zero.
|
||||
* - `mTxtEntry.mKey` is set to `nullptr` (value is treated as already encoded data).
|
||||
* - `mTxtEntry.mValue` points to a buffer which can be retrieved using `GetTxtBuffer()`
|
||||
* - `mTxtEntry.mValueLength` is set to zero.
|
||||
* - All related data/string buffers and arrays are cleared to all zero.
|
||||
*
|
||||
* @returns A pointer to the newly allocated service entry or `nullptr` if not more entry available in the pool.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user