enum Keys: String {
case tunnelConfiguration = "TunnelConfiguration"
case tunnelConfigurationVersion = "TunnelConfigurationVersion"
- case isActivateOnDemandEnabled = "IsActivateOnDemandEnabled"
}
var tunnelConfiguration: TunnelConfiguration? {
return try? JSONDecoder().decode(TunnelConfiguration.self, from: tunnelConfigurationData!)
}
- var isActivateOnDemandEnabled: Bool {
- return providerConfiguration?[Keys.isActivateOnDemandEnabled.rawValue] as? Bool ?? false
- }
-
- convenience init?(tunnelConfiguration: TunnelConfiguration, isActivateOnDemandEnabled: Bool) {
+ convenience init?(tunnelConfiguration: TunnelConfiguration) {
assert(!tunnelConfiguration.interface.name.isEmpty)
guard let tunnelConfigData = try? JSONEncoder().encode(tunnelConfiguration) else { return nil }
providerBundleIdentifier = "\(appId).network-extension"
providerConfiguration = [
Keys.tunnelConfiguration.rawValue: tunnelConfigDictionary,
- Keys.tunnelConfigurationVersion.rawValue: tunnelConfigurationVersion,
- Keys.isActivateOnDemandEnabled.rawValue: isActivateOnDemandEnabled
+ Keys.tunnelConfigurationVersion.rawValue: tunnelConfigurationVersion
]
let endpoints = tunnelConfiguration.peers.compactMap { $0.endpoint }
private func migrateFromConfigurationV1() {
guard let serializedTunnelConfiguration = providerConfiguration?["tunnelConfiguration"] as? Data else { return }
guard let configuration = try? JSONDecoder().decode(LegacyTunnelConfiguration.self, from: serializedTunnelConfiguration) else { return }
- guard let isActivateOnDemandEnabled = providerConfiguration?["isActivateOnDemandEnabled"] as? Bool else { return }
guard let tunnelConfigData = try? JSONEncoder().encode(configuration.migrated) else { return }
guard let tunnelConfigDictionary = try? JSONSerialization.jsonObject(with: tunnelConfigData, options: .allowFragments) else { return }
providerConfiguration = [
Keys.tunnelConfiguration.rawValue: tunnelConfigDictionary,
- Keys.tunnelConfigurationVersion.rawValue: tunnelConfigurationVersion,
- Keys.isActivateOnDemandEnabled.rawValue: isActivateOnDemandEnabled
+ Keys.tunnelConfigurationVersion.rawValue: tunnelConfigurationVersion
]
}
"alertTunnelActivationFailureTitle" = "Activation failure";
"alertTunnelActivationFailureMessage" = "The tunnel could not be activated. Please ensure that you are connected to the Internet.";
+"alertTunnelActivationFailureOnDemandAddendum" = " This tunnel has Activate On Demand enabled, so this tunnel might be re-activated automatically by the OS. You may turn off Activate On Demand in this app by editing the tunnel configuration.";
"alertTunnelNameEmptyTitle" = "No name provided";
"alertTunnelNameEmptyMessage" = "Cannot create tunnel with an empty name";
let tunnelConfiguration = TunnelConfiguration(interface: interface, peers: [peer])
let tunnelProviderManager = NETunnelProviderManager()
- tunnelProviderManager.protocolConfiguration = NETunnelProviderProtocol(tunnelConfiguration: tunnelConfiguration, isActivateOnDemandEnabled: false)
+ tunnelProviderManager.protocolConfiguration = NETunnelProviderProtocol(tunnelConfiguration: tunnelConfiguration)
tunnelProviderManager.localizedDescription = tunnelName
tunnelProviderManager.isEnabled = true
}
enum TunnelsManagerActivationError: WireGuardAppError {
- case activationFailed
- case activationFailedWithExtensionError(title: String, message: String)
+ case activationFailed(wasOnDemandEnabled: Bool)
+ case activationFailedWithExtensionError(title: String, message: String, wasOnDemandEnabled: Bool)
var alertText: AlertText {
switch self {
- case .activationFailed:
- return (tr("alertTunnelActivationFailureTitle"), tr("alertTunnelActivationFailureMessage"))
- case .activationFailedWithExtensionError(let title, let message):
- return (title, message)
+ case .activationFailed(let wasOnDemandEnabled):
+ return (tr("alertTunnelActivationFailureTitle"), tr("alertTunnelActivationFailureMessage") + (wasOnDemandEnabled ? tr("alertTunnelActivationFailureOnDemandAddendum") : ""))
+ case .activationFailedWithExtensionError(let title, let message, let wasOnDemandEnabled):
+ return (title, message + (wasOnDemandEnabled ? tr("alertTunnelActivationFailureOnDemandAddendum") : ""))
}
}
}
}
let tunnelProviderManager = NETunnelProviderManager()
- tunnelProviderManager.protocolConfiguration = NETunnelProviderProtocol(tunnelConfiguration: tunnelConfiguration, isActivateOnDemandEnabled: activateOnDemandSetting.isActivateOnDemandEnabled)
+ tunnelProviderManager.protocolConfiguration = NETunnelProviderProtocol(tunnelConfiguration: tunnelConfiguration)
tunnelProviderManager.localizedDescription = tunnelName
tunnelProviderManager.isEnabled = true
let shouldRestartIfActive = !((tunnelProviderManager.protocolConfiguration as? NETunnelProviderProtocol)?.hasTunnelConfiguration(tunnelConfiguration: tunnelConfiguration) ?? false)
- tunnelProviderManager.protocolConfiguration = NETunnelProviderProtocol(tunnelConfiguration: tunnelConfiguration, isActivateOnDemandEnabled: activateOnDemandSetting.isActivateOnDemandEnabled)
+ tunnelProviderManager.protocolConfiguration = NETunnelProviderProtocol(tunnelConfiguration: tunnelConfiguration)
tunnelProviderManager.localizedDescription = tunnelName
tunnelProviderManager.isEnabled = true
} else if session.status == .disconnected {
tunnel.isAttemptingActivation = false
if let (title, message) = self.lastErrorTextFromNetworkExtension(for: tunnel) {
- self.activationDelegate?.tunnelActivationFailed(tunnel: tunnel, error: .activationFailedWithExtensionError(title: title, message: message))
+ self.activationDelegate?.tunnelActivationFailed(tunnel: tunnel, error: .activationFailedWithExtensionError(title: title, message: message, wasOnDemandEnabled: tunnelProvider.isOnDemandEnabled))
} else {
- self.activationDelegate?.tunnelActivationFailed(tunnel: tunnel, error: .activationFailed)
+ self.activationDelegate?.tunnelActivationFailed(tunnel: tunnel, error: .activationFailed(wasOnDemandEnabled: tunnelProvider.isOnDemandEnabled))
}
}
}
weak var tunnelProvider: NEPacketTunnelProvider?
var tunnelName: String?
- var isActivateOnDemandEnabled = false
init(activationAttemptId: String?, tunnelProvider: NEPacketTunnelProvider) {
self.activationAttemptId = activationAttemptId
}
func notify(_ error: PacketTunnelProviderError) {
- guard let (title, message) = errorMessage(for: error) else { return }
- if let activationAttemptId = activationAttemptId, let lastErrorFilePath = FileManager.networkExtensionLastErrorFileURL?.path {
- // The tunnel was started from the app
- let onDemandMessage = isActivateOnDemandEnabled ? " This tunnel has Activate On Demand enabled, so this tunnel might be activated automatically. You may turn off Activate On Demand in the WireGuard app by navigating to: '\(tunnelName ?? "tunnel")' > Edit." : ""
- let errorMessageData = "\(activationAttemptId)\n\(title)\n\(message)\(onDemandMessage)".data(using: .utf8)
- FileManager.default.createFile(atPath: lastErrorFilePath, contents: errorMessageData, attributes: nil)
- } else {
- // The tunnel was probably started from iOS Settings app or activated on-demand
- if let tunnelProvider = self.tunnelProvider {
- // displayMessage() is deprecated, but there's no better alternative if invoked from iOS Settings
- if !isActivateOnDemandEnabled { // If using activate-on-demand, don't use displayMessage
- tunnelProvider.displayMessage("\(title): \(message)") { _ in }
- }
- }
- }
+ guard let (title, message) = errorMessage(for: error), let activationAttemptId = activationAttemptId, let lastErrorFilePath = FileManager.networkExtensionLastErrorFileURL?.path else { return }
+ let errorMessageData = "\(activationAttemptId)\n\(title)\n\(message)".data(using: .utf8)
+ FileManager.default.createFile(atPath: lastErrorFilePath, contents: errorMessageData, attributes: nil)
}
static func removeLastErrorFile() {
networkMonitor?.cancel()
}
- //swiftlint:disable:next function_body_length
override func startTunnel(options: [String: NSObject]?, completionHandler startTunnelCompletionHandler: @escaping (Error?) -> Void) {
let activationAttemptId = options?["activationAttemptId"] as? String
let errorNotifier = ErrorNotifier(activationAttemptId: activationAttemptId, tunnelProvider: self)
configureLogger()
let tunnelName = tunnelConfiguration.interface.name
- wg_log(.info, message: "Starting tunnel '\(tunnelName)'")
+ wg_log(.info, message: "Starting tunnel '\(tunnelName)' from the " + (activationAttemptId == nil ? "OS directly, rather than the app" : "app"))
- if activationAttemptId != nil {
- wg_log(.info, staticMessage: "Tunnel activated from the app")
- } else {
- wg_log(.info, staticMessage: "Tunnel not activated from the app")
- }
-
- let isActivateOnDemandEnabled = tunnelProviderProtocol.isActivateOnDemandEnabled
- if isActivateOnDemandEnabled {
- wg_log(.info, staticMessage: "Tunnel has Activate On Demand enabled")
- } else {
- wg_log(.info, staticMessage: "Tunnel has Activate On Demand disabled")
- }
-
- errorNotifier.isActivateOnDemandEnabled = isActivateOnDemandEnabled
errorNotifier.tunnelName = tunnelName
let endpoints = tunnelConfiguration.peers.map { $0.endpoint }