]> git.ipfire.org Git - thirdparty/wireguard-apple.git/commitdiff
Model, Tunnels manager: Rewrite the model for VPN-on-demand
authorRoopesh Chander <roop@roopc.net>
Mon, 12 Nov 2018 08:32:09 +0000 (14:02 +0530)
committerRoopesh Chander <roop@roopc.net>
Mon, 12 Nov 2018 13:54:13 +0000 (19:24 +0530)
The VPN-on-demand settings should not be part of the tunnel
configuration. Rather, the onDemandRules stored in the
tunnel provider configuration serve as the one place
where the VPN-on-demand settings are stored.

Signed-off-by: Roopesh Chander <roop@roopc.net>
WireGuard/Shared/Model/ActivationType.swift [deleted file]
WireGuard/Shared/Model/Configuration.swift
WireGuard/Shared/NETunnelProviderProtocol+Extension.swift
WireGuard/WireGuard.xcodeproj/project.pbxproj
WireGuard/WireGuard/VPN/ActivateOnDemandSetting.swift [new file with mode: 0644]
WireGuard/WireGuard/VPN/TunnelsManager.swift

diff --git a/WireGuard/Shared/Model/ActivationType.swift b/WireGuard/Shared/Model/ActivationType.swift
deleted file mode 100644 (file)
index ea5927d..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright © 2018 WireGuard LLC. All Rights Reserved.
-
-enum ActivationType {
-    case activateManually
-    case useOnDemandOverWifiAndCellular
-    case useOnDemandOverWifiOnly
-    case useOnDemandOverCellularOnly
-}
-
-extension ActivationType: Codable {
-    // We use separate coding keys in case we might have a enum with associated values in the future
-    enum CodingKeys: CodingKey {
-        case activateManually
-        case useOnDemandOverWifiAndCellular
-        case useOnDemandOverWifiOnly
-        case useOnDemandOverCellularOnly
-    }
-
-    // Decoding error
-    enum DecodingError: Error {
-        case invalidInput
-    }
-
-    // Encoding
-    func encode(to encoder: Encoder) throws {
-        var container = encoder.container(keyedBy: CodingKeys.self)
-        switch self {
-        case .activateManually:
-            try container.encode(true, forKey: CodingKeys.activateManually)
-        case .useOnDemandOverWifiAndCellular:
-            try container.encode(true, forKey: CodingKeys.useOnDemandOverWifiAndCellular)
-        case .useOnDemandOverWifiOnly:
-            try container.encode(true, forKey: CodingKeys.useOnDemandOverWifiOnly)
-        case .useOnDemandOverCellularOnly:
-            try container.encode(true, forKey: CodingKeys.useOnDemandOverCellularOnly)
-        }
-    }
-
-    // Decoding
-    init(from decoder: Decoder) throws {
-        let container = try decoder.container(keyedBy: CodingKeys.self)
-
-        if let isValid = try? container.decode(Bool.self, forKey: CodingKeys.activateManually), isValid {
-            self = .activateManually
-            return
-        }
-
-        if let isValid = try? container.decode(Bool.self, forKey: CodingKeys.useOnDemandOverWifiAndCellular), isValid {
-            self = .useOnDemandOverWifiAndCellular
-            return
-        }
-
-        if let isValid = try? container.decode(Bool.self, forKey: CodingKeys.useOnDemandOverWifiOnly), isValid {
-            self = .useOnDemandOverWifiOnly
-            return
-        }
-
-        if let isValid = try? container.decode(Bool.self, forKey: CodingKeys.useOnDemandOverCellularOnly), isValid {
-            self = .useOnDemandOverCellularOnly
-            return
-        }
-
-        throw DecodingError.invalidInput
-    }
-}
index f6598bba980122c1516ac2c846e3d8c767c81d0a..41ff7bc013c4bc586ea8a84b0741e8d4bac31f40 100644 (file)
@@ -4,14 +4,12 @@
 import Foundation
 
 @available(OSX 10.14, iOS 12.0, *)
-final class TunnelConfiguration {
+final class TunnelConfiguration: Codable {
     var interface: InterfaceConfiguration
     let peers: [PeerConfiguration]
-    var activationType: ActivationType
     init(interface: InterfaceConfiguration, peers: [PeerConfiguration]) {
         self.interface = interface
         self.peers = peers
-        self.activationType = .activateManually
 
         let peerPublicKeysArray = peers.map { $0.publicKey }
         let peerPublicKeysSet = Set<Data>(peerPublicKeysArray)
@@ -57,21 +55,3 @@ struct PeerConfiguration: Codable {
         if (publicKey.count != 32) { fatalError("Invalid public key") }
     }
 }
-
-extension TunnelConfiguration: Encodable { }
-extension TunnelConfiguration: Decodable {
-    enum CodingKeys: CodingKey {
-        case interface
-        case peers
-        case activationType
-    }
-    convenience init(from decoder: Decoder) throws {
-        let values = try decoder.container(keyedBy: CodingKeys.self)
-        let interface = try values.decode(InterfaceConfiguration.self, forKey: .interface)
-        let peers = try values.decode([PeerConfiguration].self, forKey: .peers)
-        let activationType = (try? values.decode(ActivationType.self, forKey: .activationType)) ?? .activateManually
-
-        self.init(interface: interface, peers: peers)
-        self.activationType = activationType
-    }
-}
index 591b0eb3c672b82567cb78d398f55cda4759227b..ec8b294a588485738b69e265cea5c490796a6cfa 100644 (file)
@@ -14,7 +14,7 @@ extension NETunnelProviderProtocol {
         providerBundleIdentifier = "\(appId).network-extension"
         providerConfiguration = [
             "tunnelConfiguration": serializedTunnelConfiguration,
-            "tunnelConfigurationVersion": 2
+            "tunnelConfigurationVersion": 1
         ]
 
         let endpoints = tunnelConfiguration.peers.compactMap({$0.endpoint})
index 5145ce14dc71609e0b999d2e5017516d6b174e84..508b0f929a0058b1890e6703cb0c0dcd913b05d5 100644 (file)
@@ -49,8 +49,7 @@
                6FFA5D952194454A0001E2F7 /* NETunnelProviderProtocol+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFA5D942194454A0001E2F7 /* NETunnelProviderProtocol+Extension.swift */; };
                6FFA5D96219446380001E2F7 /* NETunnelProviderProtocol+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFA5D942194454A0001E2F7 /* NETunnelProviderProtocol+Extension.swift */; };
                6FFA5DA021958ECC0001E2F7 /* ErrorNotifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFA5D9F21958ECC0001E2F7 /* ErrorNotifier.swift */; };
-               6FFA5DA42197085D0001E2F7 /* ActivationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFA5DA32197085D0001E2F7 /* ActivationType.swift */; };
-               6FFA5DA521970B370001E2F7 /* ActivationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFA5DA32197085D0001E2F7 /* ActivationType.swift */; };
+               6FFA5DA42197085D0001E2F7 /* ActivateOnDemandSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFA5DA32197085D0001E2F7 /* ActivateOnDemandSetting.swift */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
                6FF4AC482120B9E0002C96EB /* WireGuard.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WireGuard.entitlements; sourceTree = "<group>"; };
                6FFA5D942194454A0001E2F7 /* NETunnelProviderProtocol+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NETunnelProviderProtocol+Extension.swift"; sourceTree = "<group>"; };
                6FFA5D9F21958ECC0001E2F7 /* ErrorNotifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorNotifier.swift; sourceTree = "<group>"; };
-               6FFA5DA32197085D0001E2F7 /* ActivationType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivationType.swift; sourceTree = "<group>"; };
+               6FFA5DA32197085D0001E2F7 /* ActivateOnDemandSetting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivateOnDemandSetting.swift; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
                                6F7774E9217229DB006A79B3 /* IPAddressRange.swift */,
                                6F693A552179E556008551C1 /* Endpoint.swift */,
                                6F628C3E217F3413003482A3 /* DNSServer.swift */,
-                               6FFA5DA32197085D0001E2F7 /* ActivationType.swift */,
                        );
                        path = Model;
                        sourceTree = "<group>";
                        isa = PBXGroup;
                        children = (
                                6F7774EE21722D97006A79B3 /* TunnelsManager.swift */,
+                               6FFA5DA32197085D0001E2F7 /* ActivateOnDemandSetting.swift */,
                        );
                        path = VPN;
                        sourceTree = "<group>";
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               6FFA5DA521970B370001E2F7 /* ActivationType.swift in Sources */,
                                6FFA5DA021958ECC0001E2F7 /* ErrorNotifier.swift in Sources */,
                                6FFA5D96219446380001E2F7 /* NETunnelProviderProtocol+Extension.swift in Sources */,
                                6FFA5D8E2194370D0001E2F7 /* Configuration.swift in Sources */,
                                6FDEF802218646BA00D8FBF6 /* ZipArchive.swift in Sources */,
                                6FDEF806218725D200D8FBF6 /* SettingsTableViewController.swift in Sources */,
                                6F7774E1217181B1006A79B3 /* MainViewController.swift in Sources */,
-                               6FFA5DA42197085D0001E2F7 /* ActivationType.swift in Sources */,
+                               6FFA5DA42197085D0001E2F7 /* ActivateOnDemandSetting.swift in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
diff --git a/WireGuard/WireGuard/VPN/ActivateOnDemandSetting.swift b/WireGuard/WireGuard/VPN/ActivateOnDemandSetting.swift
new file mode 100644 (file)
index 0000000..a2cbe00
--- /dev/null
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: MIT
+// Copyright © 2018 WireGuard LLC. All Rights Reserved.
+
+import NetworkExtension
+
+struct ActivateOnDemandSetting {
+    var isActivateOnDemandEnabled: Bool
+    var activateOnDemandOption: ActivateOnDemandOption
+}
+
+enum ActivateOnDemandOption {
+    case none // Valid only when isActivateOnDemandEnabled is false
+    case useOnDemandOverWifiOrCellular
+    case useOnDemandOverWifiOnly
+    case useOnDemandOverCellularOnly
+}
+
+extension ActivateOnDemandSetting {
+    func apply(on tunnelProviderManager: NETunnelProviderManager) {
+        tunnelProviderManager.isOnDemandEnabled = isActivateOnDemandEnabled
+        let rules: [NEOnDemandRule]?
+        let connectRule = NEOnDemandRuleConnect()
+        let disconnectRule = NEOnDemandRuleDisconnect()
+        switch (activateOnDemandOption) {
+        case .none:
+            rules = nil
+        case .useOnDemandOverWifiOrCellular:
+            rules = [connectRule]
+        case .useOnDemandOverWifiOnly:
+            connectRule.interfaceTypeMatch = .wiFi
+            disconnectRule.interfaceTypeMatch = .cellular
+            rules = [connectRule, disconnectRule]
+        case .useOnDemandOverCellularOnly:
+            connectRule.interfaceTypeMatch = .cellular
+            disconnectRule.interfaceTypeMatch = .wiFi
+            rules = [connectRule, disconnectRule]
+        }
+        tunnelProviderManager.onDemandRules = rules
+    }
+
+    init(from tunnelProviderManager: NETunnelProviderManager) {
+        let rules = tunnelProviderManager.onDemandRules ?? []
+        let activateOnDemandOption: ActivateOnDemandOption
+        switch (rules.count) {
+        case 0:
+            activateOnDemandOption = .none
+        case 1:
+            let rule = rules[0]
+            precondition(rule.action == .connect)
+            activateOnDemandOption = .useOnDemandOverWifiOrCellular
+        case 2:
+            let connectRule = rules.first(where: { $0.action == .connect })!
+            let disconnectRule = rules.first(where: { $0.action == .disconnect })!
+            if (connectRule.interfaceTypeMatch == .wiFi && disconnectRule.interfaceTypeMatch == .cellular) {
+                activateOnDemandOption = .useOnDemandOverWifiOnly
+            } else if (connectRule.interfaceTypeMatch == .cellular && disconnectRule.interfaceTypeMatch == .wiFi) {
+                activateOnDemandOption = .useOnDemandOverCellularOnly
+            } else {
+                fatalError("Unexpected onDemandRules set on tunnel provider manager")
+            }
+        default:
+            fatalError("Unexpected number of onDemandRules set on tunnel provider manager")
+        }
+        self.activateOnDemandOption = activateOnDemandOption
+        if (activateOnDemandOption == .none) {
+            self.isActivateOnDemandEnabled = false
+        } else {
+            self.isActivateOnDemandEnabled = tunnelProviderManager.isOnDemandEnabled
+        }
+    }
+}
+
+extension ActivateOnDemandSetting {
+    static var defaultSetting = ActivateOnDemandSetting(isActivateOnDemandEnabled: false, activateOnDemandOption: .none)
+}
index 8eb21123ca1c2702c50195c864153ed958fe8f36..d75e6c056ef238f7e097275b21ea12d0ebc7c778 100644 (file)
@@ -54,7 +54,9 @@ class TunnelsManager {
         #endif
     }
 
-    func add(tunnelConfiguration: TunnelConfiguration, completionHandler: @escaping (TunnelContainer?, TunnelManagementError?) -> Void) {
+    func add(tunnelConfiguration: TunnelConfiguration,
+             activateOnDemandSetting: ActivateOnDemandSetting = ActivateOnDemandSetting.defaultSetting,
+             completionHandler: @escaping (TunnelContainer?, TunnelManagementError?) -> Void) {
         let tunnelName = tunnelConfiguration.interface.name
         if tunnelName.isEmpty {
             completionHandler(nil, TunnelManagementError.tunnelAlreadyExistsWithThatName)
@@ -72,13 +74,7 @@ class TunnelsManager {
         tunnelProviderManager.localizedDescription = tunnelName
         tunnelProviderManager.isEnabled = true
 
-        if (tunnelConfiguration.activationType == .activateManually) {
-            tunnelProviderManager.onDemandRules = []
-            tunnelProviderManager.isOnDemandEnabled = false
-        } else {
-            tunnelProviderManager.onDemandRules = onDemandRules(for: tunnelConfiguration.activationType)
-            tunnelProviderManager.isOnDemandEnabled = true
-        }
+        activateOnDemandSetting.apply(on: tunnelProviderManager)
 
         tunnelProviderManager.saveToPreferences { [weak self] (error) in
             defer { self?.isAddingTunnel = false }
@@ -114,7 +110,8 @@ class TunnelsManager {
         }
     }
 
-    func modify(tunnel: TunnelContainer, with tunnelConfiguration: TunnelConfiguration, completionHandler: @escaping (TunnelManagementError?) -> Void) {
+    func modify(tunnel: TunnelContainer, tunnelConfiguration: TunnelConfiguration,
+                activateOnDemandSetting: ActivateOnDemandSetting, completionHandler: @escaping (TunnelManagementError?) -> Void) {
         let tunnelName = tunnelConfiguration.interface.name
         if tunnelName.isEmpty {
             completionHandler(TunnelManagementError.tunnelAlreadyExistsWithThatName)
@@ -138,13 +135,7 @@ class TunnelsManager {
         tunnelProviderManager.localizedDescription = tunnelName
         tunnelProviderManager.isEnabled = true
 
-        if (tunnelConfiguration.activationType == .activateManually) {
-            tunnelProviderManager.onDemandRules = []
-            tunnelProviderManager.isOnDemandEnabled = false
-        } else {
-            tunnelProviderManager.onDemandRules = onDemandRules(for: tunnelConfiguration.activationType)
-            tunnelProviderManager.isOnDemandEnabled = true
-        }
+        activateOnDemandSetting.apply(on: tunnelProviderManager)
 
         tunnelProviderManager.saveToPreferences { [weak self] (error) in
             defer { self?.isModifyingTunnel = false }
@@ -229,26 +220,6 @@ class TunnelsManager {
             t.refreshConnectionStatus()
         }
     }
-
-    func onDemandRules(for activationType: ActivationType) -> [NEOnDemandRule] {
-        switch (activationType) {
-        case .activateManually: return []
-        case .useOnDemandOverWifiAndCellular:
-            return [NEOnDemandRuleConnect()]
-        case .useOnDemandOverWifiOnly:
-            let connectOnWifiRule = NEOnDemandRuleConnect()
-            connectOnWifiRule.interfaceTypeMatch = .wiFi
-            let disconnectOnCellularRule = NEOnDemandRuleDisconnect()
-            disconnectOnCellularRule.interfaceTypeMatch = .cellular
-            return [connectOnWifiRule, disconnectOnCellularRule]
-        case .useOnDemandOverCellularOnly:
-            let connectOnCellularRule = NEOnDemandRuleConnect()
-            connectOnCellularRule.interfaceTypeMatch = .cellular
-            let disconnectOnWifiRule = NEOnDemandRuleDisconnect()
-            disconnectOnWifiRule.interfaceTypeMatch = .wiFi
-            return [connectOnCellularRule, disconnectOnWifiRule]
-        }
-    }
 }
 
 class TunnelContainer: NSObject {
@@ -275,6 +246,10 @@ class TunnelContainer: NSObject {
         return (tunnelProvider.protocolConfiguration as! NETunnelProviderProtocol).tunnelConfiguration()
     }
 
+    func activateOnDemandSetting() -> ActivateOnDemandSetting {
+        return ActivateOnDemandSetting(from: tunnelProvider)
+    }
+
     func refreshConnectionStatus() {
         let status = TunnelStatus(from: self.tunnelProvider.connection.status)
         self.status = status