| /* |
| * |
| * 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 |