2018-10-24 01:37:28 +00:00
// S P D X - L i c e n s e - I d e n t i f i e r : M I T
2019-01-02 00:56:33 +00:00
// C o p y r i g h t © 2 0 1 8 - 2 0 1 9 W i r e G u a r d L L C . A l l R i g h t s R e s e r v e d .
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 )
2019-01-23 10:15:24 +00:00
func tunnelRemoved ( at index : Int , tunnel : TunnelContainer )
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 ) // s t a r t T u n n e l w a s n ' t c a l l e d o r f a i l e d
func tunnelActivationAttemptSucceeded ( tunnel : TunnelContainer ) // s t a r t T u n n e l s u c c e e d e d
func tunnelActivationFailed ( tunnel : TunnelContainer , error : TunnelsManagerActivationError ) // s t a t u s d i d n ' t c h a n g e t o c o n n e c t e d
func tunnelActivationSucceeded ( tunnel : TunnelContainer ) // s t a t u s c h a n g e d t o c o n n e c t e d
}
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-12-18 14:15:00 +00:00
private var waiteeObservationToken : AnyObject ?
2019-01-22 13:41:51 +00:00
private var configurationsObservationToken : AnyObject ?
2018-10-15 08:05:24 +00:00
2018-10-25 10:20:27 +00:00
init ( tunnelProviders : [ NETunnelProviderManager ] ) {
2018-12-14 23:12:59 +00:00
tunnels = tunnelProviders . map { TunnelContainer ( tunnel : $0 ) } . sorted { $0 . name < $1 . name }
startObservingTunnelStatuses ( )
2019-01-22 13:41:51 +00:00
startObservingTunnelConfigurations ( )
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 )
2018-12-15 19:41:23 +00:00
completionHandler ( . success ( TunnelsManager ( tunnelProviders : MockTunnels . createMockTunnels ( ) ) ) )
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-17 13:26:26 +00:00
completionHandler ( . failure ( TunnelsManagerError . systemErrorOnListingTunnels ( systemError : error ) ) )
2018-10-25 10:20:27 +00:00
return
}
2018-12-21 22:34:56 +00:00
2018-12-21 04:52:45 +00:00
let tunnelManagers = managers ? ? [ ]
2018-12-21 21:16:09 +00:00
tunnelManagers . forEach { tunnelManager in
if ( tunnelManager . protocolConfiguration as ? NETunnelProviderProtocol ) ? . migrateConfigurationIfNeeded ( ) = = true {
tunnelManager . saveToPreferences { _ in }
2018-12-21 04:52:45 +00:00
}
}
completionHandler ( . success ( TunnelsManager ( tunnelProviders : tunnelManagers ) ) )
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-12-22 04:30:35 +00:00
2019-01-22 13:41:51 +00:00
func reload ( ) {
NETunnelProviderManager . loadAllFromPreferences { [ weak self ] managers , _ in
guard let self = self else { return }
2018-12-22 04:30:35 +00:00
2019-01-22 13:41:51 +00:00
let loadedTunnelProviders = managers ? ? [ ]
2019-01-22 14:27:35 +00:00
for ( index , currentTunnel ) in self . tunnels . enumerated ( ) . reversed ( ) {
2019-01-22 13:41:51 +00:00
if ! loadedTunnelProviders . contains ( where : { $0 . tunnelConfiguration = = currentTunnel . tunnelConfiguration } ) {
// T u n n e l w a s d e l e t e d o u t s i d e t h e a p p
2019-01-22 14:27:35 +00:00
self . tunnels . remove ( at : index )
2019-01-23 10:15:24 +00:00
self . tunnelsListDelegate ? . tunnelRemoved ( at : index , tunnel : currentTunnel )
2019-01-22 13:41:51 +00:00
}
}
for loadedTunnelProvider in loadedTunnelProviders {
if let matchingTunnel = self . tunnels . first ( where : { $0 . tunnelConfiguration = = loadedTunnelProvider . tunnelConfiguration } ) {
matchingTunnel . tunnelProvider = loadedTunnelProvider
2019-01-22 14:35:14 +00:00
matchingTunnel . refreshStatus ( )
2019-01-22 13:41:51 +00:00
} else {
// T u n n e l w a s a d d e d o u t s i d e t h e a p p
let tunnel = TunnelContainer ( tunnel : loadedTunnelProvider )
self . tunnels . append ( tunnel )
self . tunnels . sort { $0 . name < $1 . name }
self . tunnelsListDelegate ? . tunnelAdded ( at : self . tunnels . firstIndex ( of : tunnel ) ! )
}
2018-12-22 03:59:43 +00:00
}
}
}
2018-10-15 08:05:24 +00:00
2018-12-13 18:58:50 +00:00
func add ( tunnelConfiguration : TunnelConfiguration , activateOnDemandSetting : ActivateOnDemandSetting = ActivateOnDemandSetting . defaultSetting , completionHandler : @ escaping ( WireGuardResult < TunnelContainer > ) -> Void ) {
2018-12-21 23:28:18 +00:00
let tunnelName = tunnelConfiguration . 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-12-14 23:12:59 +00:00
if 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 ( )
2018-12-21 17:50:32 +00:00
tunnelProviderManager . protocolConfiguration = NETunnelProviderProtocol ( tunnelConfiguration : tunnelConfiguration )
2018-12-21 23:28:18 +00:00
tunnelProviderManager . localizedDescription = tunnelConfiguration . name
2018-10-25 10:20:27 +00:00
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-17 13:26:26 +00:00
completionHandler ( . failure ( TunnelsManagerError . systemErrorOnAddTunnel ( systemError : error ! ) ) )
2018-10-25 10:20:27 +00:00
return
}
2018-12-21 22:34:56 +00:00
2018-12-13 18:58:50 +00:00
guard let self = self else { return }
2018-12-21 22:34:56 +00:00
2018-12-13 18:58:50 +00:00
let tunnel = TunnelContainer ( tunnel : tunnelProviderManager )
self . tunnels . append ( tunnel )
self . tunnels . sort { $0 . name < $1 . name }
self . tunnelsListDelegate ? . tunnelAdded ( at : self . tunnels . firstIndex ( of : tunnel ) ! )
completionHandler ( . success ( tunnel ) )
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-14 23:12:59 +00:00
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-12-13 18:58:50 +00:00
func modify ( tunnel : TunnelContainer , tunnelConfiguration : TunnelConfiguration , activateOnDemandSetting : ActivateOnDemandSetting , completionHandler : @ escaping ( TunnelsManagerError ? ) -> Void ) {
2018-12-21 23:28:18 +00:00
let tunnelName = tunnelConfiguration . 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
2018-12-17 05:51:25 +00:00
let isNameChanged = tunnelName != tunnelProviderManager . localizedDescription
2018-12-12 18:28:27 +00:00
if isNameChanged {
2018-12-17 05:51:25 +00:00
guard ! tunnels . contains ( where : { $0 . name = = tunnelName } ) else {
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-12-19 10:32:48 +00:00
2018-12-21 17:50:32 +00:00
tunnelProviderManager . protocolConfiguration = NETunnelProviderProtocol ( tunnelConfiguration : tunnelConfiguration )
2018-12-21 23:28:18 +00:00
tunnelProviderManager . localizedDescription = tunnelConfiguration . name
2018-10-25 10:20:27 +00:00
tunnelProviderManager . isEnabled = true
2018-12-21 22:34:56 +00:00
2018-12-17 05:51:25 +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-17 13:26:26 +00:00
completionHandler ( TunnelsManagerError . systemErrorOnModifyTunnel ( systemError : error ! ) )
2018-10-25 10:20:27 +00:00
return
}
2018-12-13 18:58:50 +00:00
guard let self = self else { return }
2018-12-21 22:34:56 +00:00
2018-12-13 18:58:50 +00:00
if isNameChanged {
let oldIndex = self . tunnels . firstIndex ( of : tunnel ) !
self . tunnels . sort { $0 . name < $1 . name }
let newIndex = self . tunnels . firstIndex ( of : tunnel ) !
self . tunnelsListDelegate ? . tunnelMoved ( from : oldIndex , to : newIndex )
}
self . tunnelsListDelegate ? . tunnelModified ( at : self . tunnels . firstIndex ( of : tunnel ) ! )
2018-12-19 10:32:48 +00:00
2018-12-21 21:16:09 +00:00
if tunnel . status = = . active || tunnel . status = = . activating || tunnel . status = = . reasserting {
// T u r n o f f t h e t u n n e l , a n d t h e n t u r n i t b a c k o n , s o t h e c h a n g e s a r e m a d e e f f e c t i v e
tunnel . status = . restarting
( tunnel . tunnelProvider . connection as ? NETunnelProviderSession ) ? . stopTunnel ( )
2018-12-13 18:58:50 +00:00
}
2018-12-19 10:32:48 +00:00
2018-12-13 18:58:50 +00:00
if isActivatingOnDemand {
// R e l o a d t u n n e l a f t e r s a v i n g .
// W i t h o u t t h i s , t h e t u n n e l s t o p e s g e t t i n g u p d a t e s o n t h e t u n n e l s t a t u s f r o m i O S .
tunnelProviderManager . loadFromPreferences { error in
tunnel . isActivateOnDemandEnabled = tunnelProviderManager . isOnDemandEnabled
guard error = = nil else {
wg_log ( . error , message : " Modify: Re-loading after saving configuration failed: \( error ! ) " )
2018-12-17 13:26:26 +00:00
completionHandler ( TunnelsManagerError . systemErrorOnModifyTunnel ( systemError : error ! ) )
2018-12-13 18:58:50 +00:00
return
2018-11-14 07:30:52 +00:00
}
completionHandler ( nil )
}
2018-12-13 18:58:50 +00:00
} 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-17 13:26:26 +00:00
completionHandler ( TunnelsManagerError . systemErrorOnRemoveTunnel ( systemError : error ! ) )
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 )
2019-01-23 10:15:24 +00:00
self . tunnelsListDelegate ? . tunnelRemoved ( at : index , tunnel : tunnel )
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
2019-01-24 13:05:07 +00:00
func index ( of tunnel : TunnelContainer ) -> Int ? {
return tunnels . firstIndex ( of : tunnel )
}
2018-12-07 13:35:04 +00:00
func tunnel ( named tunnelName : String ) -> TunnelContainer ? {
2018-12-14 23:12:59 +00:00
return tunnels . first { $0 . name = = tunnelName }
2018-12-07 13:35:04 +00:00
}
2019-01-15 19:30:42 +00:00
func waitingTunnel ( ) -> TunnelContainer ? {
return tunnels . first { $0 . status = = . waiting }
}
2019-01-24 12:41:26 +00:00
func tunnelInOperation ( ) -> TunnelContainer ? {
if let waitingTunnelObject = waitingTunnel ( ) {
return waitingTunnelObject
}
return tunnels . first { $0 . status != . inactive }
}
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 } // E n s u r e i t ' s n o t d e l e t e d
2018-12-12 18:28:27 +00:00
guard tunnel . status = = . inactive else {
2018-12-14 23:12:59 +00:00
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
2018-12-18 14:15:00 +00:00
activateWaitingTunnelOnDeactivation ( of : tunnelInOperation )
2018-12-13 17:43:54 +00:00
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-15 19:41:23 +00:00
#if targetEnvironment ( simulator )
tunnel . status = . active
#else
2018-12-14 23:12:59 +00:00
tunnel . startActivation ( activationDelegate : activationDelegate )
2018-12-15 19:41:23 +00:00
#endif
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-13 18:58:50 +00:00
guard tunnel . status != . inactive && tunnel . status != . deactivating else { return }
2018-12-15 19:41:23 +00:00
#if targetEnvironment ( simulator )
tunnel . status = . inactive
#else
2018-10-27 13:00:07 +00:00
tunnel . startDeactivation ( )
2018-12-15 19:41:23 +00:00
#endif
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
2018-12-18 14:15:00 +00:00
private func activateWaitingTunnelOnDeactivation ( of tunnel : TunnelContainer ) {
waiteeObservationToken = tunnel . observe ( \ . status ) { [ weak self ] tunnel , _ in
guard let self = self else { return }
if tunnel . status = = . inactive {
if let waitingTunnel = self . tunnels . first ( where : { $0 . status = = . waiting } ) {
waitingTunnel . startActivation ( activationDelegate : self . activationDelegate )
}
self . waiteeObservationToken = nil
}
}
}
2018-12-08 13:13:24 +00:00
private func startObservingTunnelStatuses ( ) {
2018-12-13 03:09:52 +00:00
statusObservationToken = NotificationCenter . default . addObserver ( forName : . NEVPNStatusDidChange , object : nil , queue : OperationQueue . main ) { [ weak self ] statusChangeNotification in
2018-12-13 18:58:50 +00:00
guard let self = self ,
let session = statusChangeNotification . object as ? NETunnelProviderSession ,
let tunnelProvider = session . manager as ? NETunnelProviderManager ,
2019-01-22 14:20:50 +00:00
let tunnelConfiguration = tunnelProvider . tunnelConfiguration ,
2018-12-22 05:56:12 +00:00
let tunnel = self . tunnels . first ( where : { $0 . tunnelConfiguration = = tunnelConfiguration } ) else { return }
2018-12-22 06:07:53 +00:00
if tunnel . tunnelProvider != tunnelProvider {
tunnel . tunnelProvider = tunnelProvider
tunnel . refreshStatus ( )
}
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
if tunnel . isAttemptingActivation {
if session . status = = . connected {
tunnel . isAttemptingActivation = false
self . activationDelegate ? . tunnelActivationSucceeded ( tunnel : tunnel )
} else if session . status = = . disconnected {
tunnel . isAttemptingActivation = false
2018-12-22 03:59:43 +00:00
if let ( title , message ) = lastErrorTextFromNetworkExtension ( for : tunnel ) {
2018-12-21 17:50:32 +00:00
self . activationDelegate ? . tunnelActivationFailed ( tunnel : tunnel , error : . activationFailedWithExtensionError ( title : title , message : message , wasOnDemandEnabled : tunnelProvider . isOnDemandEnabled ) )
2018-12-13 20:54:53 +00:00
} else {
2018-12-21 17:50:32 +00:00
self . activationDelegate ? . tunnelActivationFailed ( tunnel : tunnel , error : . activationFailed ( wasOnDemandEnabled : tunnelProvider . isOnDemandEnabled ) )
2018-12-13 20:54:53 +00:00
}
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
if ( tunnel . status = = . restarting ) && ( session . status = = . disconnected || session . status = = . disconnecting ) {
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-08 13:13:24 +00:00
}
}
2019-01-22 13:41:51 +00:00
func startObservingTunnelConfigurations ( ) {
configurationsObservationToken = NotificationCenter . default . addObserver ( forName : . NEVPNConfigurationChange , object : nil , queue : OperationQueue . main ) { [ weak self ] _ in
2019-01-26 09:01:38 +00:00
DispatchQueue . main . async { [ weak self ] in
// W e s c h e d u l e r e l o a d ( ) i n a s u b s e q u e n t r u n l o o p t o e n s u r e t h a t t h e c o m p l e t i o n h a n d l e r o f l o a d A l l F r o m P r e f e r e n c e s
// ( r e l o a d ( ) c a l l s l o a d A l l F r o m P r e f e r e n c e s ) i s c a l l e d a f t e r t h e c o m p l e t i o n h a n d l e r o f t h e s a v e T o P r e f e r e n c e s o r
// r e m o v e F r o m P r e f e r e n c e s c a l l , i f a n y , t h a t c a u s e d t h i s n o t i f i c a t i o n t o f i r e . T h i s n o t i f i c a t i o n c a n a l s o f i r e
// a s a r e s u l t o f a t u n n e l g e t t i n g a d d e d o r r e m o v e d o u t s i d e o f t h e a p p .
self ? . reload ( )
}
2019-01-22 13:41:51 +00:00
}
}
2018-12-22 03:59:43 +00:00
}
2018-12-13 20:54:53 +00:00
2018-12-22 03:59:43 +00:00
private func lastErrorTextFromNetworkExtension ( for tunnel : TunnelContainer ) -> ( title : String , message : String ) ? {
guard let lastErrorFileURL = FileManager . networkExtensionLastErrorFileURL else { return nil }
guard let lastErrorData = try ? Data ( contentsOf : lastErrorFileURL ) else { return nil }
guard let lastErrorStrings = String ( data : lastErrorData , encoding : . utf8 ) ? . splitToArray ( separator : " \n " ) else { return nil }
guard lastErrorStrings . count = = 2 && tunnel . activationAttemptId = = lastErrorStrings [ 0 ] else { return nil }
2018-12-22 04:30:35 +00:00
2018-12-22 10:35:35 +00:00
if let extensionError = PacketTunnelProviderError ( rawValue : lastErrorStrings [ 1 ] ) {
return extensionError . alertText
2018-12-10 11:34:24 +00:00
}
2018-12-22 10:35:35 +00:00
return ( tr ( " alertTunnelActivationFailureTitle " ) , tr ( " alertTunnelActivationFailureMessage " ) )
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-14 12:03:52 +00:00
var isAttemptingActivation = false {
didSet {
if isAttemptingActivation {
2018-12-18 14:15:00 +00:00
self . activationTimer ? . invalidate ( )
2018-12-14 12:03:52 +00:00
let activationTimer = Timer ( timeInterval : 5 /* s e c o n d s */ , repeats : true ) { [ weak self ] _ in
guard let self = self else { return }
2018-12-18 14:15:00 +00:00
wg_log ( . debug , message : " Status update notification timeout for tunnel ' \( self . name ) '. Tunnel status is now ' \( self . tunnelProvider . connection . status ) '. " )
switch self . tunnelProvider . connection . status {
case . connected , . disconnected , . invalid :
self . activationTimer ? . invalidate ( )
self . activationTimer = nil
default :
break
2018-12-14 12:03:52 +00:00
}
2018-12-18 14:15:00 +00:00
self . refreshStatus ( )
2018-12-14 12:03:52 +00:00
}
self . activationTimer = activationTimer
2019-01-31 11:34:34 +00:00
RunLoop . main . add ( activationTimer , forMode : . common )
2018-12-14 12:03:52 +00:00
}
}
}
2018-12-13 20:54:53 +00:00
var activationAttemptId : String ?
2018-12-14 12:03:52 +00:00
var activationTimer : Timer ?
2019-01-25 12:44:48 +00:00
var deactivationTimer : Timer ?
2018-11-10 10:20:56 +00:00
2018-12-22 05:56:12 +00:00
fileprivate var tunnelProvider : NETunnelProviderManager
2018-10-26 09:25:20 +00:00
2018-12-21 04:52:45 +00:00
var tunnelConfiguration : TunnelConfiguration ? {
2019-01-22 13:41:51 +00:00
return tunnelProvider . tunnelConfiguration
2018-12-21 04:52:45 +00:00
}
2018-12-21 22:34:56 +00:00
2018-12-21 04:52:45 +00:00
var activateOnDemandSetting : ActivateOnDemandSetting {
return ActivateOnDemandSetting ( from : tunnelProvider )
}
2018-12-21 22:34:56 +00:00
2018-11-03 02:40:23 +00:00
init ( tunnel : NETunnelProviderManager ) {
2018-12-14 23:12:59 +00:00
name = tunnel . localizedDescription ? ? " Unnamed "
2018-10-26 09:25:20 +00:00
let status = TunnelStatus ( from : tunnel . connection . status )
self . status = status
2018-12-14 23:12:59 +00:00
isActivateOnDemandEnabled = tunnel . isOnDemandEnabled
tunnelProvider = tunnel
2018-10-26 09:25:20 +00:00
super . init ( )
}
2019-01-23 23:00:46 +00:00
func getRuntimeTunnelConfiguration ( completionHandler : @ escaping ( ( TunnelConfiguration ? ) -> Void ) ) {
guard status != . inactive , let session = tunnelProvider . connection as ? NETunnelProviderSession else {
completionHandler ( tunnelConfiguration )
return
}
guard nil != ( try ? session . sendProviderMessage ( Data ( bytes : [ 0 ] ) , responseHandler : {
guard self . status != . inactive , let data = $0 , let base = self . tunnelConfiguration , let settings = String ( data : data , encoding : . utf8 ) else {
completionHandler ( self . tunnelConfiguration )
return
}
completionHandler ( ( try ? TunnelConfiguration ( fromUapiConfig : settings , basedOn : base ) ) ? ? self . tunnelConfiguration )
} ) ) else {
completionHandler ( tunnelConfiguration )
return
}
}
2018-11-12 10:34:03 +00:00
func refreshStatus ( ) {
2019-01-25 12:44:48 +00:00
#if os ( macOS )
// I n m a c O S , w e w a i t f o r a f e w s e c o n d s a f t e r d e a c t i v a t i o n t o w o r k a r o u n d a s y s t e m b u g .
// I f a t u n n e l g e t s a c t i v a t e d i n t h i s t i m e i n t e r v a l , i t ' s s t o p p e d b y t h e s y s t e m a u t o m a t i c a l l y i n ~ 2 5 s e c o n d s .
if self . status = = . deactivating && tunnelProvider . connection . status = = . disconnected {
self . deactivationTimer ? . invalidate ( )
2019-02-05 15:56:17 +00:00
let deactivationTimer = Timer ( timeInterval : 6 /* s e c o n d s */ , repeats : false ) { [ weak self ] _ in
2019-01-25 12:44:48 +00:00
guard let self = self else { return }
self . status = TunnelStatus ( from : self . tunnelProvider . connection . status )
self . isActivateOnDemandEnabled = self . tunnelProvider . isOnDemandEnabled
}
self . deactivationTimer = deactivationTimer
2019-01-31 11:34:34 +00:00
RunLoop . main . add ( deactivationTimer , forMode : . common )
2019-01-25 12:44:48 +00:00
return
}
#endif
status = TunnelStatus ( from : tunnelProvider . connection . status )
2018-12-14 23:12:59 +00:00
isActivateOnDemandEnabled = tunnelProvider . isOnDemandEnabled
2018-11-09 13:49:32 +00:00
}
2018-12-13 18:58:50 +00:00
// s w i f t l i n t : d i s a b l e : n e x t f u n c t i o n _ b o d y _ l e n g t h
2018-12-13 18:31:41 +00:00
fileprivate func startActivation ( recursionCount : UInt = 0 , lastError : Error ? = nil , 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-17 13:26:26 +00:00
activationDelegate ? . tunnelActivationAttemptFailed ( tunnel : self , error : . failedBecauseOfTooManyErrors ( lastSystemError : lastError ! ) )
2018-10-31 14:58:03 +00:00
return
}
2018-12-14 23:12:59 +00:00
wg_log ( . debug , message : " startActivation: Entering (tunnel: \( name ) ) " )
2018-10-31 11:12:29 +00:00
2018-12-14 23:12:59 +00:00
status = . activating // E n s u r e t h a t n o o t h e r t u n n e l c a n a t t e m p t a c t i v a t i o n u n t i l t h i s t u n n e l i s d o n e t r y i n g
2018-12-13 18:04:00 +00:00
2018-12-12 18:28:27 +00:00
guard tunnelProvider . isEnabled else {
2018-10-31 11:12:29 +00:00
// I n c a s e t h e t u n n e l h a d g o t t e n d i s a b l e d , r e - e n a b l e a n d s a v e i t ,
// t h e n c a l l t h i s f u n c t i o n a g a i n .
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-17 13:26:26 +00:00
activationDelegate ? . tunnelActivationAttemptFailed ( tunnel : self , error : . failedWhileSaving ( systemError : error ! ) )
2018-10-31 11:12:29 +00:00
return
}
2018-12-22 01:21:07 +00:00
wg_log ( . debug , staticMessage : " startActivation: Tunnel saved after re-enabling, invoking startActivation " )
2018-12-17 05:51:25 +00:00
self . startActivation ( recursionCount : recursionCount + 1 , lastError : NEVPNError ( NEVPNError . configurationUnknown ) , activationDelegate : activationDelegate )
2018-10-31 11:12:29 +00:00
}
return
}
2018-10-30 11:04:46 +00:00
// S t a r t t h e t u n n e l
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-14 23:12:59 +00:00
isAttemptingActivation = true
2018-12-13 20:54:53 +00:00
let activationAttemptId = UUID ( ) . uuidString
self . activationAttemptId = activationAttemptId
try ( tunnelProvider . connection as ? NETunnelProviderSession ) ? . startTunnel ( options : [ " activationAttemptId " : activationAttemptId ] )
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-14 23:12:59 +00:00
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-17 13:26:26 +00:00
activationDelegate ? . tunnelActivationAttemptFailed ( tunnel : self , error : . failedWhileStarting ( systemError : error ) )
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-17 13:26:26 +00:00
activationDelegate ? . tunnelActivationAttemptFailed ( tunnel : self , error : . failedWhileStarting ( systemError : systemError ) )
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
2018-12-17 13:26:26 +00:00
activationDelegate ? . tunnelActivationAttemptFailed ( tunnel : self , error : . failedWhileLoading ( systemError : systemError ) )
2018-10-31 11:12:29 +00:00
return
}
2018-12-22 01:21:07 +00:00
wg_log ( . debug , staticMessage : " startActivation: Tunnel reloaded, invoking startActivation " )
2018-12-13 18:31:41 +00:00
self . startActivation ( recursionCount : recursionCount + 1 , lastError : systemError , 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
}
}
2019-01-22 13:41:51 +00:00
extension NETunnelProviderManager {
var tunnelConfiguration : TunnelConfiguration ? {
return ( protocolConfiguration as ? NETunnelProviderProtocol ) ? . asTunnelConfiguration ( called : localizedDescription )
}
}