From 14351ec405e19cd0f863f5bce0fb744dee1d52c8 Mon Sep 17 00:00:00 2001 From: Jamil Date: Sat, 23 Dec 2023 09:17:27 -0800 Subject: [PATCH] Temporarily set matchDomains when tunnel is reasserting (#3012) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I realized we had the right approach in the comments on PR #2967, but had a bug in the implementation. This implements that approach. Tested to work under the following scenarios: - macOS with Ethernet and WiFi connected - Disable Ethernet adapter -> ✅ - Disable WiFi (no internet) -> Enable WiFi -> ✅ - Enable Ethernet adapter -> Disable WiFi -> ✅ - Disable both Ethernet and WiFi -> Enable WiFi -> ✅ - Switching WiFi networks -> ✅ - iOS with WiFi and cellular connected - Disable WiFi -> ✅ - Disable cellular -> ✅ - Disable both WiFi and cellular -> Enable WiFi -> ✅ - Switching WiFi networks -> ✅ Under all of the tests above, Split DNS with connlib worked reliably after the `onTunnelReady` callback completed. --- .../apple/Firezone.xcodeproj/project.pbxproj | 6 ------ .../FirezoneNetworkExtension/Adapter.swift | 18 +++++++++++++++++- .../DNSResolvers.swift | 19 ------------------- .../NetworkSettings.swift | 13 +++++++++++-- .../FirezoneNetworkExtension/Resolv.swift | 1 + 5 files changed, 29 insertions(+), 28 deletions(-) delete mode 100644 swift/apple/FirezoneNetworkExtension/DNSResolvers.swift diff --git a/swift/apple/Firezone.xcodeproj/project.pbxproj b/swift/apple/Firezone.xcodeproj/project.pbxproj index 4d80ab590..f37a2d771 100644 --- a/swift/apple/Firezone.xcodeproj/project.pbxproj +++ b/swift/apple/Firezone.xcodeproj/project.pbxproj @@ -35,8 +35,6 @@ 8D2F64EF2A973F7000B6176A /* PrimaryMacAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D2F64EC2A97336C00B6176A /* PrimaryMacAddress.swift */; }; 8DC08BCB2B296C4500675F46 /* libresolv.9.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC08BCA2B296C4500675F46 /* libresolv.9.tbd */; }; 8DC08BCD2B296C5900675F46 /* libresolv.9.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC08BCC2B296C5900675F46 /* libresolv.9.tbd */; }; - 8DC08BCF2B29791E00675F46 /* DNSResolvers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DC08BCE2B29791E00675F46 /* DNSResolvers.swift */; }; - 8DC08BD02B29791E00675F46 /* DNSResolvers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DC08BCE2B29791E00675F46 /* DNSResolvers.swift */; }; 8DC08BD22B297B7B00675F46 /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC08BD12B297B7B00675F46 /* libresolv.tbd */; }; 8DC08BD42B297B8200675F46 /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC08BD32B297B8200675F46 /* libresolv.tbd */; }; 8DC08BD62B297DA400675F46 /* FirezoneNetworkExtension-Bridging-Header.h in Sources */ = {isa = PBXBuildFile; fileRef = 6FE455112A5D13A2006549B1 /* FirezoneNetworkExtension-Bridging-Header.h */; }; @@ -123,7 +121,6 @@ 8D2F64EC2A97336C00B6176A /* PrimaryMacAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimaryMacAddress.swift; sourceTree = ""; }; 8DC08BCA2B296C4500675F46 /* libresolv.9.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.9.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.2.sdk/usr/lib/libresolv.9.tbd; sourceTree = DEVELOPER_DIR; }; 8DC08BCC2B296C5900675F46 /* libresolv.9.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.9.tbd; path = usr/lib/libresolv.9.tbd; sourceTree = SDKROOT; }; - 8DC08BCE2B29791E00675F46 /* DNSResolvers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSResolvers.swift; sourceTree = ""; }; 8DC08BD12B297B7B00675F46 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; }; 8DC08BD32B297B8200675F46 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.2.sdk/usr/lib/libresolv.tbd; sourceTree = DEVELOPER_DIR; }; 8DCC021928D512AC007E12D2 /* Firezone.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Firezone.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -193,7 +190,6 @@ 6FE4550E2A5D112C006549B1 /* connlib-client-apple.swift */, 8D2F64EC2A97336C00B6176A /* PrimaryMacAddress.swift */, 6FE455112A5D13A2006549B1 /* FirezoneNetworkExtension-Bridging-Header.h */, - 8DC08BCE2B29791E00675F46 /* DNSResolvers.swift */, 8D28EB982B35FBD70083621C /* Resolv.swift */, ); path = FirezoneNetworkExtension; @@ -512,7 +508,6 @@ 6FA39A042A6A7248000F0157 /* NetworkResource.swift in Sources */, 6FE4550C2A5D111E006549B1 /* SwiftBridgeCore.swift in Sources */, 6FE93AFB2A738D7E002D278A /* NetworkSettings.swift in Sources */, - 8DC08BCF2B29791E00675F46 /* DNSResolvers.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -521,7 +516,6 @@ buildActionMask = 2147483647; files = ( 8DC08BD62B297DA400675F46 /* FirezoneNetworkExtension-Bridging-Header.h in Sources */, - 8DC08BD02B29791E00675F46 /* DNSResolvers.swift in Sources */, 8D2F64EF2A973F7000B6176A /* PrimaryMacAddress.swift in Sources */, 6FE4550A2A5D110D006549B1 /* CallbackHandler.swift in Sources */, 8D28EB9A2B35FBD70083621C /* Resolv.swift in Sources */, diff --git a/swift/apple/FirezoneNetworkExtension/Adapter.swift b/swift/apple/FirezoneNetworkExtension/Adapter.swift index 70dd5943b..2e932dc3e 100644 --- a/swift/apple/FirezoneNetworkExtension/Adapter.swift +++ b/swift/apple/FirezoneNetworkExtension/Adapter.swift @@ -250,6 +250,18 @@ extension Adapter { // MARK: Responding to path updates extension Adapter { + private func resetToSystemDNS() { + // Setting this to anything but an empty string will populate /etc/resolv.conf with + // the default interface's DNS servers, which we read later from connlib + // during tunnel setup. + self.networkSettings?.setMatchDomains(["firezone-fd0020211111"]) + self.networkSettings?.apply( + on: self.packetTunnelProvider, + logger: self.logger, + completionHandler: nil + ) + } + private func beginPathMonitoring() { self.logger.log("Beginning path monitoring") let networkMonitor = NWPathMonitor() @@ -267,6 +279,7 @@ extension Adapter { if path.status != .satisfied { self.logger.log("Adapter.didReceivePathUpdate: Offline. Shutting down connlib.") onStarted?(nil) + resetToSystemDNS() self.packetTunnelProvider?.reasserting = true self.state = .stoppingTunnelTemporarily(session: session, onStopped: nil) session.disconnect() @@ -275,6 +288,7 @@ extension Adapter { case .tunnelReady(let session): if path.status != .satisfied { self.logger.log("Adapter.didReceivePathUpdate: Offline. Shutting down connlib.") + resetToSystemDNS() self.packetTunnelProvider?.reasserting = true self.state = .stoppingTunnelTemporarily(session: session, onStopped: nil) session.disconnect() @@ -367,6 +381,8 @@ extension Adapter: CallbackHandlerDelegate { self.logger.error("Adapter.onTunnelReady: No packet tunnel provider") return } + // Connlib's up, set it as the default DNS + networkSettings.setMatchDomains([""]) networkSettings.apply(on: packetTunnelProvider, logger: self.logger) { error in if let error = error { packetTunnelProvider.handleTunnelShutdown( @@ -503,7 +519,7 @@ extension Adapter: CallbackHandlerDelegate { } public func getSystemDefaultResolvers() -> [String] { - let resolvers = DNSResolvers.getDNSResolvers() + let resolvers = Resolv().getservers().map(Resolv.getnameinfo) self.logger.info("getSystemDefaultResolvers: \(resolvers)") return resolvers } diff --git a/swift/apple/FirezoneNetworkExtension/DNSResolvers.swift b/swift/apple/FirezoneNetworkExtension/DNSResolvers.swift deleted file mode 100644 index 912e1d3cd..000000000 --- a/swift/apple/FirezoneNetworkExtension/DNSResolvers.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// DNSResolvers.swift -// Firezone -// -// Created by Jamil Bou Kheir on 12/12/23. -// -// Returns the system's DNS Resolvers on macOS - -import Foundation - -public class DNSResolvers { - // FIXME: We need to find a method of finding the system's default resolvers that - // works on both macOS and iOS. See https://github.com/firezone/firezone/issues/2939 - private static let resolvers = ["1.1.1.1", "1.0.0.1"] - - public static func getDNSResolvers() -> [String] { - return resolvers - } -} diff --git a/swift/apple/FirezoneNetworkExtension/NetworkSettings.swift b/swift/apple/FirezoneNetworkExtension/NetworkSettings.swift index fc9f1a9f2..b3ac3323c 100644 --- a/swift/apple/FirezoneNetworkExtension/NetworkSettings.swift +++ b/swift/apple/FirezoneNetworkExtension/NetworkSettings.swift @@ -22,6 +22,7 @@ class NetworkSettings { // Modifiable values private(set) var routes: [String] = [] private(set) var resourceDomains: [String] = [] + private(set) var matchDomains: [String] = [""] // To keep track of modifications private(set) var hasUnappliedChanges: Bool @@ -57,10 +58,17 @@ class NetworkSettings { self.hasUnappliedChanges = true } + func setMatchDomains(_ matchDomains: [String]) { + self.matchDomains = matchDomains + self.hasUnappliedChanges = true + } + func apply( - on packetTunnelProvider: NEPacketTunnelProvider, logger: Logger, + on packetTunnelProvider: NEPacketTunnelProvider?, + logger: Logger, completionHandler: ((Error?) -> Void)? ) { + guard let packetTunnelProvider = packetTunnelProvider else { return } guard self.hasUnappliedChanges else { logger.error("NetworkSettings.apply: No changes to apply") @@ -141,7 +149,8 @@ class NetworkSettings { let dnsSettings = NEDNSSettings(servers: [dnsAddress]) // Intercept all DNS queries; SplitDNS will be handled by connlib - dnsSettings.matchDomains = [""] + dnsSettings.matchDomains = matchDomains + dnsSettings.matchDomainsNoSearch = true tunnelNetworkSettings.dnsSettings = dnsSettings tunnelNetworkSettings.mtu = mtu diff --git a/swift/apple/FirezoneNetworkExtension/Resolv.swift b/swift/apple/FirezoneNetworkExtension/Resolv.swift index a71db95a3..4d50c991e 100644 --- a/swift/apple/FirezoneNetworkExtension/Resolv.swift +++ b/swift/apple/FirezoneNetworkExtension/Resolv.swift @@ -4,6 +4,7 @@ // // Created by Jamil Bou Kheir on 12/22/23. // +// Reads system resolvers from libresolv, similar to reading /etc/resolv.conf but this also works on iOS public class Resolv { var state = __res_9_state()