diff --git a/config/esp32/components/chip/Kconfig b/config/esp32/components/chip/Kconfig index a65d705ac2a211..f8fe979709af77 100644 --- a/config/esp32/components/chip/Kconfig +++ b/config/esp32/components/chip/Kconfig @@ -85,7 +85,7 @@ menu "CHIP Core" config USE_MINIMAL_MDNS bool "Use the minimal mDNS implementation shipped in the CHIP library" - default y + default n help The CHIP library is shipped with a minimal mDNS implementation, enable this config to use it rather than the mDNS library in IDF. diff --git a/examples/all-clusters-app/esp32/sdkconfig.defaults b/examples/all-clusters-app/esp32/sdkconfig.defaults index 06f7c670712c57..5f996f40946d68 100644 --- a/examples/all-clusters-app/esp32/sdkconfig.defaults +++ b/examples/all-clusters-app/esp32/sdkconfig.defaults @@ -52,4 +52,4 @@ CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT=y # Serial Flasher config CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y -CONFIG_ESPTOOLPY_FLASHSIZE="4MB" \ No newline at end of file +CONFIG_ESPTOOLPY_FLASHSIZE="4MB" diff --git a/src/platform/ESP32/BUILD.gn b/src/platform/ESP32/BUILD.gn index 4a1b56eace0083..fc9323529d8b7e 100644 --- a/src/platform/ESP32/BUILD.gn +++ b/src/platform/ESP32/BUILD.gn @@ -70,6 +70,12 @@ static_library("ESP32") { "NetworkCommissioningDriver.cpp", "NetworkCommissioningDriver.h", ] + if (chip_mdns == "platform") { + sources += [ + "DnssdImpl.cpp", + "DnssdImpl.h", + ] + } } if (chip_enable_openthread) { diff --git a/src/platform/ESP32/DnssdImpl.cpp b/src/platform/ESP32/DnssdImpl.cpp index 5391f2bb0d24ca..468b900061ecf5 100644 --- a/src/platform/ESP32/DnssdImpl.cpp +++ b/src/platform/ESP32/DnssdImpl.cpp @@ -15,12 +15,12 @@ * limitations under the License. */ +#include "DnssdImpl.h" #include "lib/dnssd/platform/Dnssd.h" #include #include #include -#include #include "platform/CHIPDeviceLayer.h" #include @@ -37,6 +37,82 @@ static constexpr size_t kMaxResults = 20; namespace chip { namespace Dnssd { +struct MdnsQuery +{ + GenericContext * ctx; + MdnsQuery * next; +}; + +static MdnsQuery * sQueryList = nullptr; +void MdnsQueryNotifier(mdns_search_once_t * searchHandle); + +CHIP_ERROR AddQueryList(GenericContext * ctx) +{ + MdnsQuery * ret = static_cast(chip::Platform::MemoryAlloc(sizeof(MdnsQuery))); + if (ret == nullptr) + { + ChipLogError(DeviceLayer, "Failed to alloc memory for MdnsQuery"); + return CHIP_ERROR_NO_MEMORY; + } + ret->ctx = ctx; + ret->next = sQueryList; + sQueryList = ret; + return CHIP_NO_ERROR; +} + +GenericContext * FindMdnsQuery(mdns_search_once_t * searchHandle) +{ + MdnsQuery * current = sQueryList; + while (current) + { + if (current->ctx && current->ctx->mSearchHandle == searchHandle) + { + return current->ctx; + } + current = current->next; + } + return nullptr; +} + +CHIP_ERROR RemoveMdnsQuery(GenericContext * ctx) +{ + MdnsQuery * current = sQueryList; + MdnsQuery * front = nullptr; + + VerifyOrReturnError(ctx != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + while (current) + { + if (current->ctx == ctx) + { + break; + } + front = current; + current = current->next; + } + if (!current) + { + return CHIP_ERROR_NOT_FOUND; + } + if (front) + { + front->next = current->next; + } + else + { + sQueryList = current->next; + } + if (current->ctx->mContextType == ContextType::Browse) + { + chip::Platform::Delete(reinterpret_cast(current->ctx)); + } + else if (ctx->mContextType == ContextType::Resolve) + { + chip::Platform::Delete(reinterpret_cast(current->ctx)); + } + chip::Platform::MemoryFree(current); + return CHIP_NO_ERROR; +} + CHIP_ERROR ChipDnssdInit(DnssdAsyncReturnCallback initCallback, DnssdAsyncReturnCallback errorCallback, void * context) { CHIP_ERROR error = CHIP_NO_ERROR; @@ -110,7 +186,9 @@ CHIP_ERROR ChipDnssdPublishService(const DnssdService * service, DnssdPublishCal CHIP_ERROR ChipDnssdRemoveServices() { - return mdns_service_remove_all() == ESP_OK ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL; + mdns_service_remove("_matter", "_tcp"); + mdns_service_remove("_matterc", "_udp"); + return CHIP_NO_ERROR; } CHIP_ERROR ChipDnssdFinalizeServiceUpdate() @@ -118,16 +196,320 @@ CHIP_ERROR ChipDnssdFinalizeServiceUpdate() return CHIP_NO_ERROR; } -CHIP_ERROR ChipDnssdBrowse(const char * /*type*/, DnssdServiceProtocol /*protocol*/, chip::Inet::IPAddressType addressType, - chip::Inet::InterfaceId /*interface*/, DnssdBrowseCallback /*callback*/, void * /*context*/) +Inet::IPAddressType MapAddressType(mdns_ip_protocol_t ip_protocol) { - return CHIP_ERROR_NOT_IMPLEMENTED; + switch (ip_protocol) + { +#if INET_CONFIG_ENABLE_IPV4 + case MDNS_IP_PROTOCOL_V4: + return Inet::IPAddressType::kIPv4; +#endif + case MDNS_IP_PROTOCOL_V6: + return Inet::IPAddressType::kIPv6; + default: + return Inet::IPAddressType::kAny; + } } -CHIP_ERROR ChipDnssdResolve(DnssdService * /*service*/, chip::Inet::InterfaceId /*interface*/, DnssdResolveCallback /*callback*/, - void * /*context*/) +TextEntry * GetTextEntry(mdns_txt_item_t * txt_array, uint8_t * txt_value_len, size_t txt_count) { - return CHIP_ERROR_NOT_IMPLEMENTED; + if (txt_count == 0 || txt_array == NULL) + { + return NULL; + } + TextEntry * ret = static_cast(chip::Platform::MemoryCalloc(txt_count, sizeof(TextEntry))); + if (ret) + { + for (size_t TextEntryIndex = 0; TextEntryIndex < txt_count; ++TextEntryIndex) + { + ret[TextEntryIndex].mKey = txt_array[TextEntryIndex].key; + ret[TextEntryIndex].mData = reinterpret_cast(txt_array[TextEntryIndex].value); + ret[TextEntryIndex].mDataSize = txt_value_len[TextEntryIndex]; + } + } + return ret; +} + +CHIP_ERROR GetIPAddress(Inet::IPAddress & outIPAddress, mdns_ip_addr_t * mdnsIPAddr) +{ + if (!mdnsIPAddr) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + if (mdnsIPAddr->addr.type == ESP_IPADDR_TYPE_V6) + { + memcpy(outIPAddress.Addr, mdnsIPAddr->addr.u_addr.ip6.addr, sizeof(mdnsIPAddr->addr.u_addr.ip6.addr)); + } + else if (mdnsIPAddr->addr.type == ESP_IPADDR_TYPE_V4) + { + outIPAddress.Addr[0] = 0; + outIPAddress.Addr[1] = 0; + outIPAddress.Addr[2] = htonl(0xFFFF); + outIPAddress.Addr[3] = mdnsIPAddr->addr.u_addr.ip4.addr; + } + else + { + outIPAddress = Inet::IPAddress::Any; + } + return CHIP_NO_ERROR; +} + +size_t GetResultSize(mdns_result_t * result) +{ + size_t ret = 0; + while (result) + { + ret++; + result = result->next; + } + return ret; +} + +CHIP_ERROR OnBrowseDone(BrowseContext * ctx) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + mdns_result_t * currentResult = nullptr; + size_t servicesIndex = 0; + VerifyOrExit(ctx && ctx->mBrowseCb, error = CHIP_ERROR_INVALID_ARGUMENT); + if (ctx->mResult) + { + ctx->mServiceSize = GetResultSize(ctx->mResult); + if (ctx->mServiceSize > 0) + { + ctx->mServices = static_cast(chip::Platform::MemoryCalloc(ctx->mServiceSize, sizeof(DnssdService))); + if (!ctx->mServices) + { + ChipLogError(DeviceLayer, "Failed to alloc memory for Dnssd services"); + ctx->mServiceSize = 0; + error = CHIP_ERROR_NO_MEMORY; + ExitNow(); + } + currentResult = ctx->mResult; + servicesIndex = 0; + while (currentResult) + { + strncpy(ctx->mServices[servicesIndex].mName, currentResult->instance_name, + strnlen(currentResult->instance_name, Common::kInstanceNameMaxLength)); + strncpy(ctx->mServices[servicesIndex].mHostName, currentResult->hostname, + strnlen(currentResult->hostname, kHostNameMaxLength)); + strncpy(ctx->mServices[servicesIndex].mType, currentResult->service_type, + strnlen(currentResult->service_type, kDnssdTypeMaxSize)); + ctx->mServices[servicesIndex].mProtocol = ctx->mProtocol; + ctx->mServices[servicesIndex].mAddressType = MapAddressType(currentResult->ip_protocol); + ctx->mServices[servicesIndex].mTransportType = ctx->mAddressType; + ctx->mServices[servicesIndex].mPort = currentResult->port; + ctx->mServices[servicesIndex].mInterface = ctx->mInterfaceId; + ctx->mServices[servicesIndex].mTextEntries = + GetTextEntry(currentResult->txt, currentResult->txt_value_len, currentResult->txt_count); + ctx->mServices[servicesIndex].mTextEntrySize = currentResult->txt_count; + ctx->mServices[servicesIndex].mSubTypes = NULL; + ctx->mServices[servicesIndex].mSubTypeSize = 0; + if (currentResult->addr) + { + Inet::IPAddress IPAddr; + error = GetIPAddress(IPAddr, currentResult->addr); + SuccessOrExit(error); + ctx->mServices[servicesIndex].mAddress.SetValue(IPAddr); + } + currentResult = currentResult->next; + servicesIndex++; + } + } + } +exit: + ctx->mBrowseCb(ctx->mCbContext, ctx->mServices, ctx->mServiceSize, error); + return RemoveMdnsQuery(reinterpret_cast(ctx)); +} + +size_t GetExtraIPsSize(mdns_ip_addr_t * addr) +{ + size_t ret = 0; + while (addr) + { + ret++; + addr = addr->next; + } + return ret; +} + +CHIP_ERROR OnResolveQuerySrvDone(ResolveContext * ctx) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + mdns_ip_addr_t * extraAddr = nullptr; + size_t extraIPIndex = 0; + + VerifyOrExit(ctx && ctx->mResolveCb, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(ctx->mService == nullptr && ctx->mResolveState == ResolveContext::ResolveState::QuerySrv, + error = CHIP_ERROR_INCORRECT_STATE); + if (ctx->mResult) + { + ctx->mService = static_cast(chip::Platform::MemoryAlloc(sizeof(DnssdService))); + VerifyOrExit(ctx->mService != nullptr, error = CHIP_ERROR_NO_MEMORY); + strncpy(ctx->mService->mName, ctx->mResult->instance_name, + strnlen(ctx->mResult->instance_name, Common::kInstanceNameMaxLength)); + strncpy(ctx->mService->mHostName, ctx->mResult->hostname, strnlen(ctx->mResult->hostname, kHostNameMaxLength)); + strncpy(ctx->mService->mType, ctx->mResult->service_type, strnlen(ctx->mResult->service_type, kDnssdTypeMaxSize)); + ctx->mService->mProtocol = ctx->mProtocol; + ctx->mService->mAddressType = MapAddressType(ctx->mResult->ip_protocol); + ctx->mService->mTransportType = ctx->mService->mAddressType; + ctx->mService->mPort = ctx->mResult->port; + ctx->mService->mInterface = ctx->mInterfaceId; + ctx->mService->mSubTypes = nullptr; + ctx->mService->mSubTypeSize = 0; + + if (ctx->mResult->addr) + { + Inet::IPAddress IPAddr; + GetIPAddress(IPAddr, ctx->mResult->addr); + ctx->mService->mAddress.SetValue(IPAddr); + extraAddr = ctx->mResult->addr->next; + ctx->mExtraIPSize = GetExtraIPsSize(extraAddr); + if (ctx->mExtraIPSize > 0) + { + ctx->mExtraIPs = + static_cast(chip::Platform::MemoryCalloc(ctx->mExtraIPSize, sizeof(Inet::IPAddress))); + if (ctx->mExtraIPs == nullptr) + { + ChipLogError(DeviceLayer, "Failed to alloc memory for ExtraIPs"); + error = CHIP_ERROR_NO_MEMORY; + ctx->mExtraIPSize = 0; + ExitNow(); + } + while (extraAddr) + { + GetIPAddress(ctx->mExtraIPs[extraIPIndex], extraAddr); + extraIPIndex++; + extraAddr = extraAddr->next; + } + } + else + { + ctx->mExtraIPs = nullptr; + ctx->mExtraIPSize = 0; + } + } + } +exit: + if (error != CHIP_NO_ERROR) + { + ctx->mResolveCb(ctx->mCbContext, nullptr, Span(nullptr, 0), error); + RemoveMdnsQuery(reinterpret_cast(ctx)); + return error; + } + mdns_query_results_free(ctx->mResult); + mdns_query_async_delete(ctx->mSearchHandle); + ctx->mResult = nullptr; + ctx->mResolveState = ResolveContext::ResolveState::QueryTxt; + // then query the text entries + ctx->mSearchHandle = mdns_query_async_new(ctx->mInstanceName, ctx->mType, GetProtocolString(ctx->mProtocol), MDNS_TYPE_TXT, + kTimeoutMilli, kMaxResults, MdnsQueryNotifier); + return CHIP_NO_ERROR; +} + +CHIP_ERROR OnResolveQueryTxtDone(ResolveContext * ctx) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + + VerifyOrExit(ctx && ctx->mResolveCb, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(ctx->mService && ctx->mResolveState == ResolveContext::ResolveState::QueryTxt, error = CHIP_ERROR_INCORRECT_STATE); + if (ctx->mResult) + { + ctx->mService->mTextEntries = GetTextEntry(ctx->mResult->txt, ctx->mResult->txt_value_len, ctx->mResult->txt_count); + ctx->mService->mTextEntrySize = ctx->mResult->txt_count; + } + else + { + ctx->mService->mTextEntries = nullptr; + ctx->mService->mTextEntrySize = 0; + } +exit: + if (error != CHIP_NO_ERROR) + { + ctx->mResolveCb(ctx->mCbContext, nullptr, Span(nullptr, 0), error); + } + else + { + ctx->mResolveCb(ctx->mCbContext, ctx->mService, Span(ctx->mExtraIPs, ctx->mExtraIPSize), error); + } + RemoveMdnsQuery(reinterpret_cast(ctx)); + return error; +} + +void MdnsQueryDone(intptr_t context) +{ + if (!context) + { + return; + } + mdns_search_once_t * searchHandle = reinterpret_cast(context); + GenericContext * ctx = FindMdnsQuery(searchHandle); + if (mdns_query_async_get_results(searchHandle, 0, &(ctx->mResult))) + { + if (ctx->mContextType == ContextType::Browse) + { + OnBrowseDone(reinterpret_cast(ctx)); + } + else if (ctx->mContextType == ContextType::Resolve) + { + ResolveContext * resolveCtx = reinterpret_cast(ctx); + if (resolveCtx->mResolveState == ResolveContext::ResolveState::QuerySrv) + { + OnResolveQuerySrvDone(resolveCtx); + } + else if (resolveCtx->mResolveState == ResolveContext::ResolveState::QueryTxt) + { + OnResolveQueryTxtDone(resolveCtx); + } + } + } +} + +void MdnsQueryNotifier(mdns_search_once_t * searchHandle) +{ + chip::DeviceLayer::PlatformMgr().ScheduleWork(MdnsQueryDone, reinterpret_cast(searchHandle)); +} + +CHIP_ERROR ChipDnssdBrowse(const char * type, DnssdServiceProtocol protocol, chip::Inet::IPAddressType addressType, + chip::Inet::InterfaceId interface, DnssdBrowseCallback callback, void * context) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + mdns_search_once_t * searchHandle = + mdns_query_async_new(NULL, type, GetProtocolString(protocol), MDNS_TYPE_PTR, kTimeoutMilli, kMaxResults, MdnsQueryNotifier); + BrowseContext * ctx = + chip::Platform::New(type, protocol, interface, searchHandle, addressType, callback, context); + if (!ctx) + { + ChipLogError(DeviceLayer, "Failed to alloc memory for browse context"); + mdns_query_async_delete(searchHandle); + return CHIP_ERROR_NO_MEMORY; + } + error = AddQueryList(reinterpret_cast(ctx)); + if (error != CHIP_NO_ERROR) + { + chip::Platform::Delete(ctx); + } + return error; +} + +CHIP_ERROR ChipDnssdResolve(DnssdService * service, chip::Inet::InterfaceId interface, DnssdResolveCallback callback, + void * context) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + mdns_search_once_t * searchHandle = mdns_query_async_new(service->mName, service->mType, GetProtocolString(service->mProtocol), + MDNS_TYPE_SRV, kTimeoutMilli, kMaxResults, MdnsQueryNotifier); + ResolveContext * ctx = chip::Platform::New(service, interface, searchHandle, callback, context); + if (!ctx) + { + ChipLogError(DeviceLayer, "Failed to alloc memory for resolve context"); + mdns_query_async_delete(searchHandle); + return CHIP_ERROR_NO_MEMORY; + } + error = AddQueryList(reinterpret_cast(ctx)); + if (error != CHIP_NO_ERROR) + { + chip::Platform::Delete(ctx); + } + return error; } } // namespace Dnssd diff --git a/src/platform/ESP32/DnssdImpl.h b/src/platform/ESP32/DnssdImpl.h new file mode 100644 index 00000000000000..c5bcccfb44c47a --- /dev/null +++ b/src/platform/ESP32/DnssdImpl.h @@ -0,0 +1,152 @@ +/* + * + * Copyright (c) 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. + * + * 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. + */ + +#pragma once + +#include +#include +#include + +namespace chip { +namespace Dnssd { +enum class ContextType +{ + Browse = 1, + Resolve, + Reserved, +}; + +struct GenericContext +{ + ContextType mContextType; + void * mCbContext; + char mType[kDnssdTypeMaxSize + 1]; + DnssdServiceProtocol mProtocol; + Inet::InterfaceId mInterfaceId; + mdns_search_once_t * mSearchHandle; + mdns_result_t * mResult; +}; + +struct BrowseContext : public GenericContext +{ + DnssdBrowseCallback mBrowseCb; + Inet::IPAddressType mAddressType; + DnssdService * mServices; + size_t mServiceSize; + BrowseContext(const char * type, DnssdServiceProtocol protocol, Inet::InterfaceId ifId, mdns_search_once_t * searchHandle, + Inet::IPAddressType addrType, DnssdBrowseCallback cb, void * cbCtx) + + { + memset(mType, 0, sizeof(mType)); + strncpy(mType, type, strnlen(type, kDnssdTypeMaxSize)); + mContextType = ContextType::Browse; + mAddressType = addrType; + mProtocol = protocol; + mBrowseCb = cb; + mCbContext = cbCtx; + mInterfaceId = ifId; + mSearchHandle = searchHandle; + mResult = nullptr; + mServices = nullptr; + mServiceSize = 0; + } + + ~BrowseContext() + { + if (mServices && mServiceSize > 0) + { + for (size_t serviceIndex = 0; serviceIndex < mServiceSize; serviceIndex++) + { + if (mServices[serviceIndex].mTextEntries) + { + chip::Platform::MemoryFree(mServices[serviceIndex].mTextEntries); + } + } + chip::Platform::MemoryFree(mServices); + } + if (mResult) + { + mdns_query_results_free(mResult); + } + if (mSearchHandle) + { + mdns_query_async_delete(mSearchHandle); + } + } +}; + +struct ResolveContext : public GenericContext +{ + char mInstanceName[Common::kInstanceNameMaxLength + 1]; + DnssdResolveCallback mResolveCb; + DnssdService * mService; + Inet::IPAddress * mExtraIPs; + size_t mExtraIPSize; + + enum class ResolveState + { + QuerySrv, + QueryTxt, + } mResolveState; + + ResolveContext(DnssdService * service, Inet::InterfaceId ifId, mdns_search_once_t * searchHandle, DnssdResolveCallback cb, + void * cbCtx) + { + memset(mType, 0, sizeof(mType)); + memset(mInstanceName, 0, sizeof(mInstanceName)); + strncpy(mType, service->mType, strnlen(service->mType, kDnssdTypeMaxSize)); + mType[kDnssdTypeMaxSize] = 0; + strncpy(mInstanceName, service->mName, strnlen(service->mName, Common::kInstanceNameMaxLength)); + mInstanceName[Common::kInstanceNameMaxLength] = 0; + mContextType = ContextType::Resolve; + mProtocol = service->mProtocol; + mResolveCb = cb; + mCbContext = cbCtx; + mInterfaceId = ifId; + mSearchHandle = searchHandle; + mResolveState = ResolveState::QuerySrv; + mResult = nullptr; + mService = nullptr; + mExtraIPs = nullptr; + mExtraIPSize = 0; + } + + ~ResolveContext() + { + if (mService) + { + if (mService->mTextEntries) + { + chip::Platform::MemoryFree(mService->mTextEntries); + } + chip::Platform::MemoryFree(mService); + } + if (mExtraIPs) + { + chip::Platform::MemoryFree(mExtraIPs); + } + if (mResult) + { + mdns_query_results_free(mResult); + } + if (mSearchHandle) + { + mdns_query_async_delete(mSearchHandle); + } + } +}; + +} // namespace Dnssd +} // namespace chip