Add a ResolveProxy class to keep track of which controller is issuing… (#12481)

* Add a ResolveProxy class to keep track of which controller is issuing a dns request

* Use ReferenceCount<ResolverDelegateProxy> to make sure the stack is not trying to access a dangling pointer once a response is received
diff --git a/src/app/CASESessionManager.cpp b/src/app/CASESessionManager.cpp
index 559ae13..199d25e 100644
--- a/src/app/CASESessionManager.cpp
+++ b/src/app/CASESessionManager.cpp
@@ -53,7 +53,7 @@
         session->OnNodeIdResolved(resolutionData);
     }
 
-    CHIP_ERROR err = session->Connect(onConnection, onFailure);
+    CHIP_ERROR err = session->Connect(onConnection, onFailure, mConfig.dnsResolver);
     if (err != CHIP_NO_ERROR)
     {
         ReleaseSession(session);
@@ -69,8 +69,8 @@
 
 CHIP_ERROR CASESessionManager::ResolveDeviceAddress(NodeId nodeId)
 {
-    return Dnssd::Resolver::Instance().ResolveNodeId(GetFabricInfo()->GetPeerIdForNode(nodeId), Inet::IPAddressType::kAny,
-                                                     Dnssd::Resolver::CacheBypass::On);
+    return mConfig.dnsResolver->ResolveNodeId(GetFabricInfo()->GetPeerIdForNode(nodeId), Inet::IPAddressType::kAny,
+                                              Dnssd::Resolver::CacheBypass::On);
 }
 
 void CASESessionManager::OnNodeIdResolved(const Dnssd::ResolvedNodeData & nodeData)
diff --git a/src/app/CASESessionManager.h b/src/app/CASESessionManager.h
index f2a5b38..757d2b4 100644
--- a/src/app/CASESessionManager.h
+++ b/src/app/CASESessionManager.h
@@ -27,7 +27,7 @@
 #include <lib/support/Pool.h>
 #include <transport/SessionDelegate.h>
 
-#include <lib/dnssd/Resolver.h>
+#include <lib/dnssd/ResolverProxy.h>
 
 namespace chip {
 
@@ -36,6 +36,7 @@
     DeviceProxyInitParams sessionInitParams;
     Dnssd::DnssdCache<CHIP_CONFIG_MDNS_CACHE_SIZE> * dnsCache = nullptr;
     OperationalDeviceProxyPoolDelegate * devicePool           = nullptr;
+    Dnssd::ResolverProxy * dnsResolver                        = nullptr;
 };
 
 /**
diff --git a/src/app/OperationalDeviceProxy.cpp b/src/app/OperationalDeviceProxy.cpp
index 46ebccb..8f2859a 100644
--- a/src/app/OperationalDeviceProxy.cpp
+++ b/src/app/OperationalDeviceProxy.cpp
@@ -43,7 +43,8 @@
 namespace chip {
 
 CHIP_ERROR OperationalDeviceProxy::Connect(Callback::Callback<OnDeviceConnected> * onConnection,
-                                           Callback::Callback<OnDeviceConnectionFailure> * onFailure)
+                                           Callback::Callback<OnDeviceConnectionFailure> * onFailure,
+                                           Dnssd::ResolverProxy * resolver)
 {
     CHIP_ERROR err = CHIP_NO_ERROR;
 
@@ -54,7 +55,8 @@
         break;
 
     case State::NeedsAddress:
-        err = Dnssd::Resolver::Instance().ResolveNodeId(mPeerId, chip::Inet::IPAddressType::kAny);
+        VerifyOrReturnError(resolver != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
+        err = resolver->ResolveNodeId(mPeerId, chip::Inet::IPAddressType::kAny);
         EnqueueConnectionCallbacks(onConnection, onFailure);
         break;
 
diff --git a/src/app/OperationalDeviceProxy.h b/src/app/OperationalDeviceProxy.h
index b67384b..7ee309f 100644
--- a/src/app/OperationalDeviceProxy.h
+++ b/src/app/OperationalDeviceProxy.h
@@ -43,7 +43,7 @@
 #include <transport/raw/MessageHeader.h>
 #include <transport/raw/UDP.h>
 
-#include <lib/dnssd/Resolver.h>
+#include <lib/dnssd/ResolverProxy.h>
 
 namespace chip {
 
@@ -109,9 +109,11 @@
      * session setup fails, `onFailure` will be called.
      *
      * If the session already exists, `onConnection` will be called immediately.
+     * If the resolver is null and the device state is State::NeedsAddress, CHIP_ERROR_INVALID_ARGUMENT will be
+     * returned.
      */
     CHIP_ERROR Connect(Callback::Callback<OnDeviceConnected> * onConnection,
-                       Callback::Callback<OnDeviceConnectionFailure> * onFailure);
+                       Callback::Callback<OnDeviceConnectionFailure> * onFailure, Dnssd::ResolverProxy * resolver);
 
     bool IsConnected() const { return mState == State::SecureConnected; }
 
diff --git a/src/app/clusters/ota-requestor/OTARequestor.cpp b/src/app/clusters/ota-requestor/OTARequestor.cpp
index 703dfa7..61d0bbd 100644
--- a/src/app/clusters/ota-requestor/OTARequestor.cpp
+++ b/src/app/clusters/ota-requestor/OTARequestor.cpp
@@ -235,6 +235,7 @@
             .sessionInitParams = initParams,
             .dnsCache          = nullptr,
             .devicePool        = mServer->GetDevicePool(),
+            .dnsResolver       = nullptr,
         };
 
         mCASESessionManager = Platform::New<CASESessionManager>(sessionManagerConfig);
diff --git a/src/controller/AbstractDnssdDiscoveryController.cpp b/src/controller/AbstractDnssdDiscoveryController.cpp
index 8d34292..924ca61 100644
--- a/src/controller/AbstractDnssdDiscoveryController.cpp
+++ b/src/controller/AbstractDnssdDiscoveryController.cpp
@@ -19,10 +19,6 @@
 // module header, comes first
 #include <controller/AbstractDnssdDiscoveryController.h>
 
-#if CONFIG_DEVICE_LAYER
-#include <platform/CHIPDeviceLayer.h>
-#endif
-
 #include <lib/core/CHIPEncoding.h>
 #include <lib/support/logging/CHIPLogging.h>
 
@@ -66,11 +62,6 @@
 
 CHIP_ERROR AbstractDnssdDiscoveryController::SetUpNodeDiscovery()
 {
-#if CONFIG_DEVICE_LAYER
-    ReturnErrorOnFailure(mResolver->Init(&DeviceLayer::InetLayer()));
-#endif
-    mResolver->SetResolverDelegate(this);
-
     auto discoveredNodes = GetDiscoveredNodes();
     for (auto & discoveredNode : discoveredNodes)
     {
diff --git a/src/controller/AbstractDnssdDiscoveryController.h b/src/controller/AbstractDnssdDiscoveryController.h
index aae783c..df58457 100644
--- a/src/controller/AbstractDnssdDiscoveryController.h
+++ b/src/controller/AbstractDnssdDiscoveryController.h
@@ -19,7 +19,7 @@
 #pragma once
 
 #include <controller/DeviceDiscoveryDelegate.h>
-#include <lib/dnssd/Resolver.h>
+#include <lib/dnssd/ResolverProxy.h>
 #include <lib/support/Span.h>
 #include <platform/CHIPDeviceConfig.h>
 
@@ -39,11 +39,7 @@
 class DLL_EXPORT AbstractDnssdDiscoveryController : public Dnssd::ResolverDelegate
 {
 public:
-    AbstractDnssdDiscoveryController(chip::Dnssd::Resolver * resolver = &chip::Dnssd::Resolver::Instance())
-    {
-        mResolver = resolver;
-    }
-
+    AbstractDnssdDiscoveryController() {}
     virtual ~AbstractDnssdDiscoveryController() {}
 
     void OnNodeDiscoveryComplete(const chip::Dnssd::DiscoveredNodeData & nodeData) override;
@@ -52,9 +48,9 @@
     using DiscoveredNodeList = FixedSpan<Dnssd::DiscoveredNodeData, CHIP_DEVICE_CONFIG_MAX_DISCOVERED_NODES>;
     CHIP_ERROR SetUpNodeDiscovery();
     const Dnssd::DiscoveredNodeData * GetDiscoveredNode(int idx);
-    virtual DiscoveredNodeList GetDiscoveredNodes() = 0;
-    chip::Dnssd::Resolver * mResolver;
+    virtual DiscoveredNodeList GetDiscoveredNodes()    = 0;
     DeviceDiscoveryDelegate * mDeviceDiscoveryDelegate = nullptr;
+    Dnssd::ResolverProxy mDNSResolver;
 };
 
 } // namespace Controller
diff --git a/src/controller/CHIPCommissionableNodeController.cpp b/src/controller/CHIPCommissionableNodeController.cpp
index 34d892d..d127e2a 100644
--- a/src/controller/CHIPCommissionableNodeController.cpp
+++ b/src/controller/CHIPCommissionableNodeController.cpp
@@ -19,6 +19,10 @@
 // module header, comes first
 #include <controller/CHIPCommissionableNodeController.h>
 
+#if CONFIG_DEVICE_LAYER
+#include <platform/CHIPDeviceLayer.h>
+#endif
+
 #include <lib/support/CodeUtils.h>
 
 namespace chip {
@@ -27,7 +31,22 @@
 CHIP_ERROR CommissionableNodeController::DiscoverCommissioners(Dnssd::DiscoveryFilter discoveryFilter)
 {
     ReturnErrorOnFailure(SetUpNodeDiscovery());
-    return mResolver->FindCommissioners(discoveryFilter);
+
+    if (mResolver == nullptr)
+    {
+#if CONFIG_DEVICE_LAYER
+        ReturnErrorOnFailure(mDNSResolver.Init(&DeviceLayer::InetLayer()));
+#endif
+        mDNSResolver.SetResolverDelegate(this);
+        return mDNSResolver.FindCommissioners(discoveryFilter);
+    }
+    else
+    {
+#if CONFIG_DEVICE_LAYER
+        ReturnErrorOnFailure(mResolver->Init(&DeviceLayer::InetLayer()));
+#endif
+        return mResolver->FindCommissioners(discoveryFilter);
+    }
 }
 
 const Dnssd::DiscoveredNodeData * CommissionableNodeController::GetDiscoveredCommissioner(int idx)
diff --git a/src/controller/CHIPCommissionableNodeController.h b/src/controller/CHIPCommissionableNodeController.h
index 8bafe78..af57d1d 100644
--- a/src/controller/CHIPCommissionableNodeController.h
+++ b/src/controller/CHIPCommissionableNodeController.h
@@ -19,7 +19,6 @@
 #pragma once
 
 #include <controller/AbstractDnssdDiscoveryController.h>
-#include <lib/dnssd/Resolver.h>
 #include <lib/support/logging/CHIPLogging.h>
 #include <platform/CHIPDeviceConfig.h>
 
@@ -37,8 +36,7 @@
 class DLL_EXPORT CommissionableNodeController : public AbstractDnssdDiscoveryController
 {
 public:
-    CommissionableNodeController(chip::Dnssd::Resolver * resolver = &chip::Dnssd::Resolver::Instance()) :
-        AbstractDnssdDiscoveryController(resolver){};
+    CommissionableNodeController(chip::Dnssd::Resolver * resolver = nullptr) : mResolver(resolver) {}
     virtual ~CommissionableNodeController() {}
 
     CHIP_ERROR DiscoverCommissioners(Dnssd::DiscoveryFilter discoveryFilter = Dnssd::DiscoveryFilter());
@@ -66,6 +64,7 @@
     DiscoveredNodeList GetDiscoveredNodes() override { return DiscoveredNodeList(mDiscoveredCommissioners); }
 
 private:
+    Dnssd::Resolver * mResolver = nullptr;
     Dnssd::DiscoveredNodeData mDiscoveredCommissioners[CHIP_DEVICE_CONFIG_MAX_DISCOVERED_NODES];
 };
 
diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp
index 27e748e..1840ea1 100644
--- a/src/controller/CHIPDeviceController.cpp
+++ b/src/controller/CHIPDeviceController.cpp
@@ -45,7 +45,6 @@
 
 #include <app/InteractionModelEngine.h>
 #include <app/OperationalDeviceProxy.h>
-#include <app/util/DataModelHandler.h>
 #include <app/util/error-mapping.h>
 #include <credentials/CHIPCert.h>
 #include <credentials/DeviceAttestationCredsProvider.h>
@@ -132,14 +131,12 @@
     VerifyOrReturnError(params.systemState->TransportMgr() != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
 
 #if CHIP_DEVICE_CONFIG_ENABLE_DNSSD
-    Dnssd::Resolver::Instance().Init(params.systemState->InetLayer());
-    Dnssd::Resolver::Instance().SetResolverDelegate(this);
+    ReturnErrorOnFailure(mDNSResolver.Init(params.systemState->InetLayer()));
+    mDNSResolver.SetResolverDelegate(this);
     RegisterDeviceAddressUpdateDelegate(params.deviceAddressUpdateDelegate);
     RegisterDeviceDiscoveryDelegate(params.deviceDiscoveryDelegate);
 #endif // CHIP_DEVICE_CONFIG_ENABLE_DNSSD
 
-    InitDataModelHandler(params.systemState->ExchangeMgr());
-
     VerifyOrReturnError(params.operationalCredentialsDelegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     mOperationalCredentialsDelegate = params.operationalCredentialsDelegate;
 
@@ -160,6 +157,11 @@
         .sessionInitParams = deviceInitParams,
         .dnsCache          = &mDNSCache,
         .devicePool        = &mDevicePool,
+#if CHIP_DEVICE_CONFIG_ENABLE_DNSSD
+        .dnsResolver = &mDNSResolver,
+#else
+        .dnsResolver = nullptr,
+#endif
     };
 
     mCASESessionManager = chip::Platform::New<CASESessionManager>(sessionManagerConfig);
@@ -227,10 +229,6 @@
 
     mState = State::NotInitialized;
 
-#if CHIP_DEVICE_CONFIG_ENABLE_DNSSD
-    Dnssd::Resolver::Instance().Shutdown();
-#endif // CHIP_DEVICE_CONFIG_ENABLE_DNSSD
-
     mStorageDelegate = nullptr;
 
     mSystemState->Fabrics()->ReleaseFabricIndex(mFabricIndex);
@@ -238,7 +236,7 @@
     mSystemState = nullptr;
 
 #if CHIP_DEVICE_CONFIG_ENABLE_DNSSD
-    Dnssd::Resolver::Instance().SetResolverDelegate(nullptr);
+    mDNSResolver.Shutdown();
     mDeviceAddressUpdateDelegate = nullptr;
     mDeviceDiscoveryDelegate     = nullptr;
 #endif // CHIP_DEVICE_CONFIG_ENABLE_DNSSD
@@ -1571,7 +1569,7 @@
 CHIP_ERROR DeviceCommissioner::DiscoverCommissionableNodes(Dnssd::DiscoveryFilter filter)
 {
     ReturnErrorOnFailure(SetUpNodeDiscovery());
-    return chip::Dnssd::Resolver::Instance().FindCommissionableNodes(filter);
+    return mDNSResolver.FindCommissionableNodes(filter);
 }
 
 const Dnssd::DiscoveredNodeData * DeviceCommissioner::GetDiscoveredDevice(int idx)
@@ -1837,7 +1835,7 @@
 #if CONFIG_DEVICE_LAYER
         status = DeviceLayer::ConfigurationMgr().GetCountryCode(countryCodeStr, kMaxCountryCodeSize, actualCountryCodeSize);
 #else
-        status            = CHIP_ERROR_NOT_IMPLEMENTED;
+        status = CHIP_ERROR_NOT_IMPLEMENTED;
 #endif
         if (status != CHIP_NO_ERROR)
         {
@@ -1894,7 +1892,7 @@
         RendezvousCleanup(CHIP_NO_ERROR);
 #if CHIP_DEVICE_CONFIG_ENABLE_DNSSD
         ChipLogProgress(Controller, "Finding node on operational network");
-        Dnssd::Resolver::Instance().ResolveNodeId(peerId, Inet::IPAddressType::kAny);
+        mDNSResolver.ResolveNodeId(peerId, Inet::IPAddressType::kAny);
 #endif
     }
     break;
diff --git a/src/controller/CHIPDeviceController.h b/src/controller/CHIPDeviceController.h
index c810c6a..c23c93d 100644
--- a/src/controller/CHIPDeviceController.h
+++ b/src/controller/CHIPDeviceController.h
@@ -72,6 +72,7 @@
 #include <controller/DeviceAddressUpdateDelegate.h>
 #include <controller/DeviceDiscoveryDelegate.h>
 #include <lib/dnssd/Resolver.h>
+#include <lib/dnssd/ResolverProxy.h>
 #endif
 
 namespace chip {
diff --git a/src/controller/CHIPDeviceControllerFactory.cpp b/src/controller/CHIPDeviceControllerFactory.cpp
index 95bb7f6..929c52e 100644
--- a/src/controller/CHIPDeviceControllerFactory.cpp
+++ b/src/controller/CHIPDeviceControllerFactory.cpp
@@ -24,6 +24,7 @@
 
 #include <controller/CHIPDeviceControllerFactory.h>
 
+#include <app/util/DataModelHandler.h>
 #include <lib/support/ErrorStr.h>
 
 #if CONFIG_DEVICE_LAYER
@@ -140,9 +141,15 @@
     ReturnErrorOnFailure(stateParams.exchangeMgr->Init(stateParams.sessionMgr));
     ReturnErrorOnFailure(stateParams.messageCounterManager->Init(stateParams.exchangeMgr));
 
+    InitDataModelHandler(stateParams.exchangeMgr);
+
     stateParams.imDelegate = params.imDelegate;
     ReturnErrorOnFailure(chip::app::InteractionModelEngine::GetInstance()->Init(stateParams.exchangeMgr, stateParams.imDelegate));
 
+#if CHIP_DEVICE_CONFIG_ENABLE_DNSSD
+    ReturnErrorOnFailure(Dnssd::Resolver::Instance().Init(stateParams.inetLayer));
+#endif // CHIP_DEVICE_CONFIG_ENABLE_DNSSD
+
     // store the system state
     mSystemState = chip::Platform::New<DeviceControllerSystemState>(stateParams);
     ChipLogDetail(Controller, "System State Initialized...");
@@ -220,6 +227,10 @@
 
     ChipLogDetail(Controller, "Shutting down the System State, this will teardown the CHIP Stack");
 
+#if CHIP_DEVICE_CONFIG_ENABLE_DNSSD
+    Dnssd::Resolver::Instance().Shutdown();
+#endif // CHIP_DEVICE_CONFIG_ENABLE_DNSSD
+
     // Shut down the interaction model
     app::InteractionModelEngine::GetInstance()->Shutdown();
 
diff --git a/src/controller/tests/TestCommissionableNodeController.cpp b/src/controller/tests/TestCommissionableNodeController.cpp
index c1101ec..07500b4 100644
--- a/src/controller/tests/TestCommissionableNodeController.cpp
+++ b/src/controller/tests/TestCommissionableNodeController.cpp
@@ -180,9 +180,26 @@
 
 } // namespace
 
+int TestCommissionableNodeController_Setup(void * inContext)
+{
+    if (CHIP_NO_ERROR != chip::Platform::MemoryInit())
+    {
+        return FAILURE;
+    }
+
+    return SUCCESS;
+}
+
+int TestCommissionableNodeController_Teardown(void * inContext)
+{
+    chip::Platform::MemoryShutdown();
+    return SUCCESS;
+}
+
 int TestCommissionableNodeController()
 {
-    nlTestSuite theSuite = { "CommissionableNodeController", &sTests[0], NULL, NULL };
+    nlTestSuite theSuite = { "CommissionableNodeController", &sTests[0], TestCommissionableNodeController_Setup,
+                             TestCommissionableNodeController_Teardown };
     nlTestRunner(&theSuite, nullptr);
     return nlTestRunnerStats(&theSuite);
 }
diff --git a/src/lib/dnssd/Discovery_ImplPlatform.cpp b/src/lib/dnssd/Discovery_ImplPlatform.cpp
index 0f56448..38701b0 100644
--- a/src/lib/dnssd/Discovery_ImplPlatform.cpp
+++ b/src/lib/dnssd/Discovery_ImplPlatform.cpp
@@ -36,11 +36,119 @@
 namespace chip {
 namespace Dnssd {
 
-DiscoveryImplPlatform DiscoveryImplPlatform::sManager;
+namespace {
+
 #if CHIP_CONFIG_MDNS_CACHE_SIZE > 0
-DnssdCache<CHIP_CONFIG_MDNS_CACHE_SIZE> DiscoveryImplPlatform::sDnssdCache;
+static DnssdCache<CHIP_CONFIG_MDNS_CACHE_SIZE> sDnssdCache;
 #endif
 
+static void HandleNodeResolve(void * context, DnssdService * result, CHIP_ERROR error)
+{
+    ResolverDelegateProxy * proxy = static_cast<ResolverDelegateProxy *>(context);
+
+    if (CHIP_NO_ERROR != error)
+    {
+        proxy->Release();
+        return;
+    }
+
+    DiscoveredNodeData nodeData;
+    Platform::CopyString(nodeData.hostName, result->mHostName);
+    Platform::CopyString(nodeData.instanceName, result->mName);
+
+    if (result->mAddress.HasValue() && nodeData.numIPs < DiscoveredNodeData::kMaxIPAddresses)
+    {
+        nodeData.ipAddress[nodeData.numIPs]   = result->mAddress.Value();
+        nodeData.interfaceId[nodeData.numIPs] = result->mInterface;
+        nodeData.numIPs++;
+    }
+
+    nodeData.port = result->mPort;
+
+    for (size_t i = 0; i < result->mTextEntrySize; ++i)
+    {
+        ByteSpan key(reinterpret_cast<const uint8_t *>(result->mTextEntries[i].mKey), strlen(result->mTextEntries[i].mKey));
+        ByteSpan val(result->mTextEntries[i].mData, result->mTextEntries[i].mDataSize);
+        FillNodeDataFromTxt(key, val, nodeData);
+    }
+
+    proxy->OnNodeDiscoveryComplete(nodeData);
+    proxy->Release();
+}
+
+static void HandleNodeIdResolve(void * context, DnssdService * result, CHIP_ERROR error)
+{
+    ResolverDelegateProxy * proxy = static_cast<ResolverDelegateProxy *>(context);
+    if (CHIP_NO_ERROR != error)
+    {
+        proxy->OnNodeIdResolutionFailed(PeerId(), error);
+        proxy->Release();
+    }
+
+    if (result == nullptr)
+    {
+        proxy->OnNodeIdResolutionFailed(PeerId(), CHIP_ERROR_UNKNOWN_RESOURCE_ID);
+        proxy->Release();
+    }
+
+    PeerId peerId;
+    error = ExtractIdFromInstanceName(result->mName, &peerId);
+    if (CHIP_NO_ERROR != error)
+    {
+        proxy->OnNodeIdResolutionFailed(PeerId(), error);
+        proxy->Release();
+    }
+
+    ResolvedNodeData nodeData;
+    Platform::CopyString(nodeData.mHostName, result->mHostName);
+    nodeData.mInterfaceId = result->mInterface;
+    nodeData.mAddress[0]  = result->mAddress.ValueOr({});
+    nodeData.mPort        = result->mPort;
+    nodeData.mNumIPs      = 1;
+    nodeData.mPeerId      = peerId;
+    // TODO: Use seconds?
+    const System::Clock::Timestamp currentTime = System::SystemClock().GetMonotonicTimestamp();
+
+    nodeData.mExpiryTime = currentTime + System::Clock::Seconds16(result->mTtlSeconds);
+
+    for (size_t i = 0; i < result->mTextEntrySize; ++i)
+    {
+        ByteSpan key(reinterpret_cast<const uint8_t *>(result->mTextEntries[i].mKey), strlen(result->mTextEntries[i].mKey));
+        ByteSpan val(result->mTextEntries[i].mData, result->mTextEntries[i].mDataSize);
+        FillNodeDataFromTxt(key, val, nodeData);
+    }
+
+    nodeData.LogNodeIdResolved();
+#if CHIP_CONFIG_MDNS_CACHE_SIZE > 0
+    LogErrorOnFailure(sDnssdCache.Insert(nodeData));
+#endif
+    proxy->OnNodeIdResolved(nodeData);
+    proxy->Release();
+}
+
+static void HandleNodeBrowse(void * context, DnssdService * services, size_t servicesSize, CHIP_ERROR error)
+{
+    ResolverDelegateProxy * proxy = static_cast<ResolverDelegateProxy *>(context);
+    proxy->Release();
+
+    for (size_t i = 0; i < servicesSize; ++i)
+    {
+        proxy->Retain();
+        // For some platforms browsed services are already resolved, so verify if resolve is really needed or call resolve callback
+        if (!services[i].mAddress.HasValue())
+        {
+            ChipDnssdResolve(&services[i], services[i].mInterface, HandleNodeResolve, context);
+        }
+        else
+        {
+            HandleNodeResolve(context, &services[i], error);
+        }
+    }
+}
+} // namespace
+
+DiscoveryImplPlatform DiscoveryImplPlatform::sManager;
+
 DiscoveryImplPlatform::DiscoveryImplPlatform() = default;
 
 CHIP_ERROR DiscoveryImplPlatform::InitImpl()
@@ -460,155 +568,19 @@
                                                 Resolver::CacheBypass dnssdCacheBypass)
 {
     ReturnErrorOnFailure(InitImpl());
-
-#if CHIP_CONFIG_MDNS_CACHE_SIZE > 0
-    if (dnssdCacheBypass == Resolver::CacheBypass::Off)
-    {
-        /* see if the entry is cached and use it.... */
-        ResolvedNodeData nodeData;
-        if (sDnssdCache.Lookup(peerId, nodeData) == CHIP_NO_ERROR)
-        {
-            mResolverDelegate->OnNodeIdResolved(nodeData);
-            return CHIP_NO_ERROR;
-        }
-    }
-#endif
-
-    DnssdService service;
-
-    ReturnErrorOnFailure(MakeInstanceName(service.mName, sizeof(service.mName), peerId));
-    strncpy(service.mType, kOperationalServiceName, sizeof(service.mType));
-    service.mProtocol    = DnssdServiceProtocol::kDnssdProtocolTcp;
-    service.mAddressType = type;
-    return ChipDnssdResolve(&service, Inet::InterfaceId::Null(), HandleNodeIdResolve, this);
-}
-
-void DiscoveryImplPlatform::HandleNodeBrowse(void * context, DnssdService * services, size_t servicesSize, CHIP_ERROR error)
-{
-    for (size_t i = 0; i < servicesSize; ++i)
-    {
-        // For some platforms browsed services are already resolved, so verify if resolve is really needed or call resolve callback
-        if (!services[i].mAddress.HasValue())
-        {
-            ChipDnssdResolve(&services[i], services[i].mInterface, HandleNodeResolve, context);
-        }
-        else
-        {
-            HandleNodeResolve(context, &services[i], error);
-        }
-    }
-}
-
-void DiscoveryImplPlatform::HandleNodeResolve(void * context, DnssdService * result, CHIP_ERROR error)
-{
-    if (error != CHIP_NO_ERROR)
-    {
-        return;
-    }
-    DiscoveryImplPlatform * mgr = static_cast<DiscoveryImplPlatform *>(context);
-    DiscoveredNodeData data;
-    Platform::CopyString(data.hostName, result->mHostName);
-    Platform::CopyString(data.instanceName, result->mName);
-
-    if (result->mAddress.HasValue() && data.numIPs < DiscoveredNodeData::kMaxIPAddresses)
-    {
-        data.ipAddress[data.numIPs]   = result->mAddress.Value();
-        data.interfaceId[data.numIPs] = result->mInterface;
-        data.numIPs++;
-    }
-
-    data.port = result->mPort;
-
-    for (size_t i = 0; i < result->mTextEntrySize; ++i)
-    {
-        ByteSpan key(reinterpret_cast<const uint8_t *>(result->mTextEntries[i].mKey), strlen(result->mTextEntries[i].mKey));
-        ByteSpan val(result->mTextEntries[i].mData, result->mTextEntries[i].mDataSize);
-        FillNodeDataFromTxt(key, val, data);
-    }
-    mgr->mResolverDelegate->OnNodeDiscoveryComplete(data);
+    return mResolverProxy.ResolveNodeId(peerId, type, dnssdCacheBypass);
 }
 
 CHIP_ERROR DiscoveryImplPlatform::FindCommissionableNodes(DiscoveryFilter filter)
 {
     ReturnErrorOnFailure(InitImpl());
-    char serviceName[kMaxCommissionableServiceNameSize];
-    ReturnErrorOnFailure(MakeServiceTypeName(serviceName, sizeof(serviceName), filter, DiscoveryType::kCommissionableNode));
-
-    return ChipDnssdBrowse(serviceName, DnssdServiceProtocol::kDnssdProtocolUdp, Inet::IPAddressType::kAny,
-                           Inet::InterfaceId::Null(), HandleNodeBrowse, this);
+    return mResolverProxy.FindCommissionableNodes(filter);
 }
 
 CHIP_ERROR DiscoveryImplPlatform::FindCommissioners(DiscoveryFilter filter)
 {
     ReturnErrorOnFailure(InitImpl());
-    char serviceName[kMaxCommissionerServiceNameSize];
-    ReturnErrorOnFailure(MakeServiceTypeName(serviceName, sizeof(serviceName), filter, DiscoveryType::kCommissionerNode));
-
-    return ChipDnssdBrowse(serviceName, DnssdServiceProtocol::kDnssdProtocolUdp, Inet::IPAddressType::kAny,
-                           Inet::InterfaceId::Null(), HandleNodeBrowse, this);
-}
-
-void DiscoveryImplPlatform::HandleNodeIdResolve(void * context, DnssdService * result, CHIP_ERROR error)
-{
-    DiscoveryImplPlatform * mgr = static_cast<DiscoveryImplPlatform *>(context);
-
-    if (mgr->mResolverDelegate == nullptr)
-    {
-        return;
-    }
-
-    if (error != CHIP_NO_ERROR)
-    {
-        ChipLogError(Discovery, "Node ID resolved failed with %s", chip::ErrorStr(error));
-        mgr->mResolverDelegate->OnNodeIdResolutionFailed(PeerId(), error);
-        return;
-    }
-
-    if (result == nullptr)
-    {
-        ChipLogError(Discovery, "Node ID resolve not found");
-        mgr->mResolverDelegate->OnNodeIdResolutionFailed(PeerId(), CHIP_ERROR_UNKNOWN_RESOURCE_ID);
-        return;
-    }
-
-    ResolvedNodeData nodeData;
-
-    error = ExtractIdFromInstanceName(result->mName, &nodeData.mPeerId);
-    if (error != CHIP_NO_ERROR)
-    {
-        ChipLogError(Discovery, "Node ID resolved failed with %s", chip::ErrorStr(error));
-        mgr->mResolverDelegate->OnNodeIdResolutionFailed(PeerId(), error);
-        return;
-    }
-
-    // TODO: Expand the results to include all the addresses.
-    Platform::CopyString(nodeData.mHostName, result->mHostName);
-    nodeData.mInterfaceId = result->mInterface;
-    nodeData.mAddress[0]  = result->mAddress.ValueOr({});
-    nodeData.mPort        = result->mPort;
-    nodeData.mNumIPs      = 1;
-    // TODO: Use seconds?
-    const System::Clock::Timestamp currentTime = System::SystemClock().GetMonotonicTimestamp();
-
-    nodeData.mExpiryTime = currentTime + System::Clock::Seconds16(result->mTtlSeconds);
-
-    for (size_t i = 0; i < result->mTextEntrySize; ++i)
-    {
-        ByteSpan key(reinterpret_cast<const uint8_t *>(result->mTextEntries[i].mKey), strlen(result->mTextEntries[i].mKey));
-        ByteSpan val(result->mTextEntries[i].mData, result->mTextEntries[i].mDataSize);
-        FillNodeDataFromTxt(key, val, nodeData);
-    }
-
-    nodeData.LogNodeIdResolved();
-#if CHIP_CONFIG_MDNS_CACHE_SIZE > 0
-    error = mgr->sDnssdCache.Insert(nodeData);
-
-    if (CHIP_NO_ERROR != error)
-    {
-        ChipLogError(Discovery, "DnssdCache insert failed with %s", chip::ErrorStr(error));
-    }
-#endif
-    mgr->mResolverDelegate->OnNodeIdResolved(nodeData);
+    return mResolverProxy.FindCommissioners(filter);
 }
 
 DiscoveryImplPlatform & DiscoveryImplPlatform::GetInstance()
@@ -626,5 +598,57 @@
     return DiscoveryImplPlatform::GetInstance();
 }
 
+CHIP_ERROR ResolverProxy::ResolveNodeId(const PeerId & peerId, Inet::IPAddressType type, Resolver::CacheBypass dnssdCacheBypass)
+{
+    VerifyOrReturnError(mDelegate != nullptr, CHIP_ERROR_INCORRECT_STATE);
+    mDelegate->Retain();
+
+#if CHIP_CONFIG_MDNS_CACHE_SIZE > 0
+    if (dnssdCacheBypass == Resolver::CacheBypass::Off)
+    {
+        /* see if the entry is cached and use it.... */
+        ResolvedNodeData nodeData;
+        if (sDnssdCache.Lookup(peerId, nodeData) == CHIP_NO_ERROR)
+        {
+            mDelegate->OnNodeIdResolved(nodeData);
+            mDelegate->Release();
+            return CHIP_NO_ERROR;
+        }
+    }
+#endif
+
+    DnssdService service;
+
+    ReturnErrorOnFailure(MakeInstanceName(service.mName, sizeof(service.mName), peerId));
+    strncpy(service.mType, kOperationalServiceName, sizeof(service.mType));
+    service.mProtocol    = DnssdServiceProtocol::kDnssdProtocolTcp;
+    service.mAddressType = type;
+    return ChipDnssdResolve(&service, Inet::InterfaceId::Null(), HandleNodeIdResolve, mDelegate);
+}
+
+CHIP_ERROR ResolverProxy::FindCommissionableNodes(DiscoveryFilter filter)
+{
+    VerifyOrReturnError(mDelegate != nullptr, CHIP_ERROR_INCORRECT_STATE);
+    mDelegate->Retain();
+
+    char serviceName[kMaxCommissionableServiceNameSize];
+    ReturnErrorOnFailure(MakeServiceTypeName(serviceName, sizeof(serviceName), filter, DiscoveryType::kCommissionableNode));
+
+    return ChipDnssdBrowse(serviceName, DnssdServiceProtocol::kDnssdProtocolUdp, Inet::IPAddressType::kAny,
+                           Inet::InterfaceId::Null(), HandleNodeBrowse, mDelegate);
+}
+
+CHIP_ERROR ResolverProxy::FindCommissioners(DiscoveryFilter filter)
+{
+    VerifyOrReturnError(mDelegate != nullptr, CHIP_ERROR_INCORRECT_STATE);
+    mDelegate->Retain();
+
+    char serviceName[kMaxCommissionerServiceNameSize];
+    ReturnErrorOnFailure(MakeServiceTypeName(serviceName, sizeof(serviceName), filter, DiscoveryType::kCommissionerNode));
+
+    return ChipDnssdBrowse(serviceName, DnssdServiceProtocol::kDnssdProtocolUdp, Inet::IPAddressType::kAny,
+                           Inet::InterfaceId::Null(), HandleNodeBrowse, mDelegate);
+}
+
 } // namespace Dnssd
 } // namespace chip
diff --git a/src/lib/dnssd/Discovery_ImplPlatform.h b/src/lib/dnssd/Discovery_ImplPlatform.h
index 5ac1753..c297ed3 100644
--- a/src/lib/dnssd/Discovery_ImplPlatform.h
+++ b/src/lib/dnssd/Discovery_ImplPlatform.h
@@ -23,6 +23,7 @@
 #include <lib/dnssd/Advertiser.h>
 #include <lib/dnssd/DnssdCache.h>
 #include <lib/dnssd/Resolver.h>
+#include <lib/dnssd/ResolverProxy.h>
 #include <lib/dnssd/platform/Dnssd.h>
 #include <platform/CHIPDeviceConfig.h>
 
@@ -48,7 +49,7 @@
     CHIP_ERROR GetCommissionableInstanceName(char * instanceName, size_t maxLength) override;
 
     // Members that implement Resolver interface.
-    void SetResolverDelegate(ResolverDelegate * delegate) override { mResolverDelegate = delegate; }
+    void SetResolverDelegate(ResolverDelegate * delegate) override { mResolverProxy.SetResolverDelegate(delegate); }
     CHIP_ERROR ResolveNodeId(const PeerId & peerId, Inet::IPAddressType type, Resolver::CacheBypass dnssdCacheBypass) override;
     CHIP_ERROR FindCommissionableNodes(DiscoveryFilter filter = DiscoveryFilter()) override;
     CHIP_ERROR FindCommissioners(DiscoveryFilter filter = DiscoveryFilter()) override;
@@ -65,11 +66,8 @@
     CHIP_ERROR PublishUnprovisionedDevice(chip::Inet::IPAddressType addressType, chip::Inet::InterfaceId interface);
     CHIP_ERROR PublishProvisionedDevice(chip::Inet::IPAddressType addressType, chip::Inet::InterfaceId interface);
 
-    static void HandleNodeIdResolve(void * context, DnssdService * result, CHIP_ERROR error);
     static void HandleDnssdInit(void * context, CHIP_ERROR initError);
     static void HandleDnssdError(void * context, CHIP_ERROR initError);
-    static void HandleNodeBrowse(void * context, DnssdService * services, size_t servicesSize, CHIP_ERROR error);
-    static void HandleNodeResolve(void * context, DnssdService * result, CHIP_ERROR error);
     static CHIP_ERROR GenerateRotatingDeviceId(char rotatingDeviceIdHexBuffer[], size_t & rotatingDeviceIdHexBufferSize);
 #ifdef DETAIL_LOGGING
     static void PrintEntries(const DnssdService * service);
@@ -83,13 +81,11 @@
     bool mIsCommissionerPublishing       = false;
     uint8_t mCommissionableInstanceName[sizeof(uint64_t)];
 
-    bool mDnssdInitialized               = false;
-    ResolverDelegate * mResolverDelegate = nullptr;
+    bool mDnssdInitialized = false;
+
+    ResolverProxy mResolverProxy;
 
     static DiscoveryImplPlatform sManager;
-#if CHIP_CONFIG_MDNS_CACHE_SIZE > 0
-    static DnssdCache<CHIP_CONFIG_MDNS_CACHE_SIZE> sDnssdCache;
-#endif
 };
 
 } // namespace Dnssd
diff --git a/src/lib/dnssd/ResolverProxy.h b/src/lib/dnssd/ResolverProxy.h
new file mode 100644
index 0000000..88ac1d4
--- /dev/null
+++ b/src/lib/dnssd/ResolverProxy.h
@@ -0,0 +1,98 @@
+/*
+ *
+ *    Copyright (c) 2021 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.
+ */
+
+#pragma once
+
+#include <lib/core/ReferenceCounted.h>
+#include <lib/dnssd/Resolver.h>
+
+namespace chip {
+namespace Dnssd {
+
+class ResolverDelegateProxy : public ReferenceCounted<ResolverDelegateProxy>, public ResolverDelegate
+{
+public:
+    void SetDelegate(ResolverDelegate * delegate) { mDelegate = delegate; }
+
+    /// ResolverDelegate interface
+    void OnNodeIdResolved(const ResolvedNodeData & nodeData) override
+    {
+        if (mDelegate != nullptr)
+        {
+            mDelegate->OnNodeIdResolved(nodeData);
+        }
+    }
+
+    void OnNodeIdResolutionFailed(const PeerId & peerId, CHIP_ERROR error) override
+    {
+        if (mDelegate != nullptr)
+        {
+            mDelegate->OnNodeIdResolutionFailed(peerId, error);
+        }
+    }
+
+    void OnNodeDiscoveryComplete(const DiscoveredNodeData & nodeData) override
+    {
+        if (mDelegate != nullptr)
+        {
+            mDelegate->OnNodeDiscoveryComplete(nodeData);
+        }
+    }
+
+private:
+    ResolverDelegate * mDelegate = nullptr;
+};
+
+class ResolverProxy : public Resolver
+{
+public:
+    ResolverProxy() {}
+
+    // Resolver interface.
+    CHIP_ERROR Init(Inet::InetLayer * inetLayer = nullptr) override
+    {
+        ReturnErrorOnFailure(chip::Dnssd::Resolver::Instance().Init(inetLayer));
+        VerifyOrReturnError(mDelegate == nullptr, CHIP_ERROR_INCORRECT_STATE);
+        mDelegate = chip::Platform::New<ResolverDelegateProxy>();
+        return CHIP_NO_ERROR;
+    }
+
+    void SetResolverDelegate(ResolverDelegate * delegate) override
+    {
+        VerifyOrReturn(mDelegate != nullptr);
+        mDelegate->SetDelegate(delegate);
+    }
+
+    void Shutdown() override
+    {
+        VerifyOrReturn(mDelegate != nullptr);
+        mDelegate->SetDelegate(nullptr);
+        mDelegate->Release();
+        mDelegate = nullptr;
+    }
+
+    CHIP_ERROR ResolveNodeId(const PeerId & peerId, Inet::IPAddressType type,
+                             Resolver::CacheBypass dnssdCacheBypass = CacheBypass::Off) override;
+    CHIP_ERROR FindCommissionableNodes(DiscoveryFilter filter = DiscoveryFilter()) override;
+    CHIP_ERROR FindCommissioners(DiscoveryFilter filter = DiscoveryFilter()) override;
+
+private:
+    ResolverDelegateProxy * mDelegate = nullptr;
+};
+
+} // namespace Dnssd
+} // namespace chip
diff --git a/src/lib/dnssd/Resolver_ImplMinimalMdns.cpp b/src/lib/dnssd/Resolver_ImplMinimalMdns.cpp
index 5252d51..fa081b3 100644
--- a/src/lib/dnssd/Resolver_ImplMinimalMdns.cpp
+++ b/src/lib/dnssd/Resolver_ImplMinimalMdns.cpp
@@ -23,6 +23,7 @@
 #include <inet/IPPacketInfo.h>
 #include <lib/core/CHIPConfig.h>
 #include <lib/dnssd/MinimalMdnsServer.h>
+#include <lib/dnssd/ResolverProxy.h>
 #include <lib/dnssd/ServiceNaming.h>
 #include <lib/dnssd/TxtFields.h>
 #include <lib/dnssd/minimal_mdns/ActiveResolveAttempts.h>
@@ -598,5 +599,30 @@
     return gResolver;
 }
 
+// Minimal implementation does not support associating a context to a request (while platforms implementations do). So keep
+// updating the delegate that ends up beeing used by the server by calling 'SetResolverDelegate'.
+// This effectively allow minimal to have multiple controllers issuing requests as long the requests are serialized, but
+// it won't work well if requests are issued in parallel.
+CHIP_ERROR ResolverProxy::ResolveNodeId(const PeerId & peerId, Inet::IPAddressType type, Resolver::CacheBypass dnssdCacheBypass)
+{
+    VerifyOrReturnError(mDelegate != nullptr, CHIP_ERROR_INCORRECT_STATE);
+    chip::Dnssd::Resolver::Instance().SetResolverDelegate(mDelegate);
+    return chip::Dnssd::Resolver::Instance().ResolveNodeId(peerId, type, dnssdCacheBypass);
+}
+
+CHIP_ERROR ResolverProxy::FindCommissionableNodes(DiscoveryFilter filter)
+{
+    VerifyOrReturnError(mDelegate != nullptr, CHIP_ERROR_INCORRECT_STATE);
+    chip::Dnssd::Resolver::Instance().SetResolverDelegate(mDelegate);
+    return chip::Dnssd::Resolver::Instance().FindCommissionableNodes(filter);
+}
+
+CHIP_ERROR ResolverProxy::FindCommissioners(DiscoveryFilter filter)
+{
+    VerifyOrReturnError(mDelegate != nullptr, CHIP_ERROR_INCORRECT_STATE);
+    chip::Dnssd::Resolver::Instance().SetResolverDelegate(mDelegate);
+    return chip::Dnssd::Resolver::Instance().FindCommissioners(filter);
+}
+
 } // namespace Dnssd
 } // namespace chip
diff --git a/src/lib/shell/commands/Ota.cpp b/src/lib/shell/commands/Ota.cpp
index 7581c32..20c398a 100644
--- a/src/lib/shell/commands/Ota.cpp
+++ b/src/lib/shell/commands/Ota.cpp
@@ -22,6 +22,7 @@
 #include <app/server/Server.h>
 #include <lib/core/CHIPCallback.h>
 #include <lib/core/Optional.h>
+#include <lib/dnssd/Resolver.h>
 #include <lib/shell/Commands.h>
 #include <lib/shell/Engine.h>
 #include <lib/shell/commands/Help.h>
@@ -299,7 +300,7 @@
     VerifyOrReturn(deviceProxy != nullptr);
 
     deviceProxy->UpdateDeviceData(sOtaContext.providerAddress, deviceProxy->GetMRPConfig());
-    deviceProxy->Connect(&successCallback, &failureCallback);
+    deviceProxy->Connect(&successCallback, &failureCallback, nullptr);
 }
 
 template <OnDeviceConnected OnConnected>