aboutsummaryrefslogtreecommitdiffstats
path: root/WireGuard
diff options
context:
space:
mode:
authorJeroen Leenarts <jeroen.leenarts@gmail.com>2018-08-04 21:28:19 +0200
committerJeroen Leenarts <jeroen.leenarts@gmail.com>2018-08-04 21:28:19 +0200
commit2ae798462966beee384bf3916b458c864ffbc18a (patch)
tree80fc5ce4b5a9e41e1b30fdfb875d038ded10c17f /WireGuard
parentFinish trampoline code. (diff)
downloadwireguard-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.swift188
-rw-r--r--WireGuard/Models/Tunnel+Extension.swift16
-rw-r--r--WireGuard/ViewControllers/TunnelsTableViewController.swift2
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)
}