Temporarily set matchDomains when tunnel is reasserting (#3012)

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.
This commit is contained in:
Jamil
2023-12-23 09:17:27 -08:00
committed by GitHub
parent ffe0c79003
commit 14351ec405
5 changed files with 29 additions and 28 deletions

View File

@@ -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 = "<group>"; };
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 = "<group>"; };
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 */,

View File

@@ -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
}

View File

@@ -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
}
}

View File

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

View File

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