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
This commit is contained in:
Jamil
2025-01-27 19:28:57 -08:00
committed by GitHub
parent 3daac8730f
commit 78f6800dbd
2 changed files with 26 additions and 14 deletions

View File

@@ -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()

View File

@@ -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/<service-id>/Interface"
/// for a matching "InterfaceName".