From b1c18a1e218511f1d583180809a50a8e2d845174 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 6 Sep 2023 14:05:47 -0700 Subject: [PATCH] [mdns] add `PublishKey()` & `UnpublishKey()` methods This commit adds new methods in `Mdns::Publisher` to publish or unpublish a key record for a given (host or service instance) name. New methods are implemented for both MDNSResponder and Avahi sub-classes. In the MDNSResponder implementation, if a key registration is for a service instance name matching a service registration, `DNSServiceAddRecord()` is used to associate the new record with the service. Otherwise, `DNSServiceRegisterRecord()` is used to register the KEY record on its own. The implementation handles cases when related service and key registrations are updated or unregistered. This commit also simplifies and updates the `test/mdns/main.cpp` tests, adding a common `Test()` function that takes a function pointer to run the test and handles all common boilerplate code, as well as adding a common callback to check the registration result. New test cases are also added to check key registration, registering keys on their own, and registering keys and services in different orders. --- CMakeLists.txt | 1 - src/common/types.hpp | 2 + src/mdns/mdns.cpp | 130 +++++++++- src/mdns/mdns.hpp | 70 ++++- src/mdns/mdns_avahi.cpp | 92 +++++++ src/mdns/mdns_avahi.hpp | 24 ++ src/mdns/mdns_mdnssd.cpp | 211 ++++++++++++++- src/mdns/mdns_mdnssd.hpp | 34 ++- tests/mdns/main.cpp | 547 +++++++++++++++++++-------------------- 9 files changed, 818 insertions(+), 293 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4fefd2798b7..7936efcb0da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,7 +113,6 @@ add_subdirectory(third_party EXCLUDE_FROM_ALL) add_subdirectory(src) add_subdirectory(tools) - if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) include(CTest) diff --git a/src/common/types.hpp b/src/common/types.hpp index 649f0b6371d..7ba0065ba33 100644 --- a/src/common/types.hpp +++ b/src/common/types.hpp @@ -402,11 +402,13 @@ struct MdnsTelemetryInfo "kEmaFactorDenominator must be greater than kEmaFactorNumerator"); MdnsResponseCounters mHostRegistrations; + MdnsResponseCounters mKeyRegistrations; MdnsResponseCounters mServiceRegistrations; MdnsResponseCounters mHostResolutions; MdnsResponseCounters mServiceResolutions; uint32_t mHostRegistrationEmaLatency; ///< The EMA latency of host registrations in milliseconds + uint32_t mKeyRegistrationEmaLatency; ///< The EMA latency of key registrations in milliseconds uint32_t mServiceRegistrationEmaLatency; ///< The EMA latency of service registrations in milliseconds uint32_t mHostResolutionEmaLatency; ///< The EMA latency of host resolutions in milliseconds uint32_t mServiceResolutionEmaLatency; ///< The EMA latency of service resolutions in milliseconds diff --git a/src/mdns/mdns.cpp b/src/mdns/mdns.cpp index d7e093531a5..b0721465e42 100644 --- a/src/mdns/mdns.cpp +++ b/src/mdns/mdns.cpp @@ -79,6 +79,19 @@ void Publisher::PublishHost(const std::string &aName, const AddressList &aAddres } } +void Publisher::PublishKey(const std::string &aName, const KeyData &aKeyData, ResultCallback &&aCallback) +{ + otbrError error; + + mKeyRegistrationBeginTime[aName] = Clock::now(); + + error = PublishKeyImpl(aName, aKeyData, std::move(aCallback)); + if (error != OTBR_ERROR_NONE) + { + UpdateMdnsResponseCounters(mTelemetryInfo.mKeyRegistrations, error); + } +} + void Publisher::OnServiceResolveFailed(std::string aType, std::string aInstanceName, int32_t aErrorCode) { UpdateMdnsResponseCounters(mTelemetryInfo.mServiceResolutions, DnsErrorToOtbrError(aErrorCode)); @@ -289,7 +302,7 @@ std::string Publisher::MakeFullServiceName(const std::string &aName, const std:: return aName + "." + aType + ".local"; } -std::string Publisher::MakeFullHostName(const std::string &aName) +std::string Publisher::MakeFullName(const std::string &aName) { return aName + ".local"; } @@ -325,6 +338,13 @@ Publisher::ServiceRegistration *Publisher::FindServiceRegistration(const std::st return it != mServiceRegistrations.end() ? it->second.get() : nullptr; } +Publisher::ServiceRegistration *Publisher::FindServiceRegistration(const std::string &aNameAndType) +{ + auto it = mServiceRegistrations.find(MakeFullName(aNameAndType)); + + return it != mServiceRegistrations.end() ? it->second.get() : nullptr; +} + Publisher::ResultCallback Publisher::HandleDuplicateServiceRegistration(const std::string &aHostName, const std::string &aName, const std::string &aType, @@ -435,6 +455,82 @@ Publisher::HostRegistration *Publisher::FindHostRegistration(const std::string & return it != mHostRegistrations.end() ? it->second.get() : nullptr; } +Publisher::ResultCallback Publisher::HandleDuplicateKeyRegistration(const std::string &aName, + const KeyData &aKeyData, + ResultCallback &&aCallback) +{ + KeyRegistration *keyReg = FindKeyRegistration(aName); + + VerifyOrExit(keyReg != nullptr); + + if (keyReg->IsOutdated(aName, aKeyData)) + { + otbrLogInfo("Removing existing key %s: outdated", aName.c_str()); + RemoveKeyRegistration(keyReg->mName, OTBR_ERROR_ABORTED); + } + else if (keyReg->IsCompleted()) + { + // Returns success if the same key has already been + // registered with exactly the same parameters. + std::move(aCallback)(OTBR_ERROR_NONE); + } + else + { + // If the same key is being registered with the same parameters, + // let's join the waiting queue for the result. + keyReg->mCallback = std::bind( + [](std::shared_ptr aExistingCallback, std::shared_ptr aNewCallback, + otbrError aError) { + std::move (*aExistingCallback)(aError); + std::move (*aNewCallback)(aError); + }, + std::make_shared(std::move(keyReg->mCallback)), + std::make_shared(std::move(aCallback)), std::placeholders::_1); + } + +exit: + return std::move(aCallback); +} + +void Publisher::AddKeyRegistration(KeyRegistrationPtr &&aKeyReg) +{ + mKeyRegistrations.emplace(MakeFullKeyName(aKeyReg->mName), std::move(aKeyReg)); +} + +void Publisher::RemoveKeyRegistration(const std::string &aName, otbrError aError) +{ + auto it = mKeyRegistrations.find(MakeFullKeyName(aName)); + KeyRegistrationPtr keyReg; + + otbrLogInfo("Removing key %s", aName.c_str()); + VerifyOrExit(it != mKeyRegistrations.end()); + + // Keep the KeyRegistration around before calling `Complete` + // to invoke the callback. This is for avoiding invalid access + // to the KeyRegistration when it's freed from the callback. + keyReg = std::move(it->second); + mKeyRegistrations.erase(it); + keyReg->Complete(aError); + otbrLogInfo("Removed key %s", aName.c_str()); + +exit: + return; +} + +Publisher::KeyRegistration *Publisher::FindKeyRegistration(const std::string &aName) +{ + auto it = mKeyRegistrations.find(MakeFullKeyName(aName)); + + return it != mKeyRegistrations.end() ? it->second.get() : nullptr; +} + +Publisher::KeyRegistration *Publisher::FindKeyRegistration(const std::string &aName, const std::string &aType) +{ + auto it = mKeyRegistrations.find(MakeFullServiceName(aName, aType)); + + return it != mKeyRegistrations.end() ? it->second.get() : nullptr; +} + Publisher::Registration::~Registration(void) { TriggerCompleteCallback(OTBR_ERROR_ABORTED); @@ -486,6 +582,26 @@ void Publisher::HostRegistration::OnComplete(otbrError aError) } } +bool Publisher::KeyRegistration::IsOutdated(const std::string &aName, const KeyData &aKeyData) const +{ + return !(mName == aName && mKeyData == aKeyData); +} + +void Publisher::KeyRegistration::Complete(otbrError aError) +{ + OnComplete(aError); + Registration::TriggerCompleteCallback(aError); +} + +void Publisher::KeyRegistration::OnComplete(otbrError aError) +{ + if (!IsCompleted()) + { + mPublisher->UpdateMdnsResponseCounters(mPublisher->mTelemetryInfo.mKeyRegistrations, aError); + mPublisher->UpdateKeyRegistrationEmaLatency(mName, aError); + } +} + void Publisher::UpdateMdnsResponseCounters(otbr::MdnsResponseCounters &aCounters, otbrError aError) { switch (aError) @@ -564,6 +680,18 @@ void Publisher::UpdateHostRegistrationEmaLatency(const std::string &aHostName, o } } +void Publisher::UpdateKeyRegistrationEmaLatency(const std::string &aKeyName, otbrError aError) +{ + auto it = mKeyRegistrationBeginTime.find(aKeyName); + + if (it != mKeyRegistrationBeginTime.end()) + { + uint32_t latency = std::chrono::duration_cast(Clock::now() - it->second).count(); + UpdateEmaLatency(mTelemetryInfo.mKeyRegistrationEmaLatency, latency, aError); + mKeyRegistrationBeginTime.erase(it); + } +} + void Publisher::UpdateServiceInstanceResolutionEmaLatency(const std::string &aInstanceName, const std::string &aType, otbrError aError) diff --git a/src/mdns/mdns.hpp b/src/mdns/mdns.hpp index 9a28b884cda..ea154042d39 100644 --- a/src/mdns/mdns.hpp +++ b/src/mdns/mdns.hpp @@ -118,6 +118,7 @@ class Publisher : private NonCopyable typedef std::vector TxtList; typedef std::vector SubTypeList; typedef std::vector AddressList; + typedef std::vector KeyData; /** * This structure represents information of a discovered service instance. @@ -266,6 +267,29 @@ class Publisher : private NonCopyable */ virtual void UnpublishHost(const std::string &aName, ResultCallback &&aCallback) = 0; + /** + * This method publishes or updates a key record for a name. + * + * @param[in] aName The name associated with key record (can be host name or service instance name). + * @param[in] aKeyData The key data to publish. + * @param[in] aCallback The callback for receiving the publishing result.`OTBR_ERROR_NONE` will be + * returned if the operation is successful and all other values indicate a + * failure. Specifically, `OTBR_ERROR_DUPLICATED` indicates that the name has + * already been published and the caller can re-publish with a new name if an + * alternative name is available/acceptable. + * + */ + void PublishKey(const std::string &aName, const KeyData &aKeyData, ResultCallback &&aCallback); + + /** + * This method un-publishes a key record + * + * @param[in] aName The name associated with key record. + * @param[in] aCallback The callback for receiving the publishing result. + * + */ + virtual void UnpublishKey(const std::string &aName, ResultCallback &&aCallback) = 0; + /** * This method subscribes a given service or service instance. * @@ -501,15 +525,43 @@ class Publisher : private NonCopyable void OnComplete(otbrError aError); }; + class KeyRegistration : public Registration + { + public: + std::string mName; + KeyData mKeyData; + + KeyRegistration(std::string aName, KeyData aKeyData, ResultCallback &&aCallback, Publisher *aPublisher) + : Registration(std::move(aCallback), aPublisher) + , mName(std::move(aName)) + , mKeyData(std::move(aKeyData)) + { + } + + ~KeyRegistration(void) { OnComplete(OTBR_ERROR_ABORTED); } + + void Complete(otbrError aError); + + // Tells whether this `KeyRegistration` object is outdated comparing to the given parameters. + bool IsOutdated(const std::string &aName, const KeyData &aKeyData) const; + + private: + void OnComplete(otbrError aError); + }; + using ServiceRegistrationPtr = std::unique_ptr; using ServiceRegistrationMap = std::map; using HostRegistrationPtr = std::unique_ptr; using HostRegistrationMap = std::map; + using KeyRegistrationPtr = std::unique_ptr; + using KeyRegistrationMap = std::map; static SubTypeList SortSubTypeList(SubTypeList aSubTypeList); static AddressList SortAddressList(AddressList aAddressList); + static std::string MakeFullName(const std::string &aName); static std::string MakeFullServiceName(const std::string &aName, const std::string &aType); - static std::string MakeFullHostName(const std::string &aName); + static std::string MakeFullHostName(const std::string &aName) { return MakeFullName(aName); } + static std::string MakeFullKeyName(const std::string &aName) { return MakeFullName(aName); } virtual otbrError PublishServiceImpl(const std::string &aHostName, const std::string &aName, @@ -523,6 +575,8 @@ class Publisher : private NonCopyable const AddressList &aAddresses, ResultCallback &&aCallback) = 0; + virtual otbrError PublishKeyImpl(const std::string &aName, const KeyData &aKeyData, ResultCallback &&aCallback) = 0; + virtual void OnServiceResolveFailedImpl(const std::string &aType, const std::string &aInstanceName, int32_t aErrorCode) = 0; @@ -534,6 +588,7 @@ class Publisher : private NonCopyable void AddServiceRegistration(ServiceRegistrationPtr &&aServiceReg); void RemoveServiceRegistration(const std::string &aName, const std::string &aType, otbrError aError); ServiceRegistration *FindServiceRegistration(const std::string &aName, const std::string &aType); + ServiceRegistration *FindServiceRegistration(const std::string &aNameAndType); void OnServiceResolved(std::string aType, DiscoveredInstanceInfo aInstanceInfo); void OnServiceResolveFailed(std::string aType, std::string aInstanceName, int32_t aErrorCode); @@ -556,10 +611,19 @@ class Publisher : private NonCopyable const AddressList &aAddresses, ResultCallback &&aCallback); + ResultCallback HandleDuplicateKeyRegistration(const std::string &aName, + const KeyData &aKeyData, + ResultCallback &&aCallback); + void AddHostRegistration(HostRegistrationPtr &&aHostReg); void RemoveHostRegistration(const std::string &aName, otbrError aError); HostRegistration *FindHostRegistration(const std::string &aName); + void AddKeyRegistration(KeyRegistrationPtr &&aKeyReg); + void RemoveKeyRegistration(const std::string &aName, otbrError aError); + KeyRegistration *FindKeyRegistration(const std::string &aName); + KeyRegistration *FindKeyRegistration(const std::string &aName, const std::string &aType); + static void UpdateMdnsResponseCounters(MdnsResponseCounters &aCounters, otbrError aError); static void UpdateEmaLatency(uint32_t &aEmaLatency, uint32_t aLatency, otbrError aError); @@ -567,6 +631,7 @@ class Publisher : private NonCopyable const std::string &aType, otbrError aError); void UpdateHostRegistrationEmaLatency(const std::string &aHostName, otbrError aError); + void UpdateKeyRegistrationEmaLatency(const std::string &aKeyName, otbrError aError); void UpdateServiceInstanceResolutionEmaLatency(const std::string &aInstanceName, const std::string &aType, otbrError aError); @@ -574,6 +639,7 @@ class Publisher : private NonCopyable ServiceRegistrationMap mServiceRegistrations; HostRegistrationMap mHostRegistrations; + KeyRegistrationMap mKeyRegistrations; uint64_t mNextSubscriberId = 1; @@ -582,6 +648,8 @@ class Publisher : private NonCopyable std::map, Timepoint> mServiceRegistrationBeginTime; // host name -> the timepoint to begin host registration std::map mHostRegistrationBeginTime; + // key name -> the timepoint to begin key registration + std::map mKeyRegistrationBeginTime; // {instance name, service type} -> the timepoint to begin service resolution std::map, Timepoint> mServiceInstanceResolutionBeginTime; // host name -> the timepoint to begin host resolution diff --git a/src/mdns/mdns_avahi.cpp b/src/mdns/mdns_avahi.cpp index 613be8dec9b..b2e1b10980d 100644 --- a/src/mdns/mdns_avahi.cpp +++ b/src/mdns/mdns_avahi.cpp @@ -440,6 +440,11 @@ PublisherAvahi::AvahiHostRegistration::~AvahiHostRegistration(void) ReleaseGroup(mEntryGroup); } +PublisherAvahi::AvahiKeyRegistration::~AvahiKeyRegistration(void) +{ + ReleaseGroup(mEntryGroup); +} + otbrError PublisherAvahi::Start(void) { otbrError error = OTBR_ERROR_NONE; @@ -524,6 +529,7 @@ void PublisherAvahi::CallHostOrServiceCallback(AvahiEntryGroup *aGroup, otbrErro { ServiceRegistration *serviceReg; HostRegistration *hostReg; + KeyRegistration *keyReg; if ((serviceReg = FindServiceRegistration(aGroup)) != nullptr) { @@ -547,6 +553,17 @@ void PublisherAvahi::CallHostOrServiceCallback(AvahiEntryGroup *aGroup, otbrErro RemoveHostRegistration(hostReg->mName, aError); } } + else if ((keyReg = FindKeyRegistration(aGroup)) != nullptr) + { + if (aError == OTBR_ERROR_NONE) + { + keyReg->Complete(aError); + } + else + { + RemoveKeyRegistration(keyReg->mName, aError); + } + } else { otbrLogWarning("No registered service or host matches avahi group @%p", aGroup); @@ -789,6 +806,64 @@ void PublisherAvahi::UnpublishHost(const std::string &aName, ResultCallback &&aC std::move(aCallback)(error); } +otbrError PublisherAvahi::PublishKeyImpl(const std::string &aName, const KeyData &aKeyData, ResultCallback &&aCallback) +{ + otbrError error = OTBR_ERROR_NONE; + int avahiError = AVAHI_OK; + std::string fullKeyName; + AvahiEntryGroup *group = nullptr; + + VerifyOrExit(mState == State::kReady, error = OTBR_ERROR_INVALID_STATE); + VerifyOrExit(mClient != nullptr, error = OTBR_ERROR_INVALID_STATE); + + aCallback = HandleDuplicateKeyRegistration(aName, aKeyData, std::move(aCallback)); + VerifyOrExit(!aCallback.IsNull()); + + VerifyOrExit((group = CreateGroup(mClient)) != nullptr, error = OTBR_ERROR_MDNS); + + fullKeyName = MakeFullKeyName(aName); + + avahiError = avahi_entry_group_add_record(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, + fullKeyName.c_str(), AVAHI_DNS_CLASS_IN, kDnsKeyRecordType, kDefaultTtl, + aKeyData.data(), aKeyData.size()); + VerifyOrExit(avahiError == AVAHI_OK); + + otbrLogInfo("Commit avahi key record for %s", aName.c_str()); + avahiError = avahi_entry_group_commit(group); + VerifyOrExit(avahiError == AVAHI_OK); + + AddKeyRegistration(std::unique_ptr( + new AvahiKeyRegistration(aName, aKeyData, std::move(aCallback), group, this))); + +exit: + if (avahiError != AVAHI_OK || error != OTBR_ERROR_NONE) + { + if (avahiError != AVAHI_OK) + { + error = OTBR_ERROR_MDNS; + otbrLogErr("Failed to publish key record - avahi error: %s!", avahi_strerror(avahiError)); + } + + if (group != nullptr) + { + ReleaseGroup(group); + } + std::move(aCallback)(error); + } + return error; +} + +void PublisherAvahi::UnpublishKey(const std::string &aName, ResultCallback &&aCallback) +{ + otbrError error = OTBR_ERROR_NONE; + + VerifyOrExit(mState == Publisher::State::kReady, error = OTBR_ERROR_INVALID_STATE); + RemoveKeyRegistration(aName, OTBR_ERROR_ABORTED); + +exit: + std::move(aCallback)(error); +} + otbrError PublisherAvahi::TxtDataToAvahiStringList(const TxtData &aTxtData, AvahiStringList *aBuffer, size_t aBufferSize, @@ -870,6 +945,23 @@ Publisher::HostRegistration *PublisherAvahi::FindHostRegistration(const AvahiEnt return result; } +Publisher::KeyRegistration *PublisherAvahi::FindKeyRegistration(const AvahiEntryGroup *aEntryGroup) +{ + KeyRegistration *result = nullptr; + + for (const auto &entry : mKeyRegistrations) + { + const auto &keyReg = static_cast(*entry.second); + if (keyReg.GetEntryGroup() == aEntryGroup) + { + result = entry.second.get(); + break; + } + } + + return result; +} + void PublisherAvahi::SubscribeService(const std::string &aType, const std::string &aInstanceName) { auto service = MakeUnique(*this, aType, aInstanceName); diff --git a/src/mdns/mdns_avahi.hpp b/src/mdns/mdns_avahi.hpp index 844e242949f..d42f9a2b595 100644 --- a/src/mdns/mdns_avahi.hpp +++ b/src/mdns/mdns_avahi.hpp @@ -78,6 +78,7 @@ class PublisherAvahi : public Publisher void UnpublishService(const std::string &aName, const std::string &aType, ResultCallback &&aCallback) override; void UnpublishHost(const std::string &aName, ResultCallback &&aCallback) override; + void UnpublishKey(const std::string &aName, ResultCallback &&aCallback) override; void SubscribeService(const std::string &aType, const std::string &aInstanceName) override; void UnsubscribeService(const std::string &aType, const std::string &aInstanceName) override; void SubscribeHost(const std::string &aHostName) override; @@ -97,6 +98,7 @@ class PublisherAvahi : public Publisher otbrError PublishHostImpl(const std::string &aName, const AddressList &aAddresses, ResultCallback &&aCallback) override; + otbrError PublishKeyImpl(const std::string &aName, const KeyData &aKeyData, ResultCallback &&aCallback) override; void OnServiceResolveFailedImpl(const std::string &aType, const std::string &aInstanceName, int32_t aErrorCode) override; @@ -106,6 +108,7 @@ class PublisherAvahi : public Publisher private: static constexpr size_t kMaxSizeOfTxtRecord = 1024; static constexpr uint32_t kDefaultTtl = 10; // In seconds. + static constexpr uint16_t kDnsKeyRecordType = 25; class AvahiServiceRegistration : public ServiceRegistration { @@ -158,6 +161,26 @@ class PublisherAvahi : public Publisher AvahiEntryGroup *mEntryGroup; }; + class AvahiKeyRegistration : public KeyRegistration + { + public: + AvahiKeyRegistration(const std::string &aName, + const KeyData &aKeyData, + ResultCallback &&aCallback, + AvahiEntryGroup *aEntryGroup, + PublisherAvahi *aPublisher) + : KeyRegistration(aName, aKeyData, std::move(aCallback), aPublisher) + , mEntryGroup(aEntryGroup) + { + } + + ~AvahiKeyRegistration(void) override; + const AvahiEntryGroup *GetEntryGroup(void) const { return mEntryGroup; } + + private: + AvahiEntryGroup *mEntryGroup; + }; + struct Subscription : private ::NonCopyable { PublisherAvahi *mPublisherAvahi; @@ -347,6 +370,7 @@ class PublisherAvahi : public Publisher ServiceRegistration *FindServiceRegistration(const AvahiEntryGroup *aEntryGroup); HostRegistration *FindHostRegistration(const AvahiEntryGroup *aEntryGroup); + KeyRegistration *FindKeyRegistration(const AvahiEntryGroup *aEntryGroup); AvahiClient *mClient; std::unique_ptr mPoller; diff --git a/src/mdns/mdns_mdnssd.cpp b/src/mdns/mdns_mdnssd.cpp index 5a07559f805..febcfb8807d 100644 --- a/src/mdns/mdns_mdnssd.cpp +++ b/src/mdns/mdns_mdnssd.cpp @@ -259,6 +259,7 @@ void PublisherMDnsSd::Stop(StopMode aStopMode) mServiceRegistrations.clear(); mHostRegistrations.clear(); + mKeyRegistrations.clear(); DeallocateHostsRef(); mSubscribedServices.clear(); @@ -444,11 +445,12 @@ void PublisherMDnsSd::DnssdServiceRegistration::Process(const MainloopContext otbrError PublisherMDnsSd::DnssdServiceRegistration::Register(void) { - std::string fullHostName; - std::string regType = MakeRegType(mType, mSubTypeList); - const char *hostNameCString = nullptr; - const char *serviceNameCString = nullptr; - DNSServiceErrorType dnsError; + std::string fullHostName; + std::string regType = MakeRegType(mType, mSubTypeList); + const char *hostNameCString = nullptr; + const char *serviceNameCString = nullptr; + DnssdKeyRegistration *keyReg; + DNSServiceErrorType dnsError; if (!mHostName.empty()) { @@ -461,6 +463,13 @@ otbrError PublisherMDnsSd::DnssdServiceRegistration::Register(void) serviceNameCString = mName.c_str(); } + keyReg = static_cast(GetPublisher().FindKeyRegistration(mName, mType)); + + if (keyReg != nullptr) + { + keyReg->Unregister(); + } + otbrLogInfo("Registering service %s.%s", mName.c_str(), regType.c_str()); dnsError = DNSServiceRegister(&mServiceRef, kDNSServiceFlagsNoAutoRename, kDNSServiceInterfaceIndexAny, @@ -473,17 +482,44 @@ otbrError PublisherMDnsSd::DnssdServiceRegistration::Register(void) HandleRegisterResult(/* aFlags */ 0, dnsError); } + if (keyReg != nullptr) + { + keyReg->Register(); + } + return GetPublisher().DnsErrorToOtbrError(dnsError); } void PublisherMDnsSd::DnssdServiceRegistration::Unregister(void) { - if (mServiceRef != nullptr) + DnssdKeyRegistration *keyReg = mRelatedKeyReg; + + VerifyOrExit(mServiceRef != nullptr); + + // If we have a related key registration associated with this + // service registration, we first unregister it and after we + // deallocated the `mServiceRef` try to register it again + // (which will add it as an individual record not tied to a + // service registration). Note that the `keyReg->Unregister()` + // will clear the `mRelatedKeyReg` field, so we need to keep + // a local copy to it in `keyReg`. + + if (keyReg != nullptr) { - GetPublisher().HandleServiceRefDeallocating(mServiceRef); - DNSServiceRefDeallocate(mServiceRef); - mServiceRef = nullptr; + keyReg->Unregister(); } + + GetPublisher().HandleServiceRefDeallocating(mServiceRef); + DNSServiceRefDeallocate(mServiceRef); + mServiceRef = nullptr; + + if (keyReg != nullptr) + { + keyReg->Register(); + } + +exit: + return; } void PublisherMDnsSd::DnssdServiceRegistration::HandleRegisterResult(DNSServiceRef aServiceRef, @@ -504,6 +540,11 @@ void PublisherMDnsSd::DnssdServiceRegistration::HandleRegisterResult(DNSServiceR void PublisherMDnsSd::DnssdServiceRegistration::HandleRegisterResult(DNSServiceFlags aFlags, DNSServiceErrorType aError) { + if (mRelatedKeyReg != nullptr) + { + mRelatedKeyReg->HandleRegisterResult(aError); + } + if ((aError == kDNSServiceErr_NoError) && (aFlags & kDNSServiceFlagsAdd)) { otbrLogInfo("Successfully registered service %s.%s", mName.c_str(), mType.c_str()); @@ -589,8 +630,8 @@ void PublisherMDnsSd::DnssdHostRegistration::HandleRegisterResult(DNSServiceRef DNSServiceErrorType aError, void *aContext) { - OT_UNUSED_VARIABLE(aServiceRef); - OT_UNUSED_VARIABLE(aFlags); + OTBR_UNUSED_VARIABLE(aServiceRef); + OTBR_UNUSED_VARIABLE(aFlags); static_cast(aContext)->HandleRegisterResult(aRecordRef, aError); } @@ -629,6 +670,119 @@ void PublisherMDnsSd::DnssdHostRegistration::HandleRegisterResult(DNSRecordRef a } } +otbrError PublisherMDnsSd::DnssdKeyRegistration::Register(void) +{ + DNSServiceErrorType dnsError = kDNSServiceErr_NoError; + DnssdServiceRegistration *serviceReg; + + otbrLogInfo("Registering new key %s", mName.c_str()); + + serviceReg = static_cast(GetPublisher().FindServiceRegistration(mName)); + + if ((serviceReg != nullptr) && (serviceReg->mServiceRef != nullptr)) + { + otbrLogInfo("Key %s is being registered as a record of an existing service registration", mName.c_str()); + + dnsError = DNSServiceAddRecord(serviceReg->mServiceRef, &mRecordRef, kDNSServiceFlagsUnique, + kDNSServiceType_KEY, mKeyData.size(), mKeyData.data(), /* ttl */ 0); + + VerifyOrExit(dnsError == kDNSServiceErr_NoError); + + mRelatedServiceReg = serviceReg; + serviceReg->mRelatedKeyReg = this; + + if (mRelatedServiceReg->IsCompleted()) + { + HandleRegisterResult(kDNSServiceErr_NoError); + } + + // If related service registration is not yet finished, + // we wait for service registration completion to signal + // key record registration as well. + } + else + { + otbrLogInfo("Key %s is being registered individually", mName.c_str()); + + dnsError = GetPublisher().CreateSharedHostsRef(); + VerifyOrExit(dnsError == kDNSServiceErr_NoError); + + dnsError = DNSServiceRegisterRecord(GetPublisher().mHostsRef, &mRecordRef, kDNSServiceFlagsUnique, + kDNSServiceInterfaceIndexAny, MakeFullKeyName(mName).c_str(), + kDNSServiceType_KEY, kDNSServiceClass_IN, mKeyData.size(), mKeyData.data(), + /* ttl */ 0, HandleRegisterResult, this); + VerifyOrExit(dnsError == kDNSServiceErr_NoError); + } + +exit: + if (dnsError != kDNSServiceErr_NoError) + { + HandleRegisterResult(dnsError); + } + + return GetPublisher().DnsErrorToOtbrError(dnsError); +} + +void PublisherMDnsSd::DnssdKeyRegistration::Unregister(void) +{ + DNSServiceErrorType dnsError; + DNSServiceRef serviceRef; + + VerifyOrExit(mRecordRef != nullptr); + + if (mRelatedServiceReg != nullptr) + { + serviceRef = mRelatedServiceReg->mServiceRef; + + mRelatedServiceReg->mRelatedKeyReg = nullptr; + mRelatedServiceReg = nullptr; + + otbrLogInfo("Unregistering key %s (was registered as a record of a service)", mName.c_str()); + } + else + { + serviceRef = GetPublisher().mHostsRef; + + otbrLogInfo("Unregistering key %s (was registered individually)", mName.c_str()); + } + + VerifyOrExit(serviceRef != nullptr); + + dnsError = DNSServiceRemoveRecord(serviceRef, mRecordRef, /* flags */ 0); + + otbrLogInfo("Unregistered key %s: error:%s", mName.c_str(), DNSErrorToString(dnsError)); + +exit: + return; +} + +void PublisherMDnsSd::DnssdKeyRegistration::HandleRegisterResult(DNSServiceRef aServiceRef, + DNSRecordRef aRecordRef, + DNSServiceFlags aFlags, + DNSServiceErrorType aError, + void *aContext) +{ + OTBR_UNUSED_VARIABLE(aServiceRef); + OTBR_UNUSED_VARIABLE(aRecordRef); + OTBR_UNUSED_VARIABLE(aFlags); + + static_cast(aContext)->HandleRegisterResult(aError); +} + +void PublisherMDnsSd::DnssdKeyRegistration::HandleRegisterResult(DNSServiceErrorType aError) +{ + if (aError != kDNSServiceErr_NoError) + { + otbrLogErr("Failed to register key %s: %s", mName.c_str(), DNSErrorToString(aError)); + GetPublisher().RemoveKeyRegistration(mName, DNSErrorToOtbrError(aError)); + } + else + { + otbrLogInfo("Successfully registered key %s", mName.c_str()); + Complete(OTBR_ERROR_NONE); + } +} + otbrError PublisherMDnsSd::PublishServiceImpl(const std::string &aHostName, const std::string &aName, const std::string &aType, @@ -714,6 +868,41 @@ void PublisherMDnsSd::UnpublishHost(const std::string &aName, ResultCallback &&a std::move(aCallback)(error); } +otbrError PublisherMDnsSd::PublishKeyImpl(const std::string &aName, const KeyData &aKeyData, ResultCallback &&aCallback) +{ + otbrError error = OTBR_ERROR_NONE; + DnssdKeyRegistration *keyReg; + + if (mState != State::kReady) + { + error = OTBR_ERROR_INVALID_STATE; + std::move(aCallback)(error); + ExitNow(); + } + + aCallback = HandleDuplicateKeyRegistration(aName, aKeyData, std::move(aCallback)); + VerifyOrExit(!aCallback.IsNull()); + + keyReg = new DnssdKeyRegistration(aName, aKeyData, std::move(aCallback), this); + AddKeyRegistration(std::unique_ptr(keyReg)); + + error = keyReg->Register(); + +exit: + return error; +} + +void PublisherMDnsSd::UnpublishKey(const std::string &aName, ResultCallback &&aCallback) +{ + otbrError error = OTBR_ERROR_NONE; + + VerifyOrExit(mState == Publisher::State::kReady, error = OTBR_ERROR_INVALID_STATE); + RemoveKeyRegistration(aName, OTBR_ERROR_ABORTED); + +exit: + std::move(aCallback)(error); +} + // See `regtype` parameter of the DNSServiceRegister() function for more information. std::string PublisherMDnsSd::MakeRegType(const std::string &aType, SubTypeList aSubTypeList) { diff --git a/src/mdns/mdns_mdnssd.hpp b/src/mdns/mdns_mdnssd.hpp index fa4bf4d1a73..40dd7689677 100644 --- a/src/mdns/mdns_mdnssd.hpp +++ b/src/mdns/mdns_mdnssd.hpp @@ -70,6 +70,7 @@ class PublisherMDnsSd : public MainloopProcessor, public Publisher void UnpublishService(const std::string &aName, const std::string &aType, ResultCallback &&aCallback) override; void UnpublishHost(const std::string &aName, ResultCallback &&aCallback) override; + void UnpublishKey(const std::string &aName, ResultCallback &&aCallback) override; void SubscribeService(const std::string &aType, const std::string &aInstanceName) override; void UnsubscribeService(const std::string &aType, const std::string &aInstanceName) override; void SubscribeHost(const std::string &aHostName) override; @@ -94,6 +95,7 @@ class PublisherMDnsSd : public MainloopProcessor, public Publisher otbrError PublishHostImpl(const std::string &aName, const AddressList &aAddress, ResultCallback &&aCallback) override; + otbrError PublishKeyImpl(const std::string &aName, const KeyData &aKeyData, ResultCallback &&aCallback) override; void OnServiceResolveFailedImpl(const std::string &aType, const std::string &aInstanceName, int32_t aErrorCode) override; @@ -109,8 +111,12 @@ class PublisherMDnsSd : public MainloopProcessor, public Publisher kStopOnServiceNotRunningError, }; + class DnssdKeyRegistration; + class DnssdServiceRegistration : public ServiceRegistration { + friend class DnssdKeyRegistration; + public: using ServiceRegistration::ServiceRegistration; // Inherit base constructor @@ -132,7 +138,8 @@ class PublisherMDnsSd : public MainloopProcessor, public Publisher const char *aDomain, void *aContext); - DNSServiceRef mServiceRef = nullptr; + DNSServiceRef mServiceRef = nullptr; + DnssdKeyRegistration *mRelatedKeyReg = nullptr; }; class DnssdHostRegistration : public HostRegistration @@ -158,6 +165,31 @@ class PublisherMDnsSd : public MainloopProcessor, public Publisher std::vector mAddrRegistered; }; + class DnssdKeyRegistration : public KeyRegistration + { + friend class DnssdServiceRegistration; + + public: + using KeyRegistration::KeyRegistration; // Inherit base class constructor + + ~DnssdKeyRegistration(void) override { Unregister(); } + + otbrError Register(void); + + private: + void Unregister(void); + PublisherMDnsSd &GetPublisher(void) { return *static_cast(mPublisher); } + void HandleRegisterResult(DNSServiceErrorType aError); + static void HandleRegisterResult(DNSServiceRef aServiceRef, + DNSRecordRef aRecordRef, + DNSServiceFlags aFlags, + DNSServiceErrorType aErrorCode, + void *aContext); + + DNSRecordRef mRecordRef = nullptr; + DnssdServiceRegistration *mRelatedServiceReg = nullptr; + }; + struct ServiceRef : private ::NonCopyable { DNSServiceRef mServiceRef; diff --git a/tests/mdns/main.cpp b/tests/mdns/main.cpp index 66f6366c5c7..e8fe35522ba 100644 --- a/tests/mdns/main.cpp +++ b/tests/mdns/main.cpp @@ -26,6 +26,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#define OTBR_LOG_TAG "TEST" + #include #include #include @@ -36,6 +38,7 @@ #include #include +#include #include #include "common/code_utils.hpp" @@ -45,12 +48,11 @@ #include "mdns/mdns.hpp" using namespace otbr; +using namespace otbr::Mdns; -static struct Context -{ - Mdns::Publisher *mPublisher; - bool mUpdate; -} sContext; +static Publisher *sPublisher = nullptr; + +typedef std::function TestRunner; int RunMainloop(void) { @@ -82,327 +84,300 @@ int RunMainloop(void) return rval; } -void PublishSingleServiceWithCustomHost(void *aContext, Mdns::Publisher::State aState) +std::function ErrorChecker(std::string aMessage) { - uint8_t xpanid[kSizeExtPanId] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; - uint8_t extAddr[kSizeExtAddr] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; - uint8_t hostAddr[OTBR_IP6_ADDRESS_SIZE] = {0}; - const char hostName[] = "custom-host"; + return [aMessage](otbrError aError) { + if (aError == OTBR_ERROR_NONE) + { + otbrLogInfo("Got success callback: %s", aMessage.c_str()); + } + else + { + otbrLogEmerg("Got error %d callback: %s", aError, aMessage.c_str()); + exit(-1); + } + }; +} + +void PublishSingleServiceWithCustomHost(void) +{ + uint8_t xpanid[kSizeExtPanId] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; + uint8_t extAddr[kSizeExtAddr] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; + uint8_t hostAddr[OTBR_IP6_ADDRESS_SIZE] = {0}; + const char hostName[] = "custom-host"; + std::vector keyData = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; + Publisher::TxtData txtData; + Publisher::TxtList txtList{ + {"nn", "cool"}, + {"xp", xpanid, sizeof(xpanid)}, + {"tv", "1.1.1"}, + {"xa", extAddr, sizeof(extAddr)}, + }; + + otbrLogInfo("PublishSingleServiceWithCustomHost"); hostAddr[0] = 0x20; hostAddr[1] = 0x02; hostAddr[15] = 0x01; - VerifyOrDie(aContext == &sContext, "unexpected context"); - if (aState == Mdns::Publisher::State::kReady) - { - Mdns::Publisher::TxtData txtData; - Mdns::Publisher::TxtList txtList{ - {"nn", "cool"}, {"xp", xpanid, sizeof(xpanid)}, {"tv", "1.1.1"}, {"xa", extAddr, sizeof(extAddr)}}; - - Mdns::Publisher::EncodeTxtData(txtList, txtData); + Publisher::EncodeTxtData(txtList, txtData); - sContext.mPublisher->PublishHost(hostName, {Ip6Address(hostAddr)}, - [](otbrError aError) { SuccessOrDie(aError, "cannot publish the host"); }); - - sContext.mPublisher->PublishService( - hostName, "SingleService", "_meshcop._udp", Mdns::Publisher::SubTypeList{}, 12345, txtData, - [](otbrError aError) { SuccessOrDie(aError, "cannot publish the service"); }); - } + sPublisher->PublishKey(hostName, keyData, ErrorChecker("publish key for host")); + sPublisher->PublishHost(hostName, {Ip6Address(hostAddr)}, ErrorChecker("publish the host")); + sPublisher->PublishService(hostName, "SingleService", "_meshcop._udp", Publisher::SubTypeList{}, 12345, txtData, + ErrorChecker("publish the service")); + sPublisher->PublishKey("SingleService._meshcop._udp", keyData, ErrorChecker("publish key for service")); } -void PublishMultipleServicesWithCustomHost(void *aContext, Mdns::Publisher::State aState) +void PublishSingleServiceWithKeyAfterwards(void) { - uint8_t xpanid[kSizeExtPanId] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; - uint8_t extAddr[kSizeExtAddr] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; - uint8_t hostAddr[OTBR_IP6_ADDRESS_SIZE] = {0}; - const char hostName1[] = "custom-host-1"; - const char hostName2[] = "custom-host-2"; + uint8_t hostAddr[OTBR_IP6_ADDRESS_SIZE] = {0}; + const char hostName[] = "custom-host"; + Publisher::TxtData txtData; + + otbrLogInfo("PublishSingleServiceWithKeyAfterwards"); hostAddr[0] = 0x20; hostAddr[1] = 0x02; hostAddr[15] = 0x01; - VerifyOrDie(aContext == &sContext, "unexpected context"); - if (aState == Mdns::Publisher::State::kReady) - { - Mdns::Publisher::TxtData txtData; - Mdns::Publisher::TxtList txtList{ - {"nn", "cool"}, {"xp", xpanid, sizeof(xpanid)}, {"tv", "1.1.1"}, {"xa", extAddr, sizeof(extAddr)}}; - - Mdns::Publisher::EncodeTxtData(txtList, txtData); + txtData.push_back(0); - sContext.mPublisher->PublishHost(hostName1, {Ip6Address(hostAddr)}, - [](otbrError aError) { SuccessOrDie(aError, "cannot publish the host"); }); + sPublisher->PublishHost(hostName, {Ip6Address(hostAddr)}, ErrorChecker("publish the host")); - sContext.mPublisher->PublishService( - hostName1, "MultipleService11", "_meshcop._udp", Mdns::Publisher::SubTypeList{}, 12345, txtData, - [](otbrError aError) { SuccessOrDie(aError, "cannot publish the first service"); }); + sPublisher->PublishService( + hostName, "SingleService", "_meshcop._udp", Publisher::SubTypeList{}, 12345, txtData, [](otbrError aError) { + std::vector keyData = {0x55, 0xaa, 0xbb, 0xcc, 0x77, 0x33}; - sContext.mPublisher->PublishService( - hostName1, "MultipleService12", "_meshcop._udp", Mdns::Publisher::SubTypeList{}, 12345, txtData, - [](otbrError aError) { SuccessOrDie(aError, "cannot publish the second service"); }); + SuccessOrDie(aError, "publish the service"); - sContext.mPublisher->PublishHost(hostName2, {Ip6Address(hostAddr)}, [](otbrError aError) { - SuccessOrDie(aError, "cannot publish the second host"); + sPublisher->PublishKey("SingleService._meshcop._udp", keyData, ErrorChecker("publish key for service")); }); - - sContext.mPublisher->PublishService( - hostName2, "MultipleService21", "_meshcop._udp", Mdns::Publisher::SubTypeList{}, 12345, txtData, - [](otbrError aError) { SuccessOrDie(aError, "cannot publish the first service"); }); - - sContext.mPublisher->PublishService( - hostName2, "MultipleService22", "_meshcop._udp", Mdns::Publisher::SubTypeList{}, 12345, txtData, - [](otbrError aError) { SuccessOrDie(aError, "cannot publish the second service"); }); - } } -void PublishSingleService(void *aContext, Mdns::Publisher::State aState) +void PublishMultipleServicesWithCustomHost(void) { - OT_UNUSED_VARIABLE(aContext); + uint8_t xpanid[kSizeExtPanId] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; + uint8_t extAddr[kSizeExtAddr] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; + uint8_t hostAddr[OTBR_IP6_ADDRESS_SIZE] = {0}; + const char hostName1[] = "custom-host-1"; + const char hostName2[] = "custom-host-2"; + std::vector keyData1 = {0x10, 0x20, 0x03, 0x15}; + std::vector keyData2 = {0xCA, 0xFE, 0xBE, 0xEF}; + Publisher::TxtData txtData; + Publisher::TxtList txtList{ + {"nn", "cool"}, + {"xp", xpanid, sizeof(xpanid)}, + {"tv", "1.1.1"}, + {"xa", extAddr, sizeof(extAddr)}, + }; + + otbrLogInfo("PublishMultipleServicesWithCustomHost"); - uint8_t xpanid[kSizeExtPanId] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; - uint8_t extAddr[kSizeExtAddr] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; - Mdns::Publisher::TxtData txtData; - Mdns::Publisher::TxtList txtList{ - {"nn", "cool"}, {"xp", xpanid, sizeof(xpanid)}, {"tv", "1.1.1"}, {"xa", extAddr, sizeof(extAddr)}}; + hostAddr[0] = 0x20; + hostAddr[1] = 0x02; + hostAddr[15] = 0x01; - Mdns::Publisher::EncodeTxtData(txtList, txtData); + Publisher::EncodeTxtData(txtList, txtData); - assert(aContext == &sContext); - if (aState == Mdns::Publisher::State::kReady) - { - sContext.mPublisher->PublishService( - "", "SingleService", "_meshcop._udp", Mdns::Publisher::SubTypeList{}, 12345, txtData, - [](otbrError aError) { SuccessOrDie(aError, "SingleService._meshcop._udp"); }); - } -} + // For host1 and its services we register keys first, then host/services -void PublishSingleServiceWithEmptyName(void *aContext, Mdns::Publisher::State aState) -{ - OT_UNUSED_VARIABLE(aContext); + sPublisher->PublishKey(hostName1, keyData1, ErrorChecker("publish key for host1")); + sPublisher->PublishKey("MultipleService11._meshcop._udp", keyData1, ErrorChecker("publish key for service11")); + sPublisher->PublishKey("MultipleService12._meshcop._udp", keyData1, ErrorChecker("publish key for service12")); - uint8_t xpanid[kSizeExtPanId] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; - uint8_t extAddr[kSizeExtAddr] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; - Mdns::Publisher::TxtData txtData; - Mdns::Publisher::TxtList txtList{ - {"nn", "cool"}, {"xp", xpanid, sizeof(xpanid)}, {"tv", "1.1.1"}, {"xa", extAddr, sizeof(extAddr)}}; + sPublisher->PublishHost(hostName1, {Ip6Address(hostAddr)}, ErrorChecker("publish the host1")); + sPublisher->PublishService(hostName1, "MultipleService11", "_meshcop._udp", Publisher::SubTypeList{}, 12345, + txtData, ErrorChecker("publish service11")); + sPublisher->PublishService(hostName1, "MultipleService12", "_meshcop._udp", Publisher::SubTypeList{}, 12345, + txtData, ErrorChecker("publish service12")); - Mdns::Publisher::EncodeTxtData(txtList, txtData); + // For host2 and its services we register host and services first, then keys. - assert(aContext == &sContext); - if (aState == Mdns::Publisher::State::kReady) - { - sContext.mPublisher->PublishService("", "", "_meshcop._udp", Mdns::Publisher::SubTypeList{}, 12345, txtData, - [](otbrError aError) { SuccessOrDie(aError, "(empty)._meshcop._udp"); }); - } + sPublisher->PublishHost(hostName2, {Ip6Address(hostAddr)}, ErrorChecker("publish host2")); + sPublisher->PublishService(hostName2, "MultipleService21", "_meshcop._udp", Publisher::SubTypeList{}, 12345, + txtData, ErrorChecker("publish service21")); + sPublisher->PublishService(hostName2, "MultipleService22", "_meshcop._udp", Publisher::SubTypeList{}, 12345, + txtData, ErrorChecker("publish service22")); + + sPublisher->PublishKey(hostName2, keyData2, ErrorChecker("publish key for host2")); + sPublisher->PublishKey("MultipleService21._meshcop._udp", keyData2, ErrorChecker("publish key for service21")); + sPublisher->PublishKey("MultipleService22._meshcop._udp", keyData2, ErrorChecker("publish key for service22")); } -void PublishMultipleServices(void *aContext, Mdns::Publisher::State aState) +void PublishSingleService(void) { - OT_UNUSED_VARIABLE(aContext); - - uint8_t xpanid[kSizeExtPanId] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; - uint8_t extAddr[kSizeExtAddr] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; - - assert(aContext == &sContext); - if (aState == Mdns::Publisher::State::kReady) - { - Mdns::Publisher::TxtData txtData; - Mdns::Publisher::TxtList txtList{ - {"nn", "cool1"}, {"xp", xpanid, sizeof(xpanid)}, {"tv", "1.1.1"}, {"xa", extAddr, sizeof(extAddr)}}; - - Mdns::Publisher::EncodeTxtData(txtList, txtData); - - sContext.mPublisher->PublishService( - "", "MultipleService1", "_meshcop._udp", Mdns::Publisher::SubTypeList{}, 12345, txtData, - [](otbrError aError) { SuccessOrDie(aError, "MultipleService1._meshcop._udp"); }); - } - - if (aState == Mdns::Publisher::State::kReady) - { - Mdns::Publisher::TxtData txtData; - Mdns::Publisher::TxtList txtList{ - {"nn", "cool2"}, {"xp", xpanid, sizeof(xpanid)}, {"tv", "1.1.1"}, {"xa", extAddr, sizeof(extAddr)}}; - - Mdns::Publisher::EncodeTxtData(txtList, txtData); - - sContext.mPublisher->PublishService( - "", "MultipleService2", "_meshcop._udp", Mdns::Publisher::SubTypeList{}, 12345, txtData, - [](otbrError aError) { SuccessOrDie(aError, "MultipleService2._meshcop._udp"); }); - } + uint8_t xpanid[kSizeExtPanId] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; + uint8_t extAddr[kSizeExtAddr] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; + Publisher::TxtData txtData; + Publisher::TxtList txtList{ + {"nn", "cool"}, + {"xp", xpanid, sizeof(xpanid)}, + {"tv", "1.1.1"}, + {"xa", extAddr, sizeof(extAddr)}, + }; + + otbrLogInfo("PublishSingleService"); + + Publisher::EncodeTxtData(txtList, txtData); + + sPublisher->PublishService("", "SingleService", "_meshcop._udp", Publisher::SubTypeList{}, 12345, txtData, + ErrorChecker("publish service")); } -void PublishUpdateServices(void *aContext) +void PublishSingleServiceWithEmptyName(void) { - OT_UNUSED_VARIABLE(aContext); - - uint8_t xpanidOld[kSizeExtPanId] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; - uint8_t xpanidNew[kSizeExtPanId] = {0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41}; - uint8_t extAddr[kSizeExtAddr] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; - - assert(aContext == &sContext); - if (!sContext.mUpdate) - { - Mdns::Publisher::TxtData txtData; - Mdns::Publisher::TxtList txtList{ - {"nn", "cool"}, {"xp", xpanidOld, sizeof(xpanidOld)}, {"tv", "1.1.1"}, {"xa", extAddr, sizeof(extAddr)}}; - - Mdns::Publisher::EncodeTxtData(txtList, txtData); - - sContext.mPublisher->PublishService( - "", "UpdateService", "_meshcop._udp", Mdns::Publisher::SubTypeList{}, 12345, txtData, - [](otbrError aError) { otbrLogResult(aError, "UpdateService._meshcop._udp"); }); - } - else - { - Mdns::Publisher::TxtData txtData; - Mdns::Publisher::TxtList txtList{{"nn", "coolcool"}, - {"xp", xpanidNew, sizeof(xpanidNew)}, - {"tv", "1.1.1"}, - {"xa", extAddr, sizeof(extAddr)}}; - - Mdns::Publisher::EncodeTxtData(txtList, txtData); - - sContext.mPublisher->PublishService( - "", "UpdateService", "_meshcop._udp", Mdns::Publisher::SubTypeList{}, 12345, txtData, - [](otbrError aError) { SuccessOrDie(aError, "UpdateService._meshcop._udp"); }); - } + uint8_t xpanid[kSizeExtPanId] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; + uint8_t extAddr[kSizeExtAddr] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; + Publisher::TxtData txtData; + Publisher::TxtList txtList{ + {"nn", "cool"}, + {"xp", xpanid, sizeof(xpanid)}, + {"tv", "1.1.1"}, + {"xa", extAddr, sizeof(extAddr)}, + }; + + otbrLogInfo("PublishSingleServiceWithEmptyName"); + + Publisher::EncodeTxtData(txtList, txtData); + + sPublisher->PublishService("", "", "_meshcop._udp", Publisher::SubTypeList{}, 12345, txtData, + ErrorChecker("publish (empty)._meshcop._udp")); } -void PublishServiceSubTypes(void *aContext) +void PublishMultipleServices(void) { - Mdns::Publisher::TxtData txtData; - - txtData.push_back(0); - - OT_UNUSED_VARIABLE(aContext); - - assert(aContext == &sContext); - - Mdns::Publisher::SubTypeList subTypeList{"_subtype1", "_SUBTYPE2"}; - - subTypeList.back() = "_SUBTYPE3"; - - sContext.mPublisher->PublishService( - "", "ServiceWithSubTypes", "_meshcop._udp", subTypeList, 12345, txtData, - [](otbrError aError) { SuccessOrDie(aError, "ServiceWithSubTypes._meshcop._udp"); }); + uint8_t xpanid[kSizeExtPanId] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; + uint8_t extAddr[kSizeExtAddr] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; + Publisher::TxtData txtData; + Publisher::TxtList txtList1{ + {"nn", "cool1"}, + {"xp", xpanid, sizeof(xpanid)}, + {"tv", "1.1.1"}, + {"xa", extAddr, sizeof(extAddr)}, + }; + Publisher::TxtList txtList2{ + {"nn", "cool2"}, + {"xp", xpanid, sizeof(xpanid)}, + {"tv", "1.1.1"}, + {"xa", extAddr, sizeof(extAddr)}, + }; + + otbrLogInfo("PublishMultipleServices"); + + Publisher::EncodeTxtData(txtList1, txtData); + + sPublisher->PublishService("", "MultipleService1", "_meshcop._udp", Publisher::SubTypeList{}, 12345, txtData, + ErrorChecker("publish MultipleService1._meshcop._udp")); + + Publisher::EncodeTxtData(txtList2, txtData); + + sPublisher->PublishService("", "MultipleService2", "_meshcop._udp", Publisher::SubTypeList{}, 12345, txtData, + ErrorChecker("publish MultipleService2._meshcop._udp")); } -otbrError TestSingleServiceWithCustomHost(void) +void PublishUpdateServices(void) { - otbrError error = OTBR_ERROR_NONE; - - Mdns::Publisher *pub = Mdns::Publisher::Create( - [](Mdns::Publisher::State aState) { PublishSingleServiceWithCustomHost(&sContext, aState); }); - sContext.mPublisher = pub; - SuccessOrExit(error = pub->Start()); - RunMainloop(); - -exit: - Mdns::Publisher::Destroy(pub); - return error; + uint8_t xpanidOld[kSizeExtPanId] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; + uint8_t xpanidNew[kSizeExtPanId] = {0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41}; + uint8_t extAddr[kSizeExtAddr] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; + Publisher::TxtData txtData; + Publisher::TxtList txtList1{ + {"nn", "cool"}, + {"xp", xpanidOld, sizeof(xpanidOld)}, + {"tv", "1.1.1"}, + {"xa", extAddr, sizeof(extAddr)}, + }; + Publisher::TxtList txtList2{ + {"nn", "coolcool"}, + {"xp", xpanidNew, sizeof(xpanidNew)}, + {"tv", "1.1.1"}, + {"xa", extAddr, sizeof(extAddr)}, + }; + + otbrLogInfo("PublishUpdateServices"); + + Publisher::EncodeTxtData(txtList1, txtData); + + sPublisher->PublishService("", "UpdateService", "_meshcop._udp", Publisher::SubTypeList{}, 12345, txtData, + [](otbrError aError) { otbrLogResult(aError, "UpdateService._meshcop._udp"); }); + + Publisher::EncodeTxtData(txtList2, txtData); + + sPublisher->PublishService("", "UpdateService", "_meshcop._udp", Publisher::SubTypeList{}, 12345, txtData, + ErrorChecker("publish UpdateService._meshcop._udp")); } -otbrError TestMultipleServicesWithCustomHost(void) +void PublishServiceSubTypes(void) { - otbrError error = OTBR_ERROR_NONE; + Publisher::TxtData txtData; + Publisher::SubTypeList subTypeList{"_subtype1", "_SUBTYPE2"}; - Mdns::Publisher *pub = Mdns::Publisher::Create( - [](Mdns::Publisher::State aState) { PublishMultipleServicesWithCustomHost(&sContext, aState); }); - sContext.mPublisher = pub; - SuccessOrExit(error = pub->Start()); - RunMainloop(); + otbrLogInfo("PublishServiceSubTypes"); -exit: - Mdns::Publisher::Destroy(pub); - return error; + txtData.push_back(0); + + subTypeList.back() = "_SUBTYPE3"; + + sPublisher->PublishService("", "ServiceWithSubTypes", "_meshcop._udp", subTypeList, 12345, txtData, + ErrorChecker("publish ServiceWithSubTypes._meshcop._udp")); } -otbrError TestSingleService(void) +void PublishKey(void) { - otbrError ret = OTBR_ERROR_NONE; + std::vector keyData = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}; - Mdns::Publisher *pub = - Mdns::Publisher::Create([](Mdns::Publisher::State aState) { PublishSingleService(&sContext, aState); }); - sContext.mPublisher = pub; - SuccessOrExit(ret = pub->Start()); - RunMainloop(); + otbrLogInfo("PublishKey"); -exit: - Mdns::Publisher::Destroy(pub); - return ret; + sPublisher->PublishKey("SingleService._meshcop._udp", keyData, ErrorChecker("publish key for service")); } -otbrError TestSingleServiceWithEmptyName(void) +void PublishKeyWithServiceRemoved(void) { - otbrError ret = OTBR_ERROR_NONE; + uint8_t hostAddr[OTBR_IP6_ADDRESS_SIZE] = {0}; + const char hostName[] = "custom-host"; + Publisher::TxtData txtData; - Mdns::Publisher *pub = Mdns::Publisher::Create( - [](Mdns::Publisher::State aState) { PublishSingleServiceWithEmptyName(&sContext, aState); }); - sContext.mPublisher = pub; - SuccessOrExit(ret = pub->Start()); - RunMainloop(); + otbrLogInfo("PublishKeyWithServiceRemoved"); -exit: - Mdns::Publisher::Destroy(pub); - return ret; -} + hostAddr[0] = 0x20; + hostAddr[1] = 0x02; + hostAddr[15] = 0x01; -otbrError TestMultipleServices(void) -{ - otbrError ret = OTBR_ERROR_NONE; + txtData.push_back(0); - Mdns::Publisher *pub = - Mdns::Publisher::Create([](Mdns::Publisher::State aState) { PublishMultipleServices(&sContext, aState); }); - sContext.mPublisher = pub; - SuccessOrExit(ret = pub->Start()); - RunMainloop(); + sPublisher->PublishHost(hostName, {Ip6Address(hostAddr)}, ErrorChecker("publish the host")); -exit: - Mdns::Publisher::Destroy(pub); - return ret; -} + sPublisher->PublishService( + hostName, "SingleService", "_meshcop._udp", Publisher::SubTypeList{}, 12345, txtData, [](otbrError aError) { + std::vector keyData = {0x55, 0xaa, 0xbb, 0xcc, 0x77, 0x33}; -otbrError TestUpdateService(void) -{ - otbrError ret = OTBR_ERROR_NONE; + SuccessOrDie(aError, "publish the service"); - Mdns::Publisher *pub = Mdns::Publisher::Create([](Mdns::Publisher::State aState) { - if (aState == Mdns::Publisher::State::kReady) - { - sContext.mUpdate = false; - PublishUpdateServices(&sContext); - sContext.mUpdate = true; - PublishUpdateServices(&sContext); - } - }); - sContext.mPublisher = pub; - SuccessOrExit(ret = pub->Start()); - RunMainloop(); + sPublisher->PublishKey("SingleService._meshcop._udp", keyData, [](otbrError aError) { + SuccessOrDie(aError, "publish key for service"); -exit: - Mdns::Publisher::Destroy(pub); - return ret; + sPublisher->UnpublishService("SingleService", "_meshcop._udp", ErrorChecker("unpublish service")); + }); + }); } -otbrError TestServiceSubTypes(void) +otbrError Test(TestRunner aTestRunner) { - otbrError ret = OTBR_ERROR_NONE; + otbrError error = OTBR_ERROR_NONE; - Mdns::Publisher *pub = Mdns::Publisher::Create([](Mdns::Publisher::State aState) { - if (aState == Mdns::Publisher::State::kReady) + sPublisher = Publisher::Create([aTestRunner](Publisher::State aState) { + if (aState == Publisher::State::kReady) { - PublishServiceSubTypes(&sContext); + aTestRunner(); } }); - sContext.mPublisher = pub; - SuccessOrExit(ret = pub->Start()); + SuccessOrExit(error = sPublisher->Start()); RunMainloop(); exit: - Mdns::Publisher::Destroy(pub); - return ret; + Publisher::Destroy(sPublisher); + return error; } void RecoverSignal(int aSignal) @@ -421,37 +396,42 @@ otbrError TestStopService(void) { otbrError ret = OTBR_ERROR_NONE; - Mdns::Publisher *pub = - Mdns::Publisher::Create([](Mdns::Publisher::State aState) { PublishSingleService(&sContext, aState); }); - sContext.mPublisher = pub; - SuccessOrExit(ret = pub->Start()); + otbrLogInfo("TestStopService"); + + sPublisher = Publisher::Create([](Publisher::State aState) { + if (aState == Publisher::State::kReady) + { + PublishSingleService(); + } + }); + SuccessOrExit(ret = sPublisher->Start()); signal(SIGUSR1, RecoverSignal); signal(SIGUSR2, RecoverSignal); RunMainloop(); - sContext.mPublisher->Stop(); + sPublisher->Stop(); RunMainloop(); - SuccessOrExit(ret = sContext.mPublisher->Start()); + SuccessOrExit(ret = sPublisher->Start()); RunMainloop(); exit: - Mdns::Publisher::Destroy(pub); + Publisher::Destroy(sPublisher); return ret; } otbrError CheckTxtDataEncoderDecoder(void) { - otbrError error = OTBR_ERROR_NONE; - Mdns::Publisher::TxtList txtList; - Mdns::Publisher::TxtList parsedTxtList; - std::vector txtData; + otbrError error = OTBR_ERROR_NONE; + Publisher::TxtList txtList; + Publisher::TxtList parsedTxtList; + std::vector txtData; // Encode empty `TxtList` - SuccessOrExit(error = Mdns::Publisher::EncodeTxtData(txtList, txtData)); + SuccessOrExit(error = Publisher::EncodeTxtData(txtList, txtData)); VerifyOrExit(txtData.size() == 1, error = OTBR_ERROR_PARSE); VerifyOrExit(txtData[0] == 0, error = OTBR_ERROR_PARSE); - SuccessOrExit(error = Mdns::Publisher::DecodeTxtData(parsedTxtList, txtData.data(), txtData.size())); + SuccessOrExit(error = Publisher::DecodeTxtData(parsedTxtList, txtData.data(), txtData.size())); VerifyOrExit(parsedTxtList.size() == 0, error = OTBR_ERROR_PARSE); // TxtList with one bool attribute @@ -459,8 +439,8 @@ otbrError CheckTxtDataEncoderDecoder(void) txtList.clear(); txtList.emplace_back("b1"); - SuccessOrExit(error = Mdns::Publisher::EncodeTxtData(txtList, txtData)); - SuccessOrExit(error = Mdns::Publisher::DecodeTxtData(parsedTxtList, txtData.data(), txtData.size())); + SuccessOrExit(error = Publisher::EncodeTxtData(txtList, txtData)); + SuccessOrExit(error = Publisher::DecodeTxtData(parsedTxtList, txtData.data(), txtData.size())); VerifyOrExit(parsedTxtList == txtList, error = OTBR_ERROR_PARSE); // TxtList with one one key/value @@ -468,8 +448,8 @@ otbrError CheckTxtDataEncoderDecoder(void) txtList.clear(); txtList.emplace_back("k1", "v1"); - SuccessOrExit(error = Mdns::Publisher::EncodeTxtData(txtList, txtData)); - SuccessOrExit(error = Mdns::Publisher::DecodeTxtData(parsedTxtList, txtData.data(), txtData.size())); + SuccessOrExit(error = Publisher::EncodeTxtData(txtList, txtData)); + SuccessOrExit(error = Publisher::DecodeTxtData(parsedTxtList, txtData.data(), txtData.size())); VerifyOrExit(parsedTxtList == txtList, error = OTBR_ERROR_PARSE); // TxtList with multiple entries @@ -480,8 +460,8 @@ otbrError CheckTxtDataEncoderDecoder(void) txtList.emplace_back("b2"); txtList.emplace_back("k2", "valu2"); - SuccessOrExit(error = Mdns::Publisher::EncodeTxtData(txtList, txtData)); - SuccessOrExit(error = Mdns::Publisher::DecodeTxtData(parsedTxtList, txtData.data(), txtData.size())); + SuccessOrExit(error = Publisher::EncodeTxtData(txtList, txtData)); + SuccessOrExit(error = Publisher::DecodeTxtData(parsedTxtList, txtData.data(), txtData.size())); VerifyOrExit(parsedTxtList == txtList, error = OTBR_ERROR_PARSE); exit: @@ -511,33 +491,44 @@ int main(int argc, char *argv[]) switch (argv[1][1]) { case 'c': - ret = TestSingleServiceWithCustomHost(); + ret = Test(PublishSingleServiceWithCustomHost); break; case 'e': - ret = TestSingleServiceWithEmptyName(); + ret = Test(PublishSingleServiceWithEmptyName); + break; + case 'k': + ret = Test(PublishSingleServiceWithKeyAfterwards); break; default: - ret = TestSingleService(); + ret = Test(PublishSingleService); break; } break; case 'm': - ret = argv[1][1] == 'c' ? TestMultipleServicesWithCustomHost() : TestMultipleServices(); + ret = argv[1][1] == 'c' ? Test(PublishMultipleServicesWithCustomHost) : Test(PublishMultipleServices); break; case 'u': - ret = TestUpdateService(); + ret = Test(PublishUpdateServices); break; case 't': - ret = TestServiceSubTypes(); + ret = Test(PublishServiceSubTypes); break; case 'k': ret = TestStopService(); break; + case 'y': + ret = Test(PublishKey); + break; + + case 'z': + ret = Test(PublishKeyWithServiceRemoved); + break; + default: ret = 1; break;