From 78f6800dbd061b2e4ca6550bc2a447a80ada8aee Mon Sep 17 00:00:00 2001 From: Jamil Date: Mon, 27 Jan 2025 19:28:57 -0800 Subject: [PATCH] fix(apple/macOS): Memoize successful SCDynamicStoreCreate (#7892) Under some conditions the call to SCDynamicStoreCreate can fail, presumably due to some kind of allocation failure. Once it succeeds, however, we can continue using the dynamic store for the lifetime of the Adapter instance. So we memoize the result of this call so as not to allocate each time we need it. See https://developer.apple.com/documentation/systemconfiguration/1437828-scdynamicstorecreate --- .../FirezoneNetworkExtension/Adapter.swift | 7 +++- .../SystemConfigurationResolvers.swift | 33 +++++++++++-------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/swift/apple/FirezoneNetworkExtension/Adapter.swift b/swift/apple/FirezoneNetworkExtension/Adapter.swift index 0d64c4fb2..27f051f09 100644 --- a/swift/apple/FirezoneNetworkExtension/Adapter.swift +++ b/swift/apple/FirezoneNetworkExtension/Adapter.swift @@ -58,6 +58,11 @@ class Adapter { private var gateways: [Network.NWEndpoint] = [] #endif +#if os(macOS) + /// Used for finding system DNS resolvers on macOS when network conditions have changed. + private let systemConfigurationResolvers = SystemConfigurationResolvers() +#endif + /// Track our last fetched DNS resolvers to know whether to tell connlib they've updated private var lastFetchedResolvers: [String] = [] @@ -453,7 +458,7 @@ extension Adapter: CallbackHandlerDelegate { private func getSystemDefaultResolvers(interfaceName: String?) -> [String] { #if os(macOS) - let resolvers = SystemConfigurationResolvers().getDefaultDNSServers( + let resolvers = self.systemConfigurationResolvers.getDefaultDNSServers( interfaceName: interfaceName) #elseif os(iOS) let resolvers = resetToSystemDNSGettingBindResolvers() diff --git a/swift/apple/FirezoneNetworkExtension/SystemConfigurationResolvers.swift b/swift/apple/FirezoneNetworkExtension/SystemConfigurationResolvers.swift index c94a49188..b10acfc0e 100644 --- a/swift/apple/FirezoneNetworkExtension/SystemConfigurationResolvers.swift +++ b/swift/apple/FirezoneNetworkExtension/SystemConfigurationResolvers.swift @@ -26,23 +26,30 @@ class SystemConfigurationResolvers { } } } - private var dynamicStore: SCDynamicStore? + + /// We use a computed property to memoize the creation of SC Dynamic Store, since this + /// can fail in some circumstances to initialize, like because of allocation failures. + private var _dynamicStore: SCDynamicStore? + private var dynamicStore: SCDynamicStore? { + get { + if self._dynamicStore == nil { + guard let dynamicStore = SCDynamicStoreCreate(nil, storeName, nil, nil) + else { + let code = SCError() + Log.error(SystemConfigurationError.failedToCreateDynamicStore(code: code)) + return nil + } + + self._dynamicStore = dynamicStore + } + + return self._dynamicStore + } + } // Arbitrary name for the connection to the store private let storeName = "dev.firezone.firezone.dns" as CFString - init() { - guard let dynamicStore = SCDynamicStoreCreate(nil, storeName, nil, nil) - else { - let code = SCError() - Log.error(SystemConfigurationError.failedToCreateDynamicStore(code: code)) - self.dynamicStore = nil - return - } - - self.dynamicStore = dynamicStore - } - /// 1. First, find the service ID that corresponds to the interface we're interested in. /// We do this by searching the configuration store at "Setup:/Network/Service//Interface" /// for a matching "InterfaceName".