blob: fe77ecbba7146ebe3cc5ec2d8d53ad662f88bbf8 [file]
/*
* Copyright (c) 2024, 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.
*/
#define OTBR_LOG_TAG "MDNS"
#include "android/mdns_publisher.hpp"
namespace otbr {
Mdns::Publisher *Mdns::Publisher::Create(Mdns::Publisher::StateCallback aCallback)
{
return new Android::MdnsPublisher(std::move(aCallback));
}
namespace Android {
otbrError DnsErrorToOtbrErrorImpl(int32_t aError)
{
return aError == 0 ? OTBR_ERROR_NONE : OTBR_ERROR_MDNS;
}
otbrError MdnsPublisher::DnsErrorToOtbrError(int32_t aError)
{
return DnsErrorToOtbrErrorImpl(aError);
}
Status MdnsPublisher::NsdStatusReceiver::onSuccess()
{
if (!mCallback.IsNull())
{
std::move(mCallback)(OTBR_ERROR_NONE);
}
return Status::ok();
}
Status MdnsPublisher::NsdDiscoverServiceCallback::onServiceDiscovered(const std::string &aName,
const std::string &aType,
bool aIsFound)
{
std::shared_ptr<ServiceSubscription> subscription = mSubscription.lock();
VerifyOrExit(subscription != nullptr);
VerifyOrExit(aIsFound, subscription->mPublisher.OnServiceRemoved(0, aType, aName));
subscription->Resolve(aName, aType);
exit:
return Status::ok();
}
Status MdnsPublisher::NsdResolveServiceCallback::onServiceResolved(const std::string &aHostname,
int aNetifIndex,
const std::string &aName,
const std::string &aType,
int aPort,
const std::vector<std::string> &aAddresses,
const std::vector<DnsTxtAttribute> &aTxt,
int aTtlSeconds)
{
DiscoveredInstanceInfo info;
TxtList txtList;
std::shared_ptr<ServiceSubscription> subscription = mSubscription.lock();
VerifyOrExit(subscription != nullptr);
info.mHostName = aHostname + ".local.";
info.mName = aName;
info.mPort = aPort;
info.mTtl = std::clamp(aTtlSeconds, kMinResolvedTtl, kMaxResolvedTtl);
info.mNetifIndex = aNetifIndex;
for (const auto &addressStr : aAddresses)
{
Ip6Address address;
// addressStr may be in the format of "fe80::1234%eth0"
std::string addrStr(addressStr.begin(), std::find(addressStr.begin(), addressStr.end(), '%'));
int error = Ip6Address::FromString(addrStr.c_str(), address);
if (error != OTBR_ERROR_NONE)
{
otbrLogInfo("Failed to parse resolved IPv6 address: %s", addressStr.c_str());
continue;
}
info.mAddresses.push_back(address);
}
for (const auto &entry : aTxt)
{
txtList.emplace_back(entry.name.c_str(), entry.value.data(), entry.value.size());
}
EncodeTxtData(txtList, info.mTxtData);
subscription->mPublisher.OnServiceResolved(aType, info);
exit:
return Status::ok();
}
Status MdnsPublisher::NsdResolveHostCallback::onHostResolved(const std::string &aName,
const std::vector<std::string> &aAddresses)
{
DiscoveredHostInfo info;
std::shared_ptr<HostSubscription> subscription = mSubscription.lock();
VerifyOrExit(subscription != nullptr);
info.mTtl = kDefaultResolvedTtl;
for (const auto &addressStr : aAddresses)
{
Ip6Address address;
int error = Ip6Address::FromString(addressStr.c_str(), address);
if (error != OTBR_ERROR_NONE)
{
otbrLogInfo("Failed to parse resolved IPv6 address: %s", addressStr.c_str());
continue;
}
info.mAddresses.push_back(address);
}
subscription->mPublisher.OnHostResolved(aName, info);
exit:
return Status::ok();
}
void MdnsPublisher::SetINsdPublisher(std::shared_ptr<INsdPublisher> aINsdPublisher)
{
otbrLogInfo("Set INsdPublisher %p", aINsdPublisher.get());
mNsdPublisher = std::move(aINsdPublisher);
if (mNsdPublisher != nullptr)
{
mStateCallback(Mdns::Publisher::State::kReady);
}
else
{
mStateCallback(Mdns::Publisher::State::kIdle);
}
}
Status MdnsPublisher::NsdStatusReceiver::onError(int aError)
{
if (!mCallback.IsNull())
{
std::move(mCallback)(DnsErrorToOtbrErrorImpl(aError));
}
return Status::ok();
}
std::shared_ptr<MdnsPublisher::NsdStatusReceiver> CreateReceiver(Mdns::Publisher::ResultCallback aCallback)
{
return ndk::SharedRefBase::make<MdnsPublisher::NsdStatusReceiver>(std::move(aCallback));
}
std::shared_ptr<MdnsPublisher::NsdDiscoverServiceCallback> CreateNsdDiscoverServiceCallback(
const std::shared_ptr<MdnsPublisher::ServiceSubscription> &aServiceSubscription)
{
return ndk::SharedRefBase::make<MdnsPublisher::NsdDiscoverServiceCallback>(aServiceSubscription);
}
std::shared_ptr<MdnsPublisher::NsdResolveServiceCallback> CreateNsdResolveServiceCallback(
const std::shared_ptr<MdnsPublisher::ServiceSubscription> &aServiceSubscription)
{
return ndk::SharedRefBase::make<MdnsPublisher::NsdResolveServiceCallback>(aServiceSubscription);
}
std::shared_ptr<MdnsPublisher::NsdResolveHostCallback> CreateNsdResolveHostCallback(
const std::shared_ptr<MdnsPublisher::HostSubscription> &aHostSubscription)
{
return ndk::SharedRefBase::make<MdnsPublisher::NsdResolveHostCallback>(aHostSubscription);
}
void DieForNotImplemented(const char *aFuncName)
{
VerifyOrDie(false, (std::string(aFuncName) + " is not implemented").c_str());
}
otbrError MdnsPublisher::PublishServiceImpl(const std::string &aHostName,
const std::string &aName,
const std::string &aType,
const SubTypeList &aSubTypeList,
uint16_t aPort,
const TxtData &aTxtData,
ResultCallback &&aCallback)
{
int32_t listenerId = AllocateListenerId();
TxtList txtList;
otbrError error = OTBR_ERROR_NONE;
std::vector<DnsTxtAttribute> txtAttributes;
VerifyOrExit(IsStarted(), error = OTBR_ERROR_MDNS);
if (mNsdPublisher == nullptr)
{
otbrLogWarning("No platform mDNS implementation registered!");
ExitNow(error = OTBR_ERROR_MDNS);
}
aCallback = HandleDuplicateServiceRegistration(aHostName, aName, aType, aSubTypeList, aPort, aTxtData,
std::move(aCallback));
VerifyOrExit(!aCallback.IsNull(), error = OTBR_ERROR_INVALID_STATE);
SuccessOrExit(error = DecodeTxtData(txtList, aTxtData.data(), aTxtData.size()));
for (const auto &txtEntry : txtList)
{
DnsTxtAttribute txtAttribute;
txtAttribute.name = txtEntry.mKey;
txtAttribute.value = txtEntry.mValue;
txtAttributes.push_back(std::move(txtAttribute));
}
AddServiceRegistration(MakeUnique<NsdServiceRegistration>(aHostName, aName, aType, aSubTypeList, aPort, aTxtData,
/* aCallback= */ nullptr, this, listenerId,
mNsdPublisher));
otbrLogInfo("Publishing service %s.%s listener ID = %d", aName.c_str(), aType.c_str(), listenerId);
mNsdPublisher->registerService(aHostName, aName, aType, aSubTypeList, aPort, txtAttributes,
CreateReceiver(std::move(aCallback)), listenerId);
exit:
return error;
}
void MdnsPublisher::UnpublishService(const std::string &aName, const std::string &aType, ResultCallback &&aCallback)
{
NsdServiceRegistration *serviceRegistration =
static_cast<NsdServiceRegistration *>(FindServiceRegistration(aName, aType));
VerifyOrExit(IsStarted(), std::move(aCallback)(OTBR_ERROR_MDNS));
if (mNsdPublisher == nullptr)
{
otbrLogWarning("No platform mDNS implementation registered!");
ExitNow(std::move(aCallback)(OTBR_ERROR_MDNS));
}
VerifyOrExit(serviceRegistration != nullptr, std::move(aCallback)(OTBR_ERROR_NONE));
serviceRegistration->mUnregisterReceiver = CreateReceiver(std::move(aCallback));
RemoveServiceRegistration(aName, aType, OTBR_ERROR_NONE);
exit:
return;
}
otbrError MdnsPublisher::PublishHostImpl(const std::string &aName,
const AddressList &aAddresses,
ResultCallback &&aCallback)
{
int32_t listenerId = AllocateListenerId();
TxtList txtList;
otbrError error = OTBR_ERROR_NONE;
std::vector<std::string> addressStrings;
VerifyOrExit(IsStarted(), error = OTBR_ERROR_MDNS);
if (mNsdPublisher == nullptr)
{
otbrLogWarning("No platform mDNS implementation registered!");
ExitNow(error = OTBR_ERROR_MDNS);
}
aCallback = HandleDuplicateHostRegistration(aName, aAddresses, std::move(aCallback));
VerifyOrExit(!aCallback.IsNull(), error = OTBR_ERROR_INVALID_STATE);
AddHostRegistration(
MakeUnique<NsdHostRegistration>(aName, aAddresses, /* aCallback= */ nullptr, this, listenerId, mNsdPublisher));
otbrLogInfo("Publishing host %s listener ID = %d", aName.c_str(), listenerId);
addressStrings.reserve(aAddresses.size());
for (const Ip6Address &address : aAddresses)
{
addressStrings.push_back(address.ToString());
}
if (aAddresses.size())
{
mNsdPublisher->registerHost(aName, addressStrings, CreateReceiver(std::move(aCallback)), listenerId);
}
else
{
// No addresses to register.
std::move(aCallback)(OTBR_ERROR_NONE);
}
exit:
return error;
}
otbrError MdnsPublisher::PublishKeyImpl(const std::string &aName, const KeyData &aKeyData, ResultCallback &&aCallback)
{
OTBR_UNUSED_VARIABLE(aName);
OTBR_UNUSED_VARIABLE(aKeyData);
OTBR_UNUSED_VARIABLE(aCallback);
DieForNotImplemented(__func__);
return OTBR_ERROR_MDNS;
}
void MdnsPublisher::UnpublishHost(const std::string &aName, ResultCallback &&aCallback)
{
NsdHostRegistration *hostRegistration = static_cast<NsdHostRegistration *>(FindHostRegistration(aName));
VerifyOrExit(IsStarted(), std::move(aCallback)(OTBR_ERROR_MDNS));
if (mNsdPublisher == nullptr)
{
otbrLogWarning("No platform mDNS implementation registered!");
ExitNow(std::move(aCallback)(OTBR_ERROR_MDNS));
}
VerifyOrExit(hostRegistration != nullptr, std::move(aCallback)(OTBR_ERROR_NONE));
hostRegistration->mUnregisterReceiver = CreateReceiver(std::move(aCallback));
RemoveHostRegistration(aName, OTBR_ERROR_NONE);
exit:
return;
}
void MdnsPublisher::UnpublishKey(const std::string &aName, ResultCallback &&aCallback)
{
OTBR_UNUSED_VARIABLE(aName);
OTBR_UNUSED_VARIABLE(aCallback);
DieForNotImplemented(__func__);
}
void MdnsPublisher::SubscribeService(const std::string &aType, const std::string &aInstanceName)
{
auto service = std::make_shared<ServiceSubscription>(aType, aInstanceName, *this, mNsdPublisher);
VerifyOrExit(IsStarted(), otbrLogWarning("No platform mDNS implementation registered!"));
mServiceSubscriptions.push_back(std::move(service));
otbrLogInfo("Subscribe service %s.%s (total %zu)", aInstanceName.c_str(), aType.c_str(),
mServiceSubscriptions.size());
if (aInstanceName.empty())
{
mServiceSubscriptions.back()->Browse();
}
else
{
mServiceSubscriptions.back()->Resolve(aInstanceName, aType);
}
exit:
return;
}
void MdnsPublisher::UnsubscribeService(const std::string &aType, const std::string &aInstanceName)
{
ServiceSubscriptionList::iterator it;
VerifyOrExit(IsStarted());
it = std::find_if(mServiceSubscriptions.begin(), mServiceSubscriptions.end(),
[&aType, &aInstanceName](const std::shared_ptr<ServiceSubscription> &aService) {
return aService->mType == aType && aService->mName == aInstanceName;
});
VerifyOrExit(it != mServiceSubscriptions.end(),
otbrLogWarning("The service %s.%s is already unsubscribed.", aInstanceName.c_str(), aType.c_str()));
{
std::shared_ptr<ServiceSubscription> service = std::move(*it);
mServiceSubscriptions.erase(it);
}
otbrLogInfo("Unsubscribe service %s.%s (left %zu)", aInstanceName.c_str(), aType.c_str(),
mServiceSubscriptions.size());
exit:
return;
}
void MdnsPublisher::SubscribeHost(const std::string &aHostName)
{
auto host = std::make_shared<HostSubscription>(aHostName, *this, mNsdPublisher, AllocateListenerId());
VerifyOrExit(IsStarted(), otbrLogWarning("No platform mDNS implementation registered!"));
mNsdPublisher->resolveHost(aHostName, CreateNsdResolveHostCallback(host), host->mListenerId);
mHostSubscriptions.push_back(std::move(host));
otbrLogInfo("Subscribe host %s (total %zu)", aHostName.c_str(), mHostSubscriptions.size());
exit:
return;
}
void MdnsPublisher::UnsubscribeHost(const std::string &aHostName)
{
HostSubscriptionList::iterator it;
VerifyOrExit(IsStarted());
it = std::find_if(
mHostSubscriptions.begin(), mHostSubscriptions.end(),
[&aHostName](const std::shared_ptr<HostSubscription> &aHost) { return aHost->mName == aHostName; });
VerifyOrExit(it != mHostSubscriptions.end(),
otbrLogWarning("The host %s is already unsubscribed.", aHostName.c_str()));
{
std::shared_ptr<HostSubscription> host = std::move(*it);
mHostSubscriptions.erase(it);
}
otbrLogInfo("Unsubscribe host %s (left %zu)", aHostName.c_str(), mHostSubscriptions.size());
exit:
return;
}
void MdnsPublisher::OnServiceResolveFailedImpl(const std::string &aType,
const std::string &aInstanceName,
int32_t aErrorCode)
{
OTBR_UNUSED_VARIABLE(aType);
OTBR_UNUSED_VARIABLE(aInstanceName);
OTBR_UNUSED_VARIABLE(aErrorCode);
DieForNotImplemented(__func__);
}
void MdnsPublisher::OnHostResolveFailedImpl(const std::string &aHostName, int32_t aErrorCode)
{
OTBR_UNUSED_VARIABLE(aHostName);
OTBR_UNUSED_VARIABLE(aErrorCode);
DieForNotImplemented(__func__);
}
int32_t MdnsPublisher::AllocateListenerId(void)
{
if (mNextListenerId == std::numeric_limits<int32_t>::max())
{
mNextListenerId = 0;
}
return mNextListenerId++;
}
MdnsPublisher::NsdServiceRegistration::~NsdServiceRegistration(void)
{
auto nsdPublisher = mNsdPublisher.lock();
VerifyOrExit(mPublisher->IsStarted() && nsdPublisher != nullptr);
otbrLogInfo("Unpublishing service %s.%s listener ID = %d", mName.c_str(), mType.c_str(), mListenerId);
if (!mUnregisterReceiver)
{
mUnregisterReceiver = CreateReceiver([](int) {});
}
nsdPublisher->unregister(mUnregisterReceiver, mListenerId);
exit:
return;
}
MdnsPublisher::NsdHostRegistration::~NsdHostRegistration(void)
{
auto nsdPublisher = mNsdPublisher.lock();
VerifyOrExit(mPublisher->IsStarted() && nsdPublisher != nullptr);
otbrLogInfo("Unpublishing host %s listener ID = %d", mName.c_str(), mListenerId);
if (!mUnregisterReceiver)
{
mUnregisterReceiver = CreateReceiver([](int) {});
}
nsdPublisher->unregister(mUnregisterReceiver, mListenerId);
exit:
return;
}
void MdnsPublisher::ServiceSubscription::Release(void)
{
otbrLogInfo("Browsing service type %s", mType.c_str());
std::vector<std::string> instanceNames;
for (const auto &nameAndResolvers : mResolvers)
{
instanceNames.push_back(nameAndResolvers.first);
}
for (const auto &name : instanceNames)
{
RemoveServiceResolver(name);
}
mNsdPublisher->stopServiceDiscovery(mBrowseListenerId);
}
void MdnsPublisher::ServiceSubscription::Browse(void)
{
VerifyOrExit(mPublisher.IsStarted());
otbrLogInfo("Browsing service type %s", mType.c_str());
mNsdPublisher->discoverService(mType, CreateNsdDiscoverServiceCallback(shared_from_this()), mBrowseListenerId);
exit:
return;
}
void MdnsPublisher::ServiceSubscription::Resolve(const std::string &aName, const std::string &aType)
{
ServiceResolver *resolver = new ServiceResolver(mPublisher.AllocateListenerId(), mNsdPublisher);
VerifyOrExit(mPublisher.IsStarted());
otbrLogInfo("Resolving service %s.%s", aName.c_str(), aType.c_str());
AddServiceResolver(aName, resolver);
mNsdPublisher->resolveService(aName, aType, CreateNsdResolveServiceCallback(shared_from_this()),
resolver->mListenerId);
exit:
return;
}
void MdnsPublisher::ServiceSubscription::AddServiceResolver(const std::string &aName, ServiceResolver *aResolver)
{
mResolvers[aName].insert(aResolver);
}
void MdnsPublisher::ServiceSubscription::RemoveServiceResolver(const std::string &aName)
{
int numResolvers = 0;
VerifyOrExit(mResolvers.find(aName) != mResolvers.end());
numResolvers = mResolvers[aName].size();
for (auto resolver : mResolvers[aName])
{
delete resolver;
}
mResolvers.erase(aName);
exit:
otbrLogDebug("Removed %d service resolver for instance %s", numResolvers, aName.c_str());
return;
}
} // namespace Android
} // namespace otbr