diff options
author | Jeroen Leenarts <jeroen.leenarts@gmail.com> | 2018-08-04 21:28:19 +0200 |
---|---|---|
committer | Jeroen Leenarts <jeroen.leenarts@gmail.com> | 2018-08-04 21:28:19 +0200 |
commit | 2ae798462966beee384bf3916b458c864ffbc18a (patch) | |
tree | 80fc5ce4b5a9e41e1b30fdfb875d038ded10c17f /WireGuard | |
parent | Finish trampoline code. (diff) | |
download | wireguard-apple-2ae798462966beee384bf3916b458c864ffbc18a.tar.xz wireguard-apple-2ae798462966beee384bf3916b458c864ffbc18a.zip |
Add NETunnelProviderManager management to AppCoordinator.
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to 'WireGuard')
-rw-r--r-- | WireGuard/Coordinators/AppCoordinator.swift | 188 | ||||
-rw-r--r-- | WireGuard/Models/Tunnel+Extension.swift | 16 | ||||
-rw-r--r-- | WireGuard/ViewControllers/TunnelsTableViewController.swift | 2 |
3 files changed, 202 insertions, 4 deletions
diff --git a/WireGuard/Coordinators/AppCoordinator.swift b/WireGuard/Coordinators/AppCoordinator.swift index ce30ae6..2487dad 100644 --- a/WireGuard/Coordinators/AppCoordinator.swift +++ b/WireGuard/Coordinators/AppCoordinator.swift @@ -7,16 +7,23 @@ // import Foundation +import NetworkExtension +import KeychainSwift +import os.log import CoreData import BNRCoreDataStack extension UINavigationController: Identifyable {} +let APPGROUP = "group.com.wireguard.ios.WireGuard" +let VPNBUNDLE = "com.wireguard.ios.WireGuard.WireGuardNetworkExtension" + class AppCoordinator: RootViewCoordinator { let persistentContainer = NSPersistentContainer(name: "WireGuard") let storyboard = UIStoryboard(name: "Main", bundle: nil) + var currentManager: NETunnelProviderManager? // MARK: - Properties @@ -26,6 +33,22 @@ class AppCoordinator: RootViewCoordinator { return self.tunnelsTableViewController } + var status = NEVPNStatus.invalid { + didSet { + switch status { + case .connected: + //TODO: signal connected + os_log("Connected VPN", log: Log.general, type: .info) + case .connecting, .disconnecting, .reasserting: + //TODO: signal connecting + os_log("Connecting VPN", log: Log.general, type: .info) + case .disconnected, .invalid: + //TODO: disconnect / invalid + os_log("Disconnecting VPN", log: Log.general, type: .info) + } + } + } + var tunnelsTableViewController: TunnelsTableViewController! /// Window to manage @@ -42,6 +65,12 @@ class AppCoordinator: RootViewCoordinator { self.window.rootViewController = self.navigationController self.window.makeKeyAndVisible() + + NotificationCenter.default.addObserver(self, + selector: #selector(VPNStatusDidChange(notification:)), + name: .NEVPNStatusDidChange, + object: nil) + reloadCurrentManager(nil) } // MARK: - Functions @@ -74,6 +103,71 @@ class AppCoordinator: RootViewCoordinator { } } + // MARK: - NEVPNManager handling + + func configureVPN(_ configure: @escaping (NETunnelProviderManager) -> NETunnelProviderProtocol?, completionHandler: @escaping (Error?) -> Void) { + reloadCurrentManager { (error) in + if let error = error { + os_log("error reloading preferences: %{public}@", log: Log.general, type: .error, error.localizedDescription) + completionHandler(error) + return + } + + let manager = self.currentManager! + if let protocolConfiguration = configure(manager) { + manager.protocolConfiguration = protocolConfiguration + } + manager.isEnabled = true + + manager.saveToPreferences { (error) in + if let error = error { + os_log("error saving preferences: %{public}@", log: Log.general, type: .error, error.localizedDescription) + completionHandler(error) + return + } + os_log("saved preferences", log: Log.general, type: .info) + self.reloadCurrentManager(completionHandler) + } + } + } + + func reloadCurrentManager(_ completionHandler: ((Error?) -> Void)?) { + NETunnelProviderManager.loadAllFromPreferences { (managers, error) in + if let error = error { + completionHandler?(error) + return + } + + var manager: NETunnelProviderManager? + + for man in managers! { + if let prot = man.protocolConfiguration as? NETunnelProviderProtocol { + if prot.providerBundleIdentifier == VPNBUNDLE { + manager = man + break + } + } + } + + if manager == nil { + manager = NETunnelProviderManager() + } + + self.currentManager = manager + self.status = manager!.connection.status + completionHandler?(nil) + } + } + + @objc private func VPNStatusDidChange(notification: NSNotification) { + guard let status = currentManager?.connection.status else { + os_log("VPNStatusDidChange", log: Log.general, type: .debug) + return + } + os_log("VPNStatusDidChange: %{public}@", log: Log.general, type: .debug, description(for: status)) + self.status = status + } + public func showError(_ error: Error) { showAlert(title: NSLocalizedString("Error", comment: "Error alert title"), message: error.localizedDescription) } @@ -83,6 +177,23 @@ class AppCoordinator: RootViewCoordinator { alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "OK button"), style: .default)) self.navigationController.present(alert, animated: true) } + + private func description(for status: NEVPNStatus) -> String { + switch status { + case .connected: + return "Connected" + case .connecting: + return "Connecting" + case .disconnected: + return "Disconnected" + case .disconnecting: + return "Disconnecting" + case .invalid: + return "Invalid" + case .reasserting: + return "Reasserting" + } + } } extension AppCoordinator: TunnelsTableViewControllerDelegate { @@ -91,9 +202,80 @@ extension AppCoordinator: TunnelsTableViewControllerDelegate { showTunnelConfigurationViewController(tunnel: nil, context: addContext) } - func connect(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) { - // TODO implement - print("connect tunnel \(tunnel)") + func connect(tunnel: Tunnel?, tunnelsTableViewController: TunnelsTableViewController) { + let block = { + switch self.status { + case .invalid, .disconnected: + self.connect(tunnel: tunnel) + + case .connected, .connecting: + // TODO: this needs to check if the passed tunnel is the actual connected tunnel config + self.disconnect() + + default: + break + } + } + + if status == .invalid { + reloadCurrentManager({ (_) in + block() + }) + } else { + block() + } + } + + private func connect(tunnel: Tunnel?) { + // TODO implement NETunnelProviderManager VC showing current connection status, pushing this config into VPN stack + os_log("connect tunnel: %{public}@", log: Log.general, type: .info, tunnel?.description ?? "-none-") + + guard let tunnel = tunnel else { + return + } + + configureVPN({ (_) in + //TODO: decide what to do with on demand + // self.currentManager?.isOnDemandEnabled = true + self.currentManager?.onDemandRules = [NEOnDemandRuleConnect()] + + let protocolConfiguration = NETunnelProviderProtocol() + let keychain = KeychainSwift() + keychain.accessGroup = APPGROUP + //TODO: Set secrets to keychain? + + protocolConfiguration.providerBundleIdentifier = VPNBUNDLE + //TODO obtain endpoint hostname +// protocolConfiguration.serverAddress = endpoint.hostname + //TODO obtain endpoint username +// protocolConfiguration.username = endpoint.username + //TODO: how to obtain this? +// protocolConfiguration.passwordReference = try? keychain.passwordReference(for: endpoint.username) + protocolConfiguration.providerConfiguration = tunnel.generateProviderConfiguration() + + return protocolConfiguration + }, completionHandler: { (error) in + if let error = error { + os_log("configure error: %{public}@", log: Log.general, type: .error, error.localizedDescription) + return + } + let session = self.currentManager?.connection as! NETunnelProviderSession //swiftlint:disable:this force_cast + do { + try session.startTunnel() + } catch let error { + os_log("error starting tunnel: %{public}@", log: Log.general, type: .error, error.localizedDescription) + } + }) + } + + func disconnect() { + configureVPN({ (_) in + //TODO: decide what to do with on demand + // self.currentManager?.isOnDemandEnabled = false + return nil + }, completionHandler: { (_) in + self.currentManager?.connection.stopVPNTunnel() + }) } func configure(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) { diff --git a/WireGuard/Models/Tunnel+Extension.swift b/WireGuard/Models/Tunnel+Extension.swift new file mode 100644 index 0000000..38ccec6 --- /dev/null +++ b/WireGuard/Models/Tunnel+Extension.swift @@ -0,0 +1,16 @@ +// +// Tunnel+Extension.swift +// WireGuard +// +// Created by Jeroen Leenarts on 04-08-18. +// Copyright © 2018 WireGuard. All rights reserved. +// + +import Foundation + +extension Tunnel { + public func generateProviderConfiguration() -> [String: Any] { + //TODO: generate ProviderConfiguration from tunnel with WireGuard config. + return [:] + } +} diff --git a/WireGuard/ViewControllers/TunnelsTableViewController.swift b/WireGuard/ViewControllers/TunnelsTableViewController.swift index 06729c4..b56cffe 100644 --- a/WireGuard/ViewControllers/TunnelsTableViewController.swift +++ b/WireGuard/ViewControllers/TunnelsTableViewController.swift @@ -13,7 +13,7 @@ import BNRCoreDataStack protocol TunnelsTableViewControllerDelegate: class { func addProvider(tunnelsTableViewController: TunnelsTableViewController) - func connect(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) + func connect(tunnel: Tunnel?, tunnelsTableViewController: TunnelsTableViewController) func configure(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) func delete(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) } |