[Darwin][Network.framework] Split the DnssdHostNameRegistrar such that the interface monitoring bits is accessible for other usage (#38911)

diff --git a/src/platform/Darwin/BUILD.gn b/src/platform/Darwin/BUILD.gn
index 7954ea1..d1ead87 100644
--- a/src/platform/Darwin/BUILD.gn
+++ b/src/platform/Darwin/BUILD.gn
@@ -60,7 +60,7 @@
     "DiagnosticDataProviderImpl.cpp",
     "DiagnosticDataProviderImpl.h",
     "DnssdContexts.cpp",
-    "DnssdHostNameRegistrar.cpp",
+    "DnssdHostNameRegistrar.mm",
     "DnssdImpl.cpp",
     "DnssdImpl.h",
     "DnssdType.cpp",
@@ -77,6 +77,8 @@
     "SystemTimeSupport.cpp",
     "UserDefaults.h",
     "UserDefaults.mm",
+    "inet/InterfacesMonitor.h",
+    "inet/InterfacesMonitor.mm",
   ]
 
   if (chip_enable_wifi) {
diff --git a/src/platform/Darwin/DnssdHostNameRegistrar.cpp b/src/platform/Darwin/DnssdHostNameRegistrar.cpp
deleted file mode 100644
index 6372e07..0000000
--- a/src/platform/Darwin/DnssdHostNameRegistrar.cpp
+++ /dev/null
@@ -1,427 +0,0 @@
-/*
- *
- *    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.
- *    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 "DnssdHostNameRegistrar.h"
-#include "DnssdImpl.h"
-#include "MdnsError.h"
-
-#include <arpa/inet.h>
-#include <ifaddrs.h>
-#include <net/ethernet.h>
-#include <net/if_dl.h>
-#include <netdb.h>
-
-#include <set>
-
-#include <platform/CHIPDeviceLayer.h>
-
-constexpr DNSServiceFlags kRegisterRecordFlags = kDNSServiceFlagsShared;
-
-namespace chip {
-namespace Dnssd {
-
-namespace {
-
-#if CHIP_PROGRESS_LOGGING
-constexpr char kPathStatusInvalid[]     = "Invalid";
-constexpr char kPathStatusUnsatisfied[] = "Unsatisfied";
-constexpr char kPathStatusSatisfied[]   = "Satisfied";
-constexpr char kPathStatusSatisfiable[] = "Satisfiable";
-constexpr char kPathStatusUnknown[]     = "Unknown";
-
-constexpr char kInterfaceTypeCellular[] = "Cellular";
-constexpr char kInterfaceTypeWiFi[]     = "WiFi";
-constexpr char kInterfaceTypeWired[]    = "Wired";
-constexpr char kInterfaceTypeLoopback[] = "Loopback";
-constexpr char kInterfaceTypeOther[]    = "Other";
-constexpr char kInterfaceTypeUnknown[]  = "Unknown";
-
-const char * GetPathStatusString(nw_path_status_t status)
-{
-    const char * str = nullptr;
-
-    if (status == nw_path_status_invalid)
-    {
-        str = kPathStatusInvalid;
-    }
-    else if (status == nw_path_status_unsatisfied)
-    {
-        str = kPathStatusUnsatisfied;
-    }
-    else if (status == nw_path_status_satisfied)
-    {
-        str = kPathStatusSatisfied;
-    }
-    else if (status == nw_path_status_satisfiable)
-    {
-        str = kPathStatusSatisfiable;
-    }
-    else
-    {
-        str = kPathStatusUnknown;
-    }
-
-    return str;
-}
-
-const char * GetInterfaceTypeString(nw_interface_type_t type)
-{
-    const char * str = nullptr;
-
-    if (type == nw_interface_type_cellular)
-    {
-        str = kInterfaceTypeCellular;
-    }
-    else if (type == nw_interface_type_wifi)
-    {
-        str = kInterfaceTypeWiFi;
-    }
-    else if (type == nw_interface_type_wired)
-    {
-        str = kInterfaceTypeWired;
-    }
-    else if (type == nw_interface_type_loopback)
-    {
-        str = kInterfaceTypeLoopback;
-    }
-    else if (type == nw_interface_type_other)
-    {
-        str = kInterfaceTypeOther;
-    }
-    else
-    {
-        str = kInterfaceTypeUnknown;
-    }
-
-    return str;
-}
-
-void LogDetails(uint32_t interfaceId, InetInterfacesVector inetInterfaces, Inet6InterfacesVector inet6Interfaces)
-{
-    for (auto & inetInterface : inetInterfaces)
-    {
-        if (interfaceId == inetInterface.first)
-        {
-            char addr[INET_ADDRSTRLEN] = {};
-            inet_ntop(AF_INET, &inetInterface.second, addr, sizeof(addr));
-            ChipLogProgress(Discovery, "\t\t* ipv4: %s", addr);
-        }
-    }
-
-    for (auto & inet6Interface : inet6Interfaces)
-    {
-        if (interfaceId == inet6Interface.first)
-        {
-            char addr[INET6_ADDRSTRLEN] = {};
-            inet_ntop(AF_INET6, &inet6Interface.second, addr, sizeof(addr));
-            ChipLogProgress(Discovery, "\t\t* ipv6: %s", addr);
-        }
-    }
-}
-
-void LogDetails(nw_path_t path)
-{
-    auto status = nw_path_get_status(path);
-    ChipLogProgress(Discovery, "Status: %s", GetPathStatusString(status));
-}
-
-void LogDetails(InetInterfacesVector inetInterfaces, Inet6InterfacesVector inet6Interfaces)
-{
-    std::set<uint32_t> interfaceIds;
-    for (auto & inetInterface : inetInterfaces)
-    {
-        interfaceIds.insert(inetInterface.first);
-    }
-
-    for (auto & inet6Interface : inet6Interfaces)
-    {
-        interfaceIds.insert(inet6Interface.first);
-    }
-
-    for (auto interfaceId : interfaceIds)
-    {
-        char interfaceName[Inet::InterfaceId::kMaxIfNameLength] = {};
-        if_indextoname(interfaceId, interfaceName);
-        ChipLogProgress(Discovery, "\t%s (%u)", interfaceName, interfaceId);
-        LogDetails(interfaceId, inetInterfaces, inet6Interfaces);
-    }
-}
-
-void LogDetails(nw_interface_t interface, InetInterfacesVector inetInterfaces, Inet6InterfacesVector inet6Interfaces)
-{
-    auto interfaceId   = nw_interface_get_index(interface);
-    auto interfaceName = nw_interface_get_name(interface);
-    auto interfaceType = nw_interface_get_type(interface);
-    ChipLogProgress(Discovery, "\t%s (%u / %s)", interfaceName, interfaceId, GetInterfaceTypeString(interfaceType));
-    LogDetails(interfaceId, inetInterfaces, inet6Interfaces);
-}
-#endif // CHIP_PROGRESS_LOGGING
-
-bool HasValidFlags(unsigned int flags, bool allowLoopbackOnly)
-{
-    VerifyOrReturnValue(!allowLoopbackOnly || (flags & IFF_LOOPBACK), false);
-    VerifyOrReturnValue((flags & IFF_RUNNING), false);
-    VerifyOrReturnValue((flags & IFF_MULTICAST), false);
-    return true;
-}
-
-bool HasValidNetworkType(nw_interface_t interface)
-{
-    auto interfaceType = nw_interface_get_type(interface);
-    return interfaceType == nw_interface_type_wifi || interfaceType == nw_interface_type_wired ||
-        interfaceType == nw_interface_type_other;
-}
-
-bool IsValidInterfaceId(uint32_t targetInterfaceId, nw_interface_t interface)
-{
-    auto currentInterfaceId = nw_interface_get_index(interface);
-    return targetInterfaceId == kDNSServiceInterfaceIndexAny || targetInterfaceId == currentInterfaceId;
-}
-
-void ShouldUseVersion(chip::Inet::IPAddressType addressType, bool & shouldUseIPv4, bool & shouldUseIPv6)
-{
-#if INET_CONFIG_ENABLE_IPV4
-    shouldUseIPv4 = addressType == Inet::IPAddressType::kIPv4 || addressType == Inet::IPAddressType::kAny;
-#else
-    shouldUseIPv4 = false;
-#endif // INET_CONFIG_ENABLE_IPV4
-    shouldUseIPv6 = addressType == Inet::IPAddressType::kIPv6 || addressType == Inet::IPAddressType::kAny;
-}
-
-static void OnRegisterRecord(DNSServiceRef sdRef, DNSRecordRef recordRef, DNSServiceFlags flags, DNSServiceErrorType err,
-                             void * context)
-{
-    ChipLogProgress(Discovery, "Mdns: %s flags: %d", __func__, flags);
-    if (kDNSServiceErr_NoError != err)
-    {
-        ChipLogError(Discovery, "%s (%s)", __func__, Error::ToString(err));
-    }
-}
-
-void GetInterfaceAddresses(uint32_t interfaceId, chip::Inet::IPAddressType addressType, InetInterfacesVector & inetInterfaces,
-                           Inet6InterfacesVector & inet6Interfaces, bool searchLoopbackOnly = false)
-{
-    bool shouldUseIPv4, shouldUseIPv6;
-    ShouldUseVersion(addressType, shouldUseIPv4, shouldUseIPv6);
-
-    ifaddrs * ifap;
-    VerifyOrReturn(getifaddrs(&ifap) >= 0);
-
-    for (struct ifaddrs * ifa = ifap; ifa != nullptr; ifa = ifa->ifa_next)
-    {
-        auto interfaceAddress = ifa->ifa_addr;
-        if (interfaceAddress == nullptr)
-        {
-            continue;
-        }
-
-        if (!HasValidFlags(ifa->ifa_flags, searchLoopbackOnly))
-        {
-            continue;
-        }
-
-        auto currentInterfaceId = if_nametoindex(ifa->ifa_name);
-        if (interfaceId != kDNSServiceInterfaceIndexAny && interfaceId != currentInterfaceId)
-        {
-            continue;
-        }
-
-        if (shouldUseIPv4 && (AF_INET == interfaceAddress->sa_family))
-        {
-            auto inetAddress = reinterpret_cast<struct sockaddr_in *>(interfaceAddress)->sin_addr;
-            inetInterfaces.push_back(InetInterface(currentInterfaceId, inetAddress));
-        }
-        else if (shouldUseIPv6 && (AF_INET6 == interfaceAddress->sa_family))
-        {
-            auto inet6Address = reinterpret_cast<struct sockaddr_in6 *>(interfaceAddress)->sin6_addr;
-            inet6Interfaces.push_back(Inet6Interface(currentInterfaceId, inet6Address));
-        }
-    }
-
-    freeifaddrs(ifap);
-}
-} // namespace
-
-HostNameRegistrar::~HostNameRegistrar()
-{
-    Unregister();
-    if (mLivenessTracker != nullptr)
-    {
-        *mLivenessTracker = false;
-    }
-}
-
-DNSServiceErrorType HostNameRegistrar::Init(const char * hostname, Inet::IPAddressType addressType, uint32_t interfaceId)
-{
-    mHostname         = hostname;
-    mInterfaceId      = interfaceId;
-    mAddressType      = addressType;
-    mServiceRef       = nullptr;
-    mInterfaceMonitor = nullptr;
-
-    mLivenessTracker = std::make_shared<bool>(true);
-    if (mLivenessTracker == nullptr)
-    {
-        return kDNSServiceErr_NoMemory;
-    }
-
-    return kDNSServiceErr_NoError;
-}
-
-CHIP_ERROR HostNameRegistrar::Register()
-{
-    // If the target interface is kDNSServiceInterfaceIndexLocalOnly, just
-    // register the loopback addresses.
-    if (IsLocalOnly())
-    {
-        ReturnErrorOnFailure(ResetSharedConnection());
-
-        // Just register the loopback IPv6 address, on mInterfaceId so that the
-        // resolution code finds it there.
-        auto loopbackIPAddress = Inet::IPAddress::Loopback(Inet::IPAddressType::kIPv6);
-        in6_addr loopbackAddr  = loopbackIPAddress.ToIPv6();
-
-        return RegisterInterface(mInterfaceId, loopbackAddr, kDNSServiceType_AAAA);
-    }
-
-    return StartMonitorInterfaces(^(InetInterfacesVector inetInterfaces, Inet6InterfacesVector inet6Interfaces) {
-        ReturnOnFailure(ResetSharedConnection());
-        RegisterInterfaces(inetInterfaces, kDNSServiceType_A);
-        RegisterInterfaces(inet6Interfaces, kDNSServiceType_AAAA);
-    });
-}
-
-CHIP_ERROR HostNameRegistrar::RegisterInterface(uint32_t interfaceId, uint16_t rtype, const void * rdata, uint16_t rdlen)
-{
-    DNSRecordRef dnsRecordRef;
-    auto err = DNSServiceRegisterRecord(mServiceRef, &dnsRecordRef, kRegisterRecordFlags, interfaceId, mHostname.c_str(), rtype,
-                                        kDNSServiceClass_IN, rdlen, rdata, 0, OnRegisterRecord, nullptr);
-    return Error::ToChipError(err);
-}
-
-void HostNameRegistrar::Unregister()
-{
-    if (!IsLocalOnly())
-    {
-        StopMonitorInterfaces();
-    }
-    StopSharedConnection();
-}
-
-CHIP_ERROR HostNameRegistrar::StartMonitorInterfaces(OnInterfaceChanges interfaceChangesBlock)
-{
-    mInterfaceMonitor = nw_path_monitor_create();
-    VerifyOrReturnError(mInterfaceMonitor != nullptr, CHIP_ERROR_NO_MEMORY);
-
-    nw_path_monitor_set_queue(mInterfaceMonitor, chip::DeviceLayer::PlatformMgrImpl().GetWorkQueue());
-
-    // Our update handler closes over "this", but can't keep us alive (because we
-    // are not refcounted).  Make sure it closes over a shared ref to our
-    // liveness tracker, which it _can_ keep alive, so it can bail out if we
-    // have been destroyed between when the task was queued and when it ran.
-    std::shared_ptr<bool> livenessTracker = mLivenessTracker;
-    nw_path_monitor_set_update_handler(mInterfaceMonitor, ^(nw_path_t path) {
-        if (!*livenessTracker)
-        {
-            // The HostNameRegistrar has been destroyed; just bail out.
-            return;
-        }
-
-#if CHIP_PROGRESS_LOGGING
-        LogDetails(path);
-#endif // CHIP_PROGRESS_LOGGING
-
-        __block InetInterfacesVector inet;
-        __block Inet6InterfacesVector inet6;
-
-        // The loopback interfaces needs to be manually added. While lo0 is usually 1, this is not guaranteed. So search for a
-        // loopback interface with the specified interface id. If the specified interface id is kDNSServiceInterfaceIndexAny, it
-        // will look for all available loopback interfaces.
-        GetInterfaceAddresses(mInterfaceId, mAddressType, inet, inet6, true /* searchLoopbackOnly */);
-#if CHIP_PROGRESS_LOGGING
-        LogDetails(inet, inet6);
-#endif // CHIP_PROGRESS_LOGGING
-
-        auto status = nw_path_get_status(path);
-        if (status == nw_path_status_satisfied)
-        {
-            nw_path_enumerate_interfaces(path, ^(nw_interface_t interface) {
-                VerifyOrReturnValue(HasValidNetworkType(interface), true);
-                VerifyOrReturnValue(IsValidInterfaceId(mInterfaceId, interface), true);
-
-                auto targetInterfaceId = nw_interface_get_index(interface);
-                GetInterfaceAddresses(targetInterfaceId, mAddressType, inet, inet6);
-#if CHIP_PROGRESS_LOGGING
-                LogDetails(interface, inet, inet6);
-#endif // CHIP_PROGRESS_LOGGING
-                return true;
-            });
-        }
-
-        interfaceChangesBlock(inet, inet6);
-    });
-
-    nw_path_monitor_start(mInterfaceMonitor);
-
-    return CHIP_NO_ERROR;
-}
-
-void HostNameRegistrar::StopMonitorInterfaces()
-{
-    if (mInterfaceMonitor != nullptr)
-    {
-        nw_path_monitor_cancel(mInterfaceMonitor);
-        nw_release(mInterfaceMonitor);
-        mInterfaceMonitor = nullptr;
-    }
-}
-
-CHIP_ERROR HostNameRegistrar::StartSharedConnection()
-{
-    VerifyOrReturnError(mServiceRef == nullptr, CHIP_ERROR_INCORRECT_STATE);
-
-    auto err = DNSServiceCreateConnection(&mServiceRef);
-    VerifyOrReturnValue(kDNSServiceErr_NoError == err, Error::ToChipError(err));
-
-    err = DNSServiceSetDispatchQueue(mServiceRef, chip::DeviceLayer::PlatformMgrImpl().GetWorkQueue());
-    if (kDNSServiceErr_NoError != err)
-    {
-        StopSharedConnection();
-    }
-
-    return Error::ToChipError(err);
-}
-
-void HostNameRegistrar::StopSharedConnection()
-{
-    if (mServiceRef != nullptr)
-    {
-        // All the DNSRecordRefs registered to the shared DNSServiceRef will be deallocated.
-        DNSServiceRefDeallocate(mServiceRef);
-        mServiceRef = nullptr;
-    }
-}
-
-CHIP_ERROR HostNameRegistrar::ResetSharedConnection()
-{
-    StopSharedConnection();
-    ReturnLogErrorOnFailure(StartSharedConnection());
-    return CHIP_NO_ERROR;
-}
-
-} // namespace Dnssd
-} // namespace chip
diff --git a/src/platform/Darwin/DnssdHostNameRegistrar.h b/src/platform/Darwin/DnssdHostNameRegistrar.h
index 7071f57..4d2e677 100644
--- a/src/platform/Darwin/DnssdHostNameRegistrar.h
+++ b/src/platform/Darwin/DnssdHostNameRegistrar.h
@@ -21,73 +21,51 @@
 #include <lib/dnssd/platform/Dnssd.h>
 
 #include <string>
-#include <vector>
 
-#include <Network/Network.h>
+#include "inet/InterfacesMonitor.h"
 
 namespace chip {
 namespace Dnssd {
 
-    typedef std::pair<uint32_t, in_addr> InetInterface;
-    typedef std::pair<uint32_t, in6_addr> Inet6Interface;
-    typedef std::vector<InetInterface> InetInterfacesVector;
-    typedef std::vector<std::pair<uint32_t, in6_addr>> Inet6InterfacesVector;
-    typedef void (^OnInterfaceChanges)(InetInterfacesVector inetInterfaces, Inet6InterfacesVector inet6Interfaces);
+class HostNameRegistrar : public Inet::Darwin::InterfacesMonitor
+{
+public:
+    ~HostNameRegistrar();
 
-    class HostNameRegistrar {
-    public:
-        ~HostNameRegistrar();
+    DNSServiceErrorType Init(const char * hostname, Inet::IPAddressType addressType, uint32_t interfaceId);
 
-        DNSServiceErrorType Init(const char * hostname, Inet::IPAddressType addressType, uint32_t interfaceId);
+    CHIP_ERROR Register();
+    void Unregister();
 
-        CHIP_ERROR Register();
-        void Unregister();
-
-    private:
-        bool IsLocalOnly() const { return mInterfaceId == kDNSServiceInterfaceIndexLocalOnly; };
-
-        template <typename T>
-        void RegisterInterfaces(std::vector<std::pair<uint32_t, T>> interfaces, uint16_t type)
+private:
+    template <typename T>
+    void RegisterInterfaces(std::vector<std::pair<uint32_t, T>> interfaces, uint16_t type)
+    {
+        for (auto & interface : interfaces)
         {
-            for (auto & interface : interfaces) {
-                auto interfaceId = interface.first;
+            auto interfaceId = interface.first;
 
-                LogErrorOnFailure(RegisterInterface(interfaceId, interface.second, type));
-            }
+            LogErrorOnFailure(RegisterInterface(interfaceId, interface.second, type));
         }
+    }
 
-        template <typename T>
-        CHIP_ERROR RegisterInterface(uint32_t interfaceId, const T & interfaceAddress, uint16_t type)
-        {
-            auto interfaceAddressLen = sizeof(interfaceAddress);
+    template <typename T>
+    CHIP_ERROR RegisterInterface(uint32_t interfaceId, const T & interfaceAddress, uint16_t type)
+    {
+        auto interfaceAddressLen = sizeof(interfaceAddress);
 
-            return RegisterInterface(interfaceId, type, &interfaceAddress, static_cast<uint16_t>(interfaceAddressLen));
-        }
+        return RegisterInterface(interfaceId, type, &interfaceAddress, static_cast<uint16_t>(interfaceAddressLen));
+    }
 
-        CHIP_ERROR RegisterInterface(uint32_t interfaceId, uint16_t rtype, const void * rdata, uint16_t rdlen);
+    CHIP_ERROR RegisterInterface(uint32_t interfaceId, uint16_t rtype, const void * rdata, uint16_t rdlen);
 
-        CHIP_ERROR StartSharedConnection();
-        void StopSharedConnection();
-        CHIP_ERROR ResetSharedConnection();
+    CHIP_ERROR StartSharedConnection();
+    void StopSharedConnection();
+    CHIP_ERROR ResetSharedConnection();
 
-        CHIP_ERROR StartMonitorInterfaces(OnInterfaceChanges interfaceChangesBlock);
-        void StopMonitorInterfaces();
-
-        DNSServiceRef mServiceRef = nullptr;
-        nw_path_monitor_t mInterfaceMonitor = nullptr;
-
-        std::string mHostname;
-        // Default to kDNSServiceInterfaceIndexLocalOnly so we don't mess around
-        // with un-registration if we never get Init() called.
-        uint32_t mInterfaceId = kDNSServiceInterfaceIndexLocalOnly;
-        Inet::IPAddressType mAddressType;
-
-        // We use mLivenessTracker to indicate to blocks that close over us that
-        // we've been destroyed.  This is needed because we're not a refcounted
-        // object, so the blocks can't keep us alive; they just close over the
-        // raw pointer to "this".
-        std::shared_ptr<bool> mLivenessTracker;
-    };
+    DNSServiceRef mServiceRef = nullptr;
+    std::string mHostname;
+};
 
 } // namespace Dnssd
 } // namespace chip
diff --git a/src/platform/Darwin/DnssdHostNameRegistrar.mm b/src/platform/Darwin/DnssdHostNameRegistrar.mm
new file mode 100644
index 0000000..451253f
--- /dev/null
+++ b/src/platform/Darwin/DnssdHostNameRegistrar.mm
@@ -0,0 +1,139 @@
+/*
+ *
+ *    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.
+ *    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.
+ */
+
+#if !__has_feature(objc_arc)
+#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+#include "DnssdHostNameRegistrar.h"
+#include "DnssdImpl.h"
+#include "MdnsError.h"
+
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <netdb.h>
+
+#include <set>
+
+#include <platform/CHIPDeviceLayer.h>
+
+constexpr DNSServiceFlags kRegisterRecordFlags = kDNSServiceFlagsShared;
+
+namespace chip {
+namespace Dnssd {
+
+    namespace {
+        static void OnRegisterRecord(DNSServiceRef sdRef, DNSRecordRef recordRef, DNSServiceFlags flags, DNSServiceErrorType err,
+            void * context)
+        {
+            ChipLogProgress(Discovery, "Mdns: %s flags: %d", __func__, flags);
+            if (kDNSServiceErr_NoError != err) {
+                ChipLogError(Discovery, "%s (%s)", __func__, Error::ToString(err));
+            }
+        }
+    } // namespace
+
+    HostNameRegistrar::~HostNameRegistrar()
+    {
+        Unregister();
+    }
+
+    DNSServiceErrorType HostNameRegistrar::Init(const char * hostname, Inet::IPAddressType addressType, uint32_t interfaceId)
+    {
+        mHostname = hostname;
+        mServiceRef = nullptr;
+
+        VerifyOrReturnError(CHIP_NO_ERROR == InterfacesMonitor::Init(chip::DeviceLayer::PlatformMgrImpl().GetWorkQueue(), addressType, interfaceId),
+            kDNSServiceErr_BadState);
+
+        return kDNSServiceErr_NoError;
+    }
+
+    CHIP_ERROR HostNameRegistrar::Register()
+    {
+        // If the target interface is kDNSServiceInterfaceIndexLocalOnly, just
+        // register the loopback addresses.
+        if (IsLocalOnly()) {
+            ReturnErrorOnFailure(ResetSharedConnection());
+
+            // Just register the loopback IPv6 address, on mInterfaceId so that the
+            // resolution code finds it there.
+            auto loopbackIPAddress = Inet::IPAddress::Loopback(Inet::IPAddressType::kIPv6);
+            in6_addr loopbackAddr = loopbackIPAddress.ToIPv6();
+
+            return RegisterInterface(kDNSServiceInterfaceIndexLocalOnly, loopbackAddr, kDNSServiceType_AAAA);
+        }
+
+        return StartMonitorInterfaces(
+            ^(Inet::Darwin::InetInterfacesVector inetInterfaces, Inet::Darwin::Inet6InterfacesVector inet6Interfaces) {
+                ReturnOnFailure(ResetSharedConnection());
+                RegisterInterfaces(inetInterfaces, kDNSServiceType_A);
+                RegisterInterfaces(inet6Interfaces, kDNSServiceType_AAAA);
+            });
+    }
+
+    CHIP_ERROR HostNameRegistrar::RegisterInterface(uint32_t interfaceId, uint16_t rtype, const void * rdata, uint16_t rdlen)
+    {
+        DNSRecordRef dnsRecordRef;
+        auto err = DNSServiceRegisterRecord(mServiceRef, &dnsRecordRef, kRegisterRecordFlags, interfaceId, mHostname.c_str(), rtype,
+            kDNSServiceClass_IN, rdlen, rdata, 0, OnRegisterRecord, nullptr);
+        return Error::ToChipError(err);
+    }
+
+    void HostNameRegistrar::Unregister()
+    {
+        if (!IsLocalOnly()) {
+            StopMonitorInterfaces();
+        }
+        StopSharedConnection();
+    }
+
+    CHIP_ERROR HostNameRegistrar::StartSharedConnection()
+    {
+        VerifyOrReturnError(mServiceRef == nullptr, CHIP_ERROR_INCORRECT_STATE);
+
+        auto err = DNSServiceCreateConnection(&mServiceRef);
+        VerifyOrReturnValue(kDNSServiceErr_NoError == err, Error::ToChipError(err));
+
+        err = DNSServiceSetDispatchQueue(mServiceRef, chip::DeviceLayer::PlatformMgrImpl().GetWorkQueue());
+        if (kDNSServiceErr_NoError != err) {
+            StopSharedConnection();
+        }
+
+        return Error::ToChipError(err);
+    }
+
+    void HostNameRegistrar::StopSharedConnection()
+    {
+        if (mServiceRef != nullptr) {
+            // All the DNSRecordRefs registered to the shared DNSServiceRef will be deallocated.
+            DNSServiceRefDeallocate(mServiceRef);
+            mServiceRef = nullptr;
+        }
+    }
+
+    CHIP_ERROR HostNameRegistrar::ResetSharedConnection()
+    {
+        StopSharedConnection();
+        ReturnLogErrorOnFailure(StartSharedConnection());
+        return CHIP_NO_ERROR;
+    }
+
+} // namespace Dnssd
+} // namespace chip
diff --git a/src/platform/Darwin/inet/InterfacesMonitor.h b/src/platform/Darwin/inet/InterfacesMonitor.h
new file mode 100644
index 0000000..26ebbb5
--- /dev/null
+++ b/src/platform/Darwin/inet/InterfacesMonitor.h
@@ -0,0 +1,67 @@
+/*
+ *
+ *    Copyright (c) 2025 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 <vector>
+
+#include <Network/Network.h>
+
+#include <inet/IPAddress.h>
+#include <lib/core/CHIPError.h>
+
+namespace chip {
+namespace Inet {
+    namespace Darwin {
+
+        typedef std::pair<uint32_t, in_addr> InetInterface;
+        typedef std::pair<uint32_t, in6_addr> Inet6Interface;
+        typedef std::vector<InetInterface> InetInterfacesVector;
+        typedef std::vector<std::pair<uint32_t, in6_addr>> Inet6InterfacesVector;
+        typedef void (^OnInterfaceChanges)(InetInterfacesVector inetInterfaces, Inet6InterfacesVector inet6Interfaces);
+
+        class InterfacesMonitor {
+        public:
+            ~InterfacesMonitor();
+
+            CHIP_ERROR Init(dispatch_queue_t workQueue, IPAddressType addressType, uint32_t interfaceId);
+
+            bool IsLocalOnly() const { return mInterfaceId == kDNSServiceInterfaceIndexLocalOnly; };
+
+            CHIP_ERROR StartMonitorInterfaces(OnInterfaceChanges interfaceChangesBlock);
+            void StopMonitorInterfaces();
+
+        private:
+            nw_path_monitor_t mInterfaceMonitor = nullptr;
+
+            // Default to kDNSServiceInterfaceIndexLocalOnly so we don't mess around
+            // with un-registration if we never get Init() called.
+            uint32_t mInterfaceId = kDNSServiceInterfaceIndexLocalOnly;
+            IPAddressType mAddressType;
+
+            // We use mLivenessTracker to indicate to blocks that close over us that
+            // we've been destroyed.  This is needed because we're not a refcounted
+            // object, so the blocks can't keep us alive; they just close over the
+            // raw pointer to "this".
+            std::shared_ptr<bool> mLivenessTracker;
+
+            dispatch_queue_t mWorkQueue = nullptr;
+        };
+
+    } // namespace Darwin
+} // namespace Inet
+} // namespace chip
diff --git a/src/platform/Darwin/inet/InterfacesMonitor.mm b/src/platform/Darwin/inet/InterfacesMonitor.mm
new file mode 100644
index 0000000..e467e78
--- /dev/null
+++ b/src/platform/Darwin/inet/InterfacesMonitor.mm
@@ -0,0 +1,303 @@
+/*
+ *
+ *    Copyright (c) 2025 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.
+ */
+
+#if !__has_feature(objc_arc)
+#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+#include "InterfacesMonitor.h"
+
+#include <lib/support/CodeUtils.h>
+
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <netdb.h>
+
+#include <set>
+
+namespace chip {
+namespace Inet {
+    namespace Darwin {
+
+        namespace {
+
+#if CHIP_PROGRESS_LOGGING
+            constexpr char kPathStatusInvalid[] = "Invalid";
+            constexpr char kPathStatusUnsatisfied[] = "Unsatisfied";
+            constexpr char kPathStatusSatisfied[] = "Satisfied";
+            constexpr char kPathStatusSatisfiable[] = "Satisfiable";
+            constexpr char kPathStatusUnknown[] = "Unknown";
+
+            constexpr char kInterfaceTypeCellular[] = "Cellular";
+            constexpr char kInterfaceTypeWiFi[] = "WiFi";
+            constexpr char kInterfaceTypeWired[] = "Wired";
+            constexpr char kInterfaceTypeLoopback[] = "Loopback";
+            constexpr char kInterfaceTypeOther[] = "Other";
+            constexpr char kInterfaceTypeUnknown[] = "Unknown";
+
+            const char * GetPathStatusString(nw_path_status_t status)
+            {
+                const char * str = nullptr;
+
+                if (status == nw_path_status_invalid) {
+                    str = kPathStatusInvalid;
+                } else if (status == nw_path_status_unsatisfied) {
+                    str = kPathStatusUnsatisfied;
+                } else if (status == nw_path_status_satisfied) {
+                    str = kPathStatusSatisfied;
+                } else if (status == nw_path_status_satisfiable) {
+                    str = kPathStatusSatisfiable;
+                } else {
+                    str = kPathStatusUnknown;
+                }
+
+                return str;
+            }
+
+            const char * GetInterfaceTypeString(nw_interface_type_t type)
+            {
+                const char * str = nullptr;
+
+                if (type == nw_interface_type_cellular) {
+                    str = kInterfaceTypeCellular;
+                } else if (type == nw_interface_type_wifi) {
+                    str = kInterfaceTypeWiFi;
+                } else if (type == nw_interface_type_wired) {
+                    str = kInterfaceTypeWired;
+                } else if (type == nw_interface_type_loopback) {
+                    str = kInterfaceTypeLoopback;
+                } else if (type == nw_interface_type_other) {
+                    str = kInterfaceTypeOther;
+                } else {
+                    str = kInterfaceTypeUnknown;
+                }
+
+                return str;
+            }
+
+            void LogDetails(uint32_t interfaceId, InetInterfacesVector inetInterfaces, Inet6InterfacesVector inet6Interfaces)
+            {
+                for (auto & inetInterface : inetInterfaces) {
+                    if (interfaceId == inetInterface.first) {
+                        char addr[INET_ADDRSTRLEN] = {};
+                        inet_ntop(AF_INET, &inetInterface.second, addr, sizeof(addr));
+                        ChipLogProgress(Inet, "\t\t* ipv4: %s", addr);
+                    }
+                }
+
+                for (auto & inet6Interface : inet6Interfaces) {
+                    if (interfaceId == inet6Interface.first) {
+                        char addr[INET6_ADDRSTRLEN] = {};
+                        inet_ntop(AF_INET6, &inet6Interface.second, addr, sizeof(addr));
+                        ChipLogProgress(Inet, "\t\t* ipv6: %s", addr);
+                    }
+                }
+            }
+
+            void LogDetails(nw_path_t path)
+            {
+                auto status = nw_path_get_status(path);
+                ChipLogProgress(Inet, "Status: %s", GetPathStatusString(status));
+            }
+
+            void LogDetails(InetInterfacesVector inetInterfaces, Inet6InterfacesVector inet6Interfaces)
+            {
+                std::set<uint32_t> interfaceIds;
+                for (auto & inetInterface : inetInterfaces) {
+                    interfaceIds.insert(inetInterface.first);
+                }
+
+                for (auto & inet6Interface : inet6Interfaces) {
+                    interfaceIds.insert(inet6Interface.first);
+                }
+
+                for (auto interfaceId : interfaceIds) {
+                    char interfaceName[Inet::InterfaceId::kMaxIfNameLength] = {};
+                    if_indextoname(interfaceId, interfaceName);
+                    ChipLogProgress(Inet, "\t%s (%u)", interfaceName, interfaceId);
+                    LogDetails(interfaceId, inetInterfaces, inet6Interfaces);
+                }
+            }
+
+            void LogDetails(nw_interface_t interface, InetInterfacesVector inetInterfaces, Inet6InterfacesVector inet6Interfaces)
+            {
+                auto interfaceId = nw_interface_get_index(interface);
+                auto interfaceName = nw_interface_get_name(interface);
+                auto interfaceType = nw_interface_get_type(interface);
+                ChipLogProgress(Inet, "\t%s (%u / %s)", interfaceName, interfaceId, GetInterfaceTypeString(interfaceType));
+                LogDetails(interfaceId, inetInterfaces, inet6Interfaces);
+            }
+#endif // CHIP_PROGRESS_LOGGING
+
+            bool HasValidFlags(unsigned int flags, bool allowLoopbackOnly)
+            {
+                VerifyOrReturnValue(!allowLoopbackOnly || (flags & IFF_LOOPBACK), false);
+                VerifyOrReturnValue((flags & IFF_RUNNING), false);
+                VerifyOrReturnValue((flags & IFF_MULTICAST), false);
+                return true;
+            }
+
+            bool HasValidNetworkType(nw_interface_t interface)
+            {
+                auto interfaceType = nw_interface_get_type(interface);
+                return interfaceType == nw_interface_type_wifi || interfaceType == nw_interface_type_wired || interfaceType == nw_interface_type_other;
+            }
+
+            bool IsValidInterfaceId(uint32_t targetInterfaceId, nw_interface_t interface)
+            {
+                auto currentInterfaceId = nw_interface_get_index(interface);
+                return targetInterfaceId == kDNSServiceInterfaceIndexAny || targetInterfaceId == currentInterfaceId;
+            }
+
+            void ShouldUseVersion(chip::Inet::IPAddressType addressType, bool & shouldUseIPv4, bool & shouldUseIPv6)
+            {
+#if INET_CONFIG_ENABLE_IPV4
+                shouldUseIPv4 = addressType == Inet::IPAddressType::kIPv4 || addressType == Inet::IPAddressType::kAny;
+#else
+                shouldUseIPv4 = false;
+#endif // INET_CONFIG_ENABLE_IPV4
+                shouldUseIPv6 = addressType == Inet::IPAddressType::kIPv6 || addressType == Inet::IPAddressType::kAny;
+            }
+
+            void GetInterfaceAddresses(uint32_t interfaceId, chip::Inet::IPAddressType addressType, InetInterfacesVector & inetInterfaces,
+                Inet6InterfacesVector & inet6Interfaces, bool searchLoopbackOnly = false)
+            {
+                bool shouldUseIPv4, shouldUseIPv6;
+                ShouldUseVersion(addressType, shouldUseIPv4, shouldUseIPv6);
+
+                ifaddrs * ifap;
+                VerifyOrReturn(getifaddrs(&ifap) >= 0);
+
+                for (struct ifaddrs * ifa = ifap; ifa != nullptr; ifa = ifa->ifa_next) {
+                    auto interfaceAddress = ifa->ifa_addr;
+                    if (interfaceAddress == nullptr) {
+                        continue;
+                    }
+
+                    if (!HasValidFlags(ifa->ifa_flags, searchLoopbackOnly)) {
+                        continue;
+                    }
+
+                    auto currentInterfaceId = if_nametoindex(ifa->ifa_name);
+                    if (interfaceId != kDNSServiceInterfaceIndexAny && interfaceId != currentInterfaceId) {
+                        continue;
+                    }
+
+                    if (shouldUseIPv4 && (AF_INET == interfaceAddress->sa_family)) {
+                        auto inetAddress = reinterpret_cast<struct sockaddr_in *>(interfaceAddress)->sin_addr;
+                        inetInterfaces.push_back(Inet::Darwin::InetInterface(currentInterfaceId, inetAddress));
+                    } else if (shouldUseIPv6 && (AF_INET6 == interfaceAddress->sa_family)) {
+                        auto inet6Address = reinterpret_cast<struct sockaddr_in6 *>(interfaceAddress)->sin6_addr;
+                        inet6Interfaces.push_back(Inet::Darwin::Inet6Interface(currentInterfaceId, inet6Address));
+                    }
+                }
+
+                freeifaddrs(ifap);
+            }
+        } // namespace
+
+        InterfacesMonitor::~InterfacesMonitor()
+        {
+            if (mLivenessTracker != nullptr) {
+                *mLivenessTracker = false;
+            }
+        }
+
+        CHIP_ERROR InterfacesMonitor::Init(dispatch_queue_t workQueue, Inet::IPAddressType addressType, uint32_t interfaceId)
+        {
+            mInterfaceId = interfaceId;
+            mAddressType = addressType;
+            mWorkQueue = workQueue;
+            mInterfaceMonitor = nullptr;
+
+            mLivenessTracker = std::make_shared<bool>(true);
+            if (mLivenessTracker == nullptr) {
+                return CHIP_ERROR_NO_MEMORY;
+            }
+
+            return CHIP_NO_ERROR;
+        }
+
+        CHIP_ERROR InterfacesMonitor::StartMonitorInterfaces(OnInterfaceChanges interfaceChangesBlock)
+        {
+            mInterfaceMonitor = nw_path_monitor_create();
+            VerifyOrReturnError(mInterfaceMonitor != nullptr, CHIP_ERROR_NO_MEMORY);
+
+            nw_path_monitor_set_queue(mInterfaceMonitor, mWorkQueue);
+
+            // Our update handler closes over "this", but can't keep us alive (because we
+            // are not refcounted).  Make sure it closes over a shared ref to our
+            // liveness tracker, which it _can_ keep alive, so it can bail out if we
+            // have been destroyed between when the task was queued and when it ran.
+            std::shared_ptr<bool> livenessTracker = mLivenessTracker;
+            nw_path_monitor_set_update_handler(mInterfaceMonitor, ^(nw_path_t path) {
+                if (!*livenessTracker) {
+                    // The InterfacesMonitor has been destroyed; just bail out.
+                    return;
+                }
+
+#if CHIP_PROGRESS_LOGGING
+                LogDetails(path);
+#endif // CHIP_PROGRESS_LOGGING
+
+                __block InetInterfacesVector inet;
+                __block Inet6InterfacesVector inet6;
+
+                // The loopback interfaces needs to be manually added. While lo0 is usually 1, this is not guaranteed. So search for a
+                // loopback interface with the specified interface id. If the specified interface id is kDNSServiceInterfaceIndexAny, it
+                // will look for all available loopback interfaces.
+                GetInterfaceAddresses(mInterfaceId, mAddressType, inet, inet6, true /* searchLoopbackOnly */);
+#if CHIP_PROGRESS_LOGGING
+                LogDetails(inet, inet6);
+#endif // CHIP_PROGRESS_LOGGING
+
+                auto status = nw_path_get_status(path);
+                if (status == nw_path_status_satisfied) {
+                    nw_path_enumerate_interfaces(path, ^(nw_interface_t interface) {
+                        VerifyOrReturnValue(HasValidNetworkType(interface), true);
+                        VerifyOrReturnValue(IsValidInterfaceId(mInterfaceId, interface), true);
+
+                        auto targetInterfaceId = nw_interface_get_index(interface);
+                        GetInterfaceAddresses(targetInterfaceId, mAddressType, inet, inet6);
+#if CHIP_PROGRESS_LOGGING
+                        LogDetails(interface, inet, inet6);
+#endif // CHIP_PROGRESS_LOGGING
+                        return true;
+                    });
+                }
+
+                interfaceChangesBlock(inet, inet6);
+            });
+
+            nw_path_monitor_start(mInterfaceMonitor);
+
+            return CHIP_NO_ERROR;
+        }
+
+        void InterfacesMonitor::StopMonitorInterfaces()
+        {
+            if (mInterfaceMonitor != nullptr) {
+                nw_path_monitor_cancel(mInterfaceMonitor);
+                mInterfaceMonitor = nullptr;
+            }
+        }
+
+    } // namespace Darwin
+} // namespace Inet
+} // namespace chip