diff options
author | Roopesh Chander <roop@roopc.net> | 2018-12-03 15:34:58 +0530 |
---|---|---|
committer | Roopesh Chander <roop@roopc.net> | 2018-12-03 18:51:42 +0530 |
commit | e1b258353c312a87129d771405148e83f0af5a04 (patch) | |
tree | c2f70b3985167cd2bb321a6992fcb23e29ad1de5 /WireGuard/WireGuard | |
parent | Settings: Export log file (diff) | |
download | wireguard-apple-e1b258353c312a87129d771405148e83f0af5a04.tar.xz wireguard-apple-e1b258353c312a87129d771405148e83f0af5a04.zip |
VPN: Error out when tunnel activation fails because there's no internet
Signed-off-by: Roopesh Chander <roop@roopc.net>
Diffstat (limited to 'WireGuard/WireGuard')
4 files changed, 105 insertions, 7 deletions
diff --git a/WireGuard/WireGuard/UI/iOS/ErrorPresenter.swift b/WireGuard/WireGuard/UI/iOS/ErrorPresenter.swift index 8aa0c1a..1116f61 100644 --- a/WireGuard/WireGuard/UI/iOS/ErrorPresenter.swift +++ b/WireGuard/WireGuard/UI/iOS/ErrorPresenter.swift @@ -21,8 +21,12 @@ class ErrorPresenter { return ("Unable to remove tunnel", "Internal error") // TunnelActivationError - case TunnelActivationError.tunnelActivationFailed: + case TunnelActivationError.tunnelActivationAttemptFailed: return ("Activation failure", "The tunnel could not be activated due to an internal error") + case TunnelActivationError.tunnelActivationFailedInternalError: + return ("Activation failure", "The tunnel could not be activated due to an internal error") + case TunnelActivationError.tunnelActivationFailedNoInternetConnection: + return ("Activation failure", "No internet connection") // Importing a zip file case ZipArchiveError.cantOpenInputZipFile: diff --git a/WireGuard/WireGuard/UI/iOS/TunnelsListTableViewController.swift b/WireGuard/WireGuard/UI/iOS/TunnelsListTableViewController.swift index 6e77433..c2c7b58 100644 --- a/WireGuard/WireGuard/UI/iOS/TunnelsListTableViewController.swift +++ b/WireGuard/WireGuard/UI/iOS/TunnelsListTableViewController.swift @@ -80,6 +80,7 @@ class TunnelsListTableViewController: UIViewController { busyIndicator.stopAnimating() tunnelsManager.delegate = s + tunnelsManager.activationDelegate = s s.tunnelsManager = tunnelsManager s.onTunnelsManagerReady?(tunnelsManager) s.onTunnelsManagerReady = nil @@ -312,6 +313,14 @@ extension TunnelsListTableViewController: TunnelsManagerDelegate { } } +// MARK: TunnelActivationDelegate + +extension TunnelsListTableViewController: TunnelActivationDelegate { + func tunnelActivationFailed(tunnel: TunnelContainer, error: TunnelActivationError) { + ErrorPresenter.showErrorAlert(error: error, from: self) + } +} + class TunnelsListTableViewCell: UITableViewCell { static let id: String = "TunnelsListTableViewCell" var tunnel: TunnelContainer? { diff --git a/WireGuard/WireGuard/VPN/InternetReachability.swift b/WireGuard/WireGuard/VPN/InternetReachability.swift new file mode 100644 index 0000000..7298d3f --- /dev/null +++ b/WireGuard/WireGuard/VPN/InternetReachability.swift @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +// Copyright © 2018 WireGuard LLC. All Rights Reserved. + +import SystemConfiguration + +class InternetReachability { + + enum Status { + case unknown + case notReachable + case reachableOverWiFi + case reachableOverCellular + } + + static func currentStatus() -> Status { + var status: Status = .unknown + if let reachabilityRef = InternetReachability.reachabilityRef() { + var flags = SCNetworkReachabilityFlags(rawValue: 0) + SCNetworkReachabilityGetFlags(reachabilityRef, &flags) + status = Status(reachabilityFlags: flags) + } + return status + } + + private static func reachabilityRef() -> SCNetworkReachability? { + let addrIn = sockaddr_in(sin_len: UInt8(MemoryLayout<sockaddr_in>.size), + sin_family: sa_family_t(AF_INET), + sin_port: 0, + sin_addr: in_addr(s_addr: 0), + sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)) + return withUnsafePointer(to: addrIn) { (addrInPtr) -> SCNetworkReachability? in + addrInPtr.withMemoryRebound(to: sockaddr.self, capacity: 1) { (addrPtr) -> SCNetworkReachability? in + return SCNetworkReachabilityCreateWithAddress(nil, addrPtr) + } + } + } +} + +extension InternetReachability.Status { + init(reachabilityFlags flags: SCNetworkReachabilityFlags) { + var status: InternetReachability.Status = .notReachable + if (flags.contains(.reachable)) { + if (flags.contains(.isWWAN)) { + status = .reachableOverCellular + } else { + status = .reachableOverWiFi + } + } + self = status + } +} diff --git a/WireGuard/WireGuard/VPN/TunnelsManager.swift b/WireGuard/WireGuard/VPN/TunnelsManager.swift index 0a3dd87..4306fd1 100644 --- a/WireGuard/WireGuard/VPN/TunnelsManager.swift +++ b/WireGuard/WireGuard/VPN/TunnelsManager.swift @@ -12,8 +12,14 @@ protocol TunnelsManagerDelegate: class { func tunnelRemoved(at: Int) } +protocol TunnelActivationDelegate: class { + func tunnelActivationFailed(tunnel: TunnelContainer, error: TunnelActivationError) +} + enum TunnelActivationError: Error { - case tunnelActivationFailed + case tunnelActivationAttemptFailed // startTunnel() throwed + case tunnelActivationFailedInternalError // startTunnel() succeeded, but activation failed + case tunnelActivationFailedNoInternetConnection // startTunnel() succeeded, but activation failed since no internet case attemptingActivationWhenTunnelIsNotInactive case attemptingDeactivationWhenTunnelIsInactive } @@ -30,6 +36,7 @@ class TunnelsManager { private var tunnels: [TunnelContainer] weak var delegate: TunnelsManagerDelegate? + weak var activationDelegate: TunnelActivationDelegate? private var isAddingTunnel: Bool = false private var isModifyingTunnel: Bool = false @@ -212,14 +219,27 @@ class TunnelsManager { completionHandler(TunnelActivationError.attemptingActivationWhenTunnelIsNotInactive) return } + + func _startActivation(of tunnel: TunnelContainer, completionHandler: @escaping (Error?) -> Void) { + tunnel.onActivationCommitted = { [weak self] (success) in + if (!success) { + let error = (InternetReachability.currentStatus() == .notReachable ? + TunnelActivationError.tunnelActivationFailedNoInternetConnection : + TunnelActivationError.tunnelActivationFailedInternalError) + self?.activationDelegate?.tunnelActivationFailed(tunnel: tunnel, error: error) + } + } + tunnel.startActivation(completionHandler: completionHandler) + } + if let tunnelInOperation = tunnels.first(where: { $0.status != .inactive }) { tunnel.status = .waiting tunnelInOperation.onDeactivationComplete = { - tunnel.startActivation(completionHandler: completionHandler) + _startActivation(of: tunnel, completionHandler: completionHandler) } startDeactivation(of: tunnelInOperation) } else { - tunnel.startActivation(completionHandler: completionHandler) + _startActivation(of: tunnel, completionHandler: completionHandler) } } @@ -249,6 +269,8 @@ class TunnelContainer: NSObject { } } + var isAttemptingActivation: Bool = false + var onActivationCommitted: ((Bool) -> Void)? var onDeactivationComplete: (() -> Void)? fileprivate let tunnelProvider: NETunnelProviderManager @@ -289,8 +311,8 @@ class TunnelContainer: NSObject { guard let tunnelConfiguration = tunnelConfiguration() else { fatalError() } onDeactivationComplete = nil - startActivation(tunnelConfiguration: tunnelConfiguration, - completionHandler: completionHandler) + isAttemptingActivation = true + startActivation(tunnelConfiguration: tunnelConfiguration, completionHandler: completionHandler) } fileprivate func startActivation(recursionCount: UInt = 0, @@ -299,7 +321,7 @@ class TunnelContainer: NSObject { completionHandler: @escaping (Error?) -> Void) { if (recursionCount >= 8) { os_log("startActivation: Failed after 8 attempts. Giving up with %{public}@", log: OSLog.default, type: .error, "\(lastError!)") - completionHandler(TunnelActivationError.tunnelActivationFailed) + completionHandler(TunnelActivationError.tunnelActivationAttemptFailed) return } @@ -386,6 +408,18 @@ class TunnelContainer: NSObject { object: connection, queue: nil) { [weak self] (_) in guard let s = self else { return } + if (s.isAttemptingActivation) { + if (connection.status == .connecting || connection.status == .connected) { + // We tried to start the tunnel, and that attempt is on track to become succeessful + s.onActivationCommitted?(true) + s.onActivationCommitted = nil + } else if (connection.status == .disconnecting || connection.status == .disconnected) { + // We tried to start the tunnel, but that attempt didn't succeed + s.onActivationCommitted?(false) + s.onActivationCommitted = nil + } + s.isAttemptingActivation = false + } if ((s.status == .restarting) && (connection.status == .disconnected || connection.status == .disconnecting)) { // Don't change s.status when disconnecting for a restart if (connection.status == .disconnected) { |