Add support to browse and resolve on both local and SRP domain (#32779)

* Add domain names matching the DnssdServices stored in Browse Context

This is needed to pass the domain returned from a call to Browse to the Resolve.

* Restyled by clang-format

* Add the domain names to the services vector

* Restyled by clang-format

* Add support to browse and resolve on both local and SRP domain

* Restyled by clang-format

* Fix the code that gets the domain name returned from browse and pass it into the resolve

* Restyled by clang-format

* Apply suggestions from code review

Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>

* Address review comments

* Updated the comment about shouldStartSRPTimerForResolve

* Restyled by clang-format

* Apply suggestions from code review

Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>

* Addressed more review comments

* Restyled by clang-format

* Add a null check for domain when erasing the contents of services

* Fix the check for domain name when erasing services and pass the correct ResolveContextWithType for the case where domain is not null during resolve

* Restyled by clang-format

---------

Co-authored-by: Restyled.io <commits@restyled.io>
Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
diff --git a/src/platform/Darwin/DnssdContexts.cpp b/src/platform/Darwin/DnssdContexts.cpp
index 06b6e02..66dc4c1 100644
--- a/src/platform/Darwin/DnssdContexts.cpp
+++ b/src/platform/Darwin/DnssdContexts.cpp
@@ -30,12 +30,6 @@
 
 constexpr uint8_t kDnssdKeyMaxSize          = 32;
 constexpr uint8_t kDnssdTxtRecordMaxEntries = 20;
-constexpr char kLocalDot[]                  = "local.";
-
-bool IsLocalDomain(const char * domain)
-{
-    return strcmp(kLocalDot, domain) == 0;
-}
 
 std::string GetHostNameWithoutDomain(const char * hostnameWithDomain)
 {
@@ -341,7 +335,8 @@
     mHostNameRegistrar.Register();
 }
 
-BrowseContext * BrowseContext::sContextDispatchingSuccess = nullptr;
+BrowseContext * BrowseContext::sContextDispatchingSuccess      = nullptr;
+std::vector<DnssdService> * BrowseContext::sDispatchedServices = nullptr;
 
 BrowseContext::BrowseContext(void * cbContext, DnssdBrowseCallback cb, DnssdServiceProtocol cbContextProtocol)
 {
@@ -373,7 +368,10 @@
     {
         dnsServices.push_back(std::move(iter.first));
     }
+
+    sDispatchedServices = &dnsServices;
     callback(context, dnsServices.data(), dnsServices.size(), false, CHIP_NO_ERROR);
+    sDispatchedServices        = nullptr;
     sContextDispatchingSuccess = nullptr;
     services.clear();
 }
@@ -393,7 +391,6 @@
     ChipLogProgress(Discovery, "Mdns: %s  name: %s, type: %s, domain: %s, interface: %" PRIu32, __func__, StringOrNullMarker(name),
                     StringOrNullMarker(type), StringOrNullMarker(domain), interfaceId);
 
-    VerifyOrReturn(IsLocalDomain(domain));
     auto service = GetService(name, type, protocol, interfaceId);
     services.push_back(std::make_pair(std::move(service), std::string(domain)));
 }
@@ -404,13 +401,13 @@
                     StringOrNullMarker(type), StringOrNullMarker(domain), interfaceId);
 
     VerifyOrReturn(name != nullptr);
-    VerifyOrReturn(IsLocalDomain(domain));
+    std::string domain_str(domain);
 
     services.erase(std::remove_if(services.begin(), services.end(),
-                                  [name, type, interfaceId, domain](const auto & service) {
+                                  [name, type, interfaceId, &domain_str](const auto & service) {
                                       return strcmp(name, service.first.mName) == 0 && type == GetFullType(&service.first) &&
                                           service.first.mInterface == chip::Inet::InterfaceId(interfaceId) &&
-                                          strcmp(domain, service.second.c_str()) == 0;
+                                          service.second == domain_str;
                                   }),
                    services.end());
 }
@@ -449,8 +446,6 @@
     ChipLogProgress(Discovery, "Mdns: %s  name: %s, type: %s, domain: %s, interface: %" PRIu32, __func__, StringOrNullMarker(name),
                     StringOrNullMarker(type), StringOrNullMarker(domain), interfaceId);
 
-    VerifyOrReturn(IsLocalDomain(domain));
-
     auto delegate = static_cast<DnssdBrowseDelegate *>(context);
     auto service  = GetService(name, type, protocol, interfaceId);
     delegate->OnBrowseAdd(service);
@@ -462,7 +457,6 @@
                     StringOrNullMarker(type), StringOrNullMarker(domain), interfaceId);
 
     VerifyOrReturn(name != nullptr);
-    VerifyOrReturn(IsLocalDomain(domain));
 
     auto delegate = static_cast<DnssdBrowseDelegate *>(context);
     auto service  = GetService(name, type, protocol, interfaceId);
@@ -494,7 +488,13 @@
     consumerCounter = std::move(consumerCounterToUse);
 }
 
-ResolveContext::~ResolveContext() {}
+ResolveContext::~ResolveContext()
+{
+    if (isSRPTimerRunning)
+    {
+        CancelSRPTimer();
+    }
+}
 
 void ResolveContext::DispatchFailure(const char * errorStr, CHIP_ERROR err)
 {
@@ -554,7 +554,8 @@
 
     for (auto & interface : interfaces)
     {
-        if (TryReportingResultsForInterfaceIndex(interface.first))
+        if (TryReportingResultsForInterfaceIndex(interface.first.interfaceId, interface.first.hostname,
+                                                 interface.first.isSRPResult))
         {
             break;
         }
@@ -566,7 +567,7 @@
     }
 }
 
-bool ResolveContext::TryReportingResultsForInterfaceIndex(uint32_t interfaceIndex)
+bool ResolveContext::TryReportingResultsForInterfaceIndex(uint32_t interfaceIndex, const std::string & hostname, bool isSRPResult)
 {
     if (interfaceIndex == 0)
     {
@@ -574,8 +575,9 @@
         return false;
     }
 
-    auto & interface = interfaces[interfaceIndex];
-    auto & ips       = interface.addresses;
+    InterfaceKey interfaceKey = { interfaceIndex, hostname, isSRPResult };
+    auto & interface          = interfaces[interfaceKey];
+    auto & ips                = interface.addresses;
 
     // Some interface may not have any ips, just ignore them.
     if (ips.size() == 0)
@@ -602,7 +604,35 @@
     return true;
 }
 
-CHIP_ERROR ResolveContext::OnNewAddress(uint32_t interfaceId, const struct sockaddr * address)
+bool ResolveContext::TryReportingResultsForInterfaceIndex(uint32_t interfaceIndex)
+{
+    for (auto & interface : interfaces)
+    {
+        if (interface.first.interfaceId == interfaceIndex)
+        {
+            if (TryReportingResultsForInterfaceIndex(interface.first.interfaceId, interface.first.hostname,
+                                                     interface.first.isSRPResult))
+            {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+void ResolveContext::SRPTimerExpiredCallback(chip::System::Layer * systemLayer, void * callbackContext)
+{
+    auto sdCtx = static_cast<ResolveContext *>(callbackContext);
+    VerifyOrDie(sdCtx != nullptr);
+    sdCtx->Finalize();
+}
+
+void ResolveContext::CancelSRPTimer()
+{
+    DeviceLayer::SystemLayer().CancelTimer(SRPTimerExpiredCallback, static_cast<void *>(this));
+}
+
+CHIP_ERROR ResolveContext::OnNewAddress(const InterfaceKey & interfaceKey, const struct sockaddr * address)
 {
     // If we don't have any information about this interfaceId, just ignore the
     // address, since it won't be usable anyway without things like the port.
@@ -610,7 +640,9 @@
     // on the system, because the hostnames we are looking up all end in
     // ".local".  In other words, we can get regular DNS results in here, not
     // just DNS-SD ones.
-    if (interfaces.find(interfaceId) == interfaces.end())
+    auto interfaceId = interfaceKey.interfaceId;
+
+    if (interfaces.find(interfaceKey) == interfaces.end())
     {
         return CHIP_NO_ERROR;
     }
@@ -633,7 +665,7 @@
         return CHIP_NO_ERROR;
     }
 
-    interfaces[interfaceId].addresses.push_back(ip);
+    interfaces[interfaceKey].addresses.push_back(ip);
 
     return CHIP_NO_ERROR;
 }
@@ -652,7 +684,7 @@
 }
 
 void ResolveContext::OnNewInterface(uint32_t interfaceId, const char * fullname, const char * hostnameWithDomain, uint16_t port,
-                                    uint16_t txtLen, const unsigned char * txtRecord)
+                                    uint16_t txtLen, const unsigned char * txtRecord, bool isFromSRPResolve)
 {
 #if CHIP_PROGRESS_LOGGING
     std::string txtString;
@@ -715,7 +747,8 @@
     // resolving.
     interface.fullyQualifiedDomainName = hostnameWithDomain;
 
-    interfaces.insert(std::make_pair(interfaceId, std::move(interface)));
+    InterfaceKey interfaceKey = { interfaceId, hostnameWithDomain, isFromSRPResolve };
+    interfaces.insert(std::make_pair(std::move(interfaceKey), std::move(interface)));
 }
 
 bool ResolveContext::HasInterface()
@@ -731,7 +764,7 @@
 
 InterfaceInfo::InterfaceInfo(InterfaceInfo && other) :
     service(std::move(other.service)), addresses(std::move(other.addresses)),
-    fullyQualifiedDomainName(std::move(other.fullyQualifiedDomainName))
+    fullyQualifiedDomainName(std::move(other.fullyQualifiedDomainName)), isDNSLookUpRequested(other.isDNSLookUpRequested)
 {
     // Make sure we're not trying to free any state from the other DnssdService,
     // since we took over ownership of its allocated bits.
diff --git a/src/platform/Darwin/DnssdImpl.cpp b/src/platform/Darwin/DnssdImpl.cpp
index 7e9e7af..a6b1fd9 100644
--- a/src/platform/Darwin/DnssdImpl.cpp
+++ b/src/platform/Darwin/DnssdImpl.cpp
@@ -24,7 +24,6 @@
 #include <lib/support/CodeUtils.h>
 #include <lib/support/SafeInt.h>
 #include <lib/support/logging/CHIPLogging.h>
-#include <platform/CHIPDeviceLayer.h>
 
 using namespace chip::Dnssd;
 using namespace chip::Dnssd::Internal;
@@ -33,8 +32,13 @@
 
 constexpr char kLocalDot[] = "local.";
 
+constexpr char kSRPDot[] = "default.service.arpa.";
+
+// The extra time in milliseconds that we will wait for the resolution on the SRP domain to complete.
+constexpr uint16_t kSRPTimeoutInMsec = 250;
+
 constexpr DNSServiceFlags kRegisterFlags        = kDNSServiceFlagsNoAutoRename;
-constexpr DNSServiceFlags kBrowseFlags          = 0;
+constexpr DNSServiceFlags kBrowseFlags          = kDNSServiceFlagsShareConnection;
 constexpr DNSServiceFlags kGetAddrInfoFlags     = kDNSServiceFlagsTimeout | kDNSServiceFlagsShareConnection;
 constexpr DNSServiceFlags kResolveFlags         = kDNSServiceFlagsShareConnection;
 constexpr DNSServiceFlags kReconfirmRecordFlags = 0;
@@ -62,6 +66,19 @@
     }
 }
 
+/**
+ * @brief Starts a timer to wait for the resolution on the kSRPDot domain to happen.
+ *
+ * @param[in] timeoutSeconds The timeout in seconds.
+ * @param[in] ResolveContext The resolve context.
+ */
+CHIP_ERROR StartSRPTimer(uint16_t timeoutInMSecs, ResolveContext * ctx)
+{
+    VerifyOrReturnValue(ctx != nullptr, CHIP_ERROR_INCORRECT_STATE);
+    return chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Milliseconds16(timeoutInMSecs),
+                                                       ResolveContext::SRPTimerExpiredCallback, static_cast<void *>(ctx));
+}
+
 class ScopedTXTRecord
 {
 public:
@@ -181,14 +198,28 @@
     sdCtx->OnBrowse(flags, name, type, domain, interfaceId);
 }
 
+CHIP_ERROR BrowseOnDomain(BrowseHandler * sdCtx, uint32_t interfaceId, const char * type, const char * domain)
+{
+    auto sdRef = sdCtx->serviceRef; // Mandatory copy because of kDNSServiceFlagsShareConnection
+
+    auto err = DNSServiceBrowse(&sdRef, kBrowseFlags, interfaceId, type, domain, OnBrowse, sdCtx);
+    VerifyOrReturnError(kDNSServiceErr_NoError == err, sdCtx->Finalize(err));
+    return CHIP_NO_ERROR;
+}
+
 CHIP_ERROR Browse(BrowseHandler * sdCtx, uint32_t interfaceId, const char * type)
 {
-    ChipLogProgress(Discovery, "Browsing for: %s", StringOrNullMarker(type));
-    DNSServiceRef sdRef;
-    auto err = DNSServiceBrowse(&sdRef, kBrowseFlags, interfaceId, type, kLocalDot, OnBrowse, sdCtx);
+    auto err = DNSServiceCreateConnection(&sdCtx->serviceRef);
     VerifyOrReturnError(kDNSServiceErr_NoError == err, sdCtx->Finalize(err));
 
-    return MdnsContexts::GetInstance().Add(sdCtx, sdRef);
+    // We will browse on both the local domain and the SRP domain.
+    ChipLogProgress(Discovery, "Browsing for: %s on local domain", StringOrNullMarker(type));
+    ReturnErrorOnFailure(BrowseOnDomain(sdCtx, interfaceId, type, kLocalDot));
+
+    ChipLogProgress(Discovery, "Browsing for: %s on %s domain", StringOrNullMarker(type), kSRPDot);
+    ReturnErrorOnFailure(BrowseOnDomain(sdCtx, interfaceId, type, kSRPDot));
+
+    return MdnsContexts::GetInstance().Add(sdCtx, sdCtx->serviceRef);
 }
 
 CHIP_ERROR Browse(void * context, DnssdBrowseCallback callback, uint32_t interfaceId, const char * type,
@@ -215,20 +246,56 @@
 {
     ChipLogProgress(Discovery, "Mdns: %s flags: %d, interface: %u, hostname: %s", __func__, flags, (unsigned) interfaceId,
                     StringOrNullMarker(hostname));
-    auto sdCtx = reinterpret_cast<ResolveContext *>(context);
+
+    auto contextWithType = reinterpret_cast<ResolveContextWithType *>(context);
+    VerifyOrReturn(contextWithType != nullptr, ChipLogError(Discovery, "ResolveContextWithType is null"));
+
+    auto sdCtx = contextWithType->context;
     ReturnOnFailure(MdnsContexts::GetInstance().Has(sdCtx));
     LogOnFailure(__func__, err);
 
     if (kDNSServiceErr_NoError == err)
     {
-        sdCtx->OnNewAddress(interfaceId, address);
+        InterfaceKey interfaceKey = { interfaceId, hostname, contextWithType->isSRPResolve };
+        CHIP_ERROR error          = sdCtx->OnNewAddress(interfaceKey, address);
+
+        // If we saw an address resolved on the SRP domain, we don't need to wait
+        // for SRP results, so don't bother with starting a timer to wait for those.
+        if (error == CHIP_NO_ERROR && contextWithType->isSRPResolve)
+        {
+            sdCtx->shouldStartSRPTimerForResolve = false;
+        }
     }
 
-    if (!(flags & kDNSServiceFlagsMoreComing))
+    if (flags & kDNSServiceFlagsMoreComing)
     {
-        VerifyOrReturn(sdCtx->HasAddress(), sdCtx->Finalize(kDNSServiceErr_BadState));
+        return;
+    }
+
+    VerifyOrReturn(sdCtx->HasAddress(), sdCtx->Finalize(kDNSServiceErr_BadState));
+
+    // If either we didn't start a resolve on the SRP domain or we started a resolve on the SRP domain and got an address,
+    // we are done. Otherwise start the timer to give the resolve on SRP domain some extra time to complete.
+    if (!sdCtx->shouldStartSRPTimerForResolve)
+    {
         sdCtx->Finalize();
     }
+    else
+    {
+        if (!sdCtx->isSRPTimerRunning)
+        {
+            CHIP_ERROR error = StartSRPTimer(kSRPTimeoutInMsec, sdCtx);
+
+            if (error != CHIP_NO_ERROR)
+            {
+                // If we failed to start the timer, just go ahead and report whatever information
+                // we have gotten so far.
+                sdCtx->Finalize();
+                return;
+            }
+            sdCtx->isSRPTimerRunning = true;
+        }
+    }
 }
 
 static void GetAddrInfo(ResolveContext * sdCtx)
@@ -237,11 +304,21 @@
 
     for (auto & interface : sdCtx->interfaces)
     {
-        auto interfaceId = interface.first;
+        if (interface.second.isDNSLookUpRequested)
+        {
+            continue;
+        }
+
+        auto interfaceId = interface.first.interfaceId;
         auto hostname    = interface.second.fullyQualifiedDomainName.c_str();
         auto sdRefCopy   = sdCtx->serviceRef; // Mandatory copy because of kDNSServiceFlagsShareConnection
-        auto err = DNSServiceGetAddrInfo(&sdRefCopy, kGetAddrInfoFlags, interfaceId, protocol, hostname, OnGetAddrInfo, sdCtx);
+
+        ResolveContextWithType * contextWithType =
+            (interface.first.isSRPResult) ? &sdCtx->resolveContextWithSRPType : &sdCtx->resolveContextWithNonSRPType;
+        auto err =
+            DNSServiceGetAddrInfo(&sdRefCopy, kGetAddrInfoFlags, interfaceId, protocol, hostname, OnGetAddrInfo, contextWithType);
         VerifyOrReturn(kDNSServiceErr_NoError == err, sdCtx->Finalize(err));
+        interface.second.isDNSLookUpRequested = true;
     }
 }
 
@@ -251,13 +328,17 @@
 {
     ChipLogProgress(Discovery, "Mdns: %s flags: %d, interface: %u, fullname: %s, hostname: %s, port: %u", __func__, flags,
                     (unsigned) interfaceId, StringOrNullMarker(fullname), StringOrNullMarker(hostname), ntohs(port));
-    auto sdCtx = reinterpret_cast<ResolveContext *>(context);
+
+    auto contextWithType = reinterpret_cast<ResolveContextWithType *>(context);
+    VerifyOrReturn(contextWithType != nullptr, ChipLogError(Discovery, "ResolveContextWithType is null"));
+
+    auto sdCtx = contextWithType->context;
     ReturnOnFailure(MdnsContexts::GetInstance().Has(sdCtx));
     LogOnFailure(__func__, err);
 
     if (kDNSServiceErr_NoError == err)
     {
-        sdCtx->OnNewInterface(interfaceId, fullname, hostname, port, txtLen, txtRecord);
+        sdCtx->OnNewInterface(interfaceId, fullname, hostname, port, txtLen, txtRecord, contextWithType->isSRPResolve);
     }
 
     if (!(flags & kDNSServiceFlagsMoreComing))
@@ -267,8 +348,18 @@
     }
 }
 
+static CHIP_ERROR ResolveWithContext(ResolveContext * sdCtx, uint32_t interfaceId, const char * type, const char * name,
+                                     const char * domain, ResolveContextWithType * contextWithType)
+{
+    auto sdRef = sdCtx->serviceRef; // Mandatory copy because of kDNSServiceFlagsShareConnection
+
+    auto err = DNSServiceResolve(&sdRef, kResolveFlags, interfaceId, name, type, domain, OnResolve, contextWithType);
+    VerifyOrReturnError(kDNSServiceErr_NoError == err, sdCtx->Finalize(err));
+    return CHIP_NO_ERROR;
+}
+
 static CHIP_ERROR Resolve(ResolveContext * sdCtx, uint32_t interfaceId, chip::Inet::IPAddressType addressType, const char * type,
-                          const char * name)
+                          const char * name, const char * domain)
 {
     ChipLogProgress(Discovery, "Resolve type=%s name=%s interface=%" PRIu32, StringOrNullMarker(type), StringOrNullMarker(name),
                     interfaceId);
@@ -276,9 +367,23 @@
     auto err = DNSServiceCreateConnection(&sdCtx->serviceRef);
     VerifyOrReturnError(kDNSServiceErr_NoError == err, sdCtx->Finalize(err));
 
-    auto sdRefCopy = sdCtx->serviceRef; // Mandatory copy because of kDNSServiceFlagsShareConnection
-    err            = DNSServiceResolve(&sdRefCopy, kResolveFlags, interfaceId, name, type, kLocalDot, OnResolve, sdCtx);
-    VerifyOrReturnError(kDNSServiceErr_NoError == err, sdCtx->Finalize(err));
+    // If we have a single domain from a browse, we will use that for the Resolve.
+    // Otherwise we will try to resolve using both the local domain and the SRP domain.
+    if (domain != nullptr)
+    {
+        ReturnErrorOnFailure(ResolveWithContext(sdCtx, interfaceId, type, name, domain, &sdCtx->resolveContextWithNonSRPType));
+        sdCtx->shouldStartSRPTimerForResolve = false;
+    }
+    else
+    {
+        ReturnErrorOnFailure(ResolveWithContext(sdCtx, interfaceId, type, name, kLocalDot, &sdCtx->resolveContextWithNonSRPType));
+
+        ReturnErrorOnFailure(ResolveWithContext(sdCtx, interfaceId, type, name, kSRPDot, &sdCtx->resolveContextWithNonSRPType));
+
+        // Set the flag to start the timer for resolve on SRP domain to complete since a resolve has been requested on the SRP
+        // domain.
+        sdCtx->shouldStartSRPTimerForResolve = true;
+    }
 
     auto retval = MdnsContexts::GetInstance().Add(sdCtx, sdCtx->serviceRef);
     if (retval == CHIP_NO_ERROR)
@@ -289,14 +394,14 @@
 }
 
 static CHIP_ERROR Resolve(void * context, DnssdResolveCallback callback, uint32_t interfaceId,
-                          chip::Inet::IPAddressType addressType, const char * type, const char * name)
+                          chip::Inet::IPAddressType addressType, const char * type, const char * name, const char * domain)
 {
     auto counterHolder = GetCounterHolder(name);
     auto sdCtx         = chip::Platform::New<ResolveContext>(context, callback, addressType, name,
                                                      BrowseContext::sContextDispatchingSuccess, std::move(counterHolder));
     VerifyOrReturnError(nullptr != sdCtx, CHIP_ERROR_NO_MEMORY);
 
-    return Resolve(sdCtx, interfaceId, addressType, type, name);
+    return Resolve(sdCtx, interfaceId, addressType, type, name, domain);
 }
 
 static CHIP_ERROR Resolve(CommissioningResolveDelegate * delegate, uint32_t interfaceId, chip::Inet::IPAddressType addressType,
@@ -306,7 +411,7 @@
     auto sdCtx         = chip::Platform::New<ResolveContext>(delegate, addressType, name, std::move(counterHolder));
     VerifyOrReturnError(nullptr != sdCtx, CHIP_ERROR_NO_MEMORY);
 
-    return Resolve(sdCtx, interfaceId, addressType, type, name);
+    return Resolve(sdCtx, interfaceId, addressType, type, name, nullptr);
 }
 
 } // namespace
@@ -442,9 +547,23 @@
     VerifyOrReturnError(service != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     VerifyOrReturnError(IsSupportedProtocol(service->mProtocol), CHIP_ERROR_INVALID_ARGUMENT);
 
-    auto regtype     = GetFullType(service);
-    auto interfaceId = GetInterfaceId(interface);
-    return Resolve(context, callback, interfaceId, service->mAddressType, regtype.c_str(), service->mName);
+    auto regtype        = GetFullType(service);
+    auto interfaceId    = GetInterfaceId(interface);
+    const char * domain = nullptr;
+
+    if (BrowseContext::sContextDispatchingSuccess != nullptr)
+    {
+        for (size_t i = 0; i < BrowseContext::sDispatchedServices->size(); ++i)
+        {
+            if (service == &BrowseContext::sDispatchedServices->at(i))
+            {
+                domain = BrowseContext::sContextDispatchingSuccess->services[i].second.c_str();
+                break;
+            }
+        }
+    }
+
+    return Resolve(context, callback, interfaceId, service->mAddressType, regtype.c_str(), service->mName, domain);
 }
 
 CHIP_ERROR ChipDnssdResolve(DnssdService * service, chip::Inet::InterfaceId interface, CommissioningResolveDelegate * delegate)
diff --git a/src/platform/Darwin/DnssdImpl.h b/src/platform/Darwin/DnssdImpl.h
index f095180..351926a 100644
--- a/src/platform/Darwin/DnssdImpl.h
+++ b/src/platform/Darwin/DnssdImpl.h
@@ -20,6 +20,7 @@
 #include <dns_sd.h>
 #include <lib/core/Global.h>
 #include <lib/dnssd/platform/Dnssd.h>
+#include <platform/CHIPDeviceLayer.h>
 
 #include "DnssdHostNameRegistrar.h"
 
@@ -194,6 +195,7 @@
     // TODO: Consider fixing the higher-level APIs to make it possible to pass
     // in multiple IPs for a successful browse result.
     static BrowseContext * sContextDispatchingSuccess;
+    static std::vector<DnssdService> * sDispatchedServices;
 };
 
 struct BrowseWithDelegateContext : public BrowseHandler
@@ -222,17 +224,52 @@
     DnssdService service;
     std::vector<Inet::IPAddress> addresses;
     std::string fullyQualifiedDomainName;
+    bool isDNSLookUpRequested = false;
+};
+
+struct InterfaceKey
+{
+    InterfaceKey()  = default;
+    ~InterfaceKey() = default;
+    inline bool operator<(const InterfaceKey & other) const
+    {
+        return (this->interfaceId < other.interfaceId) ||
+            ((this->interfaceId == other.interfaceId) && (this->hostname < other.hostname)) ||
+            ((this->interfaceId == other.interfaceId) && (this->hostname == other.hostname) &&
+             (this->isSRPResult < other.isSRPResult));
+    }
+
+    uint32_t interfaceId;
+    std::string hostname;
+    bool isSRPResult = false;
+};
+
+struct ResolveContextWithType
+{
+    ResolveContextWithType()  = delete;
+    ~ResolveContextWithType() = default;
+
+    ResolveContext * const context;
+    const bool isSRPResolve;
 };
 
 struct ResolveContext : public GenericContext
 {
     DnssdResolveCallback callback;
-    std::map<uint32_t, InterfaceInfo> interfaces;
+    std::map<InterfaceKey, InterfaceInfo> interfaces;
     DNSServiceProtocol protocol;
     std::string instanceName;
     std::shared_ptr<uint32_t> consumerCounter;
     BrowseContext * const browseThatCausedResolve; // Can be null
 
+    // Indicates whether the timer for 250 msecs should be started
+    // to give the resolve on SRP domain some extra time to complete.
+    bool shouldStartSRPTimerForResolve = false;
+    bool isSRPTimerRunning             = false;
+
+    ResolveContextWithType resolveContextWithSRPType    = { this, true };
+    ResolveContextWithType resolveContextWithNonSRPType = { this, false };
+
     // browseCausingResolve can be null.
     ResolveContext(void * cbContext, DnssdResolveCallback cb, chip::Inet::IPAddressType cbAddressType,
                    const char * instanceNameToResolve, BrowseContext * browseCausingResolve,
@@ -244,21 +281,37 @@
     void DispatchFailure(const char * errorStr, CHIP_ERROR err) override;
     void DispatchSuccess() override;
 
-    CHIP_ERROR OnNewAddress(uint32_t interfaceId, const struct sockaddr * address);
+    CHIP_ERROR OnNewAddress(const InterfaceKey & interfaceKey, const struct sockaddr * address);
     bool HasAddress();
 
     void OnNewInterface(uint32_t interfaceId, const char * fullname, const char * hostname, uint16_t port, uint16_t txtLen,
-                        const unsigned char * txtRecord);
+                        const unsigned char * txtRecord, bool isSRPResult);
     bool HasInterface();
     bool Matches(const char * otherInstanceName) const { return instanceName == otherInstanceName; }
 
+    /**
+     * @brief Callback that is called when the timeout for resolving on the kSRPDot domain has expired.
+     *
+     * @param[in] systemLayer The system layer.
+     * @param[in] callbackContext The context passed to the timer callback.
+     */
+    static void SRPTimerExpiredCallback(chip::System::Layer * systemLayer, void * callbackContext);
+
 private:
     /**
      * Try reporting the results we got on the provided interface index.
      * Returns true if information was reported, false if not (e.g. if there
      * were no IP addresses, etc).
      */
+    bool TryReportingResultsForInterfaceIndex(uint32_t interfaceIndex, const std::string & hostname, bool isSRPResult);
+
     bool TryReportingResultsForInterfaceIndex(uint32_t interfaceIndex);
+
+    /**
+     * @brief Cancels the timer that was started to wait for the resolution on the kSRPDot domain to happen.
+     *
+     */
+    void CancelSRPTimer();
 };
 
 } // namespace Dnssd