2018-10-24 01:37:28 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2018-10-30 02:57:35 +00:00
|
|
|
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
|
2018-10-15 08:05:24 +00:00
|
|
|
|
|
|
|
import Foundation
|
2018-10-25 10:20:27 +00:00
|
|
|
import NetworkExtension
|
|
|
|
import os.log
|
2018-10-15 08:05:24 +00:00
|
|
|
|
2018-12-03 13:21:51 +00:00
|
|
|
protocol TunnelsManagerListDelegate: class {
|
2018-12-12 18:28:27 +00:00
|
|
|
func tunnelAdded(at index: Int)
|
|
|
|
func tunnelModified(at index: Int)
|
|
|
|
func tunnelMoved(from oldIndex: Int, to newIndex: Int)
|
|
|
|
func tunnelRemoved(at index: Int)
|
2018-10-23 12:11:37 +00:00
|
|
|
}
|
|
|
|
|
2018-12-03 13:21:51 +00:00
|
|
|
protocol TunnelsManagerActivationDelegate: class {
|
2018-12-13 13:25:20 +00:00
|
|
|
func tunnelActivationAttemptFailed(tunnel: TunnelContainer, error: TunnelsManagerActivationAttemptError) // startTunnel wasn't called or failed
|
|
|
|
func tunnelActivationAttemptSucceeded(tunnel: TunnelContainer) // startTunnel succeeded
|
|
|
|
func tunnelActivationFailed(tunnel: TunnelContainer, error: TunnelsManagerActivationError) // status didn't change to connected
|
|
|
|
func tunnelActivationSucceeded(tunnel: TunnelContainer) // status changed to connected
|
|
|
|
}
|
|
|
|
|
|
|
|
enum TunnelsManagerActivationAttemptError: WireGuardAppError {
|
|
|
|
case tunnelIsNotInactive
|
|
|
|
case anotherTunnelIsOperational(otherTunnelName: String)
|
|
|
|
case failedWhileStarting // startTunnel() throwed
|
|
|
|
case failedWhileSaving // save config after re-enabling throwed
|
|
|
|
case failedWhileLoading // reloading config throwed
|
|
|
|
case failedBecauseOfTooManyErrors // recursion limit reached
|
|
|
|
|
|
|
|
func alertText() -> AlertText {
|
|
|
|
switch self {
|
|
|
|
case .tunnelIsNotInactive:
|
|
|
|
return ("Activation failure", "The tunnel is already active or in the process of being activated")
|
|
|
|
case .anotherTunnelIsOperational(let otherTunnelName):
|
|
|
|
return ("Activation failure", "Please disconnect '\(otherTunnelName)' before enabling this tunnel.")
|
|
|
|
case .failedWhileStarting, .failedWhileSaving, .failedWhileLoading, .failedBecauseOfTooManyErrors:
|
2018-12-13 18:23:17 +00:00
|
|
|
return ("Activation failure", "The tunnel could not be activated.")
|
2018-12-13 13:25:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
enum TunnelsManagerActivationError: WireGuardAppError {
|
|
|
|
case activationFailed
|
|
|
|
func alertText() -> AlertText {
|
2018-12-13 18:17:12 +00:00
|
|
|
return ("Activation failure", "The tunnel could not be activated. Please ensure you are connected to the Internet.")
|
2018-12-13 13:25:20 +00:00
|
|
|
}
|
2018-12-03 10:04:58 +00:00
|
|
|
}
|
|
|
|
|
2018-12-06 10:43:48 +00:00
|
|
|
enum TunnelsManagerError: WireGuardAppError {
|
|
|
|
// Tunnels list management
|
2018-12-06 10:28:27 +00:00
|
|
|
case tunnelNameEmpty
|
|
|
|
case tunnelAlreadyExistsWithThatName
|
2018-12-13 06:50:10 +00:00
|
|
|
case systemErrorOnListingTunnels
|
|
|
|
case systemErrorOnAddTunnel
|
|
|
|
case systemErrorOnModifyTunnel
|
|
|
|
case systemErrorOnRemoveTunnel
|
2018-12-06 10:28:27 +00:00
|
|
|
|
2018-12-13 03:09:52 +00:00
|
|
|
func alertText() -> AlertText {
|
2018-12-12 18:28:27 +00:00
|
|
|
switch self {
|
2018-12-06 10:43:48 +00:00
|
|
|
case .tunnelNameEmpty:
|
|
|
|
return ("No name provided", "Can't create tunnel with an empty name")
|
|
|
|
case .tunnelAlreadyExistsWithThatName:
|
|
|
|
return ("Name already exists", "A tunnel with that name already exists")
|
2018-12-13 06:50:10 +00:00
|
|
|
case .systemErrorOnListingTunnels:
|
2018-12-13 18:23:17 +00:00
|
|
|
return ("Unable to list tunnels", "")
|
2018-12-13 06:50:10 +00:00
|
|
|
case .systemErrorOnAddTunnel:
|
2018-12-13 18:23:17 +00:00
|
|
|
return ("Unable to create tunnel", "")
|
2018-12-13 06:50:10 +00:00
|
|
|
case .systemErrorOnModifyTunnel:
|
2018-12-13 18:23:17 +00:00
|
|
|
return ("Unable to modify tunnel", "")
|
2018-12-13 06:50:10 +00:00
|
|
|
case .systemErrorOnRemoveTunnel:
|
2018-12-13 18:23:17 +00:00
|
|
|
return ("Unable to remove tunnel", "")
|
2018-12-06 10:43:48 +00:00
|
|
|
}
|
|
|
|
}
|
2018-10-27 13:00:07 +00:00
|
|
|
}
|
|
|
|
|
2018-10-15 08:05:24 +00:00
|
|
|
class TunnelsManager {
|
|
|
|
|
2018-11-03 02:53:08 +00:00
|
|
|
private var tunnels: [TunnelContainer]
|
2018-12-03 13:21:51 +00:00
|
|
|
weak var tunnelsListDelegate: TunnelsManagerListDelegate?
|
|
|
|
weak var activationDelegate: TunnelsManagerActivationDelegate?
|
2018-12-08 13:13:24 +00:00
|
|
|
private var statusObservationToken: AnyObject?
|
2018-10-15 08:05:24 +00:00
|
|
|
|
2018-10-25 10:20:27 +00:00
|
|
|
init(tunnelProviders: [NETunnelProviderManager]) {
|
2018-11-03 05:22:57 +00:00
|
|
|
self.tunnels = tunnelProviders.map { TunnelContainer(tunnel: $0) }.sorted { $0.name < $1.name }
|
2018-12-08 13:13:24 +00:00
|
|
|
self.startObservingTunnelStatuses()
|
2018-10-15 08:05:24 +00:00
|
|
|
}
|
|
|
|
|
2018-12-06 11:00:11 +00:00
|
|
|
static func create(completionHandler: @escaping (WireGuardResult<TunnelsManager>) -> Void) {
|
2018-11-07 12:43:50 +00:00
|
|
|
#if targetEnvironment(simulator)
|
|
|
|
// NETunnelProviderManager APIs don't work on the simulator
|
2018-12-06 10:28:27 +00:00
|
|
|
completionHandler(.success(TunnelsManager(tunnelProviders: [])))
|
2018-11-07 12:43:50 +00:00
|
|
|
#else
|
2018-12-12 21:33:14 +00:00
|
|
|
NETunnelProviderManager.loadAllFromPreferences { managers, error in
|
2018-10-25 10:20:27 +00:00
|
|
|
if let error = error {
|
2018-12-13 12:00:02 +00:00
|
|
|
wg_log(.error, message: "Failed to load tunnel provider managers: \(error)")
|
2018-12-13 06:50:10 +00:00
|
|
|
completionHandler(.failure(TunnelsManagerError.systemErrorOnListingTunnels))
|
2018-10-25 10:20:27 +00:00
|
|
|
return
|
|
|
|
}
|
2018-12-06 10:28:27 +00:00
|
|
|
completionHandler(.success(TunnelsManager(tunnelProviders: managers ?? [])))
|
2018-10-25 10:20:27 +00:00
|
|
|
}
|
2018-11-07 12:43:50 +00:00
|
|
|
#endif
|
2018-10-15 08:05:24 +00:00
|
|
|
}
|
|
|
|
|
2018-11-12 08:32:09 +00:00
|
|
|
func add(tunnelConfiguration: TunnelConfiguration,
|
|
|
|
activateOnDemandSetting: ActivateOnDemandSetting = ActivateOnDemandSetting.defaultSetting,
|
2018-12-06 11:00:11 +00:00
|
|
|
completionHandler: @escaping (WireGuardResult<TunnelContainer>) -> Void) {
|
2018-10-25 10:20:27 +00:00
|
|
|
let tunnelName = tunnelConfiguration.interface.name
|
2018-11-03 02:40:23 +00:00
|
|
|
if tunnelName.isEmpty {
|
2018-12-06 10:28:27 +00:00
|
|
|
completionHandler(.failure(TunnelsManagerError.tunnelNameEmpty))
|
2018-11-03 02:40:23 +00:00
|
|
|
return
|
|
|
|
}
|
2018-11-03 18:35:25 +00:00
|
|
|
|
2018-11-03 02:53:08 +00:00
|
|
|
if self.tunnels.contains(where: { $0.name == tunnelName }) {
|
2018-12-06 10:28:27 +00:00
|
|
|
completionHandler(.failure(TunnelsManagerError.tunnelAlreadyExistsWithThatName))
|
2018-11-01 06:12:32 +00:00
|
|
|
return
|
|
|
|
}
|
2018-10-25 10:20:27 +00:00
|
|
|
|
|
|
|
let tunnelProviderManager = NETunnelProviderManager()
|
|
|
|
tunnelProviderManager.protocolConfiguration = NETunnelProviderProtocol(tunnelConfiguration: tunnelConfiguration)
|
|
|
|
tunnelProviderManager.localizedDescription = tunnelName
|
|
|
|
tunnelProviderManager.isEnabled = true
|
|
|
|
|
2018-11-12 08:32:09 +00:00
|
|
|
activateOnDemandSetting.apply(on: tunnelProviderManager)
|
2018-11-10 20:01:38 +00:00
|
|
|
|
2018-12-12 21:33:14 +00:00
|
|
|
tunnelProviderManager.saveToPreferences { [weak self] error in
|
2018-12-12 18:28:27 +00:00
|
|
|
guard error == nil else {
|
2018-12-13 12:00:02 +00:00
|
|
|
wg_log(.error, message: "Add: Saving configuration failed: \(error!)")
|
2018-12-13 06:50:10 +00:00
|
|
|
completionHandler(.failure(TunnelsManagerError.systemErrorOnAddTunnel))
|
2018-10-25 10:20:27 +00:00
|
|
|
return
|
|
|
|
}
|
2018-12-12 17:40:57 +00:00
|
|
|
if let self = self {
|
2018-11-03 02:40:23 +00:00
|
|
|
let tunnel = TunnelContainer(tunnel: tunnelProviderManager)
|
2018-12-12 17:40:57 +00:00
|
|
|
self.tunnels.append(tunnel)
|
|
|
|
self.tunnels.sort { $0.name < $1.name }
|
|
|
|
self.tunnelsListDelegate?.tunnelAdded(at: self.tunnels.firstIndex(of: tunnel)!)
|
2018-12-06 10:28:27 +00:00
|
|
|
completionHandler(.success(tunnel))
|
2018-10-25 10:20:27 +00:00
|
|
|
}
|
2018-10-15 08:05:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-03 01:51:32 +00:00
|
|
|
func addMultiple(tunnelConfigurations: [TunnelConfiguration], completionHandler: @escaping (UInt) -> Void) {
|
2018-11-03 03:37:56 +00:00
|
|
|
addMultiple(tunnelConfigurations: ArraySlice(tunnelConfigurations), numberSuccessful: 0, completionHandler: completionHandler)
|
2018-10-31 08:59:54 +00:00
|
|
|
}
|
|
|
|
|
2018-11-03 01:51:32 +00:00
|
|
|
private func addMultiple(tunnelConfigurations: ArraySlice<TunnelConfiguration>, numberSuccessful: UInt, completionHandler: @escaping (UInt) -> Void) {
|
2018-11-03 03:37:56 +00:00
|
|
|
guard let head = tunnelConfigurations.first else {
|
2018-11-03 01:51:32 +00:00
|
|
|
completionHandler(numberSuccessful)
|
|
|
|
return
|
|
|
|
}
|
2018-11-03 03:37:56 +00:00
|
|
|
let tail = tunnelConfigurations.dropFirst()
|
2018-12-12 21:33:14 +00:00
|
|
|
self.add(tunnelConfiguration: head) { [weak self, tail] result in
|
2018-11-03 01:51:32 +00:00
|
|
|
DispatchQueue.main.async {
|
2018-12-06 10:28:27 +00:00
|
|
|
self?.addMultiple(tunnelConfigurations: tail, numberSuccessful: numberSuccessful + (result.isSuccess ? 1 : 0), completionHandler: completionHandler)
|
2018-10-31 08:59:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-12 08:32:09 +00:00
|
|
|
func modify(tunnel: TunnelContainer, tunnelConfiguration: TunnelConfiguration,
|
2018-12-06 10:28:27 +00:00
|
|
|
activateOnDemandSetting: ActivateOnDemandSetting, completionHandler: @escaping (TunnelsManagerError?) -> Void) {
|
2018-10-25 10:20:27 +00:00
|
|
|
let tunnelName = tunnelConfiguration.interface.name
|
2018-11-03 02:40:23 +00:00
|
|
|
if tunnelName.isEmpty {
|
2018-12-06 10:28:27 +00:00
|
|
|
completionHandler(TunnelsManagerError.tunnelNameEmpty)
|
2018-11-03 02:40:23 +00:00
|
|
|
return
|
|
|
|
}
|
2018-10-25 10:20:27 +00:00
|
|
|
|
|
|
|
let tunnelProviderManager = tunnel.tunnelProvider
|
|
|
|
let isNameChanged = (tunnelName != tunnelProviderManager.localizedDescription)
|
2018-12-12 18:28:27 +00:00
|
|
|
if isNameChanged {
|
2018-11-03 02:53:08 +00:00
|
|
|
if self.tunnels.contains(where: { $0.name == tunnelName }) {
|
2018-12-06 10:28:27 +00:00
|
|
|
completionHandler(TunnelsManagerError.tunnelAlreadyExistsWithThatName)
|
2018-11-01 06:12:32 +00:00
|
|
|
return
|
|
|
|
}
|
2018-10-28 09:25:24 +00:00
|
|
|
tunnel.name = tunnelName
|
|
|
|
}
|
2018-10-25 10:20:27 +00:00
|
|
|
tunnelProviderManager.protocolConfiguration = NETunnelProviderProtocol(tunnelConfiguration: tunnelConfiguration)
|
|
|
|
tunnelProviderManager.localizedDescription = tunnelName
|
|
|
|
tunnelProviderManager.isEnabled = true
|
|
|
|
|
2018-11-14 07:30:52 +00:00
|
|
|
let isActivatingOnDemand = (!tunnelProviderManager.isOnDemandEnabled && activateOnDemandSetting.isActivateOnDemandEnabled)
|
2018-11-12 08:32:09 +00:00
|
|
|
activateOnDemandSetting.apply(on: tunnelProviderManager)
|
2018-11-10 20:01:38 +00:00
|
|
|
|
2018-12-12 21:33:14 +00:00
|
|
|
tunnelProviderManager.saveToPreferences { [weak self] error in
|
2018-12-12 18:28:27 +00:00
|
|
|
guard error == nil else {
|
2018-12-13 12:00:02 +00:00
|
|
|
wg_log(.error, message: "Modify: Saving configuration failed: \(error!)")
|
2018-12-13 06:50:10 +00:00
|
|
|
completionHandler(TunnelsManagerError.systemErrorOnModifyTunnel)
|
2018-10-25 10:20:27 +00:00
|
|
|
return
|
|
|
|
}
|
2018-12-12 17:40:57 +00:00
|
|
|
if let self = self {
|
2018-12-12 18:28:27 +00:00
|
|
|
if isNameChanged {
|
2018-12-12 17:40:57 +00:00
|
|
|
let oldIndex = self.tunnels.firstIndex(of: tunnel)!
|
|
|
|
self.tunnels.sort { $0.name < $1.name }
|
|
|
|
let newIndex = self.tunnels.firstIndex(of: tunnel)!
|
2018-12-12 18:28:27 +00:00
|
|
|
self.tunnelsListDelegate?.tunnelMoved(from: oldIndex, to: newIndex)
|
2018-10-25 10:20:27 +00:00
|
|
|
}
|
2018-12-12 17:40:57 +00:00
|
|
|
self.tunnelsListDelegate?.tunnelModified(at: self.tunnels.firstIndex(of: tunnel)!)
|
2018-11-01 10:06:59 +00:00
|
|
|
|
2018-12-12 18:28:27 +00:00
|
|
|
if tunnel.status == .active || tunnel.status == .activating || tunnel.status == .reasserting {
|
2018-11-01 10:06:59 +00:00
|
|
|
// Turn off the tunnel, and then turn it back on, so the changes are made effective
|
2018-12-10 11:34:24 +00:00
|
|
|
tunnel.status = .restarting
|
2018-12-12 21:33:14 +00:00
|
|
|
(tunnel.tunnelProvider.connection as? NETunnelProviderSession)?.stopTunnel()
|
2018-11-01 10:06:59 +00:00
|
|
|
}
|
|
|
|
|
2018-12-12 18:28:27 +00:00
|
|
|
if isActivatingOnDemand {
|
2018-11-14 07:30:52 +00:00
|
|
|
// Reload tunnel after saving.
|
|
|
|
// Without this, the tunnel stopes getting updates on the tunnel status from iOS.
|
2018-12-12 21:33:14 +00:00
|
|
|
tunnelProviderManager.loadFromPreferences { error in
|
2018-11-14 07:30:52 +00:00
|
|
|
tunnel.isActivateOnDemandEnabled = tunnelProviderManager.isOnDemandEnabled
|
2018-12-12 18:28:27 +00:00
|
|
|
guard error == nil else {
|
2018-12-13 12:00:02 +00:00
|
|
|
wg_log(.error, message: "Modify: Re-loading after saving configuration failed: \(error!)")
|
2018-12-13 06:50:10 +00:00
|
|
|
completionHandler(TunnelsManagerError.systemErrorOnModifyTunnel)
|
2018-11-14 07:30:52 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
completionHandler(nil)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
completionHandler(nil)
|
|
|
|
}
|
2018-10-25 10:20:27 +00:00
|
|
|
}
|
|
|
|
}
|
2018-10-15 08:05:24 +00:00
|
|
|
}
|
|
|
|
|
2018-12-06 10:28:27 +00:00
|
|
|
func remove(tunnel: TunnelContainer, completionHandler: @escaping (TunnelsManagerError?) -> Void) {
|
2018-10-25 10:20:27 +00:00
|
|
|
let tunnelProviderManager = tunnel.tunnelProvider
|
|
|
|
|
2018-12-12 21:33:14 +00:00
|
|
|
tunnelProviderManager.removeFromPreferences { [weak self] error in
|
2018-12-12 18:28:27 +00:00
|
|
|
guard error == nil else {
|
2018-12-13 12:00:02 +00:00
|
|
|
wg_log(.error, message: "Remove: Saving configuration failed: \(error!)")
|
2018-12-13 06:50:10 +00:00
|
|
|
completionHandler(TunnelsManagerError.systemErrorOnRemoveTunnel)
|
2018-10-25 10:20:27 +00:00
|
|
|
return
|
|
|
|
}
|
2018-12-12 17:40:57 +00:00
|
|
|
if let self = self {
|
|
|
|
let index = self.tunnels.firstIndex(of: tunnel)!
|
|
|
|
self.tunnels.remove(at: index)
|
|
|
|
self.tunnelsListDelegate?.tunnelRemoved(at: index)
|
2018-10-25 10:20:27 +00:00
|
|
|
}
|
2018-11-03 16:23:03 +00:00
|
|
|
completionHandler(nil)
|
2018-10-15 08:05:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func numberOfTunnels() -> Int {
|
|
|
|
return tunnels.count
|
|
|
|
}
|
|
|
|
|
|
|
|
func tunnel(at index: Int) -> TunnelContainer {
|
|
|
|
return tunnels[index]
|
|
|
|
}
|
2018-10-26 09:25:20 +00:00
|
|
|
|
2018-12-07 13:35:04 +00:00
|
|
|
func tunnel(named tunnelName: String) -> TunnelContainer? {
|
2018-12-12 21:33:14 +00:00
|
|
|
return self.tunnels.first { $0.name == tunnelName }
|
2018-12-07 13:35:04 +00:00
|
|
|
}
|
|
|
|
|
2018-12-13 13:25:20 +00:00
|
|
|
func startActivation(of tunnel: TunnelContainer) {
|
2018-12-10 11:34:24 +00:00
|
|
|
guard tunnels.contains(tunnel) else { return } // Ensure it's not deleted
|
2018-12-12 18:28:27 +00:00
|
|
|
guard tunnel.status == .inactive else {
|
2018-12-13 13:25:20 +00:00
|
|
|
self.activationDelegate?.tunnelActivationAttemptFailed(tunnel: tunnel, error: .tunnelIsNotInactive)
|
2018-10-26 09:25:20 +00:00
|
|
|
return
|
|
|
|
}
|
2018-12-03 10:04:58 +00:00
|
|
|
|
2018-12-13 17:43:54 +00:00
|
|
|
if let alreadyWaitingTunnel = tunnels.first(where: { $0.status == .waiting }) {
|
|
|
|
alreadyWaitingTunnel.status = .inactive
|
|
|
|
}
|
|
|
|
|
2018-12-10 21:54:28 +00:00
|
|
|
if let tunnelInOperation = tunnels.first(where: { $0.status != .inactive }) {
|
2018-12-13 17:43:54 +00:00
|
|
|
wg_log(.info, message: "Tunnel '\(tunnel.name)' waiting for deactivation of '\(tunnelInOperation.name)'")
|
|
|
|
tunnel.status = .waiting
|
|
|
|
if tunnelInOperation.status != .deactivating {
|
|
|
|
startDeactivation(of: tunnelInOperation)
|
|
|
|
}
|
2018-12-10 21:54:28 +00:00
|
|
|
return
|
2018-12-03 10:04:58 +00:00
|
|
|
}
|
|
|
|
|
2018-12-13 13:25:20 +00:00
|
|
|
tunnel.startActivation(activationDelegate: self.activationDelegate)
|
2018-10-26 09:25:20 +00:00
|
|
|
}
|
|
|
|
|
2018-11-10 06:55:17 +00:00
|
|
|
func startDeactivation(of tunnel: TunnelContainer) {
|
2018-12-13 17:43:54 +00:00
|
|
|
tunnel.isAttemptingActivation = false
|
2018-12-12 18:28:27 +00:00
|
|
|
if tunnel.status == .inactive || tunnel.status == .deactivating {
|
2018-10-26 09:25:20 +00:00
|
|
|
return
|
|
|
|
}
|
2018-10-27 13:00:07 +00:00
|
|
|
tunnel.startDeactivation()
|
2018-10-26 09:25:20 +00:00
|
|
|
}
|
2018-11-09 13:49:32 +00:00
|
|
|
|
2018-11-12 10:34:03 +00:00
|
|
|
func refreshStatuses() {
|
2018-12-12 17:40:57 +00:00
|
|
|
tunnels.forEach { $0.refreshStatus() }
|
2018-11-09 13:49:32 +00:00
|
|
|
}
|
2018-12-08 13:13:24 +00:00
|
|
|
|
|
|
|
private func startObservingTunnelStatuses() {
|
2018-12-13 03:09:52 +00:00
|
|
|
guard statusObservationToken == nil else { return }
|
2018-12-13 04:26:04 +00:00
|
|
|
|
2018-12-13 03:09:52 +00:00
|
|
|
statusObservationToken = NotificationCenter.default.addObserver(forName: .NEVPNStatusDidChange, object: nil, queue: OperationQueue.main) { [weak self] statusChangeNotification in
|
|
|
|
guard let self = self else { return }
|
|
|
|
guard let session = statusChangeNotification.object as? NETunnelProviderSession else { return }
|
|
|
|
guard let tunnelProvider = session.manager as? NETunnelProviderManager else { return }
|
|
|
|
guard let tunnel = self.tunnels.first(where: { $0.tunnelProvider == tunnelProvider }) else { return }
|
2018-12-13 04:26:04 +00:00
|
|
|
|
2018-12-13 12:00:02 +00:00
|
|
|
wg_log(.debug, message: "Tunnel '\(tunnel.name)' connection status changed to '\(tunnel.tunnelProvider.connection.status)'")
|
2018-12-13 04:26:04 +00:00
|
|
|
|
2018-12-13 13:25:20 +00:00
|
|
|
// Track what happened to our attempt to start the tunnel
|
|
|
|
if tunnel.isAttemptingActivation {
|
|
|
|
if session.status == .connected {
|
|
|
|
tunnel.isAttemptingActivation = false
|
|
|
|
self.activationDelegate?.tunnelActivationSucceeded(tunnel: tunnel)
|
|
|
|
} else if session.status == .disconnected {
|
|
|
|
tunnel.isAttemptingActivation = false
|
|
|
|
self.activationDelegate?.tunnelActivationFailed(tunnel: tunnel, error: .activationFailed)
|
2018-12-10 11:34:24 +00:00
|
|
|
}
|
2018-12-13 03:09:52 +00:00
|
|
|
}
|
2018-12-13 04:26:04 +00:00
|
|
|
|
2018-12-13 03:09:52 +00:00
|
|
|
// In case we're restarting the tunnel
|
|
|
|
if (tunnel.status == .restarting) && (session.status == .disconnected || session.status == .disconnecting) {
|
|
|
|
// Don't change tunnel.status when disconnecting for a restart
|
|
|
|
if session.status == .disconnected {
|
2018-12-13 13:25:20 +00:00
|
|
|
tunnel.startActivation(activationDelegate: self.activationDelegate)
|
2018-12-10 11:34:24 +00:00
|
|
|
}
|
2018-12-13 03:09:52 +00:00
|
|
|
return
|
|
|
|
}
|
2018-12-13 04:26:04 +00:00
|
|
|
|
2018-12-13 03:09:52 +00:00
|
|
|
tunnel.refreshStatus()
|
2018-12-13 17:43:54 +00:00
|
|
|
|
|
|
|
// In case some other tunnel is waiting for this tunnel to get deactivated
|
|
|
|
if session.status == .disconnected || session.status == .invalid {
|
|
|
|
if let waitingTunnel = self.tunnels.first(where: { $0.status == .waiting }) {
|
|
|
|
waitingTunnel.startActivation(activationDelegate: self.activationDelegate)
|
|
|
|
}
|
|
|
|
}
|
2018-12-08 13:13:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-10 11:34:24 +00:00
|
|
|
deinit {
|
|
|
|
if let statusObservationToken = self.statusObservationToken {
|
|
|
|
NotificationCenter.default.removeObserver(statusObservationToken)
|
|
|
|
}
|
|
|
|
}
|
2018-10-15 08:05:24 +00:00
|
|
|
}
|
2018-10-25 10:20:27 +00:00
|
|
|
|
2018-10-26 09:25:20 +00:00
|
|
|
class TunnelContainer: NSObject {
|
|
|
|
@objc dynamic var name: String
|
|
|
|
@objc dynamic var status: TunnelStatus
|
2018-11-12 12:46:44 +00:00
|
|
|
|
2018-12-08 13:13:24 +00:00
|
|
|
@objc dynamic var isActivateOnDemandEnabled: Bool
|
2018-10-26 09:25:20 +00:00
|
|
|
|
2018-12-03 10:04:58 +00:00
|
|
|
var isAttemptingActivation: Bool = false
|
2018-11-10 10:20:56 +00:00
|
|
|
|
2018-10-26 09:25:20 +00:00
|
|
|
fileprivate let tunnelProvider: NETunnelProviderManager
|
2018-12-08 13:13:24 +00:00
|
|
|
private var lastTunnelConnectionStatus: NEVPNStatus?
|
2018-10-26 09:25:20 +00:00
|
|
|
|
2018-11-03 02:40:23 +00:00
|
|
|
init(tunnel: NETunnelProviderManager) {
|
2018-10-26 09:25:20 +00:00
|
|
|
self.name = tunnel.localizedDescription ?? "Unnamed"
|
|
|
|
let status = TunnelStatus(from: tunnel.connection.status)
|
|
|
|
self.status = status
|
2018-11-12 10:34:03 +00:00
|
|
|
self.isActivateOnDemandEnabled = tunnel.isOnDemandEnabled
|
2018-10-26 09:25:20 +00:00
|
|
|
self.tunnelProvider = tunnel
|
|
|
|
super.init()
|
|
|
|
}
|
|
|
|
|
|
|
|
func tunnelConfiguration() -> TunnelConfiguration? {
|
2018-12-12 21:33:14 +00:00
|
|
|
return (tunnelProvider.protocolConfiguration as? NETunnelProviderProtocol)?.tunnelConfiguration()
|
2018-10-26 09:25:20 +00:00
|
|
|
}
|
|
|
|
|
2018-11-12 08:32:09 +00:00
|
|
|
func activateOnDemandSetting() -> ActivateOnDemandSetting {
|
|
|
|
return ActivateOnDemandSetting(from: tunnelProvider)
|
|
|
|
}
|
|
|
|
|
2018-11-12 10:34:03 +00:00
|
|
|
func refreshStatus() {
|
2018-11-09 13:49:32 +00:00
|
|
|
let status = TunnelStatus(from: self.tunnelProvider.connection.status)
|
|
|
|
self.status = status
|
2018-11-12 10:34:03 +00:00
|
|
|
self.isActivateOnDemandEnabled = self.tunnelProvider.isOnDemandEnabled
|
2018-11-09 13:49:32 +00:00
|
|
|
}
|
|
|
|
|
2018-12-13 13:25:20 +00:00
|
|
|
fileprivate func startActivation(activationDelegate: TunnelsManagerActivationDelegate?) {
|
2018-12-13 17:43:54 +00:00
|
|
|
assert(status == .inactive || status == .restarting || status == .waiting)
|
2018-10-30 11:04:46 +00:00
|
|
|
|
2018-10-26 13:39:11 +00:00
|
|
|
guard let tunnelConfiguration = tunnelConfiguration() else { fatalError() }
|
2018-11-08 09:34:12 +00:00
|
|
|
|
2018-12-13 13:25:20 +00:00
|
|
|
startActivation(tunnelConfiguration: tunnelConfiguration, activationDelegate: activationDelegate)
|
2018-10-30 11:04:46 +00:00
|
|
|
}
|
|
|
|
|
2018-10-31 14:58:03 +00:00
|
|
|
fileprivate func startActivation(recursionCount: UInt = 0,
|
|
|
|
lastError: Error? = nil,
|
|
|
|
tunnelConfiguration: TunnelConfiguration,
|
2018-12-13 13:25:20 +00:00
|
|
|
activationDelegate: TunnelsManagerActivationDelegate?) {
|
2018-12-12 18:28:27 +00:00
|
|
|
if recursionCount >= 8 {
|
2018-12-13 12:00:02 +00:00
|
|
|
wg_log(.error, message: "startActivation: Failed after 8 attempts. Giving up with \(lastError!)")
|
2018-12-13 13:25:20 +00:00
|
|
|
activationDelegate?.tunnelActivationAttemptFailed(tunnel: self, error: .failedBecauseOfTooManyErrors)
|
2018-10-31 14:58:03 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-13 12:00:02 +00:00
|
|
|
wg_log(.debug, message: "startActivation: Entering (tunnel: \(self.name))")
|
2018-10-31 11:12:29 +00:00
|
|
|
|
2018-12-13 18:04:00 +00:00
|
|
|
self.status = .activating // Ensure that no other tunnel can attempt activation until this tunnel is done trying
|
|
|
|
|
2018-12-12 18:28:27 +00:00
|
|
|
guard tunnelProvider.isEnabled else {
|
2018-10-31 11:12:29 +00:00
|
|
|
// In case the tunnel had gotten disabled, re-enable and save it,
|
|
|
|
// then call this function again.
|
2018-12-13 12:00:02 +00:00
|
|
|
wg_log(.debug, staticMessage: "startActivation: Tunnel is disabled. Re-enabling and saving")
|
2018-10-31 11:12:29 +00:00
|
|
|
tunnelProvider.isEnabled = true
|
2018-12-12 21:33:14 +00:00
|
|
|
tunnelProvider.saveToPreferences { [weak self] error in
|
2018-12-13 13:25:20 +00:00
|
|
|
guard let self = self else { return }
|
2018-12-12 18:28:27 +00:00
|
|
|
if error != nil {
|
2018-12-13 12:00:02 +00:00
|
|
|
wg_log(.error, message: "Error saving tunnel after re-enabling: \(error!)")
|
2018-12-13 13:25:20 +00:00
|
|
|
activationDelegate?.tunnelActivationAttemptFailed(tunnel: self, error: .failedWhileSaving)
|
2018-10-31 11:12:29 +00:00
|
|
|
return
|
|
|
|
}
|
2018-12-13 12:00:02 +00:00
|
|
|
wg_log(.debug, staticMessage: "startActivation: Tunnel saved after re-enabling")
|
|
|
|
wg_log(.debug, staticMessage: "startActivation: Invoking startActivation")
|
2018-12-13 13:25:20 +00:00
|
|
|
self.startActivation(recursionCount: recursionCount + 1, lastError: NEVPNError(NEVPNError.configurationUnknown),
|
|
|
|
tunnelConfiguration: tunnelConfiguration, activationDelegate: activationDelegate)
|
2018-10-31 11:12:29 +00:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-10-30 11:04:46 +00:00
|
|
|
// Start the tunnel
|
2018-10-31 11:12:29 +00:00
|
|
|
do {
|
2018-12-13 12:00:02 +00:00
|
|
|
wg_log(.debug, staticMessage: "startActivation: Starting tunnel")
|
2018-12-13 13:25:20 +00:00
|
|
|
self.isAttemptingActivation = true
|
2018-12-12 21:33:14 +00:00
|
|
|
try (tunnelProvider.connection as? NETunnelProviderSession)?.startTunnel()
|
2018-12-13 12:00:02 +00:00
|
|
|
wg_log(.debug, staticMessage: "startActivation: Success")
|
2018-12-13 13:25:20 +00:00
|
|
|
activationDelegate?.tunnelActivationAttemptSucceeded(tunnel: self)
|
2018-12-12 18:28:27 +00:00
|
|
|
} catch let error {
|
2018-12-13 13:25:20 +00:00
|
|
|
self.isAttemptingActivation = false
|
2018-12-13 06:50:10 +00:00
|
|
|
guard let systemError = error as? NEVPNError else {
|
2018-12-13 12:00:02 +00:00
|
|
|
wg_log(.error, message: "Failed to activate tunnel: Error: \(error)")
|
2018-11-03 16:23:03 +00:00
|
|
|
status = .inactive
|
2018-12-13 13:25:20 +00:00
|
|
|
activationDelegate?.tunnelActivationAttemptFailed(tunnel: self, error: .failedWhileStarting)
|
2018-10-31 11:12:29 +00:00
|
|
|
return
|
|
|
|
}
|
2018-12-13 06:50:10 +00:00
|
|
|
guard systemError.code == NEVPNError.configurationInvalid || systemError.code == NEVPNError.configurationStale else {
|
2018-12-13 12:00:02 +00:00
|
|
|
wg_log(.error, message: "Failed to activate tunnel: VPN Error: \(error)")
|
2018-11-06 17:12:53 +00:00
|
|
|
status = .inactive
|
2018-12-13 13:25:20 +00:00
|
|
|
activationDelegate?.tunnelActivationAttemptFailed(tunnel: self, error: .failedWhileStarting)
|
2018-11-06 17:12:53 +00:00
|
|
|
return
|
2018-10-31 11:12:29 +00:00
|
|
|
}
|
2018-12-13 12:00:02 +00:00
|
|
|
wg_log(.debug, staticMessage: "startActivation: Will reload tunnel and then try to start it.")
|
2018-12-12 21:33:14 +00:00
|
|
|
tunnelProvider.loadFromPreferences { [weak self] error in
|
2018-12-13 13:25:20 +00:00
|
|
|
guard let self = self else { return }
|
2018-12-12 18:28:27 +00:00
|
|
|
if error != nil {
|
2018-12-13 12:00:02 +00:00
|
|
|
wg_log(.error, message: "startActivation: Error reloading tunnel: \(error!)")
|
2018-12-13 13:25:20 +00:00
|
|
|
self.status = .inactive
|
|
|
|
activationDelegate?.tunnelActivationAttemptFailed(tunnel: self, error: .failedWhileLoading)
|
2018-10-31 11:12:29 +00:00
|
|
|
return
|
|
|
|
}
|
2018-12-13 12:00:02 +00:00
|
|
|
wg_log(.debug, staticMessage: "startActivation: Tunnel reloaded")
|
|
|
|
wg_log(.debug, staticMessage: "startActivation: Invoking startActivation")
|
2018-12-13 13:25:20 +00:00
|
|
|
self.startActivation(recursionCount: recursionCount + 1, lastError: systemError, tunnelConfiguration: tunnelConfiguration, activationDelegate: activationDelegate)
|
2018-10-29 00:30:31 +00:00
|
|
|
}
|
2018-10-26 09:25:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-27 13:00:07 +00:00
|
|
|
fileprivate func startDeactivation() {
|
2018-12-12 21:33:14 +00:00
|
|
|
(tunnelProvider.connection as? NETunnelProviderSession)?.stopTunnel()
|
2018-10-26 09:25:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc enum TunnelStatus: Int {
|
|
|
|
case inactive
|
|
|
|
case activating
|
|
|
|
case active
|
|
|
|
case deactivating
|
2018-11-01 10:06:59 +00:00
|
|
|
case reasserting // Not a possible state at present
|
2018-10-26 09:25:20 +00:00
|
|
|
|
2018-11-01 10:06:59 +00:00
|
|
|
case restarting // Restarting tunnel (done after saving modifications to an active tunnel)
|
2018-12-13 17:43:54 +00:00
|
|
|
case waiting // Waiting for another tunnel to be brought down
|
2018-10-26 09:25:20 +00:00
|
|
|
|
2018-12-13 06:50:10 +00:00
|
|
|
init(from systemStatus: NEVPNStatus) {
|
|
|
|
switch systemStatus {
|
2018-10-26 09:25:20 +00:00
|
|
|
case .connected:
|
|
|
|
self = .active
|
|
|
|
case .connecting:
|
|
|
|
self = .activating
|
|
|
|
case .disconnected:
|
|
|
|
self = .inactive
|
|
|
|
case .disconnecting:
|
|
|
|
self = .deactivating
|
|
|
|
case .reasserting:
|
|
|
|
self = .reasserting
|
|
|
|
case .invalid:
|
|
|
|
self = .inactive
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-12-10 11:01:36 +00:00
|
|
|
|
|
|
|
extension TunnelStatus: CustomDebugStringConvertible {
|
|
|
|
public var debugDescription: String {
|
2018-12-12 18:28:27 +00:00
|
|
|
switch self {
|
2018-12-10 11:01:36 +00:00
|
|
|
case .inactive: return "inactive"
|
|
|
|
case .activating: return "activating"
|
|
|
|
case .active: return "active"
|
|
|
|
case .deactivating: return "deactivating"
|
|
|
|
case .reasserting: return "reasserting"
|
|
|
|
case .restarting: return "restarting"
|
2018-12-13 17:43:54 +00:00
|
|
|
case .waiting: return "waiting"
|
2018-12-10 11:01:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extension NEVPNStatus: CustomDebugStringConvertible {
|
|
|
|
public var debugDescription: String {
|
2018-12-12 18:28:27 +00:00
|
|
|
switch self {
|
2018-12-10 11:01:36 +00:00
|
|
|
case .connected: return "connected"
|
|
|
|
case .connecting: return "connecting"
|
|
|
|
case .disconnected: return "disconnected"
|
|
|
|
case .disconnecting: return "disconnecting"
|
|
|
|
case .reasserting: return "reasserting"
|
|
|
|
case .invalid: return "invalid"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|