diff --git a/EduVPN-redesign/Controllers/PreferencesViewController.swift b/EduVPN-redesign/Controllers/PreferencesViewController.swift index 09ebeb21..a551c9ee 100644 --- a/EduVPN-redesign/Controllers/PreferencesViewController.swift +++ b/EduVPN-redesign/Controllers/PreferencesViewController.swift @@ -32,6 +32,7 @@ class PreferencesViewController: ViewController, ParametrizedViewController { @IBOutlet weak var showInStatusBarCheckbox: NSButton! @IBOutlet weak var showInDockCheckbox: NSButton! @IBOutlet weak var launchAtLoginCheckbox: NSButton! + @IBOutlet weak var assistPathUpgradingCheckbox: NSButton! func initializeParameters(_ parameters: Parameters) { guard self.parameters == nil else { @@ -46,11 +47,13 @@ class PreferencesViewController: ViewController, ParametrizedViewController { let isShowInStatusBarEnabled = userDefaults.showInStatusBar let isShowInDockEnabled = userDefaults.showInDock let isLaunchAtLoginEnabled = userDefaults.launchAtLogin + let isAssistPathUpgradingEnabled = userDefaults.assistPathUpgradingWithPathMonitor useTCPOnlyCheckbox.state = isForceTCPEnabled ? .on : .off showInStatusBarCheckbox.state = isShowInStatusBarEnabled ? .on : .off showInDockCheckbox.state = isShowInDockEnabled ? .on : .off launchAtLoginCheckbox.state = isLaunchAtLoginEnabled ? .on : .off + assistPathUpgradingCheckbox.state = isAssistPathUpgradingEnabled ? .on : .off // If one of "Show in status bar" or "Show in Dock" is off, // disable editing the other @@ -67,6 +70,11 @@ class PreferencesViewController: ViewController, ParametrizedViewController { UserDefaults.standard.forceTCP = isUseTCPOnlyChecked } + @IBAction func assistPathUpgradingCheckboxClicked(_ sender: Any) { + let isChecked = (assistPathUpgradingCheckbox.state == .on) + UserDefaults.standard.assistPathUpgradingWithPathMonitor = isChecked + } + @IBAction func viewLogClicked(_ sender: Any) { let connectionService = parameters.environment.connectionService guard connectionService.isInitialized else { return } diff --git a/EduVPN-redesign/Helpers/UserDefaults+Preferences.swift b/EduVPN-redesign/Helpers/UserDefaults+Preferences.swift index d04f05ea..0363f073 100644 --- a/EduVPN-redesign/Helpers/UserDefaults+Preferences.swift +++ b/EduVPN-redesign/Helpers/UserDefaults+Preferences.swift @@ -12,6 +12,8 @@ extension UserDefaults { private static let showInDockKey = "showInDock" private static let launchAtLoginKey = "launchAtLogin" + private static let assistPathUpgradingWithPathMonitorKey = "assistPathUpgradingWithPathMonitor" + var forceTCP: Bool { get { // swiftlint:disable:this implicit_getter return bool(forKey: Self.forceTCPDefaultsKey) @@ -53,4 +55,13 @@ extension UserDefaults { set(newValue, forKey: Self.launchAtLoginKey) } } + + var assistPathUpgradingWithPathMonitor: Bool { + get { // swiftlint:disable:this implicit_getter + return bool(forKey: Self.assistPathUpgradingWithPathMonitorKey) + } + set { + set(newValue, forKey: Self.assistPathUpgradingWithPathMonitorKey) + } + } } diff --git a/EduVPN-redesign/Resources/Mac/Base.lproj/Main.storyboard b/EduVPN-redesign/Resources/Mac/Base.lproj/Main.storyboard index f5d93c85..39c95e8c 100644 --- a/EduVPN-redesign/Resources/Mac/Base.lproj/Main.storyboard +++ b/EduVPN-redesign/Resources/Mac/Base.lproj/Main.storyboard @@ -529,13 +529,13 @@ Gw - + - + - + @@ -764,11 +764,11 @@ Gw - + - + @@ -776,7 +776,7 @@ Gw - + @@ -790,12 +790,12 @@ Gw - + - + - + @@ -1476,10 +1476,10 @@ Gw - + - + @@ -1487,10 +1487,10 @@ Gw - + - + @@ -1508,10 +1508,10 @@ Gw - + - + @@ -1542,10 +1542,10 @@ Gw - + - + - + + @@ -1618,6 +1628,7 @@ Gw + @@ -1633,6 +1644,7 @@ Gw + @@ -1644,6 +1656,7 @@ Gw + diff --git a/EduVPN-redesign/Services/ConnectionService.swift b/EduVPN-redesign/Services/ConnectionService.swift index 1d54e483..41b50ec0 100644 --- a/EduVPN-redesign/Services/ConnectionService.swift +++ b/EduVPN-redesign/Services/ConnectionService.swift @@ -254,7 +254,11 @@ private extension ConnectionService { } return Promise { resolver in do { - try tunnelManager.session.startTunnel() + let options: [String: Any] = [ + "assistPathUpgradingWithPathMonitor": + UserDefaults.standard.assistPathUpgradingWithPathMonitor + ] + try tunnelManager.session.startTunnel(options: options) } catch { throw error } diff --git a/EduVPNTunnelExtension-macOS/PacketTunnelProvider.swift b/EduVPNTunnelExtension-macOS/PacketTunnelProvider.swift index ee6509ad..c7d735ea 100644 --- a/EduVPNTunnelExtension-macOS/PacketTunnelProvider.swift +++ b/EduVPNTunnelExtension-macOS/PacketTunnelProvider.swift @@ -6,4 +6,11 @@ import TunnelKit class PacketTunnelProvider: OpenVPNTunnelProvider { + override func startTunnel(options: [String: NSObject]? = nil, completionHandler: @escaping (Error?) -> Void) { + let assistPathUpgradingWithPathMonitor = (options?["assistPathUpgradingWithPathMonitor"] as? NSNumber)?.boolValue ?? false + if assistPathUpgradingWithPathMonitor { + setPathMonitorUsageMode(.assistPathUpgrading) + } + super.startTunnel(options: options, completionHandler: completionHandler) + } } diff --git a/Pods/TunnelKit/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider.swift b/Pods/TunnelKit/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider.swift index 0c46660e..1f740adc 100644 --- a/Pods/TunnelKit/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider.swift +++ b/Pods/TunnelKit/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider.swift @@ -130,6 +130,25 @@ open class OpenVPNTunnelProvider: NEPacketTunnelProvider { private var shouldReconnect = false + // MARK: NWPathMonitor usage + + private var pathMonitor: AnyObject? + + public enum PathMonitorUsageMode { + // Do not use path monitor. The default mode. + case none + + // Use path monitor to help in reacting to hasBetterPath becoming true + case assistPathUpgrading + } + + private(set) var pathMonitorUsageMode: PathMonitorUsageMode = .none + + @available(OSXApplicationExtension 10.14, iOSApplicationExtension 12.0, *) + public func setPathMonitorUsageMode(_ mode: PathMonitorUsageMode) { + pathMonitorUsageMode = mode + } + // MARK: NEPacketTunnelProvider (XPC queue) open override var reasserting: Bool { @@ -503,10 +522,32 @@ extension OpenVPNTunnelProvider: GenericSocketDelegate { /// :nodoc: public func socketHasBetterPath(_ socket: GenericSocket) { + if #available(OSXApplicationExtension 10.14, iOSApplicationExtension 12.0, *) { + if let pathMonitor = self.pathMonitor as? NWPathMonitor { + log.debug("Socket has better path. Path status: \(pathMonitor.currentPath.status). Interfaces: \(pathMonitor.currentPath.availableInterfaces)") + if self.pathMonitorUsageMode == .assistPathUpgrading && !pathMonitor.currentPath.isValid { + log.debug("Ignoring spurious better path call") + return + } + } + } log.debug("Stopping tunnel due to a new better path") logCurrentSSID() session?.reconnect(error: ProviderError.networkChanged) } + +} + +@available(OSXApplicationExtension 10.14, iOSApplicationExtension 12.0, *) +extension Network.NWPath { + var isValid: Bool { + guard status == .satisfied else { return false } + guard let primaryInterface = availableInterfaces.first else { return false } + if primaryInterface.type == .other && primaryInterface.name.hasPrefix("u") { + return false + } + return true + } } extension OpenVPNTunnelProvider: OpenVPNSessionDelegate { @@ -570,6 +611,15 @@ extension OpenVPNTunnelProvider: OpenVPNSessionDelegate { session.setTunnel(tunnel: NETunnelInterface(impl: self.packetFlow)) + if #available(OSXApplicationExtension 10.14, iOSApplicationExtension 12.0, *) { + if self.pathMonitorUsageMode != .none { + log.debug("Setting up path monitor") + let pathMonitor = NWPathMonitor() + pathMonitor.start(queue: self.tunnelQueue) + self.pathMonitor = pathMonitor + } + } + self.pendingStartHandler?(nil) self.pendingStartHandler = nil }