diff options
author | Andrej Mihajlov <and@mullvad.net> | 2020-12-02 12:27:39 +0100 |
---|---|---|
committer | Andrej Mihajlov <and@mullvad.net> | 2020-12-03 13:32:24 +0100 |
commit | ec574085703ea1c8b2d4538596961beb910c4382 (patch) | |
tree | 73cf8bbdb74fe5575606664bccd0232ffa911803 /Sources/WireGuardApp/Tunnel/ActivateOnDemandOption.swift | |
parent | WireGuardKit: Assert that resolutionResults must not contain failures (diff) | |
download | wireguard-apple-ec574085703ea1c8b2d4538596961beb910c4382.tar.xz wireguard-apple-ec574085703ea1c8b2d4538596961beb910c4382.zip |
Move all source files to `Sources/` and rename WireGuardKit targets
Signed-off-by: Andrej Mihajlov <and@mullvad.net>
Diffstat (limited to 'Sources/WireGuardApp/Tunnel/ActivateOnDemandOption.swift')
-rw-r--r-- | Sources/WireGuardApp/Tunnel/ActivateOnDemandOption.swift | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/Sources/WireGuardApp/Tunnel/ActivateOnDemandOption.swift b/Sources/WireGuardApp/Tunnel/ActivateOnDemandOption.swift new file mode 100644 index 0000000..d44e1d6 --- /dev/null +++ b/Sources/WireGuardApp/Tunnel/ActivateOnDemandOption.swift @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: MIT +// Copyright © 2018-2019 WireGuard LLC. All Rights Reserved. + +import NetworkExtension + +enum ActivateOnDemandOption: Equatable { + case off + case wiFiInterfaceOnly(ActivateOnDemandSSIDOption) + case nonWiFiInterfaceOnly + case anyInterface(ActivateOnDemandSSIDOption) +} + +#if os(iOS) +private let nonWiFiInterfaceType: NEOnDemandRuleInterfaceType = .cellular +#elseif os(macOS) +private let nonWiFiInterfaceType: NEOnDemandRuleInterfaceType = .ethernet +#else +#error("Unimplemented") +#endif + +enum ActivateOnDemandSSIDOption: Equatable { + case anySSID + case onlySpecificSSIDs([String]) + case exceptSpecificSSIDs([String]) +} + +extension ActivateOnDemandOption { + func apply(on tunnelProviderManager: NETunnelProviderManager) { + let rules: [NEOnDemandRule]? + switch self { + case .off: + rules = nil + case .wiFiInterfaceOnly(let ssidOption): + rules = ssidOnDemandRules(option: ssidOption) + [NEOnDemandRuleDisconnect(interfaceType: nonWiFiInterfaceType)] + case .nonWiFiInterfaceOnly: + rules = [NEOnDemandRuleConnect(interfaceType: nonWiFiInterfaceType), NEOnDemandRuleDisconnect(interfaceType: .wiFi)] + case .anyInterface(let ssidOption): + if case .anySSID = ssidOption { + rules = [NEOnDemandRuleConnect(interfaceType: .any)] + } else { + rules = ssidOnDemandRules(option: ssidOption) + [NEOnDemandRuleConnect(interfaceType: nonWiFiInterfaceType)] + } + } + tunnelProviderManager.onDemandRules = rules + tunnelProviderManager.isOnDemandEnabled = self != .off + } + + init(from tunnelProviderManager: NETunnelProviderManager) { + if tunnelProviderManager.isOnDemandEnabled, let onDemandRules = tunnelProviderManager.onDemandRules { + self = ActivateOnDemandOption.create(from: onDemandRules) + } else { + self = .off + } + } + + private static func create(from rules: [NEOnDemandRule]) -> ActivateOnDemandOption { + switch rules.count { + case 0: + return .off + case 1: + let rule = rules[0] + guard rule.action == .connect else { return .off } + return .anyInterface(.anySSID) + case 2: + guard let connectRule = rules.first(where: { $0.action == .connect }) else { + wg_log(.error, message: "Unexpected onDemandRules set on tunnel provider manager: \(rules.count) rules found but no connect rule.") + return .off + } + guard let disconnectRule = rules.first(where: { $0.action == .disconnect }) else { + wg_log(.error, message: "Unexpected onDemandRules set on tunnel provider manager: \(rules.count) rules found but no disconnect rule.") + return .off + } + if connectRule.interfaceTypeMatch == .wiFi && disconnectRule.interfaceTypeMatch == nonWiFiInterfaceType { + return .wiFiInterfaceOnly(.anySSID) + } else if connectRule.interfaceTypeMatch == nonWiFiInterfaceType && disconnectRule.interfaceTypeMatch == .wiFi { + return .nonWiFiInterfaceOnly + } else { + wg_log(.error, message: "Unexpected onDemandRules set on tunnel provider manager: \(rules.count) rules found but interface types are inconsistent.") + return .off + } + case 3: + guard let ssidRule = rules.first(where: { $0.interfaceTypeMatch == .wiFi && $0.ssidMatch != nil }) else { return .off } + guard let nonWiFiRule = rules.first(where: { $0.interfaceTypeMatch == nonWiFiInterfaceType }) else { return .off } + let ssids = ssidRule.ssidMatch! + switch (ssidRule.action, nonWiFiRule.action) { + case (.connect, .connect): + return .anyInterface(.onlySpecificSSIDs(ssids)) + case (.connect, .disconnect): + return .wiFiInterfaceOnly(.onlySpecificSSIDs(ssids)) + case (.disconnect, .connect): + return .anyInterface(.exceptSpecificSSIDs(ssids)) + case (.disconnect, .disconnect): + return .wiFiInterfaceOnly(.exceptSpecificSSIDs(ssids)) + default: + wg_log(.error, message: "Unexpected onDemandRules set on tunnel provider manager: \(rules.count) rules found") + return .off + } + default: + wg_log(.error, message: "Unexpected number of onDemandRules set on tunnel provider manager: \(rules.count) rules found") + return .off + } + } +} + +private extension NEOnDemandRuleConnect { + convenience init(interfaceType: NEOnDemandRuleInterfaceType, ssids: [String]? = nil) { + self.init() + interfaceTypeMatch = interfaceType + ssidMatch = ssids + } +} + +private extension NEOnDemandRuleDisconnect { + convenience init(interfaceType: NEOnDemandRuleInterfaceType, ssids: [String]? = nil) { + self.init() + interfaceTypeMatch = interfaceType + ssidMatch = ssids + } +} + +private func ssidOnDemandRules(option: ActivateOnDemandSSIDOption) -> [NEOnDemandRule] { + switch option { + case .anySSID: + return [NEOnDemandRuleConnect(interfaceType: .wiFi)] + case .onlySpecificSSIDs(let ssids): + assert(!ssids.isEmpty) + return [NEOnDemandRuleConnect(interfaceType: .wiFi, ssids: ssids), + NEOnDemandRuleDisconnect(interfaceType: .wiFi)] + case .exceptSpecificSSIDs(let ssids): + return [NEOnDemandRuleDisconnect(interfaceType: .wiFi, ssids: ssids), + NEOnDemandRuleConnect(interfaceType: .wiFi)] + } +} |