mirror of
https://github.com/espressif/openthread.git
synced 2026-07-26 22:09:05 +00:00
[srp-server] add support for service subtypes (#6760)
This commit adds support for service subtypes in SRP server. It updates the internal data model to store services in `Srp::Server`. Every `Host` now has a list of `Service` entries along with a list of `Service::Description` entries. These types mirror the SRP update message format and the set of instructions that form the SRP message. The `Service` entries represent the "Service Discovery Instructions", i.e., the PTR records mapping a service name or a subtype name to a service instance. A `Service::Description` entry represents the the SRV and TXT records. A `Service` entry is always associated with a `Service::Description` and the subtypes of the same service instance all share the same `Service::Description` entry. This commit also adds a new method `Host::FindNextService()` and the public API `otSrpServerHostFindNextService()` which is very flexible and can be used in different ways. It can be used to iterate over the full list of services, or over a specific subset of services matching certain conditions, e.g., iterate over all base services excluding subtypes, or over all subtypes of an instance, or over all deleted services, etc. It can also be used to find a specific service with a given instance and service names. This commit also simplifies and enhances the logging in `Srp::Server`. In particular, when a new host is added, we now also log the list of services being added along with it. Also a change to a `Service` is only logged if the `Service` is marked as committed. This ensures that temporary `Service` entries associated with a newly received SRP update message are not logged (e.g., when an associated temporary `Host` object is being freed after its content is merged with an existing `Host` entry). Finally, this commit adds a test `test_srp_sub_type.py` to cover the subtype service registration on SRP client and server.
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 (132)
|
||||
#define OPENTHREAD_API_VERSION (133)
|
||||
|
||||
/**
|
||||
* @addtogroup api-instance
|
||||
|
||||
@@ -73,6 +73,64 @@ typedef void otSrpServerService;
|
||||
*/
|
||||
typedef uint32_t otSrpServerServiceUpdateId;
|
||||
|
||||
/**
|
||||
* The service flag type to indicate which services to include or exclude when searching in (or iterating over) the
|
||||
* list of SRP services.
|
||||
*
|
||||
* This is a combination of bit-flags. The specific bit-flags are defined in the enumeration `OT_SRP_SERVER_FLAG_*`.
|
||||
*
|
||||
*/
|
||||
typedef uint8_t otSrpServerServiceFlags;
|
||||
|
||||
enum
|
||||
{
|
||||
OT_SRP_SERVER_SERVICE_FLAG_BASE_TYPE = 1 << 0, ///< Include base services (not a sub-type).
|
||||
OT_SRP_SERVER_SERVICE_FLAG_SUB_TYPE = 1 << 1, ///< Include sub-type services.
|
||||
OT_SRP_SERVER_SERVICE_FLAG_ACTIVE = 1 << 2, ///< Include active (not deleted) services.
|
||||
OT_SRP_SERVER_SERVICE_FLAG_DELETED = 1 << 3, ///< Include deleted services.
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
/**
|
||||
* This constant defines an `otSrpServerServiceFlags` combination accepting any service (base/sub-type,
|
||||
* active/deleted).
|
||||
*
|
||||
*/
|
||||
OT_SRP_SERVER_FLAGS_ANY_SERVICE = (OT_SRP_SERVER_SERVICE_FLAG_BASE_TYPE | OT_SRP_SERVER_SERVICE_FLAG_SUB_TYPE |
|
||||
OT_SRP_SERVER_SERVICE_FLAG_ACTIVE | OT_SRP_SERVER_SERVICE_FLAG_DELETED),
|
||||
|
||||
/**
|
||||
* This constant defines an `otSrpServerServiceFlags` combination accepting base service only.
|
||||
*
|
||||
*/
|
||||
OT_SRP_SERVER_FLAGS_BASE_TYPE_SERVICE_ONLY =
|
||||
(OT_SRP_SERVER_SERVICE_FLAG_BASE_TYPE | OT_SRP_SERVER_SERVICE_FLAG_ACTIVE | OT_SRP_SERVER_SERVICE_FLAG_DELETED),
|
||||
|
||||
/**
|
||||
* This constant defines an `otSrpServerServiceFlags` combination accepting sub-type service only.
|
||||
*
|
||||
*/
|
||||
OT_SRP_SERVER_FLAGS_SUB_TYPE_SERVICE_ONLY =
|
||||
(OT_SRP_SERVER_SERVICE_FLAG_SUB_TYPE | OT_SRP_SERVER_SERVICE_FLAG_ACTIVE | OT_SRP_SERVER_SERVICE_FLAG_DELETED),
|
||||
|
||||
/**
|
||||
* This constant defines an `otSrpServerServiceFlags` combination accepting any active service (not deleted).
|
||||
*
|
||||
*/
|
||||
OT_SRP_SERVER_FLAGS_ANY_TYPE_ACTIVE_SERVICE =
|
||||
(OT_SRP_SERVER_SERVICE_FLAG_BASE_TYPE | OT_SRP_SERVER_SERVICE_FLAG_SUB_TYPE |
|
||||
OT_SRP_SERVER_SERVICE_FLAG_ACTIVE),
|
||||
|
||||
/**
|
||||
* This constant defines an `otSrpServerServiceFlags` combination accepting any deleted service.
|
||||
*
|
||||
*/
|
||||
OT_SRP_SERVER_FLAGS_ANY_TYPE_DELETED_SERVICE =
|
||||
(OT_SRP_SERVER_SERVICE_FLAG_BASE_TYPE | OT_SRP_SERVER_SERVICE_FLAG_SUB_TYPE |
|
||||
OT_SRP_SERVER_SERVICE_FLAG_ACTIVE),
|
||||
};
|
||||
|
||||
/**
|
||||
* This structure includes SRP server LEASE and KEY-LEASE configurations.
|
||||
*
|
||||
@@ -262,7 +320,10 @@ const char *otSrpServerHostGetFullName(const otSrpServerHost *aHost);
|
||||
const otIp6Address *otSrpServerHostGetAddresses(const otSrpServerHost *aHost, uint8_t *aAddressesNum);
|
||||
|
||||
/**
|
||||
* This function returns the next service of given host.
|
||||
* This function returns the next service (excluding any sub-type services) of given host.
|
||||
*
|
||||
* @note This function is being deprecated and will be removed. `otSrpServerHostFindNextService()` can be used
|
||||
* instead.
|
||||
*
|
||||
* @param[in] aHost A pointer to the SRP service host.
|
||||
* @param[in] aService A pointer to current SRP service instance; use NULL to get the first service.
|
||||
@@ -274,7 +335,45 @@ const otSrpServerService *otSrpServerHostGetNextService(const otSrpServerHost *
|
||||
const otSrpServerService *aService);
|
||||
|
||||
/**
|
||||
* This function tells if the SRP service has been deleted.
|
||||
* This function finds the next matching service on the host.
|
||||
*
|
||||
* The combination of flags and service and instance names enables iterating over the full list of services and/or a
|
||||
* subset of them matching certain conditions, or finding a specific service.
|
||||
*
|
||||
* To iterate over all services of a host:
|
||||
* service = otSrpServerHostFindNextService(host, service, OT_SRP_SERVER_FLAGS_ANY_SERVICE, NULL, NULL);
|
||||
*
|
||||
* To iterate over base services only (exclude sub-types):
|
||||
* service = otSrpServerHostFindNextService(host, service, OT_SRP_SERVER_FLAGS_BASE_TYPE_SERVICE_ONLY, NULL, NULL);
|
||||
*
|
||||
* To iterate over sub-types of a specific instance name `instanceName`:
|
||||
* service = otSrpServerHostFindNextService(host, service, OT_SRP_SERVER_FLAGS_SUB_TYPE_SERVICE_ONLY, NULL,
|
||||
* insatnceName);
|
||||
*
|
||||
* To find a specific service with service name `serviceName` and service instance name `instanceName`:
|
||||
* service = otSrpServerHostFindNextService(host, NULL, OT_SRP_SERVER_FLAGS_ANY_SERVICE, serviceName, instanceName);
|
||||
*
|
||||
* To find the base type service with a given service instance name `instanceName`:
|
||||
* service = otSrpServerHostFindNextService(host, NULL, OT_SRP_SERVER_FLAGS_BASE_TYPE_SERVICE_ONLY, NULL,
|
||||
* instanceName);
|
||||
*
|
||||
* @param[in] aHost A pointer to the SRP service host (MUST NOT be NULL).
|
||||
* @param[in] aPrevService A pointer to the previous service or NULL to start from the beginning of the list.
|
||||
* @param[in] aFlags Flags indicating which services to include (base/sub-type, active/deleted).
|
||||
* @param[in] aServiceName The service name to match. Set to NULL to accept any name.
|
||||
* @param[in] aInstanceName The service instance name to match. Set to NULL to accept any name.
|
||||
*
|
||||
* @returns A pointer to the next matching service or NULL if no matching service could be found.
|
||||
*
|
||||
*/
|
||||
const otSrpServerService *otSrpServerHostFindNextService(const otSrpServerHost * aHost,
|
||||
const otSrpServerService *aPrevService,
|
||||
otSrpServerServiceFlags aFlags,
|
||||
const char * aServiceName,
|
||||
const char * aInstanceName);
|
||||
|
||||
/**
|
||||
* This function indicates whether or not the SRP service has been deleted.
|
||||
*
|
||||
* A SRP service can be deleted but retains its name for future uses.
|
||||
* In this case, the service instance is not removed from the SRP server/registry.
|
||||
@@ -288,14 +387,70 @@ const otSrpServerService *otSrpServerHostGetNextService(const otSrpServerHost *
|
||||
bool otSrpServerServiceIsDeleted(const otSrpServerService *aService);
|
||||
|
||||
/**
|
||||
* This function returns the full name of the service.
|
||||
* This function indicates whether or not the SRP service is sub-type.
|
||||
*
|
||||
* @param[in] aService A pointer to the SRP service.
|
||||
*
|
||||
* @returns TRUE if the service is a sub-type, FALSE if not.
|
||||
*
|
||||
*/
|
||||
bool otSrpServerServiceIsSubType(const otSrpServerService *aService);
|
||||
|
||||
/**
|
||||
* This function returns the full service instance name of the service.
|
||||
*
|
||||
* @note This function is being deprecated and will be removed. `otSrpServerServiceGetInstanceName()` can be used
|
||||
* instead.
|
||||
*
|
||||
* @param[in] aService A pointer to the SRP service.
|
||||
*
|
||||
* @returns A pointer to the null-terminated service instance name string.
|
||||
*
|
||||
*/
|
||||
const char *otSrpServerServiceGetFullName(const otSrpServerService *aService);
|
||||
|
||||
/**
|
||||
* This function returns the full service instance name of the service.
|
||||
*
|
||||
* @param[in] aService A pointer to the SRP service.
|
||||
*
|
||||
* @returns A pointer to the null-terminated service instance name string.
|
||||
*
|
||||
*/
|
||||
const char *otSrpServerServiceGetInstanceName(const otSrpServerService *aService);
|
||||
|
||||
/**
|
||||
* This function returns the full service name of the service.
|
||||
*
|
||||
* @param[in] aService A pointer to the SRP service.
|
||||
*
|
||||
* @returns A pointer to the null-terminated service name string.
|
||||
*
|
||||
*/
|
||||
const char *otSrpServerServiceGetFullName(const otSrpServerService *aService);
|
||||
const char *otSrpServerServiceGetServiceName(const otSrpServerService *aService);
|
||||
|
||||
/**
|
||||
* This function gets the sub-type label from service name.
|
||||
*
|
||||
* This function is intended to be used when the @p aService is a sub-type, i.e., `otSrpServerServiceIsSubType()` for
|
||||
* the service returns TRUE. If it is not a sub-type this function returns `OT_ERROR_INVALID_ARGS`.
|
||||
*
|
||||
* The full service name for a sub-type service follows "<sub-label>._sub.<service-labels>.<domain>.". This function
|
||||
* copies the `<sub-label>` into the @p aLabel buffer.
|
||||
*
|
||||
* The @p aLabel is ensured to always be null-terminated after returning even in case of failure.
|
||||
*
|
||||
* @param[in] aService A pointer to the SRP service.
|
||||
* @param[out] aLabel A pointer to a buffer to copy the sub-type label name into.
|
||||
* @param[in] aMaxSize Maximum size of @p aLabel buffer.
|
||||
*
|
||||
* @retval OT_ERROR_NONE @p aLabel was updated successfully.
|
||||
* @retval OT_ERROR_NO_BUFS The sub-type label could not fit in @p aLabel buffer (number of chars from label
|
||||
* that could fit are copied in @p aLabel ensuring it is null-terminated).
|
||||
* @retval OT_ERROR_INVALID_ARGS SRP service is not a sub-type.
|
||||
*
|
||||
*/
|
||||
otError otSrpServerServiceGetServiceSubTypeLabel(const otSrpServerService *aService, char *aLabel, uint8_t aMaxSize);
|
||||
|
||||
/**
|
||||
* This function returns the port of the service instance.
|
||||
|
||||
@@ -124,6 +124,7 @@ Print information of all registered services.
|
||||
> srp server service
|
||||
srp-api-test-1._ipps._tcp.default.service.arpa.
|
||||
deleted: false
|
||||
subtypes: (null)
|
||||
port: 49152
|
||||
priority: 0
|
||||
weight: 0
|
||||
@@ -132,6 +133,7 @@ srp-api-test-1._ipps._tcp.default.service.arpa.
|
||||
addresses: [fdde:ad00:beef:0:0:ff:fe00:fc10]
|
||||
srp-api-test-0._ipps._tcp.default.service.arpa.
|
||||
deleted: false
|
||||
subtypes: _sub1,_sub2
|
||||
port: 49152
|
||||
priority: 0
|
||||
weight: 0
|
||||
|
||||
@@ -191,29 +191,51 @@ void SrpServer::OutputHostAddresses(const otSrpServerHost *aHost)
|
||||
|
||||
otError SrpServer::ProcessService(Arg aArgs[])
|
||||
{
|
||||
static constexpr char *kAnyServiceName = nullptr;
|
||||
static constexpr char *kAnyInstanceName = nullptr;
|
||||
|
||||
otError error = OT_ERROR_NONE;
|
||||
const otSrpServerHost *host;
|
||||
const otSrpServerHost *host = nullptr;
|
||||
|
||||
VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
||||
|
||||
host = nullptr;
|
||||
while ((host = otSrpServerGetNextHost(mInterpreter.mInstance, host)) != nullptr)
|
||||
{
|
||||
const otSrpServerService *service = nullptr;
|
||||
|
||||
while ((service = otSrpServerHostGetNextService(host, service)) != nullptr)
|
||||
while ((service = otSrpServerHostFindNextService(host, service, OT_SRP_SERVER_FLAGS_BASE_TYPE_SERVICE_ONLY,
|
||||
kAnyServiceName, kAnyInstanceName)) != nullptr)
|
||||
{
|
||||
bool isDeleted = otSrpServerServiceIsDeleted(service);
|
||||
const uint8_t *txtData;
|
||||
uint16_t txtDataLength;
|
||||
bool isDeleted = otSrpServerServiceIsDeleted(service);
|
||||
const char * instanceName = otSrpServerServiceGetInstanceName(service);
|
||||
const otSrpServerService *subService = nullptr;
|
||||
const uint8_t * txtData;
|
||||
uint16_t txtDataLength;
|
||||
bool hasSubType = false;
|
||||
|
||||
mInterpreter.OutputLine("%s", otSrpServerServiceGetFullName(service));
|
||||
mInterpreter.OutputLine("%s", instanceName);
|
||||
mInterpreter.OutputLine(Interpreter::kIndentSize, "deleted: %s", isDeleted ? "true" : "false");
|
||||
|
||||
if (isDeleted)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
mInterpreter.OutputFormat(Interpreter::kIndentSize, "subtypes: ");
|
||||
|
||||
while ((subService = otSrpServerHostFindNextService(
|
||||
host, subService, (OT_SRP_SERVER_SERVICE_FLAG_SUB_TYPE | OT_SRP_SERVER_SERVICE_FLAG_ACTIVE),
|
||||
kAnyServiceName, instanceName)) != nullptr)
|
||||
{
|
||||
char subLabel[OT_DNS_MAX_LABEL_SIZE];
|
||||
|
||||
IgnoreError(otSrpServerServiceGetServiceSubTypeLabel(subService, subLabel, sizeof(subLabel)));
|
||||
mInterpreter.OutputFormat("%s%s", hasSubType ? "," : "", subLabel);
|
||||
hasSubType = true;
|
||||
}
|
||||
|
||||
mInterpreter.OutputLine(hasSubType ? "" : "(null)");
|
||||
|
||||
mInterpreter.OutputLine(Interpreter::kIndentSize, "port: %hu", otSrpServerServiceGetPort(service));
|
||||
mInterpreter.OutputLine(Interpreter::kIndentSize, "priority: %hu", otSrpServerServiceGetPriority(service));
|
||||
mInterpreter.OutputLine(Interpreter::kIndentSize, "weight: %hu", otSrpServerServiceGetWeight(service));
|
||||
|
||||
@@ -122,7 +122,20 @@ const otSrpServerService *otSrpServerHostGetNextService(const otSrpServerHost *
|
||||
{
|
||||
auto host = static_cast<const Srp::Server::Host *>(aHost);
|
||||
|
||||
return host->GetNextService(static_cast<const Srp::Server::Service *>(aService));
|
||||
return host->FindNextService(static_cast<const Srp::Server::Service *>(aService),
|
||||
Srp::Server::kFlagsBaseTypeServiceOnly);
|
||||
}
|
||||
|
||||
const otSrpServerService *otSrpServerHostFindNextService(const otSrpServerHost * aHost,
|
||||
const otSrpServerService *aPrevService,
|
||||
otSrpServerServiceFlags aFlags,
|
||||
const char * aServiceName,
|
||||
const char * aInstanceName)
|
||||
{
|
||||
auto host = static_cast<const Srp::Server::Host *>(aHost);
|
||||
|
||||
return host->FindNextService(static_cast<const Srp::Server::Service *>(aPrevService), aFlags, aServiceName,
|
||||
aInstanceName);
|
||||
}
|
||||
|
||||
bool otSrpServerServiceIsDeleted(const otSrpServerService *aService)
|
||||
@@ -130,9 +143,29 @@ bool otSrpServerServiceIsDeleted(const otSrpServerService *aService)
|
||||
return static_cast<const Srp::Server::Service *>(aService)->IsDeleted();
|
||||
}
|
||||
|
||||
bool otSrpServerServiceIsSubType(const otSrpServerService *aService)
|
||||
{
|
||||
return static_cast<const Srp::Server::Service *>(aService)->IsSubType();
|
||||
}
|
||||
|
||||
const char *otSrpServerServiceGetFullName(const otSrpServerService *aService)
|
||||
{
|
||||
return static_cast<const Srp::Server::Service *>(aService)->GetFullName();
|
||||
return static_cast<const Srp::Server::Service *>(aService)->GetInstanceName();
|
||||
}
|
||||
|
||||
const char *otSrpServerServiceGetInstanceName(const otSrpServerService *aService)
|
||||
{
|
||||
return static_cast<const Srp::Server::Service *>(aService)->GetInstanceName();
|
||||
}
|
||||
|
||||
const char *otSrpServerServiceGetServiceName(const otSrpServerService *aService)
|
||||
{
|
||||
return static_cast<const Srp::Server::Service *>(aService)->GetServiceName();
|
||||
}
|
||||
|
||||
otError otSrpServerServiceGetServiceSubTypeLabel(const otSrpServerService *aService, char *aLabel, uint8_t aMaxSize)
|
||||
{
|
||||
return static_cast<const Srp::Server::Service *>(aService)->GetServiceSubTypeLabel(aLabel, aMaxSize);
|
||||
}
|
||||
|
||||
uint16_t otSrpServerServiceGetPort(const otSrpServerService *aService)
|
||||
|
||||
@@ -657,9 +657,9 @@ Header::Response Server::ResolveQuestionBySrp(const char * aName,
|
||||
while ((service = GetNextSrpService(*host, service)) != nullptr)
|
||||
{
|
||||
uint32_t instanceTtl = TimeMilli::MsecToSec(service->GetExpireTime() - TimerMilli::GetNow());
|
||||
const char *instanceName = service->GetFullName();
|
||||
const char *instanceName = service->GetInstanceName();
|
||||
bool serviceNameMatched = service->MatchesServiceName(aName);
|
||||
bool instanceNameMatched = service->Matches(aName);
|
||||
bool instanceNameMatched = service->MatchesInstanceName(aName);
|
||||
bool ptrQueryMatched = qtype == ResourceRecord::kTypePtr && serviceNameMatched;
|
||||
bool srvQueryMatched = qtype == ResourceRecord::kTypeSrv && instanceNameMatched;
|
||||
bool txtQueryMatched = qtype == ResourceRecord::kTypeTxt && instanceNameMatched;
|
||||
@@ -738,14 +738,7 @@ const Srp::Server::Host *Server::GetNextSrpHost(const Srp::Server::Host *aHost)
|
||||
const Srp::Server::Service *Server::GetNextSrpService(const Srp::Server::Host & aHost,
|
||||
const Srp::Server::Service *aService)
|
||||
{
|
||||
const Srp::Server::Service *service = aHost.GetNextService(aService);
|
||||
|
||||
while (service != nullptr && service->IsDeleted())
|
||||
{
|
||||
service = aHost.GetNextService(service);
|
||||
}
|
||||
|
||||
return service;
|
||||
return aHost.FindNextService(aService, Srp::Server::kFlagsAnyTypeActiveService);
|
||||
}
|
||||
#endif // OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
|
||||
|
||||
|
||||
+497
-264
File diff suppressed because it is too large
Load Diff
+190
-90
@@ -99,26 +99,25 @@ public:
|
||||
*/
|
||||
class Service : public LinkedListEntry<Service>, private NonCopyable
|
||||
{
|
||||
friend class LinkedListEntry<Service>;
|
||||
friend class Server;
|
||||
friend class LinkedList<Service>;
|
||||
friend class LinkedListEntry<Service>;
|
||||
|
||||
public:
|
||||
/**
|
||||
* This method creates a new Service object with given full name.
|
||||
*
|
||||
* @param[in] aFullName The full name of the service instance.
|
||||
*
|
||||
* @returns A pointer to the newly created Service object, nullptr if
|
||||
* cannot allocate memory for the object.
|
||||
* This type represents the flags which indicates which services to include or exclude when searching in (or
|
||||
* iterating over) the list of SRP services.
|
||||
*
|
||||
*/
|
||||
static Service *New(const char *aFullName);
|
||||
typedef otSrpServerServiceFlags Flags;
|
||||
|
||||
/**
|
||||
* This method frees the Service object.
|
||||
*
|
||||
*/
|
||||
void Free(void);
|
||||
enum
|
||||
{
|
||||
kFlagBaseType = OT_SRP_SERVER_SERVICE_FLAG_BASE_TYPE, ///< Include base services (not a sub-type).
|
||||
kFlagSubType = OT_SRP_SERVER_SERVICE_FLAG_SUB_TYPE, ///< Include sub-type services.
|
||||
kFlagActive = OT_SRP_SERVER_SERVICE_FLAG_ACTIVE, ///< Include active (not deleted) services.
|
||||
kFlagDeleted = OT_SRP_SERVER_SERVICE_FLAG_DELETED, ///< Include deleted services.
|
||||
};
|
||||
|
||||
/**
|
||||
* This method tells if the SRP service has been deleted.
|
||||
@@ -133,12 +132,48 @@ public:
|
||||
bool IsDeleted(void) const { return mIsDeleted; }
|
||||
|
||||
/**
|
||||
* This method returns the full name of the service.
|
||||
* This method indicates whether the SRP service is a sub-type.
|
||||
*
|
||||
* @returns A pointer to the null-terminated service name string.
|
||||
* @retval TRUE If the service is a sub-type.
|
||||
* @retval FALSE If the service is not a sub-type.
|
||||
*
|
||||
*/
|
||||
const char *GetFullName(void) const { return mFullName.AsCString(); }
|
||||
bool IsSubType(void) const { return mIsSubType; }
|
||||
|
||||
/**
|
||||
* This method gets the full service instance name of the service.
|
||||
*
|
||||
* @returns A pointer service instance name (as a null-terminated C string).
|
||||
*
|
||||
*/
|
||||
const char *GetInstanceName(void) const { return mDescription.mInstanceName.AsCString(); }
|
||||
|
||||
/**
|
||||
* This method gets the full service name of the service.
|
||||
*
|
||||
* @returns A pointer service name (as a null-terminated C string).
|
||||
*
|
||||
*/
|
||||
const char *GetServiceName(void) const { return mServiceName.AsCString(); }
|
||||
|
||||
/**
|
||||
* This method gets the sub-type label from service name.
|
||||
*
|
||||
* The full service name for a sub-type service follows "<sub-label>._sub.<service-labels>.<domain>.". This
|
||||
* method copies the `<sub-label>` into the @p aLabel buffer.
|
||||
*
|
||||
* The @p aLabel is ensured to always be null-terminated after returning even in case of failure.
|
||||
*
|
||||
* @param[out] aLabel A pointer to a buffer to copy the sub-type label name.
|
||||
* @param[in] aMaxSize Maximum size of @p aLabel buffer.
|
||||
*
|
||||
* @retval kErrorNone @p aLabel was updated successfully.
|
||||
* @retval kErrorNoBufs The sub-type label could not fit in @p aLabel buffer (number of chars from label
|
||||
* that could fit are copied in @p aLabel ensuring it is null-terminated).
|
||||
* @retval kErrorInvalidArgs SRP service is not a sub-type.
|
||||
*
|
||||
*/
|
||||
Error GetServiceSubTypeLabel(char *aLabel, uint8_t aMaxSize) const;
|
||||
|
||||
/**
|
||||
* This method returns the port of the service instance.
|
||||
@@ -146,7 +181,7 @@ public:
|
||||
* @returns The port of the service.
|
||||
*
|
||||
*/
|
||||
uint16_t GetPort(void) const { return mPort; }
|
||||
uint16_t GetPort(void) const { return mDescription.mPort; }
|
||||
|
||||
/**
|
||||
* This method returns the weight of the service instance.
|
||||
@@ -154,7 +189,7 @@ public:
|
||||
* @returns The weight of the service.
|
||||
*
|
||||
*/
|
||||
uint16_t GetWeight(void) const { return mWeight; }
|
||||
uint16_t GetWeight(void) const { return mDescription.mWeight; }
|
||||
|
||||
/**
|
||||
* This method returns the priority of the service instance.
|
||||
@@ -164,7 +199,7 @@ public:
|
||||
* @returns The priority of the service.
|
||||
*
|
||||
*/
|
||||
uint16_t GetPriority(void) const { return mPriority; }
|
||||
uint16_t GetPriority(void) const { return mDescription.mPriority; }
|
||||
|
||||
/**
|
||||
* This method returns the TXT record data of the service instance.
|
||||
@@ -172,7 +207,7 @@ public:
|
||||
* @returns A pointer to the buffer containing the TXT record data.
|
||||
*
|
||||
*/
|
||||
const uint8_t *GetTxtData(void) const { return mTxtData; }
|
||||
const uint8_t *GetTxtData(void) const { return mDescription.mTxtData; }
|
||||
|
||||
/**
|
||||
* This method returns the TXT record data length of the service instance.
|
||||
@@ -180,7 +215,7 @@ public:
|
||||
* @return The TXT record data length (number of bytes in buffer returned from `GetTxtData()`).
|
||||
*
|
||||
*/
|
||||
uint16_t GetTxtDataLength(void) const { return mTxtLength; }
|
||||
uint16_t GetTxtDataLength(void) const { return mDescription.mTxtLength; }
|
||||
|
||||
/**
|
||||
* This method returns the host which the service instance reside on.
|
||||
@@ -188,7 +223,7 @@ public:
|
||||
* @returns A reference to the host instance.
|
||||
*
|
||||
*/
|
||||
const Host &GetHost(void) const { return *static_cast<const Host *>(mHost); }
|
||||
const Host &GetHost(void) const { return mDescription.mHost; }
|
||||
|
||||
/**
|
||||
* This method returns the expire time (in milliseconds) of the service.
|
||||
@@ -207,17 +242,21 @@ public:
|
||||
TimeMilli GetKeyExpireTime(void) const;
|
||||
|
||||
/**
|
||||
* This method tells whether this service matches a given full name.
|
||||
* This method indicates whether this service matches a given service instance name.
|
||||
*
|
||||
* @param[in] aFullName The full name.
|
||||
* @param[in] aInstanceName The service instance name.
|
||||
*
|
||||
* @returns TRUE if the service matches the full name, FALSE if doesn't match.
|
||||
* @retval TRUE If the service matches the service instance name.
|
||||
* @retval FALSE If the service does not match the service instance name.
|
||||
*
|
||||
*/
|
||||
bool Matches(const char *aFullName) const { return (mFullName == aFullName); }
|
||||
bool MatchesInstanceName(const char *aInstanceName) const
|
||||
{
|
||||
return (mDescription.mInstanceName == aInstanceName);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method tells whether this service matches a given service name <Service>.<Domain>.
|
||||
* This method tells whether this service matches a given service name.
|
||||
*
|
||||
* @param[in] aServiceName The full service name to match.
|
||||
*
|
||||
@@ -225,25 +264,57 @@ public:
|
||||
* @retval FALSE If the service does not match the full service name.
|
||||
*
|
||||
*/
|
||||
bool MatchesServiceName(const char *aServiceName) const;
|
||||
bool MatchesServiceName(const char *aServiceName) const { return (mServiceName == aServiceName); }
|
||||
|
||||
private:
|
||||
explicit Service(void);
|
||||
Error SetFullName(const char *aFullName) { return mFullName.Set(aFullName); }
|
||||
Error SetTxtDataFromMessage(const Message &aMessage, uint16_t aOffset, uint16_t aLength);
|
||||
void TakeResourcesFrom(Service &aService);
|
||||
void ClearResources(void);
|
||||
struct Description : public LinkedListEntry<Description>, private NonCopyable
|
||||
{
|
||||
static Description *New(const char *aInstanceName, Host &aHost);
|
||||
|
||||
HeapString mFullName;
|
||||
uint16_t mPriority;
|
||||
uint16_t mWeight;
|
||||
uint16_t mPort;
|
||||
uint16_t mTxtLength;
|
||||
uint8_t * mTxtData;
|
||||
otSrpServerHost *mHost;
|
||||
Service * mNext;
|
||||
TimeMilli mTimeLastUpdate;
|
||||
bool mIsDeleted;
|
||||
explicit Description(Host &aHost);
|
||||
void Free(void);
|
||||
const char *GetInstanceName(void) const { return mInstanceName.AsCString(); }
|
||||
bool Matches(const char *aInstanceName) const { return (mInstanceName == aInstanceName); }
|
||||
void ClearResources(void);
|
||||
void TakeResourcesFrom(Description &aDescription);
|
||||
Error SetTxtDataFromMessage(const Message &aMessage, uint16_t aOffset, uint16_t aLength);
|
||||
|
||||
Description *mNext;
|
||||
HeapString mInstanceName;
|
||||
Host & mHost;
|
||||
uint16_t mPriority;
|
||||
uint16_t mWeight;
|
||||
uint16_t mPort;
|
||||
uint16_t mTxtLength;
|
||||
uint8_t * mTxtData;
|
||||
TimeMilli mTimeLastUpdate;
|
||||
};
|
||||
|
||||
enum Action : uint8_t
|
||||
{
|
||||
kAddNew,
|
||||
kUpdateExisting,
|
||||
kRemoveButRetainName,
|
||||
kFullyRemove,
|
||||
kLeaseExpired,
|
||||
kKeyLeaseExpired,
|
||||
};
|
||||
|
||||
static Service *New(const char *aServiceName, Description &aDescription, bool aIsSubType);
|
||||
|
||||
Service(Description &aDescription, bool aIsSubType);
|
||||
|
||||
void Free(void);
|
||||
bool MatchesFlags(Flags aFlags) const;
|
||||
void Log(Action aAction) const;
|
||||
|
||||
HeapString mServiceName;
|
||||
Description &mDescription;
|
||||
Service * mNext;
|
||||
TimeMilli mTimeLastUpdate;
|
||||
bool mIsDeleted : 1;
|
||||
bool mIsSubType : 1;
|
||||
bool mIsCommitted : 1;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -254,26 +325,8 @@ public:
|
||||
{
|
||||
friend class LinkedListEntry<Host>;
|
||||
friend class Server;
|
||||
friend class UpdateMetadata;
|
||||
|
||||
public:
|
||||
/**
|
||||
* This method creates a new Host object.
|
||||
*
|
||||
* @param[in] aInstance A reference to the OpenThread instance.
|
||||
*
|
||||
* @returns A pointer to the newly created Host object, nullptr if
|
||||
* cannot allocate memory for the object.
|
||||
*
|
||||
*/
|
||||
static Host *New(Instance &aInstance);
|
||||
|
||||
/**
|
||||
* This method Frees the Host object.
|
||||
*
|
||||
*/
|
||||
void Free(void);
|
||||
|
||||
/**
|
||||
* This method tells whether the Host object has been deleted.
|
||||
*
|
||||
@@ -349,17 +402,28 @@ public:
|
||||
TimeMilli GetKeyExpireTime(void) const;
|
||||
|
||||
/**
|
||||
* This method returns the next service of the host.
|
||||
* This method returns the head of `Service` linked list associated with the host.
|
||||
*
|
||||
* @param[in] aService A pointer to current service.
|
||||
*
|
||||
* @returns A pointer to the next service or NULL if no more services exist.
|
||||
* @returns A pointer to the head of `Service` linked list.
|
||||
*
|
||||
*/
|
||||
const Service *GetNextService(const Service *aService) const
|
||||
{
|
||||
return aService ? aService->GetNext() : mServices.GetHead();
|
||||
}
|
||||
const Service *GetServices(void) const { return mServices.GetHead(); }
|
||||
|
||||
/**
|
||||
* This method finds the next matching service on the host.
|
||||
*
|
||||
* @param[in] aPrevService A pointer to the previous service or `nullptr` to start from beginning of the list.
|
||||
* @param[in] aFlags Flags indicating which services to include (base/sub-type, active/deleted).
|
||||
* @param[in] aServiceName The service name to match. Set to `nullptr` to accept any name.
|
||||
* @param[in] aInstanceName The service instance name to match. Set to `nullptr` to accept any name.
|
||||
*
|
||||
* @returns A pointer to the next matching service or `nullptr` if no matching service could be found.
|
||||
*
|
||||
*/
|
||||
const Service *FindNextService(const Service *aPrevService,
|
||||
Service::Flags aFlags = kFlagsAnyService,
|
||||
const char * aServiceName = nullptr,
|
||||
const char * aInstanceName = nullptr) const;
|
||||
|
||||
/**
|
||||
* This method tells whether the host matches a given full name.
|
||||
@@ -377,31 +441,38 @@ public:
|
||||
kMaxAddressesNum = OPENTHREAD_CONFIG_SRP_SERVER_MAX_ADDRESSES_NUM,
|
||||
};
|
||||
|
||||
static Host *New(Instance &aInstance);
|
||||
|
||||
explicit Host(Instance &aInstance);
|
||||
Error SetFullName(const char *aFullName) { return mFullName.Set(aFullName); }
|
||||
void SetKey(Dns::Ecdsa256KeyRecord &aKey);
|
||||
void SetLease(uint32_t aLease) { mLease = aLease; }
|
||||
void SetKeyLease(uint32_t aKeyLease) { mKeyLease = aKeyLease; }
|
||||
Service *GetNextService(Service *aService) { return aService ? aService->GetNext() : mServices.GetHead(); }
|
||||
Service *AddService(const char *aFullName);
|
||||
void RemoveService(Service *aService, bool aRetainName, bool aNotifyServiceHandler);
|
||||
void FreeAllServices(void);
|
||||
void ClearResources(void);
|
||||
void CopyResourcesFrom(const Host &aHost);
|
||||
Service *FindService(const char *aFullName);
|
||||
const Service *FindService(const char *aFullName) const;
|
||||
Error AddIp6Address(const Ip6::Address &aIp6Address);
|
||||
void Free(void);
|
||||
Error SetFullName(const char *aFullName);
|
||||
void SetKey(Dns::Ecdsa256KeyRecord &aKey);
|
||||
void SetLease(uint32_t aLease) { mLease = aLease; }
|
||||
void SetKeyLease(uint32_t aKeyLease) { mKeyLease = aKeyLease; }
|
||||
Service * GetServices(void) { return mServices.GetHead(); }
|
||||
Service * AddNewService(const char *aServiceName, const char *aInstanceName, bool aIsSubType);
|
||||
void RemoveService(Service *aService, bool aRetainName, bool aNotifyServiceHandler);
|
||||
void FreeAllServices(void);
|
||||
void FreeUnusedServiceDescriptions(void);
|
||||
void ClearResources(void);
|
||||
Error MergeServicesAndResourcesFrom(Host &aHost);
|
||||
Error AddIp6Address(const Ip6::Address &aIp6Address);
|
||||
Service::Description * FindServiceDescription(const char *aInstanceName);
|
||||
const Service::Description *FindServiceDescription(const char *aInstanceName) const;
|
||||
Service * FindService(const char *aServiceName, const char *aInstanceName);
|
||||
const Service * FindService(const char *aServiceName, const char *aInstanceName) const;
|
||||
|
||||
HeapString mFullName;
|
||||
Ip6::Address mAddresses[kMaxAddressesNum];
|
||||
uint8_t mAddressesNum;
|
||||
Host * mNext;
|
||||
|
||||
Dns::Ecdsa256KeyRecord mKey;
|
||||
uint32_t mLease; // The LEASE time in seconds.
|
||||
uint32_t mKeyLease; // The KEY-LEASE time in seconds.
|
||||
TimeMilli mTimeLastUpdate;
|
||||
LinkedList<Service> mServices;
|
||||
Dns::Ecdsa256KeyRecord mKey;
|
||||
uint32_t mLease; // The LEASE time in seconds.
|
||||
uint32_t mKeyLease; // The KEY-LEASE time in seconds.
|
||||
TimeMilli mTimeLastUpdate;
|
||||
LinkedList<Service> mServices;
|
||||
LinkedList<Service::Description> mServiceDescriptions;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -425,6 +496,36 @@ public:
|
||||
uint32_t GrantKeyLease(uint32_t aKeyLease) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* This constant defines a `Service::Flags` combination accepting any service (base/sub-type, active/deleted).
|
||||
*
|
||||
*/
|
||||
static constexpr Service::Flags kFlagsAnyService = OT_SRP_SERVER_FLAGS_ANY_SERVICE;
|
||||
|
||||
/**
|
||||
* This constant defines a `Service::Flags` combination accepting base services only.
|
||||
*
|
||||
*/
|
||||
static constexpr Service::Flags kFlagsBaseTypeServiceOnly = OT_SRP_SERVER_FLAGS_BASE_TYPE_SERVICE_ONLY;
|
||||
|
||||
/**
|
||||
* This constant defines a `Service::Flags` combination accepting sub-type services only.
|
||||
*
|
||||
*/
|
||||
static constexpr Service::Flags kFlagsSubTypeServiceOnly = OT_SRP_SERVER_FLAGS_SUB_TYPE_SERVICE_ONLY;
|
||||
|
||||
/**
|
||||
* This constant defines a `Service::Flags` combination accepting any active services (not deleted).
|
||||
*
|
||||
*/
|
||||
static constexpr Service::Flags kFlagsAnyTypeActiveService = OT_SRP_SERVER_FLAGS_ANY_TYPE_ACTIVE_SERVICE;
|
||||
|
||||
/**
|
||||
* This constant defines a `Service::Flags` combination accepting any deleted services.
|
||||
*
|
||||
*/
|
||||
static constexpr Service::Flags kFlagsAnyTypeDeletedService = OT_SRP_SERVER_FLAGS_ANY_TYPE_DELETED_SERVICE;
|
||||
|
||||
/**
|
||||
* This constructor initializes the SRP server object.
|
||||
*
|
||||
@@ -633,10 +734,9 @@ private:
|
||||
const Dns::Zone & aZone,
|
||||
uint16_t & aOffset) const;
|
||||
|
||||
static bool IsValidDeleteAllRecord(const Dns::ResourceRecord &aRecord);
|
||||
const Service *FindService(const char *aFullName) const;
|
||||
static bool IsValidDeleteAllRecord(const Dns::ResourceRecord &aRecord);
|
||||
|
||||
void HandleUpdate(const Dns::UpdateHeader &aDnsHeader, Host *aHost, const Ip6::MessageInfo &aMessageInfo);
|
||||
void HandleUpdate(const Dns::UpdateHeader &aDnsHeader, Host &aHost, const Ip6::MessageInfo &aMessageInfo);
|
||||
void AddHost(Host &aHost);
|
||||
void RemoveHost(Host *aHost, bool aRetainName, bool aNotifyServiceHandler);
|
||||
bool HasNameConflictsWith(Host &aHost) const;
|
||||
|
||||
@@ -180,6 +180,7 @@ EXTRA_DIST = \
|
||||
test_srp_lease.py \
|
||||
test_srp_name_conflicts.py \
|
||||
test_srp_register_single_service.py \
|
||||
test_srp_sub_type.py \
|
||||
thread_cert.py \
|
||||
tlvs_parsing.py \
|
||||
thread_cert.py \
|
||||
@@ -238,6 +239,7 @@ check_SCRIPTS = \
|
||||
test_srp_lease.py \
|
||||
test_srp_name_conflicts.py \
|
||||
test_srp_register_single_service.py \
|
||||
test_srp_sub_type.py \
|
||||
Cert_5_1_01_RouterAttach.py \
|
||||
Cert_5_1_02_ChildAddressTimeout.py \
|
||||
Cert_5_1_03_RouterAddressReallocation.py \
|
||||
|
||||
@@ -890,8 +890,8 @@ class NodeImpl:
|
||||
service_list.append(service)
|
||||
continue
|
||||
|
||||
# 'port', 'priority', 'weight'
|
||||
for i in range(0, 3):
|
||||
# 'subtypes', port', 'priority', 'weight'
|
||||
for i in range(0, 4):
|
||||
key_value = lines.pop(0).strip().split(':')
|
||||
service[key_value[0].strip()] = key_value[1].strip()
|
||||
|
||||
@@ -1044,7 +1044,7 @@ class NodeImpl:
|
||||
Note that value of 'port', 'priority' and 'weight' are represented
|
||||
as strings but not integers.
|
||||
"""
|
||||
key_values = [word.strip().split(':') for word in line.split(',')]
|
||||
key_values = [word.strip().split(':') for word in line.split(', ')]
|
||||
keys = [key_value[0] for key_value in key_values]
|
||||
values = [key_value[1].strip('"') for key_value in key_values]
|
||||
return dict(zip(keys, values))
|
||||
|
||||
@@ -195,6 +195,7 @@ class SrpRegisterSingleService(thread_cert.TestCase):
|
||||
self.assertEqual(server_service['deleted'], 'false')
|
||||
self.assertEqual(server_service['instance'], client_service['instance'])
|
||||
self.assertEqual(server_service['name'], client_service['name'])
|
||||
self.assertEqual(server_service['subtypes'], '(null)')
|
||||
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']))
|
||||
|
||||
@@ -166,6 +166,7 @@ class SrpServerRebootPort(thread_cert.TestCase):
|
||||
self.assertEqual(server_service['deleted'], 'false')
|
||||
self.assertEqual(server_service['instance'], client_service['instance'])
|
||||
self.assertEqual(server_service['name'], client_service['name'])
|
||||
self.assertEqual(server_service['subtypes'], '(null)')
|
||||
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']))
|
||||
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2021, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
import ipaddress
|
||||
import unittest
|
||||
|
||||
import command
|
||||
import thread_cert
|
||||
|
||||
# Test description:
|
||||
# This test verifies SRP client and server support for registering sub-type services.
|
||||
#
|
||||
# Topology:
|
||||
#
|
||||
# LEADER (SRP server)
|
||||
# |
|
||||
# |
|
||||
# ROUTER (SRP client)
|
||||
#
|
||||
|
||||
SERVER = 1
|
||||
CLIENT = 2
|
||||
|
||||
|
||||
class SrpSubType(thread_cert.TestCase):
|
||||
USE_MESSAGE_FACTORY = False
|
||||
SUPPORT_NCP = False
|
||||
|
||||
TOPOLOGY = {
|
||||
SERVER: {
|
||||
'name': 'SRP_SERVER',
|
||||
'mode': 'rdn',
|
||||
},
|
||||
CLIENT: {
|
||||
'name': 'SRP_CLIENT',
|
||||
'mode': 'rdn',
|
||||
},
|
||||
}
|
||||
|
||||
def test(self):
|
||||
server = self.nodes[SERVER]
|
||||
client = self.nodes[CLIENT]
|
||||
|
||||
# Start the server & client devices.
|
||||
|
||||
server.start()
|
||||
self.simulator.go(5)
|
||||
self.assertEqual(server.get_state(), 'leader')
|
||||
|
||||
client.start()
|
||||
self.simulator.go(5)
|
||||
self.assertEqual(client.get_state(), 'router')
|
||||
|
||||
server.srp_server_set_enabled(True)
|
||||
client.srp_client_enable_auto_start_mode()
|
||||
|
||||
# Register a single service with 3 subtypes and verify that it worked.
|
||||
|
||||
client.srp_client_set_host_name('host1')
|
||||
client.srp_client_set_host_address('2001::1')
|
||||
client.srp_client_add_service('ins1', '_srv._udp,_s1,_s2,_s3', 1977)
|
||||
self.simulator.go(2)
|
||||
self.check_service_on_client_and_server(server, client)
|
||||
|
||||
# Remove the service on client
|
||||
|
||||
client.srp_client_remove_service('ins1', '_srv._udp')
|
||||
self.simulator.go(2)
|
||||
|
||||
client_services = client.srp_client_get_services()
|
||||
self.assertEqual(len(client_services), 0)
|
||||
server_services = server.srp_server_get_services()
|
||||
self.assertEqual(len(server_services), 1)
|
||||
server_service = server_services[0]
|
||||
self.assertEqual(server_service['fullname'], 'ins1._srv._udp.default.service.arpa.')
|
||||
self.assertEqual(server_service['instance'], 'ins1')
|
||||
self.assertEqual(server_service['name'], '_srv._udp')
|
||||
self.assertEqual(server_service['deleted'], 'true')
|
||||
|
||||
# Register the same service again.
|
||||
|
||||
client.srp_client_add_service('ins1', '_srv._udp,_s1,_s2,_s3', 1977)
|
||||
self.simulator.go(2)
|
||||
self.check_service_on_client_and_server(server, client)
|
||||
|
||||
def check_service_on_client_and_server(self, server, client):
|
||||
# Check the service on client
|
||||
client_services = client.srp_client_get_services()
|
||||
self.assertEqual(len(client_services), 1)
|
||||
client_service = client_services[0]
|
||||
self.assertEqual(client_service['instance'], 'ins1')
|
||||
self.assertEqual(client_service['name'], '_srv._udp,_s1,_s2,_s3')
|
||||
self.assertEqual(int(client_service['port']), 1977)
|
||||
self.assertEqual(int(client_service['priority']), 0)
|
||||
self.assertEqual(int(client_service['weight']), 0)
|
||||
self.assertEqual(client_service['state'], 'Registered')
|
||||
|
||||
# Check the service on server
|
||||
server_services = server.srp_server_get_services()
|
||||
self.assertEqual(len(server_services), 1)
|
||||
server_service = server_services[0]
|
||||
self.assertEqual(server_service['fullname'], 'ins1._srv._udp.default.service.arpa.')
|
||||
self.assertEqual(server_service['instance'], 'ins1')
|
||||
self.assertEqual(server_service['name'], '_srv._udp')
|
||||
self.assertEqual(server_service['deleted'], 'false')
|
||||
self.assertEqual(set(server_service['subtypes'].split(',')), {'_s1', '_s2', '_s3'})
|
||||
self.assertEqual(int(server_service['port']), 1977)
|
||||
self.assertEqual(int(server_service['priority']), 0)
|
||||
self.assertEqual(int(server_service['weight']), 0)
|
||||
self.assertEqual(server_service['host'], 'host1')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -918,6 +918,8 @@ class OTCI(object):
|
||||
|
||||
v = v[1:-1]
|
||||
info['addresses'] = list(map(Ip6Addr, v.split(', ')))
|
||||
elif k == 'subtypes':
|
||||
info[k] = list() if v == '(null)' else list(v.split(','))
|
||||
elif k in ('port', 'weight', 'priority'):
|
||||
info[k] = int(v)
|
||||
elif k in ('host',):
|
||||
|
||||
Reference in New Issue
Block a user