diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index 89c72e0c5f2..819625ce6bb 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -98,6 +98,12 @@ if (OTBR_DNSSD_DISCOVERY_PROXY) target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_DNSSD_DISCOVERY_PROXY=1) endif() +# When enabled the Advertising Proxy and Discovery proxy will be supported by OpenThread core +option(OTBR_DNSSD_PLAT "Enable DNS-SD plat APIs for Advertising proxy and Discovery proxy in core" OFF) +if (OTBR_DNSSD_PLAT) + target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_DNSSD_PLAT=1) +endif() + option(OTBR_UNSECURE_JOIN "Enable unsecure joining" OFF) if(OTBR_UNSECURE_JOIN) target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_UNSECURE_JOIN=1) diff --git a/script/_otbr b/script/_otbr index dbccc73c88c..0c6d81d7bdf 100644 --- a/script/_otbr +++ b/script/_otbr @@ -73,6 +73,7 @@ otbr_install() "-DOTBR_DBUS=ON" "-DOTBR_DNSSD_DISCOVERY_PROXY=ON" "-DOTBR_SRP_ADVERTISING_PROXY=ON" + "-DOTBR_DNSSD_PLAT=OFF" "-DOTBR_INFRA_IF_NAME=${INFRA_IF_NAME}" "-DOTBR_MDNS=${OTBR_MDNS:=mDNSResponder}" # Force re-evaluation of version strings diff --git a/src/agent/application.cpp b/src/agent/application.cpp index 73da484b3b0..98f33ea1701 100644 --- a/src/agent/application.cpp +++ b/src/agent/application.cpp @@ -70,6 +70,9 @@ Application::Application(const std::string &aInterfaceName, #if OTBR_ENABLE_BACKBONE_ROUTER , mBackboneAgent(mNcp, aInterfaceName, mBackboneInterfaceName) #endif +#if OTBR_ENABLE_DNSSD_PLAT + , mDnssdPlatform(mNcp, *mPublisher) +#endif #if OTBR_ENABLE_SRP_ADVERTISING_PROXY , mAdvertisingProxy(mNcp, *mPublisher) #endif @@ -109,6 +112,9 @@ void Application::Init(void) #if OTBR_ENABLE_BACKBONE_ROUTER mBackboneAgent.Init(); #endif +#if OTBR_ENABLE_DNSSD_PLAT + mDnssdPlatform.Start(); +#endif #if OTBR_ENABLE_SRP_ADVERTISING_PROXY mAdvertisingProxy.SetEnabled(true); #endif @@ -131,6 +137,9 @@ void Application::Init(void) void Application::Deinit(void) { +#if OTBR_ENABLE_DNSSD_PLAT + mDnssdPlatform.Stop(); +#endif #if OTBR_ENABLE_SRP_ADVERTISING_PROXY mAdvertisingProxy.SetEnabled(false); #endif @@ -226,6 +235,9 @@ void Application::HandleMdnsState(Mdns::Publisher::State aState) { OTBR_UNUSED_VARIABLE(aState); +#if OTBR_ENABLE_DNSSD_PLAT + mDnssdPlatform.HandleMdnsPublisherStateChange(aState); +#endif #if OTBR_ENABLE_BORDER_AGENT mBorderAgent.HandleMdnsState(aState); #endif diff --git a/src/agent/application.hpp b/src/agent/application.hpp index 2f92d69438f..81ddebb90c3 100644 --- a/src/agent/application.hpp +++ b/src/agent/application.hpp @@ -271,6 +271,9 @@ class Application : private NonCopyable #if OTBR_ENABLE_BACKBONE_ROUTER BackboneRouter::BackboneAgent mBackboneAgent; #endif +#if OTBR_ENABLE_DNSSD_PLAT + DnssdPlatform mDnssdPlatform; +#endif #if OTBR_ENABLE_SRP_ADVERTISING_PROXY AdvertisingProxy mAdvertisingProxy; #endif diff --git a/src/border_agent/border_agent.hpp b/src/border_agent/border_agent.hpp index c95dc3cfbfb..3cc72b7c93f 100644 --- a/src/border_agent/border_agent.hpp +++ b/src/border_agent/border_agent.hpp @@ -47,6 +47,7 @@ #include "backbone_router/backbone_agent.hpp" #include "common/code_utils.hpp" #include "common/mainloop.hpp" +#include "mdns/dnssd_plat.hpp" #include "mdns/mdns.hpp" #include "ncp/ncp_openthread.hpp" #include "sdp_proxy/advertising_proxy.hpp" diff --git a/src/mdns/CMakeLists.txt b/src/mdns/CMakeLists.txt index 1b28dc3cf49..6027aee1012 100644 --- a/src/mdns/CMakeLists.txt +++ b/src/mdns/CMakeLists.txt @@ -30,6 +30,7 @@ if(OTBR_MDNS STREQUAL "avahi") add_library(otbr-mdns mdns.cpp mdns_avahi.cpp + dnssd_plat.cpp ) target_compile_definitions(otbr-mdns PUBLIC OTBR_ENABLE_MDNS_AVAHI=1 @@ -48,6 +49,7 @@ if(OTBR_MDNS STREQUAL "mDNSResponder") add_library(otbr-mdns mdns.cpp mdns_mdnssd.cpp + dnssd_plat.cpp ) target_compile_definitions(otbr-mdns PUBLIC OTBR_ENABLE_MDNS_MDNSSD=1 diff --git a/src/mdns/dnssd_plat.cpp b/src/mdns/dnssd_plat.cpp new file mode 100644 index 00000000000..71efc331690 --- /dev/null +++ b/src/mdns/dnssd_plat.cpp @@ -0,0 +1,597 @@ +/* + * Copyright (c) 2023, 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. + */ + +/** + * @file + * This file includes definitions for implementing OpenThread DNS-SD platform APIs. + */ + +#define OTBR_LOG_TAG "DnssdPlat" + +#include "mdns/dnssd_plat.hpp" + +#include + +#include "common/code_utils.hpp" +#include "utils/dns_utils.hpp" + +#if OTBR_ENABLE_DNSSD_PLAT + +//---------------------------------------------------------------------------------------------------------------------- +// otPlatDnssd APIs + +extern "C" otPlatDnssdState otPlatDnssdGetState(otInstance *aInstance) +{ + OTBR_UNUSED_VARIABLE(aInstance); + + return otbr::DnssdPlatform::Get().GetState(); +} + +extern "C" void otPlatDnssdRegisterService(otInstance *aInstance, + const otPlatDnssdService *aService, + otPlatDnssdRequestId aRequestId, + otPlatDnssdRegisterCallback aCallback) +{ + OTBR_UNUSED_VARIABLE(aInstance); + otbr::DnssdPlatform::Get().RegisterService(*aService, aRequestId, aCallback); +} + +extern "C" void otPlatDnssdUnregisterService(otInstance *aInstance, + const otPlatDnssdService *aService, + otPlatDnssdRequestId aRequestId, + otPlatDnssdRegisterCallback aCallback) +{ + OTBR_UNUSED_VARIABLE(aInstance); + otbr::DnssdPlatform::Get().UnregisterService(*aService, aRequestId, aCallback); +} + +extern "C" void otPlatDnssdRegisterHost(otInstance *aInstance, + const otPlatDnssdHost *aHost, + otPlatDnssdRequestId aRequestId, + otPlatDnssdRegisterCallback aCallback) +{ + OTBR_UNUSED_VARIABLE(aInstance); + otbr::DnssdPlatform::Get().RegisterHost(*aHost, aRequestId, aCallback); +} + +extern "C" void otPlatDnssdUnregisterHost(otInstance *aInstance, + const otPlatDnssdHost *aHost, + otPlatDnssdRequestId aRequestId, + otPlatDnssdRegisterCallback aCallback) +{ + OTBR_UNUSED_VARIABLE(aInstance); + otbr::DnssdPlatform::Get().UnregisterHost(*aHost, aRequestId, aCallback); +} + +extern "C" void otPlatDnssdRegisterKey(otInstance *aInstance, + const otPlatDnssdKey *aKey, + otPlatDnssdRequestId aRequestId, + otPlatDnssdRegisterCallback aCallback) +{ + OTBR_UNUSED_VARIABLE(aInstance); + otbr::DnssdPlatform::Get().RegisterKey(*aKey, aRequestId, aCallback); +} + +extern "C" void otPlatDnssdUnregisterKey(otInstance *aInstance, + const otPlatDnssdKey *aKey, + otPlatDnssdRequestId aRequestId, + otPlatDnssdRegisterCallback aCallback) +{ + OTBR_UNUSED_VARIABLE(aInstance); + otbr::DnssdPlatform::Get().UnregisterKey(*aKey, aRequestId, aCallback); +} + +// This is a temporary config to allow us to build/test (till the PRs in OT which define these APIs are merged). +#define OTBR_DNSSD_ADD_BROWSER_RESOLVER_APIS 1 + +#if OTBR_DNSSD_ADD_BROWSER_RESOLVER_APIS + +extern "C" void otPlatDnssdStartServiceBrowser(otInstance *aInstance, const char *aServiceType, uint32_t aInfraIfIndex) +{ + OTBR_UNUSED_VARIABLE(aInstance); + otbr::DnssdPlatform::Get().StartServiceBrowser(aServiceType, aInfraIfIndex); +} + +extern "C" void otPlatDnssdStopServiceBrowser(otInstance *aInstance, const char *aServiceType, uint32_t aInfraIfIndex) +{ + OTBR_UNUSED_VARIABLE(aInstance); + otbr::DnssdPlatform::Get().StopServiceBrowser(aServiceType, aInfraIfIndex); +} + +extern "C" void otPlatDnssdStartServiceResolver(otInstance *aInstance, + const otPlatDnssdServiceInstance *aServiceInstance) +{ + OTBR_UNUSED_VARIABLE(aInstance); + otbr::DnssdPlatform::Get().StartServiceResolver(*aServiceInstance); +} + +extern "C" void otPlatDnssdStopServiceResolver(otInstance *aInstance, + const otPlatDnssdServiceInstance *aServiceInstance) +{ + OTBR_UNUSED_VARIABLE(aInstance); + otbr::DnssdPlatform::Get().StopServiceResolver(*aServiceInstance); +} + +extern "C" void otPlatDnssdStartIp6AddressResolver(otInstance *aInstance, const char *aHostName, uint32_t aInfraIfIndex) +{ + OTBR_UNUSED_VARIABLE(aInstance); + otbr::DnssdPlatform::Get().StartIp6AddressResolver(aHostName, aInfraIfIndex); +} + +extern "C" void otPlatDnssdStopIp6AddressResolver(otInstance *aInstance, const char *aHostName, uint32_t aInfraIfIndex) +{ + OTBR_UNUSED_VARIABLE(aInstance); + otbr::DnssdPlatform::Get().StopIp6AddressResolver(aHostName, aInfraIfIndex); +} + +extern "C" void otPlatDnssdStartIp4AddressResolver(otInstance *aInstance, const char *aHostName, uint32_t aInfraIfIndex) +{ + OTBR_UNUSED_VARIABLE(aInstance); + OTBR_UNUSED_VARIABLE(aHostName); + OTBR_UNUSED_VARIABLE(aInfraIfIndex); +} + +extern "C" void otPlatDnssdStopIp4AddressResolver(otInstance *aInstance, const char *aHostName, uint32_t aInfraIfIndex) +{ + OTBR_UNUSED_VARIABLE(aInstance); + OTBR_UNUSED_VARIABLE(aHostName); + OTBR_UNUSED_VARIABLE(aInfraIfIndex); +} + +#endif // OTBR_DNSSD_ADD_BROWSER_RESOLVER_APIS + +//---------------------------------------------------------------------------------------------------------------------- + +namespace otbr { + +DnssdPlatform *DnssdPlatform::sDnssdPlatform = nullptr; + +DnssdPlatform::DnssdPlatform(Ncp::ControllerOpenThread &aNcp, Mdns::Publisher &aPublisher) + : mNcp(aNcp) + , mPublisher(aPublisher) + , mState(kStateStopped) + , mRunning(false) + , mPublisherState(Mdns::Publisher::State::kIdle) +{ + sDnssdPlatform = this; +} + +void DnssdPlatform::Start(void) +{ + if (!mRunning) + { + mRunning = true; + UpdateState(); + } +} + +void DnssdPlatform::Stop(void) +{ + if (mRunning) + { + mRunning = false; + UpdateState(); + } +} + +void DnssdPlatform::HandleMdnsPublisherStateChange(Mdns::Publisher::State aState) +{ + if (mPublisherState != aState) + { + mPublisherState = aState; + UpdateState(); + } +} + +void DnssdPlatform::UpdateState(void) +{ + if (mRunning && (mPublisherState == Mdns::Publisher::State::kReady)) + { + VerifyOrExit(mState != kStateReady); + + mState = kStateReady; + mSubscriberId = mPublisher.AddSubscriptionCallbacks(HandleDiscoveredService, HandleDiscoveredHost); + } + else + { + VerifyOrExit(mState != kStateStopped); + + mServiceBrowsers.clear(); + mServiceResolvers.clear(); + mIp6AddrResolvers.clear(); + mState = kStateStopped; + mPublisher.RemoveSubscriptionCallbacks(mSubscriberId); + } + + otPlatDnssdStateHandleStateChange(mNcp.GetInstance()); + +exit: + return; +} + +otError DnssdPlatform::ResultToError(otbrError aOtbrError) +{ + otError error = OT_ERROR_FAILED; + + switch (aOtbrError) + { + case OTBR_ERROR_NONE: + error = OT_ERROR_NONE; + break; + case OTBR_ERROR_DUPLICATED: + error = OT_ERROR_DUPLICATED; + break; + case OTBR_ERROR_INVALID_ARGS: + error = OT_ERROR_INVALID_ARGS; + break; + case OTBR_ERROR_ABORTED: + error = OT_ERROR_ABORT; + break; + case OTBR_ERROR_INVALID_STATE: + error = OT_ERROR_INVALID_STATE; + break; + case OTBR_ERROR_NOT_IMPLEMENTED: + error = OT_ERROR_NOT_IMPLEMENTED; + break; + case OTBR_ERROR_NOT_FOUND: + error = OT_ERROR_NOT_FOUND; + break; + case OTBR_ERROR_PARSE: + error = OT_ERROR_PARSE; + break; + default: + break; + } + + return error; +} + +Mdns::Publisher::ResultCallback DnssdPlatform::MakePublisherCallback(RequestId aRequestId, RegisterCallback aCallback) +{ + otInstance *instance = mNcp.GetInstance(); + + return [instance, aRequestId, aCallback](otbrError aError) { + if (aCallback != nullptr) + { + aCallback(instance, aRequestId, DnssdPlatform::ResultToError(aError)); + } + }; +} + +void DnssdPlatform::RegisterService(const Service &aService, RequestId aRequestId, RegisterCallback aCallback) +{ + Mdns::Publisher::SubTypeList subTypeList; + Mdns::Publisher::TxtData txtData(aService.mTxtData, aService.mTxtData + aService.mTxtDataLength); + + for (uint16_t index = 0; index < aService.mSubTypeLabelsLength; index++) + { + subTypeList.push_back(aService.mSubTypeLabels[index]); + } + + mPublisher.PublishService(aService.mHostName, aService.mServiceInstance, aService.mServiceType, subTypeList, + aService.mPort, txtData, MakePublisherCallback(aRequestId, aCallback)); +} + +void DnssdPlatform::UnregisterService(const Service &aService, RequestId aRequestId, RegisterCallback aCallback) +{ + mPublisher.UnpublishService(aService.mServiceInstance, aService.mServiceType, + MakePublisherCallback(aRequestId, aCallback)); +} + +void DnssdPlatform::RegisterHost(const Host &aHost, RequestId aRequestId, RegisterCallback aCallback) +{ + Mdns::Publisher::AddressList addressList; + + for (uint16_t index = 0; index < aHost.mNumAddresses; index++) + { + addressList.push_back(Ip6Address(aHost.mAddresses[index].mFields.m8)); + } + + mPublisher.PublishHost(aHost.mHostName, addressList, MakePublisherCallback(aRequestId, aCallback)); +} + +void DnssdPlatform::UnregisterHost(const Host &aHost, RequestId aRequestId, RegisterCallback aCallback) +{ + mPublisher.UnpublishHost(aHost.mHostName, MakePublisherCallback(aRequestId, aCallback)); +} + +std::string DnssdPlatform::KeyNameFor(const Key &aKey) +{ + std::string name(aKey.mName); + + if (aKey.mServiceType != nullptr) + { + name += "."; + name += aKey.mServiceType; + } + + return name; +} + +void DnssdPlatform::RegisterKey(const Key &aKey, RequestId aRequestId, RegisterCallback aCallback) +{ + Mdns::Publisher::KeyData keyData(aKey.mKeyData, aKey.mKeyData + aKey.mKeyDataLength); + + mPublisher.PublishKey(KeyNameFor(aKey), keyData, MakePublisherCallback(aRequestId, aCallback)); +} + +void DnssdPlatform::UnregisterKey(const Key &aKey, RequestId aRequestId, RegisterCallback aCallback) +{ + mPublisher.UnpublishKey(KeyNameFor(aKey), MakePublisherCallback(aRequestId, aCallback)); +} + +void DnssdPlatform::StartServiceBrowser(const char *aServiceType, uint32_t aInfraIfIndex) +{ + NetifIndexList &list = mServiceBrowsers[DnsName(aServiceType)]; + bool wasEmpty = list.IsEmpty(); + + list.Add(aInfraIfIndex); + + if (wasEmpty) + { + mPublisher.SubscribeService(aServiceType, ""); + } +} + +void DnssdPlatform::StopServiceBrowser(const char *aServiceType, uint32_t aInfraIfIndex) +{ + auto it = mServiceBrowsers.find(DnsName(aServiceType)); + + if (it != mServiceBrowsers.end()) + { + NetifIndexList &list = it->second; + + list.Remove(aInfraIfIndex); + + if (list.IsEmpty()) + { + mServiceBrowsers.erase(it); + mPublisher.UnsubscribeService(aServiceType, ""); + } + } +} + +void DnssdPlatform::StartServiceResolver(const ServiceInstance &aInfo) +{ + NetifIndexList &list = mServiceResolvers[DnsServiceName(aInfo.mServiceInstance, aInfo.mServiceType)]; + bool wasEmpty = list.IsEmpty(); + + list.Add(aInfo.mInfraIfIndex); + + if (wasEmpty) + { + mPublisher.SubscribeService(aInfo.mServiceType, aInfo.mServiceInstance); + } +} + +void DnssdPlatform::StopServiceResolver(const ServiceInstance &aInfo) +{ + auto it = mServiceResolvers.find(DnsServiceName(aInfo.mServiceInstance, aInfo.mServiceType)); + + if (it != mServiceResolvers.end()) + { + NetifIndexList &list = it->second; + + list.Remove(aInfo.mInfraIfIndex); + + if (list.IsEmpty()) + { + mServiceResolvers.erase(it); + mPublisher.UnsubscribeService(aInfo.mServiceType, aInfo.mServiceInstance); + } + } +} + +void DnssdPlatform::StartIp6AddressResolver(const char *aHostName, uint32_t aInfraIfIndex) +{ + NetifIndexList &list = mIp6AddrResolvers[DnsName(aHostName)]; + bool wasEmpty = list.IsEmpty(); + + list.Add(aInfraIfIndex); + + if (wasEmpty) + { + mPublisher.SubscribeHost(aHostName); + } +} + +void DnssdPlatform::StopIp6AddressResolver(const char *aHostName, uint32_t aInfraIfIndex) +{ + auto it = mIp6AddrResolvers.find(DnsName(aHostName)); + + if (it != mIp6AddrResolvers.end()) + { + NetifIndexList &list = it->second; + + list.Remove(aInfraIfIndex); + + if (list.IsEmpty()) + { + mIp6AddrResolvers.erase(it); + mPublisher.UnsubscribeHost(aHostName); + } + } +} + +void DnssdPlatform::HandleDiscoveredService(const std::string &aType, + const Mdns::Publisher::DiscoveredInstanceInfo &aInfo) +{ + sDnssdPlatform->ProcessServiceBrowsers(aType, aInfo); + sDnssdPlatform->ProcessServiceResolvers(aType, aInfo); +} + +void DnssdPlatform::HandleDiscoveredHost(const std::string &aHostName, const Mdns::Publisher::DiscoveredHostInfo &aInfo) +{ + sDnssdPlatform->ProcessIp6AddrResolvers(aHostName, aInfo); +} + +void DnssdPlatform::ProcessServiceBrowsers(const std::string &aType, + const Mdns::Publisher::DiscoveredInstanceInfo &aInfo) +{ + std::string instanceName; + ServiceInstance service; + Event event; + + VerifyOrExit(mState == kStateReady); + + VerifyOrExit(mServiceBrowsers.count(aType) != 0); + VerifyOrExit(mServiceBrowsers[aType].Matches(aInfo.mNetifIndex)); + + instanceName = DnsUtils::UnescapeInstanceName(aInfo.mName); + + memset(&service, 0, sizeof(service)); + service.mServiceType = aType.c_str(); + service.mServiceInstance = instanceName.c_str(); + service.mTtl = aInfo.mTtl; + service.mInfraIfIndex = aInfo.mNetifIndex; + event = aInfo.mRemoved ? kEventEntryRemoved : kEventEntryAdded; + + otPlatDnssdHandleServiceBrowseResult(mNcp.GetInstance(), event, &service); + +exit: + return; +} + +void DnssdPlatform::ProcessServiceResolvers(const std::string &aType, + const Mdns::Publisher::DiscoveredInstanceInfo &aInfo) +{ + std::string instanceName = DnsUtils::UnescapeInstanceName(aInfo.mName); + DnsServiceName serviceName(instanceName, aType); + std::string hostName; + std::string domain; + Service service; + + VerifyOrExit(mState == kStateReady); + + VerifyOrExit(mServiceResolvers.count(serviceName) != 0); + VerifyOrExit(mServiceResolvers[serviceName].Matches(aInfo.mNetifIndex)); + + SuccessOrExit(SplitFullHostName(aInfo.mHostName, hostName, domain)); + + memset(&service, 0, sizeof(service)); + service.mServiceType = aType.c_str(); + service.mServiceInstance = instanceName.c_str(); + service.mHostName = hostName.c_str(); + service.mTxtData = aInfo.mTxtData.data(); + service.mTxtDataLength = aInfo.mTxtData.size(); + service.mPort = aInfo.mPort; + service.mPriority = aInfo.mPriority; + service.mWeight = aInfo.mWeight; + service.mTtl = aInfo.mTtl; + service.mInfraIfIndex = aInfo.mNetifIndex; + + otPlatDnssdHandleServiceResolveResult(mNcp.GetInstance(), &service); + +exit: + return; +} + +void DnssdPlatform::ProcessIp6AddrResolvers(const std::string &aHostName, + const Mdns::Publisher::DiscoveredHostInfo &aInfo) +{ + Host host; + + VerifyOrExit(mState == kStateReady); + + VerifyOrExit(mIp6AddrResolvers.count(aHostName) != 0); + VerifyOrExit(mIp6AddrResolvers[aHostName].Matches(aInfo.mNetifIndex)); + + memset(&host, 0, sizeof(host)); + host.mHostName = aHostName.c_str(); + host.mAddresses = reinterpret_cast(&aInfo.mAddresses[0]); + host.mNumAddresses = aInfo.mAddresses.size(); + host.mTtl = aInfo.mTtl; + host.mInfraIfIndex = aInfo.mNetifIndex; + + otPlatDnssdHandleIp6AddressResolveResult(mNcp.GetInstance(), kEventEntryAdded, &host); + +exit: + return; +} + +//--------------------------------------------------------------------------------------------------------------------- +// `DnssdPlatform::NetifIndexList` + +bool DnssdPlatform::NetifIndexList::Matches(uint32_t aInterfaceIndex) const +{ + bool matches = false; + + if (aInterfaceIndex == kAnyNetifIndex) + { + ExitNow(matches = true); + } + + for (uint32_t netifIndex : mList) + { + if ((netifIndex == kAnyNetifIndex) || (netifIndex == aInterfaceIndex)) + { + ExitNow(matches = true); + } + } + +exit: + return matches; +} + +bool DnssdPlatform::NetifIndexList::Contains(uint32_t aInterfaceIndex) const +{ + bool contains = false; + + for (uint32_t netifIndex : mList) + { + if (netifIndex == aInterfaceIndex) + { + contains = true; + break; + } + } + + return contains; +} + +void DnssdPlatform::NetifIndexList::Add(uint32_t aInterfaceIndex) +{ + if (!Contains(aInterfaceIndex)) + { + mList.push_back(aInterfaceIndex); + } +} + +void DnssdPlatform::NetifIndexList::Remove(uint32_t aInterfaceIndex) +{ + auto it = std::find(mList.begin(), mList.end(), aInterfaceIndex); + + if (it != mList.end()) + { + mList.erase(it); + } +} + +} // namespace otbr + +#endif // OTBR_ENABLE_DNSSD_PLAT diff --git a/src/mdns/dnssd_plat.hpp b/src/mdns/dnssd_plat.hpp new file mode 100644 index 00000000000..d20dbcc0c21 --- /dev/null +++ b/src/mdns/dnssd_plat.hpp @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2023, 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. + */ + +/** + * @file + * This file includes definitions for implementing OpenThread DNS-SD platform APIs. + */ + +#ifndef OTBR_AGENT_DNSSD_PLAT_HPP_ +#define OTBR_AGENT_DNSSD_PLAT_HPP_ + +#include "openthread-br/config.h" + +#include +#include +#include + +#include +#include + +#include "common/dns_utils.hpp" +#include "mdns/mdns.hpp" +#include "ncp/ncp_openthread.hpp" +#include "utils/string_utils.hpp" + +namespace otbr { + +/** + * This class implements the DNS-SD platform. + * + */ +class DnssdPlatform : private NonCopyable +{ +public: + /** + * Initializes the `DnssdPlatform` instance + * + * @param[in] aNcp The OT controller. + * @param[in] aPublisher A reference to `Mdns::Publisher` to use. + * + */ + DnssdPlatform(Ncp::ControllerOpenThread &aNcp, Mdns::Publisher &aPublisher); + + /** + * Starts the `DnssdPlatform` module. + * + */ + void Start(void); + + /** + * Stops the `DnssdPlatform` module + * + */ + void Stop(void); + + /** + * Gets the singleton `DnssdPlatform` instance. + * + * @retval A reference to the `DnssdPlatform` instance. + * + */ + static DnssdPlatform &Get(void) { return *sDnssdPlatform; } + + //----------------------------------------------------------------------------------------------------------------- + // `otPlatDnssd` APIs (see `openthread/include/openthread/platform/dnssd.h` for detailed documentation). + + typedef otPlatDnssdState State; + typedef otPlatDnssdServiceInstance ServiceInstance; + typedef otPlatDnssdService Service; + typedef otPlatDnssdHost Host; + typedef otPlatDnssdKey Key; + typedef otPlatDnssdRequestId RequestId; + typedef otPlatDnssdRegisterCallback RegisterCallback; + typedef otPlatDnssdEvent Event; + + State GetState(void) const { return mState; } + void RegisterService(const Service &aService, RequestId aRequestId, RegisterCallback aCallback); + void UnregisterService(const Service &aService, RequestId aRequestId, RegisterCallback aCallback); + void RegisterHost(const Host &aHost, RequestId aRequestId, RegisterCallback aCallback); + void UnregisterHost(const Host &aHost, RequestId aRequestId, RegisterCallback aCallback); + void RegisterKey(const Key &aKey, RequestId aRequestId, RegisterCallback aCallback); + void UnregisterKey(const Key &aKey, RequestId aRequestId, RegisterCallback aCallback); + void StartServiceBrowser(const char *aServiceType, uint32_t aInfraIfIndex); + void StopServiceBrowser(const char *aServiceType, uint32_t aInfraIfIndex); + void StartServiceResolver(const ServiceInstance &aInfo); + void StopServiceResolver(const ServiceInstance &aInfo); + void StartIp6AddressResolver(const char *aHostName, uint32_t aInfraIfIndex); + void StopIp6AddressResolver(const char *aHostName, uint32_t aInfraIfIndex); + + // Callback from BR agent + void HandleMdnsPublisherStateChange(Mdns::Publisher::State aState); + +private: + static constexpr State kStateReady = OT_PLAT_DNSSD_READY; + static constexpr State kStateStopped = OT_PLAT_DNSSD_STOPPED; + static constexpr Event kEventEntryAdded = OT_PLAT_DNSSD_EVENT_ENTRY_ADDED; + static constexpr Event kEventEntryRemoved = OT_PLAT_DNSSD_EVENT_ENTRY_REMOVED; + static constexpr uint32_t kAnyNetifIndex = 0; + + class DnsName + { + public: + DnsName(std::string aName) + : mName(aName) + { + } + + bool operator==(const DnsName &aOther) const + { + return StringUtils::ToLowercase(mName) == StringUtils::ToLowercase(aOther.mName); + } + + bool operator<(const DnsName &aOther) const + { + return StringUtils::ToLowercase(mName) < StringUtils::ToLowercase(aOther.mName); + } + + private: + std::string mName; + }; + + class DnsServiceName + { + public: + DnsServiceName(std::string aInstance, std::string aType) + : mInstance(aInstance) + , mType(aType) + { + } + + bool operator==(const DnsServiceName &aOther) const + { + return (mInstance == aOther.mInstance) && (mType == aOther.mType); + } + + bool operator<(const DnsServiceName &aOther) const + { + return (mInstance == aOther.mInstance) ? (mType < aOther.mType) : (mInstance < aOther.mInstance); + } + + private: + DnsName mInstance; + DnsName mType; + }; + + class NetifIndexList + { + public: + bool Matches(uint32_t aInterfaceIndex) const; + bool IsEmpty(void) const { return mList.empty(); } + bool Contains(uint32_t aInterfaceIndex) const; + void Add(uint32_t aInterfaceIndex); + void Remove(uint32_t aInterfaceIndex); + + private: + std::vector mList; + }; + + void UpdateState(void); + Mdns::Publisher::ResultCallback MakePublisherCallback(RequestId aRequestId, RegisterCallback aCallback); + + static otError ResultToError(otbrError aOtbrError); + static std::string KeyNameFor(const Key &aKey); + + static void HandleDiscoveredService(const std::string &aType, const Mdns::Publisher::DiscoveredInstanceInfo &aInfo); + static void HandleDiscoveredHost(const std::string &aHostName, const Mdns::Publisher::DiscoveredHostInfo &aInfo); + + void ProcessServiceBrowsers(const std::string &aType, const Mdns::Publisher::DiscoveredInstanceInfo &aInfo); + void ProcessServiceResolvers(const std::string &aType, const Mdns::Publisher::DiscoveredInstanceInfo &aInfo); + void ProcessIp6AddrResolvers(const std::string &aHostName, const Mdns::Publisher::DiscoveredHostInfo &aInfo); + + static DnssdPlatform *sDnssdPlatform; + + Ncp::ControllerOpenThread &mNcp; + Mdns::Publisher &mPublisher; + State mState; + bool mRunning; + Mdns::Publisher::State mPublisherState; + uint64_t mSubscriberId; + std::map mServiceBrowsers; + std::map mServiceResolvers; + std::map mIp6AddrResolvers; +}; + +} // namespace otbr + +#endif // OTBR_AGENT_DNSSD_PLAT_HPP_ diff --git a/third_party/openthread/CMakeLists.txt b/third_party/openthread/CMakeLists.txt index f07625da96b..ee695b0ecfd 100644 --- a/third_party/openthread/CMakeLists.txt +++ b/third_party/openthread/CMakeLists.txt @@ -46,7 +46,6 @@ set(OT_DAEMON ON CACHE STRING "enable daemon mode" FORCE) set(OT_DATASET_UPDATER ON CACHE STRING "enable dataset updater" FORCE) set(OT_DNS_CLIENT ON CACHE STRING "enable DNS client" FORCE) set(OT_DNS_UPSTREAM_QUERY ${OTBR_DNS_UPSTREAM_QUERY} CACHE STRING "enable sending DNS queries to upstream" FORCE) -set(OT_DNSSD_SERVER ${OTBR_DNSSD_DISCOVERY_PROXY} CACHE STRING "enable DNS-SD server support" FORCE) set(OT_ECDSA ON CACHE STRING "enable ECDSA" FORCE) set(OT_FIREWALL ON CACHE STRING "enable firewall feature") set(OT_HISTORY_TRACKER ON CACHE STRING "enable history tracker" FORCE) @@ -71,11 +70,21 @@ set(OT_TREL ${OTBR_TREL} CACHE STRING "enable TREL" FORCE) set(OT_UDP_FORWARD OFF CACHE STRING "disable udp forward" FORCE) set(OT_UPTIME ON CACHE STRING "enable uptime" FORCE) -if (OTBR_SRP_ADVERTISING_PROXY) +if (OTBR_DNSSD_PLAT) set(OT_SRP_SERVER ON CACHE STRING "enable SRP server" FORCE) set(OT_EXTERNAL_HEAP ON CACHE STRING "enable external heap" FORCE) + set(OT_SRP_ADV_PROXY ON CACHE STRING "enable SRP adv proxy" FORCE) + set(OT_DNSSD_SERVER ON CACHE STRING "enable DNS-SD server support" FORCE) + # TODO: Add for Discovery proxy later +else () + if (OTBR_SRP_ADVERTISING_PROXY) + set(OT_SRP_SERVER ON CACHE STRING "enable SRP server" FORCE) + set(OT_EXTERNAL_HEAP ON CACHE STRING "enable external heap" FORCE) + endif() + set(OT_DNSSD_SERVER ${OTBR_DNSSD_DISCOVERY_PROXY} CACHE STRING "enable DNS-SD server support" FORCE) endif() + if (NOT OT_THREAD_VERSION STREQUAL "1.1") if (OT_REFERENCE_DEVICE) set(OT_DUA ON CACHE STRING "Enable Thread 1.2 DUA for reference devices")