From 516e3da7e2e026d508858559cb2f547b53bc129b Mon Sep 17 00:00:00 2001 From: isamnezia <156459471+isamnezia@users.noreply.github.com> Date: Wed, 20 Mar 2024 18:35:36 +0300 Subject: [PATCH] Fix open log crash and side log improvements (#694) Fix open log crash --- CMakeLists.txt | 2 +- client/ios/networkextension/CMakeLists.txt | 3 +- client/platforms/ios/Log.swift | 35 +- client/platforms/ios/LogRecord.swift | 31 +- client/platforms/ios/NELogController.swift | 20 +- ...ift => PacketTunnelProvider+OpenVPN.swift} | 104 ++++- .../ios/PacketTunnelProvider+WireGuard.swift | 221 +++++++++++ .../platforms/ios/PacketTunnelProvider.swift | 374 ++---------------- client/platforms/ios/WGConfig.swift | 11 - 9 files changed, 430 insertions(+), 371 deletions(-) rename client/platforms/ios/{PacketTunnelProvider+OpenVPNAdapterDelegate.swift => PacketTunnelProvider+OpenVPN.swift} (54%) create mode 100644 client/platforms/ios/PacketTunnelProvider+WireGuard.swift diff --git a/CMakeLists.txt b/CMakeLists.txt index 58ee37c9..18c32f30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR) set(PROJECT AmneziaVPN) -project(${PROJECT} VERSION 4.4.1.4 +project(${PROJECT} VERSION 4.4.2.1 DESCRIPTION "AmneziaVPN" HOMEPAGE_URL "https://amnezia.org/" ) diff --git a/client/ios/networkextension/CMakeLists.txt b/client/ios/networkextension/CMakeLists.txt index e48215f6..4d695dfa 100644 --- a/client/ios/networkextension/CMakeLists.txt +++ b/client/ios/networkextension/CMakeLists.txt @@ -84,7 +84,8 @@ target_sources(networkextension PRIVATE ${CLIENT_ROOT_DIR}/platforms/ios/Log.swift ${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift ${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider.swift - ${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+OpenVPNAdapterDelegate.swift + ${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+WireGuard.swift + ${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+OpenVPN.swift ${CLIENT_ROOT_DIR}/platforms/ios/WGConfig.swift ${CLIENT_ROOT_DIR}/platforms/ios/iosglue.mm ) diff --git a/client/platforms/ios/Log.swift b/client/platforms/ios/Log.swift index d9512dc6..7e9b7fdb 100644 --- a/client/platforms/ios/Log.swift +++ b/client/platforms/ios/Log.swift @@ -2,6 +2,8 @@ import Foundation import os.log struct Log { + static let osLog = Logger() + private static let IsLoggingEnabledKey = "IsLoggingEnabled" static var isLoggingEnabled: Bool { get { @@ -29,16 +31,23 @@ struct Log { return dateFormatter }() - var records: [Record] + var records = [Record]() + + var lastRecordDate = Date.distantPast init() { self.records = [] } init(_ str: String) { - self.records = str.split(whereSeparator: \.isNewline) - .compactMap { - Record(String($0)) + records = str.split(whereSeparator: \.isNewline) + .map { + if let record = Record(String($0)) { + lastRecordDate = record.date + return record + } else { + return Record(date: lastRecordDate, level: .error, message: "LOG: \($0)") + } } } @@ -60,6 +69,24 @@ struct Log { self.init(str) } + static func log(_ type: OSLogType, title: String = "", message: String, url: URL = neLogURL) { + guard isLoggingEnabled else { return } + + let date = Date() + let level = Record.Level(from: type) + let messages = message.split(whereSeparator: \.isNewline) + + for index in 0.. Void) { + guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol, + let providerConfiguration = protocolConfiguration.providerConfiguration, + let openVPNConfigData = providerConfiguration[Constants.ovpnConfigKey] as? Data else { + ovpnLog(.error, message: "Can't start") + return + } + + do { + // ovpnLog(.info, message: "providerConfiguration: \(String(decoding: openVPNConfigData, as: UTF8.self))") + + let openVPNConfig = try JSONDecoder().decode(OpenVPNConfig.self, from: openVPNConfigData) + ovpnLog(.info, title: "config: ", message: openVPNConfig.str) + let ovpnConfiguration = Data(openVPNConfig.config.utf8) + setupAndlaunchOpenVPN(withConfig: ovpnConfiguration, completionHandler: completionHandler) + } catch { + ovpnLog(.error, message: "Can't parse config: \(error.localizedDescription)") + + if let underlyingError = (error as NSError).userInfo[NSUnderlyingErrorKey] as? NSError { + ovpnLog(.error, message: "Can't parse config: \(underlyingError.localizedDescription)") + } + + return + } + } + + private func setupAndlaunchOpenVPN(withConfig ovpnConfiguration: Data, + withShadowSocks viaSS: Bool = false, + completionHandler: @escaping (Error?) -> Void) { + ovpnLog(.info, message: "Setup and launch") + + let str = String(decoding: ovpnConfiguration, as: UTF8.self) + + let configuration = OpenVPNConfiguration() + configuration.fileContent = ovpnConfiguration + if str.contains("cloak") { + configuration.setPTCloak() + } + + let evaluation: OpenVPNConfigurationEvaluation + do { + evaluation = try ovpnAdapter.apply(configuration: configuration) + + } catch { + completionHandler(error) + return + } + + if !evaluation.autologin { + ovpnLog(.info, message: "Implement login with user credentials") + } + + vpnReachability.startTracking { [weak self] status in + guard status == .reachableViaWiFi else { return } + self?.ovpnAdapter.reconnect(afterTimeInterval: 5) + } + + startHandler = completionHandler + ovpnAdapter.connect(using: packetFlow) + + // let ifaces = Interface.allInterfaces() + // .filter { $0.family == .ipv4 } + // .map { iface in iface.name } + + // ovpn_log(.error, message: "Available TUN Interfaces: \(ifaces)") + } + + func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) { + guard let completionHandler = completionHandler else { return } + let bytesin = ovpnAdapter.transportStatistics.bytesIn + let bytesout = ovpnAdapter.transportStatistics.bytesOut + + let response: [String: Any] = [ + "rx_bytes": bytesin, + "tx_bytes": bytesout + ] + + completionHandler(try? JSONSerialization.data(withJSONObject: response, options: [])) + } + + func stopOpenVPN(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { + stopHandler = completionHandler + if vpnReachability.isTracking { + vpnReachability.stopTracking() + } + ovpnAdapter.disconnect() + } +} + extension PacketTunnelProvider: OpenVPNAdapterDelegate { // OpenVPNAdapter calls this delegate method to configure a VPN tunnel. // `completionHandler` callback requires an object conforming to `OpenVPNAdapterPacketFlow` @@ -116,6 +216,8 @@ extension PacketTunnelProvider: OpenVPNAdapterDelegate { // Use this method to process any log message returned by OpenVPN library. func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleLogMessage logMessage: String) { // Handle log messages - wg_log(.info, message: logMessage) + ovpnLog(.info, message: logMessage) } } + +extension NEPacketTunnelFlow: OpenVPNAdapterPacketFlow {} diff --git a/client/platforms/ios/PacketTunnelProvider+WireGuard.swift b/client/platforms/ios/PacketTunnelProvider+WireGuard.swift new file mode 100644 index 00000000..5d1505f3 --- /dev/null +++ b/client/platforms/ios/PacketTunnelProvider+WireGuard.swift @@ -0,0 +1,221 @@ +import Foundation +import NetworkExtension + +extension PacketTunnelProvider { + func startWireguard(activationAttemptId: String?, + errorNotifier: ErrorNotifier, + completionHandler: @escaping (Error?) -> Void) { + guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol, + let providerConfiguration = protocolConfiguration.providerConfiguration, + let wgConfigData: Data = providerConfiguration[Constants.wireGuardConfigKey] as? Data else { + wg_log(.error, message: "Can't start, config missing") + completionHandler(nil) + return + } + + do { + let wgConfig = try JSONDecoder().decode(WGConfig.self, from: wgConfigData) + let wgConfigStr = wgConfig.str + wg_log(.info, title: "config: ", message: wgConfig.redux) + + let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: wgConfigStr) + + if tunnelConfiguration.peers.first!.allowedIPs + .map({ $0.stringRepresentation }) + .joined(separator: ", ") == "0.0.0.0/0, ::/0" { + if wgConfig.splitTunnelType == 1 { + for index in tunnelConfiguration.peers.indices { + tunnelConfiguration.peers[index].allowedIPs.removeAll() + var allowedIPs = [IPAddressRange]() + + for allowedIPString in wgConfig.splitTunnelSites { + if let allowedIP = IPAddressRange(from: allowedIPString) { + allowedIPs.append(allowedIP) + } + } + + tunnelConfiguration.peers[index].allowedIPs = allowedIPs + } + } else if wgConfig.splitTunnelType == 2 { + for index in tunnelConfiguration.peers.indices { + var excludeIPs = [IPAddressRange]() + + for excludeIPString in wgConfig.splitTunnelSites { + if let excludeIP = IPAddressRange(from: excludeIPString) { + excludeIPs.append(excludeIP) + } + } + + tunnelConfiguration.peers[index].excludeIPs = excludeIPs + } + } + } + + wg_log(.info, message: "Starting tunnel from the " + + (activationAttemptId == nil ? "OS directly, rather than the app" : "app")) + + // Start the tunnel + wgAdapter.start(tunnelConfiguration: tunnelConfiguration) { adapterError in + guard let adapterError else { + let interfaceName = self.wgAdapter.interfaceName ?? "unknown" + wg_log(.info, message: "Tunnel interface is \(interfaceName)") + completionHandler(nil) + return + } + + switch adapterError { + case .cannotLocateTunnelFileDescriptor: + wg_log(.error, staticMessage: "Starting tunnel failed: could not determine file descriptor") + errorNotifier.notify(PacketTunnelProviderError.couldNotDetermineFileDescriptor) + completionHandler(PacketTunnelProviderError.couldNotDetermineFileDescriptor) + case .dnsResolution(let dnsErrors): + let hostnamesWithDnsResolutionFailure = dnsErrors.map { $0.address } + .joined(separator: ", ") + wg_log(.error, message: + "DNS resolution failed for the following hostnames: \(hostnamesWithDnsResolutionFailure)") + errorNotifier.notify(PacketTunnelProviderError.dnsResolutionFailure) + completionHandler(PacketTunnelProviderError.dnsResolutionFailure) + case .setNetworkSettings(let error): + wg_log(.error, message: + "Starting tunnel failed with setTunnelNetworkSettings returning \(error.localizedDescription)") + errorNotifier.notify(PacketTunnelProviderError.couldNotSetNetworkSettings) + completionHandler(PacketTunnelProviderError.couldNotSetNetworkSettings) + case .startWireGuardBackend(let errorCode): + wg_log(.error, message: "Starting tunnel failed with wgTurnOn returning \(errorCode)") + errorNotifier.notify(PacketTunnelProviderError.couldNotStartBackend) + completionHandler(PacketTunnelProviderError.couldNotStartBackend) + case .invalidState: + fatalError() + } + } + } catch { + wg_log(.error, message: "Can't parse WG config: \(error.localizedDescription)") + completionHandler(nil) + return + } + } + + func handleWireguardStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) { + guard let completionHandler = completionHandler else { return } + wgAdapter.getRuntimeConfiguration { settings in + var data: Data? + if let settings { + data = settings.data(using: .utf8)! + } + + let components = settings!.components(separatedBy: "\n") + + var settingsDictionary: [String: String] = [:] + for component in components { + let pair = component.components(separatedBy: "=") + if pair.count == 2 { + settingsDictionary[pair[0]] = pair[1] + } + } + + let response: [String: Any] = [ + "rx_bytes": settingsDictionary["rx_bytes"] ?? "0", + "tx_bytes": settingsDictionary["tx_bytes"] ?? "0" + ] + + completionHandler(try? JSONSerialization.data(withJSONObject: response, options: [])) + } + } + + private func handleWireguardAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) { + guard let completionHandler = completionHandler else { return } + if messageData.count == 1 && messageData[0] == 0 { + wgAdapter.getRuntimeConfiguration { settings in + var data: Data? + if let settings { + data = settings.data(using: .utf8)! + } + completionHandler(data) + } + } else if messageData.count >= 1 { + // Updates the tunnel configuration and responds with the active configuration + wg_log(.info, message: "Switching tunnel configuration") + guard let configString = String(data: messageData, encoding: .utf8) + else { + completionHandler(nil) + return + } + + do { + let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: configString) + wgAdapter.update(tunnelConfiguration: tunnelConfiguration) { error in + if let error { + wg_log(.error, message: "Failed to switch tunnel configuration: \(error.localizedDescription)") + completionHandler(nil) + return + } + + self.wgAdapter.getRuntimeConfiguration { settings in + var data: Data? + if let settings { + data = settings.data(using: .utf8)! + } + completionHandler(data) + } + } + } catch { + completionHandler(nil) + } + } else { + completionHandler(nil) + } + } + + // private func startEmptyTunnel(completionHandler: @escaping (Error?) -> Void) { + // dispatchPrecondition(condition: .onQueue(dispatchQueue)) + // + // let emptyTunnelConfiguration = TunnelConfiguration( + // name: nil, + // interface: InterfaceConfiguration(privateKey: PrivateKey()), + // peers: [] + // ) + // + // wgAdapter.start(tunnelConfiguration: emptyTunnelConfiguration) { error in + // self.dispatchQueue.async { + // if let error { + // wg_log(.error, message: "Failed to start an empty tunnel") + // completionHandler(error) + // } else { + // wg_log(.info, message: "Started an empty tunnel") + // self.tunnelAdapterDidStart() + // } + // } + // } + // + // let settings = NETunnelNetworkSettings(tunnelRemoteAddress: "1.1.1.1") + // + // self.setTunnelNetworkSettings(settings) { error in + // completionHandler(error) + // } + // } + + // private func tunnelAdapterDidStart() { + // dispatchPrecondition(condition: .onQueue(dispatchQueue)) + // // ... + // } + + func stopWireguard(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { + wg_log(.info, staticMessage: "Stopping tunnel") + + wgAdapter.stop { error in + ErrorNotifier.removeLastErrorFile() + + if let error { + wg_log(.error, message: "Failed to stop WireGuard adapter: \(error.localizedDescription)") + } + completionHandler() + +#if os(macOS) + // HACK: This is a filthy hack to work around Apple bug 32073323 (dup'd by us as 47526107). + // Remove it when they finally fix this upstream and the fix has been rolled out to + // sufficient quantities of users. + exit(0) +#endif + } + } +} diff --git a/client/platforms/ios/PacketTunnelProvider.swift b/client/platforms/ios/PacketTunnelProvider.swift index e48d95d2..0c812f93 100644 --- a/client/platforms/ios/PacketTunnelProvider.swift +++ b/client/platforms/ios/PacketTunnelProvider.swift @@ -15,7 +15,7 @@ struct Constants { static let ovpnConfigKey = "ovpn" static let wireGuardConfigKey = "wireguard" static let loggerTag = "NET" - + static let kActionStart = "start" static let kActionRestart = "restart" static let kActionStop = "stop" @@ -34,62 +34,68 @@ struct Constants { } class PacketTunnelProvider: NEPacketTunnelProvider { - private lazy var wgAdapter = { + lazy var wgAdapter = { WireGuardAdapter(with: self) { logLevel, message in wg_log(logLevel.osLogLevel, message: message) } }() - - private lazy var ovpnAdapter: OpenVPNAdapter = { + + lazy var ovpnAdapter: OpenVPNAdapter = { let adapter = OpenVPNAdapter() adapter.delegate = self return adapter }() - + /// Internal queue. private let dispatchQueue = DispatchQueue(label: "PacketTunnel", qos: .utility) - - private var openVPNConfig: Data? + var splitTunnelType: Int! var splitTunnelSites: [String]! - + let vpnReachability = OpenVPNReachability() - + var startHandler: ((Error?) -> Void)? var stopHandler: (() -> Void)? var protoType: TunnelProtoType = .none - + override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) { - let tmpStr = String(data: messageData, encoding: .utf8)! - wg_log(.error, message: tmpStr) + guard let message = String(data: messageData, encoding: .utf8) else { + if let completionHandler { + completionHandler(nil) + } + return + } + + neLog(.info, title: "App said: ", message: message) + guard let message = try? JSONSerialization.jsonObject(with: messageData, options: []) as? [String: Any] else { - log(.error, message: "Failed to serialize message from app") + neLog(.error, message: "Failed to serialize message from app") return } - + guard let completionHandler else { - log(.error, message: "Missing message completion handler") + neLog(.error, message: "Missing message completion handler") return } - + guard let action = message[Constants.kMessageKeyAction] as? String else { - log(.error, message: "Missing action key in app message") + neLog(.error, message: "Missing action key in app message") completionHandler(nil) return } - + if action == Constants.kActionStatus { handleStatusAppMessage(messageData, completionHandler: completionHandler) } } - + override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) { dispatchQueue.async { let activationAttemptId = options?[Constants.kActivationAttemptId] as? String let errorNotifier = ErrorNotifier(activationAttemptId: activationAttemptId) - - log(.info, message: "PacketTunnelProvider startTunnel") - + + neLog(.info, message: "Start tunnel") + if let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol { let providerConfiguration = protocolConfiguration.providerConfiguration if (providerConfiguration?[Constants.ovpnConfigKey] as? Data) != nil { @@ -100,7 +106,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { } else { self.protoType = .none } - + switch self.protoType { case .wireguard: self.startWireguard(activationAttemptId: activationAttemptId, @@ -116,7 +122,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { } } } - + override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { dispatchQueue.async { switch self.protoType { @@ -132,7 +138,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { } } } - + func handleStatusAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) { switch protoType { case .wireguard: @@ -146,291 +152,18 @@ class PacketTunnelProvider: NEPacketTunnelProvider { break } } - - // MARK: Private methods - private func startWireguard(activationAttemptId: String?, - errorNotifier: ErrorNotifier, - completionHandler: @escaping (Error?) -> Void) { - guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol, - let providerConfiguration = protocolConfiguration.providerConfiguration, - let wgConfigData: Data = providerConfiguration[Constants.wireGuardConfigKey] as? Data else { - wg_log(.error, message: "Can't start WireGuard config missing") - completionHandler(nil) - return - } - - do { - let wgConfig = try JSONDecoder().decode(WGConfig.self, from: wgConfigData) - let wgConfigStr = wgConfig.str - log(.info, message: "wgConfig: \(wgConfig.redux.replacingOccurrences(of: "\n", with: " "))") - - let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: wgConfigStr) - - if tunnelConfiguration.peers.first!.allowedIPs - .map({ $0.stringRepresentation }) - .joined(separator: ", ") == "0.0.0.0/0, ::/0" { - if wgConfig.splitTunnelType == 1 { - for index in tunnelConfiguration.peers.indices { - tunnelConfiguration.peers[index].allowedIPs.removeAll() - var allowedIPs = [IPAddressRange]() - - for allowedIPString in wgConfig.splitTunnelSites { - if let allowedIP = IPAddressRange(from: allowedIPString) { - allowedIPs.append(allowedIP) - } - } - - tunnelConfiguration.peers[index].allowedIPs = allowedIPs - } - } else if wgConfig.splitTunnelType == 2 { - for index in tunnelConfiguration.peers.indices { - var excludeIPs = [IPAddressRange]() - - for excludeIPString in wgConfig.splitTunnelSites { - if let excludeIP = IPAddressRange(from: excludeIPString) { - excludeIPs.append(excludeIP) - } - } - - tunnelConfiguration.peers[index].excludeIPs = excludeIPs - } - } - } - - wg_log(.info, message: "Starting wireguard tunnel from the " + - (activationAttemptId == nil ? "OS directly, rather than the app" : "app")) - - // Start the tunnel - wgAdapter.start(tunnelConfiguration: tunnelConfiguration) { adapterError in - guard let adapterError else { - let interfaceName = self.wgAdapter.interfaceName ?? "unknown" - wg_log(.info, message: "Tunnel interface is \(interfaceName)") - completionHandler(nil) - return - } - - switch adapterError { - case .cannotLocateTunnelFileDescriptor: - wg_log(.error, staticMessage: "Starting tunnel failed: could not determine file descriptor") - errorNotifier.notify(PacketTunnelProviderError.couldNotDetermineFileDescriptor) - completionHandler(PacketTunnelProviderError.couldNotDetermineFileDescriptor) - case .dnsResolution(let dnsErrors): - let hostnamesWithDnsResolutionFailure = dnsErrors.map { $0.address } - .joined(separator: ", ") - wg_log(.error, message: - "DNS resolution failed for the following hostnames: \(hostnamesWithDnsResolutionFailure)") - errorNotifier.notify(PacketTunnelProviderError.dnsResolutionFailure) - completionHandler(PacketTunnelProviderError.dnsResolutionFailure) - case .setNetworkSettings(let error): - wg_log(.error, message: - "Starting tunnel failed with setTunnelNetworkSettings returning \(error.localizedDescription)") - errorNotifier.notify(PacketTunnelProviderError.couldNotSetNetworkSettings) - completionHandler(PacketTunnelProviderError.couldNotSetNetworkSettings) - case .startWireGuardBackend(let errorCode): - wg_log(.error, message: "Starting tunnel failed with wgTurnOn returning \(errorCode)") - errorNotifier.notify(PacketTunnelProviderError.couldNotStartBackend) - completionHandler(PacketTunnelProviderError.couldNotStartBackend) - case .invalidState: - fatalError() - } - } - } catch { - log(.error, message: "Can't parse WG config: \(error.localizedDescription)") - completionHandler(nil) - return - } - } - - private func startOpenVPN(completionHandler: @escaping (Error?) -> Void) { - guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol, - let providerConfiguration = protocolConfiguration.providerConfiguration, - let openVPNConfigData = providerConfiguration[Constants.ovpnConfigKey] as? Data else { - wg_log(.error, message: "Can't start startOpenVPN()") - return - } - - do { - log(.info, message: "providerConfiguration: \(String(decoding: openVPNConfigData, as: UTF8.self).replacingOccurrences(of: "\n", with: " "))") - - let openVPNConfig = try JSONDecoder().decode(OpenVPNConfig.self, from: openVPNConfigData) - log(.info, message: "openVPNConfig: \(openVPNConfig.str.replacingOccurrences(of: "\n", with: " "))") - let ovpnConfiguration = Data(openVPNConfig.config.utf8) - setupAndlaunchOpenVPN(withConfig: ovpnConfiguration, completionHandler: completionHandler) - } catch { - log(.error, message: "Can't parse OpenVPN config: \(error.localizedDescription)") - - if let underlyingError = (error as NSError).userInfo[NSUnderlyingErrorKey] as? NSError { - log(.error, message: "Can't parse OpenVPN config: \(underlyingError.localizedDescription)") - } - - return - } - } - - private func stopWireguard(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { - wg_log(.info, staticMessage: "Stopping tunnel") - - wgAdapter.stop { error in - ErrorNotifier.removeLastErrorFile() - - if let error { - wg_log(.error, message: "Failed to stop WireGuard adapter: \(error.localizedDescription)") - } - completionHandler() - -#if os(macOS) - // HACK: This is a filthy hack to work around Apple bug 32073323 (dup'd by us as 47526107). - // Remove it when they finally fix this upstream and the fix has been rolled out to - // sufficient quantities of users. - exit(0) -#endif - } - } - - private func stopOpenVPN(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { - stopHandler = completionHandler - if vpnReachability.isTracking { - vpnReachability.stopTracking() - } - ovpnAdapter.disconnect() - } - - func handleWireguardStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) { - guard let completionHandler = completionHandler else { return } - wgAdapter.getRuntimeConfiguration { settings in - var data: Data? - if let settings { - data = settings.data(using: .utf8)! - } - - let components = settings!.components(separatedBy: "\n") - - var settingsDictionary: [String: String] = [:] - for component in components { - let pair = component.components(separatedBy: "=") - if pair.count == 2 { - settingsDictionary[pair[0]] = pair[1] - } - } - - let response: [String: Any] = [ - "rx_bytes": settingsDictionary["rx_bytes"] ?? "0", - "tx_bytes": settingsDictionary["tx_bytes"] ?? "0" - ] - - completionHandler(try? JSONSerialization.data(withJSONObject: response, options: [])) - } - } - - private func handleWireguardAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) { - guard let completionHandler = completionHandler else { return } - if messageData.count == 1 && messageData[0] == 0 { - wgAdapter.getRuntimeConfiguration { settings in - var data: Data? - if let settings { - data = settings.data(using: .utf8)! - } - completionHandler(data) - } - } else if messageData.count >= 1 { - // Updates the tunnel configuration and responds with the active configuration - wg_log(.info, message: "Switching tunnel configuration") - guard let configString = String(data: messageData, encoding: .utf8) - else { - completionHandler(nil) - return - } - - do { - let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: configString) - wgAdapter.update(tunnelConfiguration: tunnelConfiguration) { error in - if let error { - wg_log(.error, message: "Failed to switch tunnel configuration: \(error.localizedDescription)") - completionHandler(nil) - return - } - - self.wgAdapter.getRuntimeConfiguration { settings in - var data: Data? - if let settings { - data = settings.data(using: .utf8)! - } - completionHandler(data) - } - } - } catch { - completionHandler(nil) - } - } else { - completionHandler(nil) - } - } - - private func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) { - guard let completionHandler = completionHandler else { return } - let bytesin = ovpnAdapter.transportStatistics.bytesIn - let bytesout = ovpnAdapter.transportStatistics.bytesOut - - let response: [String: Any] = [ - "rx_bytes": bytesin, - "tx_bytes": bytesout - ] - - completionHandler(try? JSONSerialization.data(withJSONObject: response, options: [])) - } - - private func setupAndlaunchOpenVPN(withConfig ovpnConfiguration: Data, - withShadowSocks viaSS: Bool = false, - completionHandler: @escaping (Error?) -> Void) { - wg_log(.info, message: "setupAndlaunchOpenVPN") - - let str = String(decoding: ovpnConfiguration, as: UTF8.self) - - let configuration = OpenVPNConfiguration() - configuration.fileContent = ovpnConfiguration - if str.contains("cloak") { - configuration.setPTCloak() - } - - let evaluation: OpenVPNConfigurationEvaluation - do { - evaluation = try ovpnAdapter.apply(configuration: configuration) - - } catch { - completionHandler(error) - return - } - - if !evaluation.autologin { - wg_log(.info, message: "Implement login with user credentials") - } - - vpnReachability.startTracking { [weak self] status in - guard status == .reachableViaWiFi else { return } - self?.ovpnAdapter.reconnect(afterTimeInterval: 5) - } - - startHandler = completionHandler - ovpnAdapter.connect(using: packetFlow) - - // let ifaces = Interface.allInterfaces() - // .filter { $0.family == .ipv4 } - // .map { iface in iface.name } - - // wg_log(.error, message: "Available TUN Interfaces: \(ifaces)") - } - + // MARK: Network observing methods - + private func startListeningForNetworkChanges() { stopListeningForNetworkChanges() addObserver(self, forKeyPath: Constants.kDefaultPathKey, options: .old, context: nil) } - + private func stopListeningForNetworkChanges() { removeObserver(self, forKeyPath: Constants.kDefaultPathKey) } - + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, @@ -450,48 +183,13 @@ class PacketTunnelProvider: NEPacketTunnelProvider { self.handle(networkChange: self.defaultPath!) { _ in } } } - + private func handle(networkChange changePath: NWPath, completion: @escaping (Error?) -> Void) { wg_log(.info, message: "Tunnel restarted.") startTunnel(options: nil, completionHandler: completion) } - - private func startEmptyTunnel(completionHandler: @escaping (Error?) -> Void) { - dispatchPrecondition(condition: .onQueue(dispatchQueue)) - - let emptyTunnelConfiguration = TunnelConfiguration( - name: nil, - interface: InterfaceConfiguration(privateKey: PrivateKey()), - peers: [] - ) - - wgAdapter.start(tunnelConfiguration: emptyTunnelConfiguration) { error in - self.dispatchQueue.async { - if let error { - log(.error, message: "Failed to start an empty tunnel") - completionHandler(error) - } else { - log(.info, message: "Started an empty tunnel") - self.tunnelAdapterDidStart() - } - } - } - - let settings = NETunnelNetworkSettings(tunnelRemoteAddress: "1.1.1.1") - - self.setTunnelNetworkSettings(settings) { error in - completionHandler(error) - } - } - - private func tunnelAdapterDidStart() { - dispatchPrecondition(condition: .onQueue(dispatchQueue)) - // ... - } } -extension NEPacketTunnelFlow: OpenVPNAdapterPacketFlow {} - extension WireGuardLogLevel { var osLogLevel: OSLogType { switch self { diff --git a/client/platforms/ios/WGConfig.swift b/client/platforms/ios/WGConfig.swift index b703834a..4458c330 100644 --- a/client/platforms/ios/WGConfig.swift +++ b/client/platforms/ios/WGConfig.swift @@ -89,14 +89,3 @@ struct WGConfig: Decodable { """ } } - -struct OpenVPNConfig: Decodable { - let config: String - let mtu: String - let splitTunnelType: Int - let splitTunnelSites: [String] - - var str: String { - "splitTunnelType: \(splitTunnelType) splitTunnelSites: \(splitTunnelSites) mtu: \(mtu) config: \(config)" - } -}