diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2021-03-17 13:27:59 +0100 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2021-03-19 14:07:57 +0100 |
| commit | 303573b7b6b882af8a09f46148781e9675376847 (patch) | |
| tree | c31298cdf31b20111c22ad82775ced6e74ec81a6 | |
| parent | cf005068637c57ac5500cb67cbf35734f724fc6a (diff) | |
| download | mullvadvpn-303573b7b6b882af8a09f46148781e9675376847.tar.xz mullvadvpn-303573b7b6b882af8a09f46148781e9675376847.zip | |
SimulatorTunnelProviderHost: pick the actual tunnel based on relay constraints
| -rw-r--r-- | ios/MullvadVPN.xcodeproj/project.pbxproj | 6 | ||||
| -rw-r--r-- | ios/MullvadVPN/Operations/OperationObserver.swift | 1 | ||||
| -rw-r--r-- | ios/MullvadVPN/Operations/TransformOperationObserver.swift | 6 | ||||
| -rw-r--r-- | ios/MullvadVPN/RelaySelector.swift | 11 | ||||
| -rw-r--r-- | ios/MullvadVPN/SimulatorTunnelProvider.swift | 72 | ||||
| -rw-r--r-- | ios/MullvadVPN/SimulatorTunnelProviderHost.swift | 87 | ||||
| -rw-r--r-- | ios/PacketTunnel/PacketTunnelProvider.swift | 11 |
7 files changed, 156 insertions, 38 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 25de8e2f1b..1a591c36f9 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -151,6 +151,7 @@ 58B0A2AA238EE6A900BC001D /* RelaySelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58781CD422AFBA39009B9D8E /* RelaySelector.swift */; }; 58B0A2AC238EE6D500BC001D /* IPAddress+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5840250022B1124600E4CFEC /* IPAddress+Codable.swift */; }; 58B0A2AD238EE6EC00BC001D /* MullvadEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5840250322B11AB700E4CFEC /* MullvadEndpoint.swift */; }; + 58B67B482602079E008EF58E /* RelaySelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58781CD422AFBA39009B9D8E /* RelaySelector.swift */; }; 58B8743222B25A7600015324 /* WireguardAssociatedAddresses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58B8743122B25A7600015324 /* WireguardAssociatedAddresses.swift */; }; 58B8743B22B788D200015324 /* PacketTunnelSettingsGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58B8743722B25EAB00015324 /* PacketTunnelSettingsGenerator.swift */; }; 58B9814E24FEA70D00C0D59E /* WireguardKeysViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 58B9814D24FEA70D00C0D59E /* WireguardKeysViewController.xib */; }; @@ -170,6 +171,8 @@ 58C3B06724EA768100C0348E /* LogStreamerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C3B06624EA768100C0348E /* LogStreamerViewController.swift */; }; 58C3B06924EAA25000C0348E /* StringStreamIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C3B06824EAA25000C0348E /* StringStreamIterator.swift */; }; 58C4CB0124EBE5A700A22D49 /* LogEntryParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C4CB0024EBE5A700A22D49 /* LogEntryParser.swift */; }; + 58CAF4EA26025927007C5886 /* PacketTunnelIpc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5845F841236CBACD00B2D93C /* PacketTunnelIpc.swift */; }; + 58CAF4EF26025954007C5886 /* SimulatorTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58BA693023EADA6A009DC256 /* SimulatorTunnelProvider.swift */; }; 58CB0EE024B86751001EF0D8 /* MullvadRest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CB0EDF24B86751001EF0D8 /* MullvadRest.swift */; }; 58CB0EE124B86751001EF0D8 /* MullvadRest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CB0EDF24B86751001EF0D8 /* MullvadRest.swift */; }; 58CC40EF24A601900019D96E /* ObserverList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CC40EE24A601900019D96E /* ObserverList.swift */; }; @@ -905,6 +908,7 @@ 5896AE81246ACE81005B36CB /* KeychainMatchLimit.swift in Sources */, 5896AE80246ACE79005B36CB /* KeychainClass.swift in Sources */, 582AE3132440CA2700E6733A /* AccountTokenInput.swift in Sources */, + 58CAF4EF26025954007C5886 /* SimulatorTunnelProvider.swift in Sources */, 5857F23724C8446400CF6F47 /* AssociatedValue.swift in Sources */, 5857F23B24C8448600CF6F47 /* OperationProtocol.swift in Sources */, 58B0A2AA238EE6A900BC001D /* RelaySelector.swift in Sources */, @@ -928,6 +932,7 @@ 5896AE7E246ACE65005B36CB /* KeychainAttributes.swift in Sources */, 58B0A2A9238EE6A100BC001D /* RelayConstraints.swift in Sources */, 5807E2C2243203D000F5FF30 /* StringTests.swift in Sources */, + 58CAF4EA26025927007C5886 /* PacketTunnelIpc.swift in Sources */, 5857F23E24C844A000CF6F47 /* OperationBlockObserver.swift in Sources */, 5857F23024C843ED00CF6F47 /* ChainedError.swift in Sources */, 58A8BE81239FBE62006B74AC /* IPEndpoint.swift in Sources */, @@ -994,6 +999,7 @@ 580EE20F24B322E700F9D8A1 /* TransformOperation.swift in Sources */, 58B8743222B25A7600015324 /* WireguardAssociatedAddresses.swift in Sources */, 5850368C25A49E2200A43E93 /* PrivateKeyWithMetadata.swift in Sources */, + 58B67B482602079E008EF58E /* RelaySelector.swift in Sources */, 58DF28A52417CB4B00E836B0 /* AppStorePaymentManager.swift in Sources */, 580EE22124B3240100F9D8A1 /* TransformOperationObserver.swift in Sources */, 582BB1AF229566420055B6EF /* SettingsCell.swift in Sources */, diff --git a/ios/MullvadVPN/Operations/OperationObserver.swift b/ios/MullvadVPN/Operations/OperationObserver.swift index 295b993a10..a7d3ea3aa3 100644 --- a/ios/MullvadVPN/Operations/OperationObserver.swift +++ b/ios/MullvadVPN/Operations/OperationObserver.swift @@ -11,6 +11,7 @@ import Foundation protocol OperationObserver { associatedtype OperationType: OperationProtocol + func operationWillExecute(_ operation: OperationType) func operationWillFinish(_ operation: OperationType) func operationDidFinish(_ operation: OperationType) } diff --git a/ios/MullvadVPN/Operations/TransformOperationObserver.swift b/ios/MullvadVPN/Operations/TransformOperationObserver.swift index 48fbe02dbb..e6ed695e0b 100644 --- a/ios/MullvadVPN/Operations/TransformOperationObserver.swift +++ b/ios/MullvadVPN/Operations/TransformOperationObserver.swift @@ -11,14 +11,20 @@ import Foundation /// A private type erasing observer that type casts the input operation type to the expected /// operation type before calling the wrapped observer class TransformOperationObserver<S: OperationProtocol>: OperationObserver { + private let willExecute: (S) -> Void private let willFinish: (S) -> Void private let didFinish: (S) -> Void init<T: OperationObserver>(_ observer: T) { + willExecute = Self.wrap(observer.operationWillExecute) willFinish = Self.wrap(observer.operationWillFinish) didFinish = Self.wrap(observer.operationDidFinish) } + func operationWillExecute(_ operation: S) { + willExecute(operation) + } + func operationWillFinish(_ operation: S) { willFinish(operation) } diff --git a/ios/MullvadVPN/RelaySelector.swift b/ios/MullvadVPN/RelaySelector.swift index baf0595518..da1074452c 100644 --- a/ios/MullvadVPN/RelaySelector.swift +++ b/ios/MullvadVPN/RelaySelector.swift @@ -20,6 +20,17 @@ private struct RelayWithLocation { var location: Location } +extension RelaySelectorResult { + var tunnelConnectionInfo: TunnelConnectionInfo { + return TunnelConnectionInfo( + ipv4Relay: self.endpoint.ipv4Relay, + ipv6Relay: self.endpoint.ipv6Relay, + hostname: self.relay.hostname, + location: self.location + ) + } +} + struct RelaySelector { private let relays: ServerRelaysResponse diff --git a/ios/MullvadVPN/SimulatorTunnelProvider.swift b/ios/MullvadVPN/SimulatorTunnelProvider.swift index 6844dd9921..98b51dda34 100644 --- a/ios/MullvadVPN/SimulatorTunnelProvider.swift +++ b/ios/MullvadVPN/SimulatorTunnelProvider.swift @@ -50,10 +50,33 @@ extension NETunnelProviderManager: VPNTunnelProviderManagerProtocol {} // MARK: - NEPacketTunnelProvider stubs -protocol SimulatorTunnelProviderDelegate { - func startTunnel(options: [String: Any]?, completionHandler: @escaping (Error?) -> Void) - func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) - func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) +class SimulatorTunnelProviderDelegate { + fileprivate(set) var connection: SimulatorVPNConnection? + + var protocolConfiguration: NEVPNProtocol { + return connection?.protocolConfiguration ?? NEVPNProtocol() + } + + var reasserting: Bool { + get { + return connection?.reasserting ?? false + } + set { + connection?.reasserting = newValue + } + } + + func startTunnel(options: [String: Any]?, completionHandler: @escaping (Error?) -> Void) { + completionHandler(nil) + } + + func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { + completionHandler() + } + + func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) { + completionHandler?(nil) + } } class SimulatorTunnelProvider { @@ -84,6 +107,9 @@ class SimulatorTunnelProvider { class SimulatorVPNConnection: NSObject, VPNConnectionProtocol { + // Protocol configuration is automatically synced by `SimulatorTunnelInfo` + fileprivate var protocolConfiguration = NEVPNProtocol() + private let lock = NSRecursiveLock() private var _status: NEVPNStatus = .disconnected @@ -104,11 +130,36 @@ class SimulatorVPNConnection: NSObject, VPNConnectionProtocol { } } + private var statusBeforeReasserting: NEVPNStatus? + private var _reasserting = false + var reasserting: Bool { + get { + lock.withCriticalBlock { _reasserting } + } + set { + lock.withCriticalBlock { + if newValue != _reasserting { + _reasserting = newValue + + if newValue { + statusBeforeReasserting = status + status = .reasserting + } else if let newStatus = statusBeforeReasserting { + status = newStatus + statusBeforeReasserting = nil + } + } + } + } + } + func startVPNTunnel() throws { try startVPNTunnel(options: nil) } func startVPNTunnel(options: [String: NSObject]?) throws { + SimulatorTunnelProvider.shared.delegate.connection = self + status = .connecting SimulatorTunnelProvider.shared.delegate.startTunnel(options: options) { (error) in @@ -165,10 +216,17 @@ private struct SimulatorTunnelInfo { var onDemandRules = [NEOnDemandRule]() /// Protocol configuration - var protocolConfiguration: NEVPNProtocol? + var protocolConfiguration: NEVPNProtocol? { + didSet { + self.connection.protocolConfiguration = protocolConfiguration ?? NEVPNProtocol() + } + } /// Tunnel description var localizedDescription: String? + + /// Designated initializer + init() {} } class SimulatorTunnelProviderManager: VPNTunnelProviderManagerProtocol, Equatable { @@ -245,8 +303,8 @@ class SimulatorTunnelProviderManager: VPNTunnelProviderManagerProtocol, Equatabl } } - required init() { - self.tunnelInfo = SimulatorTunnelInfo() + required convenience init() { + self.init(tunnelInfo: SimulatorTunnelInfo()) } private init(tunnelInfo: SimulatorTunnelInfo) { diff --git a/ios/MullvadVPN/SimulatorTunnelProviderHost.swift b/ios/MullvadVPN/SimulatorTunnelProviderHost.swift index 0f31e5d882..37ac1f38f3 100644 --- a/ios/MullvadVPN/SimulatorTunnelProviderHost.swift +++ b/ios/MullvadVPN/SimulatorTunnelProviderHost.swift @@ -15,30 +15,25 @@ import Logging class SimulatorTunnelProviderHost: SimulatorTunnelProviderDelegate { + private enum ExclusivityCategory { + case exclusive + } + private var connectionInfo: TunnelConnectionInfo? private let logger = Logger(label: "SimulatorTunnelProviderHost") - func startTunnel(options: [String: Any]?, completionHandler: @escaping (Error?) -> Void) { - DispatchQueue.main.async { - self.connectionInfo = TunnelConnectionInfo( - ipv4Relay: IPv4Endpoint(ip: IPv4Address("10.0.0.1")!, port: 53), - ipv6Relay: nil, - hostname: "au4-wireguard", - location: Location( - country: "Australia", - countryCode: "au", - city: "Melbourne", - cityCode: "mel", - latitude: -37.815018, - longitude: 144.946014 - ) - ) + private let operationQueue = OperationQueue() + private lazy var exclusivityController = ExclusivityController<ExclusivityCategory>(operationQueue: operationQueue) + override func startTunnel(options: [String: Any]?, completionHandler: @escaping (Error?) -> Void) { + let startOperation = makeStartOperation() + startOperation.addDidFinishBlockObserver(queue: .main) { _ in completionHandler(nil) } + exclusivityController.addOperation(startOperation, categories: [.exclusive]) } - func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { + override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { DispatchQueue.main.async { self.connectionInfo = nil @@ -46,14 +41,28 @@ class SimulatorTunnelProviderHost: SimulatorTunnelProviderDelegate { } } - func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) { + override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) { DispatchQueue.main.async { let result = PacketTunnelIpcHandler.decodeRequest(messageData: messageData) switch result { case .success(let request): switch request { case .reloadTunnelSettings: - self.replyAppMessage(true, completionHandler: completionHandler) + let operationObserver = OperationBlockObserver<AsyncBlockOperation>( + queue: .main, + willExecute: { _ in + self.reasserting = true + }, + willFinish: { _ in + self.reasserting = false + }, + didFinish: { _ in + self.replyAppMessage(true, completionHandler: completionHandler) + }) + + let startOperation = self.makeStartOperation() + startOperation.addObserver(operationObserver) + self.exclusivityController.addOperation(startOperation, categories: [.exclusive]) case .tunnelInformation: self.replyAppMessage(self.connectionInfo, completionHandler: completionHandler) @@ -65,8 +74,7 @@ class SimulatorTunnelProviderHost: SimulatorTunnelProviderDelegate { } } - private func replyAppMessage<T: Codable>(_ response: T, completionHandler: ((Data?) -> Void)?) - { + private func replyAppMessage<T: Codable>(_ response: T, completionHandler: ((Data?) -> Void)?) { switch PacketTunnelIpcHandler.encodeResponse(response: response) { case .success(let data): completionHandler?(data) @@ -77,6 +85,45 @@ class SimulatorTunnelProviderHost: SimulatorTunnelProviderDelegate { } } + private func makeStartOperation() -> AsyncBlockOperation { + return AsyncBlockOperation { [weak self] (finish) in + guard let self = self else { + finish() + return + } + + self.pickRelay { (selectorResult) in + DispatchQueue.main.async { + self.connectionInfo = selectorResult?.tunnelConnectionInfo + finish() + } + } + } + } + + private func pickRelay(completion: @escaping (RelaySelectorResult?) -> Void) { + RelayCache.shared.read { (result) in + switch result { + case .success(let cachedRelays): + let keychainReference = self.protocolConfiguration.passwordReference! + switch TunnelSettingsManager.load(searchTerm: .persistentReference(keychainReference)) { + case .success(let entry): + let relayConstraints = entry.tunnelSettings.relayConstraints + let relaySelector = RelaySelector(relays: cachedRelays.relays) + let selectorResult = relaySelector.evaluate(with: relayConstraints) + completion(selectorResult) + + case .failure(let error): + self.logger.error(chainedError: error) + completion(nil) + } + case .failure(let error): + self.logger.error(chainedError: error) + completion(nil) + } + } + } + } #endif diff --git a/ios/PacketTunnel/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider.swift index 28af108148..8e12bdb9f7 100644 --- a/ios/PacketTunnel/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider.swift @@ -539,17 +539,6 @@ extension PacketTunnelState: CustomStringConvertible, CustomDebugStringConvertib } } -extension RelaySelectorResult { - var tunnelConnectionInfo: TunnelConnectionInfo { - return TunnelConnectionInfo( - ipv4Relay: self.endpoint.ipv4Relay, - ipv6Relay: self.endpoint.ipv6Relay, - hostname: self.relay.hostname, - location: self.location - ) - } -} - extension WireGuardLogLevel { var loggerLevel: Logger.Level { switch self { |
