blob: 0c68bd67694a03a1f679251012c9baa5a31a5225 [file] [log] [blame]
/*
*
* Copyright (c) 2021-2022 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "DnssdImpl.h"
#include <algorithm>
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <string>
#include <utility>
// Note: Include dns-sd-internal.h is needed, this source file uses some undocumented functions.
#include <dns-sd-internal.h>
#include <dns-sd.h>
#include <glib.h>
#include <lib/dnssd/platform/Dnssd.h>
#include <net/if.h>
#include <inet/InetBuildConfig.h>
#include <lib/support/CHIPMemString.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/SafeInt.h>
#include <lib/support/Span.h>
#include <platform/CHIPDeviceConfig.h>
#include <platform/PlatformManager.h>
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
#include <platform/ThreadStackManager.h>
#endif
namespace {
constexpr uint8_t kDnssdKeyMaxSize = 32;
constexpr const char * kEmptyAddressIpv6 = "0000:0000:0000:0000:0000:0000:0000:0000";
// The number of miliseconds which must elapse without a new "found" event before
// mDNS browsing is considered finished. We need this timeout because Tizen Native
// API does not deliver all-for-now signal (such signal is delivered by e.g. Avahi)
// and the browsing callback is called multiple times (once for each service found).
constexpr unsigned int kDnssdBrowseTimeoutMs = 250;
bool IsSupportedProtocol(chip::Dnssd::DnssdServiceProtocol protocol)
{
return (protocol == chip::Dnssd::DnssdServiceProtocol::kDnssdProtocolUdp) ||
(protocol == chip::Dnssd::DnssdServiceProtocol::kDnssdProtocolTcp);
}
const char * GetProtocolString(chip::Dnssd::DnssdServiceProtocol protocol)
{
return protocol == chip::Dnssd::DnssdServiceProtocol::kDnssdProtocolUdp ? chip::Dnssd::kCommissionProtocol
: chip::Dnssd::kOperationalProtocol;
}
std::string GetFullType(const char * type, chip::Dnssd::DnssdServiceProtocol protocol)
{
std::ostringstream typeBuilder;
typeBuilder << type << "." << GetProtocolString(protocol);
return typeBuilder.str();
}
CHIP_ERROR GetChipError(int dnssdError)
{
switch (dnssdError)
{
case DNSSD_ERROR_NONE:
return CHIP_NO_ERROR;
case DNSSD_ERROR_NAME_CONFLICT:
return CHIP_ERROR_MDNS_COLLISION;
case DNSSD_ERROR_OUT_OF_MEMORY:
return CHIP_ERROR_NO_MEMORY;
default:
return CHIP_ERROR_INTERNAL;
}
}
void OnRegister(dnssd_error_e result, dnssd_service_h service, void * data)
{
auto rCtx = reinterpret_cast<chip::Dnssd::RegisterContext *>(data);
ChipLogDetail(DeviceLayer, "DNSsd %s: name: %s, type: %s, port: %u, interfaceId: %u", __func__, rCtx->mName, rCtx->mType,
rCtx->mPort, rCtx->mInterfaceId);
if (result != DNSSD_ERROR_NONE)
{
ChipLogError(DeviceLayer, "DNSsd %s: Error: %d", __func__, result);
rCtx->mCallback(rCtx->mCbContext, nullptr, nullptr, GetChipError(result));
// After this point, the context might be no longer valid
rCtx->mInstance->RemoveContext(rCtx);
return;
}
rCtx->mCallback(rCtx->mCbContext, rCtx->mType, rCtx->mName, CHIP_NO_ERROR);
}
CHIP_ERROR RegisterAsync(chip::Dnssd::RegisterContext * rCtx)
{
ChipLogDetail(DeviceLayer, "DNSsd %s", __func__);
int ret = dnssd_register_local_service(rCtx->mServiceHandle, OnRegister, rCtx);
VerifyOrReturnValue(ret == DNSSD_ERROR_NONE, GetChipError(ret),
ChipLogError(DeviceLayer, "dnssd_register_local_service() failed. ret: %d", ret));
rCtx->mIsRegistered = true;
return CHIP_NO_ERROR;
}
gboolean OnBrowseTimeout(void * userData)
{
ChipLogDetail(DeviceLayer, "DNSsd %s: all for now", __func__);
auto * bCtx = reinterpret_cast<chip::Dnssd::BrowseContext *>(userData);
bCtx->mCallback(bCtx->mCbContext, bCtx->mServices.data(), bCtx->mServices.size(), true, CHIP_NO_ERROR);
// After this point the context might be no longer valid
bCtx->mInstance->RemoveContext(bCtx);
// This is a one-shot timer
return G_SOURCE_REMOVE;
}
void OnBrowseAdd(chip::Dnssd::BrowseContext * context, const char * type, const char * name, uint32_t interfaceId)
{
ChipLogDetail(DeviceLayer, "DNSsd %s: name: %s, type: %s, interfaceId: %u", __func__, StringOrNullMarker(name),
StringOrNullMarker(type), interfaceId);
char * tokens = strdup(type);
char * regtype = strtok(tokens, ".");
chip::Dnssd::DnssdService dnssdService = {};
chip::Platform::CopyString(dnssdService.mName, name);
chip::Platform::CopyString(dnssdService.mType, regtype);
dnssdService.mProtocol = context->mProtocol;
dnssdService.mInterface = chip::Inet::InterfaceId(interfaceId);
context->mServices.push_back(dnssdService);
free(tokens);
}
void OnBrowseRemove(chip::Dnssd::BrowseContext * context, const char * type, const char * name, uint32_t interfaceId)
{
ChipLogDetail(DeviceLayer, "DNSsd %s: name: %s, type: %s, interfaceId: %u", __func__, StringOrNullMarker(name),
StringOrNullMarker(type), interfaceId);
context->mServices.erase(std::remove_if(
context->mServices.begin(), context->mServices.end(), [name, type, interfaceId](const chip::Dnssd::DnssdService & service) {
return strcmp(name, service.mName) == 0 && type == GetFullType(service.mType, service.mProtocol) &&
interfaceId == service.mInterface.GetPlatformInterface();
}));
}
void OnBrowse(dnssd_service_state_e state, dnssd_service_h service, void * data)
{
ChipLogDetail(DeviceLayer, "DNSsd %s", __func__);
auto bCtx = reinterpret_cast<chip::Dnssd::BrowseContext *>(data);
int ret;
// If there is already a timeout source, so we need to cancel it.
if (bCtx->mTimeoutSource != nullptr)
{
g_source_destroy(bCtx->mTimeoutSource);
g_source_unref(bCtx->mTimeoutSource);
}
// Start a timer, so we could detect when there is no more on-browse events.
// The timeout callback function will be called in the same event loop as the
// browse callback (this one), so locking is not required.
auto * source = g_timeout_source_new(kDnssdBrowseTimeoutMs);
g_source_set_callback(source, OnBrowseTimeout, bCtx, nullptr);
g_source_attach(source, g_main_context_get_thread_default());
bCtx->mTimeoutSource = source;
char * type = nullptr;
char * name = nullptr;
char * ifaceName = nullptr;
uint32_t interfaceId = 0;
ret = dnssd_service_get_type(service, &type);
VerifyOrExit(ret == DNSSD_ERROR_NONE, ChipLogError(DeviceLayer, "dnssd_service_get_type() failed. ret: %d", ret));
ret = dnssd_service_get_name(service, &name);
VerifyOrExit(ret == DNSSD_ERROR_NONE, ChipLogError(DeviceLayer, "dnssd_service_get_name() failed. ret: %d", ret));
ret = dnssd_service_get_interface(service, &ifaceName);
VerifyOrExit(ret == DNSSD_ERROR_NONE, ChipLogError(DeviceLayer, "dnssd_service_get_interface() failed. ret: %d", ret));
interfaceId = if_nametoindex(ifaceName);
VerifyOrExit(interfaceId > 0, ChipLogError(DeviceLayer, "if_nametoindex() failed. errno: %d", errno);
ret = DNSSD_ERROR_OPERATION_FAILED);
if (state == DNSSD_SERVICE_STATE_AVAILABLE)
{
OnBrowseAdd(bCtx, type, name, interfaceId);
}
else
{
OnBrowseRemove(bCtx, type, name, interfaceId);
}
exit:
dnssd_destroy_remote_service(service);
if (ret != DNSSD_ERROR_NONE)
{
bCtx->mCallback(bCtx->mCbContext, nullptr, 0, true, GetChipError(ret));
// After this point the context might be no longer valid
bCtx->mInstance->RemoveContext(bCtx);
}
g_free(type);
g_free(name);
g_free(ifaceName);
}
CHIP_ERROR BrowseAsync(chip::Dnssd::BrowseContext * bCtx)
{
ChipLogDetail(DeviceLayer, "DNSsd %s", __func__);
auto interfaceId = bCtx->mInterfaceId;
int ret;
if (interfaceId == 0)
{
ret = dnssd_browse_service(bCtx->mType, nullptr, &bCtx->mBrowserHandle, OnBrowse, bCtx);
}
else
{
char iface[IF_NAMESIZE + 1] = "";
VerifyOrReturnValue(if_indextoname(interfaceId, iface) != nullptr, CHIP_ERROR_INTERNAL,
ChipLogError(DeviceLayer, "if_indextoname() failed. errno: %d", errno));
ret = dnssd_browse_service(bCtx->mType, iface, &bCtx->mBrowserHandle, OnBrowse, bCtx);
}
VerifyOrReturnValue(ret == DNSSD_ERROR_NONE, GetChipError(ret),
ChipLogError(DeviceLayer, "dnssd_browse_service() failed. ret: %d", ret));
bCtx->mIsBrowsing = true;
return CHIP_NO_ERROR;
}
void GetTextEntries(unsigned short txtLen, uint8_t * txtRecord, std::vector<chip::Dnssd::TextEntry> & textEntries)
{
VerifyOrReturn(txtLen > 1, ChipLogDetail(DeviceLayer, "DNSsd %s: No TXT records", __func__));
const uint8_t * txtRecordEnd = txtRecord + txtLen;
while (txtRecord < txtRecordEnd)
{
uint8_t txtRecordSize = txtRecord[0];
txtRecord++;
VerifyOrReturn(txtRecord + txtRecordSize <= txtRecordEnd,
ChipLogError(DeviceLayer, "DNSsd %s: Invalid TXT data", __func__));
for (size_t i = 0; i < txtRecordSize; i++)
{
if (txtRecord[i] == '=')
{
// NULL-terminate the key string
txtRecord[i] = '\0';
char * key = reinterpret_cast<char *>(txtRecord);
uint8_t * data = txtRecord + i + 1;
size_t dataSize = txtRecordSize - i - 1;
textEntries.push_back({ key, data, dataSize });
break;
}
}
// Move to the next text entry
txtRecord += txtRecordSize;
}
}
gboolean OnResolveFinalize(gpointer userData)
{
ChipLogDetail(DeviceLayer, "DNSsd %s", __func__);
auto rCtx = reinterpret_cast<chip::Dnssd::ResolveContext *>(userData);
{
// Lock the stack mutex when calling the callback function, so that the callback
// function could safely perform message exchange (e.g. PASE session pairing).
chip::DeviceLayer::StackLock lock;
rCtx->Finalize(CHIP_NO_ERROR);
}
rCtx->mInstance->RemoveContext(rCtx);
return G_SOURCE_REMOVE;
}
void OnResolve(dnssd_error_e result, dnssd_service_h service, void * userData)
{
ChipLogDetail(DeviceLayer, "DNSsd %s", __func__);
auto rCtx = reinterpret_cast<chip::Dnssd::ResolveContext *>(userData);
char * name = nullptr;
char * ipv4 = nullptr;
char * ipv6 = nullptr;
int port = 0;
char * interface = nullptr;
chip::Inet::IPAddress ipAddr;
CHIP_ERROR err = CHIP_NO_ERROR;
int ret = dnssd_service_get_name(service, &name);
VerifyOrExit(ret == DNSSD_ERROR_NONE, ChipLogError(DeviceLayer, "dnssd_service_get_name() failed. ret: %d", ret));
chip::Platform::CopyString(rCtx->mResult.mName, name);
g_free(name);
ret = dnssd_service_get_ip(service, &ipv4, &ipv6);
VerifyOrExit(ret == DNSSD_ERROR_NONE, ChipLogError(DeviceLayer, "dnssd_service_get_ip() failed. ret: %d", ret));
// If both IPv4 and IPv6 are set, IPv6 address has higher priority.
if (ipv6 != nullptr && strcmp(ipv6, kEmptyAddressIpv6) != 0)
{
if (!chip::Inet::IPAddress::FromString(ipv6, ipAddr) || ipAddr.Type() != chip::Inet::IPAddressType::kIPv6)
{
ret = DNSSD_ERROR_OPERATION_FAILED;
}
}
#if INET_CONFIG_ENABLE_IPV4
else if (ipv4 != nullptr)
{
if (!chip::Inet::IPAddress::FromString(ipv4, ipAddr) || ipAddr.Type() != chip::Inet::IPAddressType::kIPv4)
{
ret = DNSSD_ERROR_OPERATION_FAILED;
}
}
#endif
ChipLogDetail(DeviceLayer, "DNSsd %s: IPv4: %s, IPv6: %s, ret: %d", __func__, StringOrNullMarker(ipv4),
StringOrNullMarker(ipv6), ret);
g_free(ipv4);
g_free(ipv6);
VerifyOrExit(ret == DNSSD_ERROR_NONE, );
ret = dnssd_service_get_port(service, &port);
VerifyOrExit(ret == DNSSD_ERROR_NONE, ChipLogError(DeviceLayer, "dnssd_service_get_port() failed. ret: %d", ret));
rCtx->mResult.mPort = static_cast<uint16_t>(port);
ret = dnssd_service_get_interface(service, &interface);
VerifyOrExit(ret == DNSSD_ERROR_NONE, ChipLogError(DeviceLayer, "dnssd_service_get_interface() failed. ret: %d", ret));
err = chip::Inet::InterfaceId::InterfaceNameToId(interface, rCtx->mResult.mInterface);
VerifyOrExit(
err == CHIP_NO_ERROR,
ChipLogError(DeviceLayer, "chip::Inet::InterfaceId::InterfaceNameToId() failed. ret: %" CHIP_ERROR_FORMAT, err.Format()));
ret = dnssd_service_get_all_txt_record(service, &rCtx->mResultTxtRecordLen, reinterpret_cast<void **>(&rCtx->mResultTxtRecord));
VerifyOrExit(ret == DNSSD_ERROR_NONE, ChipLogError(DeviceLayer, "dnssd_service_get_all_txt_record() failed. ret: %d", ret));
rCtx->mResult.mAddress.SetValue(ipAddr);
{
// Before calling the Resolve() callback, we need to lock stack mutex.
// However, we cannot lock the stack mutex from here, because we might
// face lock inversion problem. This callback (OnResolve()) is called
// with the NSD internal mutex locked, which is also locked by the
// dnssd_create_remote_service() function called in the Resolve(), and
// the Resolve() itself is called with the stack mutex locked.
auto * sourceIdle = g_idle_source_new();
g_source_set_callback(sourceIdle, OnResolveFinalize, rCtx, NULL);
g_source_attach(sourceIdle, g_main_context_get_thread_default());
g_source_unref(sourceIdle);
}
return;
exit:
rCtx->Finalize(ret != DNSSD_ERROR_NONE ? GetChipError(ret) : err);
rCtx->mInstance->RemoveContext(rCtx);
}
CHIP_ERROR ResolveAsync(chip::Dnssd::ResolveContext * rCtx)
{
ChipLogDetail(DeviceLayer, "DNSsd %s", __func__);
int ret = dnssd_resolve_service(rCtx->mServiceHandle, OnResolve, rCtx);
VerifyOrReturnValue(ret == DNSSD_ERROR_NONE, GetChipError(ret),
ChipLogError(DeviceLayer, "dnssd_resolve_service() failed. ret: %d", ret));
rCtx->mIsResolving = true;
return CHIP_NO_ERROR;
}
} // namespace
namespace chip {
namespace Dnssd {
DnssdTizen DnssdTizen::sInstance;
RegisterContext::RegisterContext(DnssdTizen * instance, const char * type, const DnssdService & service,
DnssdPublishCallback callback, void * context) :
GenericContext(ContextType::Register, instance)
{
Platform::CopyString(mName, service.mName);
Platform::CopyString(mType, type);
mInterfaceId = service.mInterface.GetPlatformInterface();
mPort = service.mPort;
mCallback = callback;
mCbContext = context;
}
RegisterContext::~RegisterContext()
{
if (mIsRegistered)
{
dnssd_deregister_local_service(mServiceHandle);
}
if (mServiceHandle != 0)
{
dnssd_destroy_local_service(mServiceHandle);
}
}
BrowseContext::BrowseContext(DnssdTizen * instance, const char * type, Dnssd::DnssdServiceProtocol protocol, uint32_t interfaceId,
DnssdBrowseCallback callback, void * context) :
GenericContext(ContextType::Browse, instance)
{
Platform::CopyString(mType, type);
mProtocol = protocol;
mInterfaceId = interfaceId;
mCallback = callback;
mCbContext = context;
}
BrowseContext::~BrowseContext()
{
if (mTimeoutSource != nullptr)
{
g_source_destroy(mTimeoutSource);
g_source_unref(mTimeoutSource);
}
}
ResolveContext::ResolveContext(DnssdTizen * instance, const char * name, const char * type, uint32_t interfaceId,
DnssdResolveCallback callback, void * context) :
GenericContext(ContextType::Resolve, instance)
{
Platform::CopyString(mName, name);
Platform::CopyString(mType, type);
mInterfaceId = interfaceId;
mCallback = callback;
mCbContext = context;
}
ResolveContext::~ResolveContext()
{
g_free(mResultTxtRecord);
}
void ResolveContext::Finalize(CHIP_ERROR error)
{
// In case of error, run the callback function with nullptr as the result.
VerifyOrReturn(error == CHIP_NO_ERROR, mCallback(mCbContext, nullptr, chip::Span<chip::Inet::IPAddress>(), error));
std::vector<chip::Dnssd::TextEntry> textEntries;
GetTextEntries(mResultTxtRecordLen, mResultTxtRecord, textEntries);
mResult.mTextEntries = textEntries.empty() ? nullptr : textEntries.data();
mResult.mTextEntrySize = textEntries.size();
chip::Inet::IPAddress ipAddr = mResult.mAddress.Value();
mCallback(mCbContext, &mResult, chip::Span<chip::Inet::IPAddress>(&ipAddr, 1), CHIP_NO_ERROR);
}
CHIP_ERROR DnssdTizen::Init(DnssdAsyncReturnCallback initCallback, DnssdAsyncReturnCallback errorCallback, void * context)
{
int ret = dnssd_initialize();
VerifyOrExit(ret == DNSSD_ERROR_NONE || ret == DNSSD_ERROR_INVALID_OPERATION /* Already initialized */, );
initCallback(context, CHIP_NO_ERROR);
return CHIP_NO_ERROR;
exit:
errorCallback(context, CHIP_ERROR_INTERNAL);
return CHIP_ERROR_INTERNAL;
}
void DnssdTizen::Shutdown()
{
int ret = dnssd_deinitialize();
if (ret != DNSSD_ERROR_NONE)
ChipLogError(DeviceLayer, "DNSsd %s: Error: %d", __func__, ret);
}
CHIP_ERROR DnssdTizen::RegisterService(const DnssdService & service, DnssdPublishCallback callback, void * context)
{
std::string fullType = GetFullType(service.mType, service.mProtocol);
auto interfaceId = service.mInterface.GetPlatformInterface();
CHIP_ERROR err = CHIP_NO_ERROR;
ChipLogProgress(DeviceLayer, "DNSsd %s: name: %s, type: %s, interfaceId: %u, port: %u", __func__, service.mName,
fullType.c_str(), interfaceId, service.mPort);
{ // If the service was already registered, update it
std::lock_guard<std::mutex> lock(mMutex);
auto iServiceCtx = std::find_if(mContexts.begin(), mContexts.end(), [fullType, service, interfaceId](const auto & ctx) {
VerifyOrReturnValue(ctx->mContextType == ContextType::Register, false);
auto * rCtx = static_cast<RegisterContext *>(ctx.get());
return strcmp(rCtx->mName, service.mName) == 0 && strcmp(rCtx->mType, fullType.c_str()) == 0 &&
rCtx->mPort == service.mPort && rCtx->mInterfaceId == interfaceId;
});
if (iServiceCtx != mContexts.end())
{
ChipLogDetail(DeviceLayer, "DNSsd %s: Updating TXT records", __func__);
auto serviceHandle = static_cast<RegisterContext *>(iServiceCtx->get())->mServiceHandle;
for (size_t i = 0; i < service.mTextEntrySize; ++i)
{
TextEntry entry = service.mTextEntries[i];
VerifyOrReturnError(chip::CanCastTo<unsigned short>(entry.mDataSize), CHIP_ERROR_INVALID_ARGUMENT);
auto dataSize = static_cast<unsigned short>(entry.mDataSize);
int ret = dnssd_service_add_txt_record(serviceHandle, entry.mKey, dataSize, entry.mData);
if (ret != DNSSD_ERROR_NONE)
{
ChipLogError(DeviceLayer, "dnssd_service_add_txt_record() failed. ret: %d", ret);
callback(context, nullptr, nullptr, err = GetChipError(ret));
}
}
return err;
}
}
auto serviceCtx = CreateRegisterContext(fullType.c_str(), service, callback, context);
// Local service will be freed by the RegisterContext destructor
int ret = dnssd_create_local_service(fullType.c_str(), &serviceCtx->mServiceHandle);
auto serviceHandle = serviceCtx->mServiceHandle;
VerifyOrExit(ret == DNSSD_ERROR_NONE, ChipLogError(DeviceLayer, "dnssd_create_local_service() failed. ret: %d", ret);
err = GetChipError(ret));
ret = dnssd_service_set_name(serviceHandle, service.mName);
VerifyOrExit(ret == DNSSD_ERROR_NONE, ChipLogError(DeviceLayer, "dnssd_service_set_name() failed. ret: %d", ret);
err = GetChipError(ret));
ret = dnssd_service_set_port(serviceHandle, service.mPort);
VerifyOrExit(ret == DNSSD_ERROR_NONE, ChipLogError(DeviceLayer, "dnssd_service_set_port() failed. ret: %d", ret);
err = GetChipError(ret));
if (interfaceId > 0)
{
char iface[IF_NAMESIZE + 1] = "";
VerifyOrExit(if_indextoname(interfaceId, iface) != nullptr,
ChipLogError(DeviceLayer, "if_indextoname() failed. errno: %d", errno);
err = CHIP_ERROR_INTERNAL);
ret = dnssd_service_set_interface(serviceHandle, iface);
VerifyOrExit(ret == DNSSD_ERROR_NONE, ChipLogError(DeviceLayer, "dnssd_service_set_interface() failed. ret: %d", ret);
err = GetChipError(ret));
}
for (size_t i = 0; i < service.mTextEntrySize; ++i)
{
TextEntry entry = service.mTextEntries[i];
VerifyOrReturnError(chip::CanCastTo<unsigned short>(entry.mDataSize), CHIP_ERROR_INVALID_ARGUMENT);
ret = dnssd_service_add_txt_record(serviceHandle, entry.mKey, static_cast<unsigned short>(entry.mDataSize), entry.mData);
VerifyOrExit(ret == DNSSD_ERROR_NONE, ChipLogError(DeviceLayer, "dnssd_service_add_txt_record() failed. ret: %d", ret);
err = GetChipError(ret));
}
err = DeviceLayer::PlatformMgrImpl().GLibMatterContextInvokeSync(RegisterAsync, serviceCtx);
SuccessOrExit(err);
exit:
if (err != CHIP_NO_ERROR)
{ // Notify caller about error
callback(context, nullptr, nullptr, err);
RemoveContext(serviceCtx);
}
return err;
}
CHIP_ERROR DnssdTizen::UnregisterAllServices()
{
std::lock_guard<std::mutex> lock(mMutex);
unsigned int numServices = 0;
for (auto it = mContexts.begin(); it != mContexts.end(); it++)
{
if ((*it)->mContextType == ContextType::Register)
{
mContexts.erase(it--);
numServices++;
}
}
ChipLogDetail(DeviceLayer, "DNSsd %s: %u", __func__, numServices);
return CHIP_NO_ERROR;
}
CHIP_ERROR DnssdTizen::Browse(const char * type, Dnssd::DnssdServiceProtocol protocol, chip::Inet::IPAddressType addressType,
chip::Inet::InterfaceId interface, DnssdBrowseCallback callback, void * context)
{
std::string fullType = GetFullType(type, protocol);
auto interfaceId = interface.GetPlatformInterface();
CHIP_ERROR err = CHIP_NO_ERROR;
auto browseCtx = CreateBrowseContext(fullType.c_str(), protocol, interfaceId, callback, context);
err = DeviceLayer::PlatformMgrImpl().GLibMatterContextInvokeSync(BrowseAsync, browseCtx);
SuccessOrExit(err);
exit:
if (err != CHIP_NO_ERROR)
{ // Notify caller about error
callback(context, nullptr, 0, true, err);
RemoveContext(browseCtx);
}
return err;
}
CHIP_ERROR DnssdTizen::Resolve(const DnssdService & browseResult, chip::Inet::InterfaceId interface, DnssdResolveCallback callback,
void * context)
{
std::string fullType = GetFullType(browseResult.mType, browseResult.mProtocol);
auto interfaceId = interface.GetPlatformInterface();
CHIP_ERROR err = CHIP_NO_ERROR;
int ret;
ChipLogDetail(DeviceLayer, "DNSsd %s: name: %s, type: %s, interfaceId: %u", __func__, browseResult.mName, fullType.c_str(),
interfaceId);
auto resolveCtx = CreateResolveContext(browseResult.mName, fullType.c_str(), interfaceId, callback, context);
if (interfaceId == 0)
{
ret = dnssd_create_remote_service(fullType.c_str(), browseResult.mName, nullptr, &resolveCtx->mServiceHandle);
}
else
{
char iface[IF_NAMESIZE + 1] = "";
VerifyOrExit(if_indextoname(interfaceId, iface) != nullptr,
ChipLogError(DeviceLayer, "if_indextoname() failed. errno: %d", errno);
err = CHIP_ERROR_INTERNAL);
ret = dnssd_create_remote_service(fullType.c_str(), browseResult.mName, iface, &resolveCtx->mServiceHandle);
}
VerifyOrExit(ret == DNSSD_ERROR_NONE, ChipLogError(DeviceLayer, "dnssd_create_remote_service() failed. ret: %d", ret);
err = GetChipError(ret));
err = DeviceLayer::PlatformMgrImpl().GLibMatterContextInvokeSync(ResolveAsync, resolveCtx);
SuccessOrExit(err);
exit:
if (err != CHIP_NO_ERROR)
RemoveContext(resolveCtx);
return err;
}
RegisterContext * DnssdTizen::CreateRegisterContext(const char * type, const DnssdService & service, DnssdPublishCallback callback,
void * context)
{
auto ctx = std::make_unique<RegisterContext>(this, type, service, callback, context);
auto ctxPtr = ctx.get();
std::lock_guard<std::mutex> lock(mMutex);
mContexts.emplace(std::move(ctx));
return ctxPtr;
}
BrowseContext * DnssdTizen::CreateBrowseContext(const char * type, Dnssd::DnssdServiceProtocol protocol, uint32_t interfaceId,
DnssdBrowseCallback callback, void * context)
{
auto ctx = std::make_unique<BrowseContext>(this, type, protocol, interfaceId, callback, context);
auto ctxPtr = ctx.get();
std::lock_guard<std::mutex> lock(mMutex);
mContexts.emplace(std::move(ctx));
return ctxPtr;
}
ResolveContext * DnssdTizen::CreateResolveContext(const char * name, const char * type, uint32_t interfaceId,
DnssdResolveCallback callback, void * context)
{
auto ctx = std::make_unique<ResolveContext>(this, name, type, interfaceId, callback, context);
auto ctxPtr = ctx.get();
std::lock_guard<std::mutex> lock(mMutex);
mContexts.emplace(std::move(ctx));
return ctxPtr;
}
CHIP_ERROR DnssdTizen::RemoveContext(GenericContext * context)
{
std::lock_guard<std::mutex> lock(mMutex);
mContexts.erase(std::find_if(mContexts.begin(), mContexts.end(), [context](const auto & ctx) { return ctx.get() == context; }));
return CHIP_NO_ERROR;
}
CHIP_ERROR ChipDnssdInit(DnssdAsyncReturnCallback initCallback, DnssdAsyncReturnCallback errorCallback, void * context)
{
VerifyOrReturnError(initCallback != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(errorCallback != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
return DnssdTizen::GetInstance().Init(initCallback, errorCallback, context);
}
void ChipDnssdShutdown()
{
DnssdTizen::GetInstance().Shutdown();
}
CHIP_ERROR ChipDnssdPublishService(const DnssdService * service, DnssdPublishCallback callback, void * context)
{
VerifyOrReturnError(service != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(IsSupportedProtocol(service->mProtocol), CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(callback != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT
if (DeviceLayer::ThreadStackMgr().IsThreadEnabled())
{
std::string regtype = GetFullType(service->mType, service->mProtocol);
Span<const char * const> subTypes(service->mSubTypes, service->mSubTypeSize);
Span<const TextEntry> textEntries(service->mTextEntries, service->mTextEntrySize);
CHIP_ERROR err;
if (strcmp(service->mHostName, "") != 0)
{
err = DeviceLayer::ThreadStackMgr().SetupSrpHost(service->mHostName);
VerifyOrReturnError(err == CHIP_NO_ERROR, err);
}
err = DeviceLayer::ThreadStackMgr().AddSrpService(service->mName, regtype.c_str(), service->mPort, subTypes, textEntries);
VerifyOrReturnError(err == CHIP_NO_ERROR, err);
return CHIP_NO_ERROR;
}
#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT
return DnssdTizen::GetInstance().RegisterService(*service, callback, context);
}
CHIP_ERROR ChipDnssdRemoveServices()
{
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT
if (DeviceLayer::ThreadStackMgr().IsThreadEnabled())
{
DeviceLayer::ThreadStackMgr().InvalidateAllSrpServices();
return DeviceLayer::ThreadStackMgr().RemoveInvalidSrpServices();
}
#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT
return DnssdTizen::GetInstance().UnregisterAllServices();
}
CHIP_ERROR ChipDnssdFinalizeServiceUpdate()
{
return CHIP_NO_ERROR;
}
CHIP_ERROR ChipDnssdBrowse(const char * type, Dnssd::DnssdServiceProtocol protocol, chip::Inet::IPAddressType addressType,
chip::Inet::InterfaceId interface, DnssdBrowseCallback callback, void * context,
intptr_t * browseIdentifier)
{
VerifyOrReturnError(type != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(IsSupportedProtocol(protocol), CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(callback != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
*browseIdentifier = reinterpret_cast<intptr_t>(nullptr);
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT && CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT
if (DeviceLayer::ThreadStackMgr().IsThreadEnabled())
{
std::string fullType = GetFullType(type, protocol);
return DeviceLayer::ThreadStackMgr().DnsBrowse(fullType.c_str(), callback, context);
}
#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT && CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT
return DnssdTizen::GetInstance().Browse(type, protocol, addressType, interface, callback, context);
}
CHIP_ERROR ChipDnssdStopBrowse(intptr_t browseIdentifier)
{
return CHIP_ERROR_NOT_IMPLEMENTED;
}
CHIP_ERROR ChipDnssdResolve(DnssdService * browseResult, chip::Inet::InterfaceId interface, DnssdResolveCallback callback,
void * context)
{
VerifyOrReturnError(browseResult != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(IsSupportedProtocol(browseResult->mProtocol), CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(callback != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT && CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT
if (DeviceLayer::ThreadStackMgr().IsThreadEnabled())
{
std::string fullType = GetFullType(browseResult->mType, browseResult->mProtocol);
return DeviceLayer::ThreadStackMgr().DnsResolve(fullType.c_str(), browseResult->mName, callback, context);
}
#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT && CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT
return DnssdTizen::GetInstance().Resolve(*browseResult, interface, callback, context);
}
void ChipDnssdResolveNoLongerNeeded(const char * instanceName) {}
CHIP_ERROR ChipDnssdReconfirmRecord(const char * hostname, chip::Inet::IPAddress address, chip::Inet::InterfaceId interface)
{
return CHIP_ERROR_NOT_IMPLEMENTED;
}
} // namespace Dnssd
} // namespace chip