From 2b3dd0b34368e5f5361598070751b5bd4d4e9c8a Mon Sep 17 00:00:00 2001 From: AnhTVc Date: Thu, 5 Jun 2025 10:36:42 +0700 Subject: [PATCH] fix bug ios 18 fix bug: can not start vpn in ios18 --- client/3rd-prebuilt | 2 +- .../ios/PacketTunnelProvider+OpenVPN.swift | 234 ----- client/platforms/ios/ios_controller.mm | 865 ------------------ 3 files changed, 1 insertion(+), 1100 deletions(-) delete mode 100644 client/platforms/ios/PacketTunnelProvider+OpenVPN.swift delete mode 100644 client/platforms/ios/ios_controller.mm diff --git a/client/3rd-prebuilt b/client/3rd-prebuilt index 0f3748ef..a72a1aed 160000 --- a/client/3rd-prebuilt +++ b/client/3rd-prebuilt @@ -1 +1 @@ -Subproject commit 0f3748efd7cc04e0c914304b68931f925bed1259 +Subproject commit a72a1aeddfb041eaebcf9e7e09ad8adc0c3afbee diff --git a/client/platforms/ios/PacketTunnelProvider+OpenVPN.swift b/client/platforms/ios/PacketTunnelProvider+OpenVPN.swift deleted file mode 100644 index 3e0a4a07..00000000 --- a/client/platforms/ios/PacketTunnelProvider+OpenVPN.swift +++ /dev/null @@ -1,234 +0,0 @@ -import Foundation -import NetworkExtension -import OpenVPNAdapter - -struct OpenVPNConfig: Decodable { - let config: String - let splitTunnelType: Int - let splitTunnelSites: [String] - - var str: String { - "splitTunnelType: \(splitTunnelType) splitTunnelSites: \(splitTunnelSites) config: \(config)" - } -} - -extension PacketTunnelProvider { - 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 { - ovpnLog(.error, message: "Can't start") - return - } - - do { - 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 { - ovpnAdapter = OpenVPNAdapter() - ovpnAdapter?.delegate = self - evaluation = try ovpnAdapter?.apply(configuration: configuration) - - } catch { - completionHandler(error) - return - } - - if evaluation?.autologin == false { - 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) - } - - func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) { - guard let completionHandler = completionHandler else { return } - let bytesin = ovpnAdapter?.transportStatistics.bytesIn - let bytesout = ovpnAdapter?.transportStatistics.bytesOut - - guard let bytesin, let bytesout else { - completionHandler(nil) - return - } - - 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) { - ovpnLog(.info, message: "Stopping tunnel: reason: \(reason.description)") - - 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` - // protocol if the tunnel is configured without errors. Otherwise send nil. - // `OpenVPNAdapterPacketFlow` method signatures are similar to `NEPacketTunnelFlow` so - // you can just extend that class to adopt `OpenVPNAdapterPacketFlow` protocol and - // send `self.packetFlow` to `completionHandler` callback. - func openVPNAdapter( - _ openVPNAdapter: OpenVPNAdapter, - configureTunnelWithNetworkSettings networkSettings: NEPacketTunnelNetworkSettings?, - completionHandler: @escaping (Error?) -> Void - ) { - // In order to direct all DNS queries first to the VPN DNS servers before the primary DNS servers - // send empty string to NEDNSSettings.matchDomains - networkSettings?.dnsSettings?.matchDomains = [""] - - if splitTunnelType == 1 { - var ipv4IncludedRoutes = [NEIPv4Route]() - - guard let splitTunnelSites else { - completionHandler(NSError(domain: "Split tunnel sited not setted up", code: 0)) - return - } - - for allowedIPString in splitTunnelSites { - if let allowedIP = IPAddressRange(from: allowedIPString) { - ipv4IncludedRoutes.append(NEIPv4Route( - destinationAddress: "\(allowedIP.address)", - subnetMask: "\(allowedIP.subnetMask())")) - } - } - - networkSettings?.ipv4Settings?.includedRoutes = ipv4IncludedRoutes - } else { - if splitTunnelType == 2 { - var ipv4ExcludedRoutes = [NEIPv4Route]() - var ipv4IncludedRoutes = [NEIPv4Route]() - var ipv6IncludedRoutes = [NEIPv6Route]() - - guard let splitTunnelSites else { - completionHandler(NSError(domain: "Split tunnel sited not setted up", code: 0)) - return - } - - for excludeIPString in splitTunnelSites { - if let excludeIP = IPAddressRange(from: excludeIPString) { - ipv4ExcludedRoutes.append(NEIPv4Route( - destinationAddress: "\(excludeIP.address)", - subnetMask: "\(excludeIP.subnetMask())")) - } - } - - if let allIPv4 = IPAddressRange(from: "0.0.0.0/0") { - ipv4IncludedRoutes.append(NEIPv4Route( - destinationAddress: "\(allIPv4.address)", - subnetMask: "\(allIPv4.subnetMask())")) - } - if let allIPv6 = IPAddressRange(from: "::/0") { - ipv6IncludedRoutes.append(NEIPv6Route( - destinationAddress: "\(allIPv6.address)", - networkPrefixLength: NSNumber(value: allIPv6.networkPrefixLength))) - } - networkSettings?.ipv4Settings?.includedRoutes = ipv4IncludedRoutes - networkSettings?.ipv6Settings?.includedRoutes = ipv6IncludedRoutes - networkSettings?.ipv4Settings?.excludedRoutes = ipv4ExcludedRoutes - } - } - - // Set the network settings for the current tunneling session. - setTunnelNetworkSettings(networkSettings, completionHandler: completionHandler) - } - - // Process events returned by the OpenVPN library - func openVPNAdapter( - _ openVPNAdapter: OpenVPNAdapter, - handleEvent event: OpenVPNAdapterEvent, - message: String?) { - switch event { - case .connected: - if reasserting { - reasserting = false - } - - guard let startHandler = startHandler else { return } - - startHandler(nil) - self.startHandler = nil - case .disconnected: - guard let stopHandler = stopHandler else { return } - - if vpnReachability.isTracking { - vpnReachability.stopTracking() - } - - stopHandler() - self.stopHandler = nil - case .reconnecting: - reasserting = true - default: - break - } - } - - // Handle errors thrown by the OpenVPN library - func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleError error: Error) { - // Handle only fatal errors - guard let fatal = (error as NSError).userInfo[OpenVPNAdapterErrorFatalKey] as? Bool, - fatal == true else { return } - - if vpnReachability.isTracking { - vpnReachability.stopTracking() - } - - if let startHandler { - startHandler(error) - self.startHandler = nil - } else { - cancelTunnelWithError(error) - } - } - - // Use this method to process any log message returned by OpenVPN library. - func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleLogMessage logMessage: String) { - // Handle log messages - ovpnLog(.info, message: logMessage) - } -} - -extension NEPacketTunnelFlow: OpenVPNAdapterPacketFlow {} diff --git a/client/platforms/ios/ios_controller.mm b/client/platforms/ios/ios_controller.mm deleted file mode 100644 index 85fb50b7..00000000 --- a/client/platforms/ios/ios_controller.mm +++ /dev/null @@ -1,865 +0,0 @@ -#include "ios_controller.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "../protocols/vpnprotocol.h" -#import "ios_controller_wrapper.h" - -const char* Action::start = "start"; -const char* Action::restart = "restart"; -const char* Action::stop = "stop"; -const char* Action::getTunnelId = "getTunnelId"; -const char* Action::getStatus = "status"; - -const char* MessageKey::action = "action"; -const char* MessageKey::tunnelId = "tunnelId"; -const char* MessageKey::config = "config"; -const char* MessageKey::errorCode = "errorCode"; -const char* MessageKey::host = "host"; -const char* MessageKey::port = "port"; -const char* MessageKey::isOnDemand = "is-on-demand"; -const char* MessageKey::SplitTunnelType = "SplitTunnelType"; -const char* MessageKey::SplitTunnelSites = "SplitTunnelSites"; - -static UIViewController* getViewController() { - NSArray *windows = [[UIApplication sharedApplication]windows]; - for (UIWindow *window in windows) { - if (window.isKeyWindow) { - return window.rootViewController; - } - } - return nil; -} - -Vpn::ConnectionState iosStatusToState(NEVPNStatus status) { - switch (status) { - case NEVPNStatusInvalid: - return Vpn::ConnectionState::Unknown; - case NEVPNStatusDisconnected: - return Vpn::ConnectionState::Disconnected; - case NEVPNStatusConnecting: - return Vpn::ConnectionState::Connecting; - case NEVPNStatusConnected: - return Vpn::ConnectionState::Connected; - case NEVPNStatusReasserting: - return Vpn::ConnectionState::Connecting; - case NEVPNStatusDisconnecting: - return Vpn::ConnectionState::Disconnecting; - default: - return Vpn::ConnectionState::Unknown; -} -} - -namespace { -IosController* s_instance = nullptr; -} - -IosController::IosController() : QObject() -{ - s_instance = this; - m_iosControllerWrapper = [[IosControllerWrapper alloc] initWithCppController:this]; - - [[NSNotificationCenter defaultCenter] - removeObserver: (__bridge NSObject *)m_iosControllerWrapper]; - [[NSNotificationCenter defaultCenter] - addObserver: (__bridge NSObject *)m_iosControllerWrapper selector:@selector(vpnStatusDidChange:) name:NEVPNStatusDidChangeNotification object:nil]; - [[NSNotificationCenter defaultCenter] - addObserver: (__bridge NSObject *)m_iosControllerWrapper selector:@selector(vpnConfigurationDidChange:) name:NEVPNConfigurationChangeNotification object:nil]; - -} - -IosController* IosController::Instance() { - if (!s_instance) { - s_instance = new IosController(); - } - - return s_instance; -} - -bool IosController::initialize() -{ - __block bool ok = true; - [NETunnelProviderManager loadAllFromPreferencesWithCompletionHandler:^(NSArray * _Nullable managers, NSError * _Nullable error) { - @try { - if (error) { - qDebug() << "IosController::initialize : Error:" << [error.localizedDescription UTF8String]; - emit connectionStateChanged(Vpn::ConnectionState::Error); - ok = false; - return; - } - - NSInteger managerCount = managers.count; - qDebug() << "IosController::initialize : We have received managers:" << (long)managerCount; - - - for (NETunnelProviderManager *manager in managers) { - qDebug() << "IosController::initialize : VPNC: " << manager.localizedDescription; - - if (manager.connection.status == NEVPNStatusConnected) { - m_currentTunnel = manager; - qDebug() << "IosController::initialize : VPN already connected with" << manager.localizedDescription; - emit connectionStateChanged(Vpn::ConnectionState::Connected); - break; - - // TODO: show connected state - } - } - } - @catch (NSException *exception) { - qDebug() << "IosController::setTunnel : exception" << QString::fromNSString(exception.reason); - ok = false; - } - }]; - - return ok; -} - -bool IosController::connectVpn(amnezia::Proto proto, const QJsonObject& configuration) -{ - m_proto = proto; - m_rawConfig = configuration; - m_serverAddress = configuration.value(config_key::hostName).toString().toNSString(); - - QString tunnelName; - if (configuration.value(config_key::description).toString().isEmpty()) { - tunnelName = QString("%1 %2") - .arg(configuration.value(config_key::hostName).toString()) - .arg(ProtocolProps::protoToString(proto)); - } - else { - tunnelName = QString("%1 (%2) %3") - .arg(configuration.value(config_key::description).toString()) - .arg(configuration.value(config_key::hostName).toString()) - .arg(ProtocolProps::protoToString(proto)); - } - - qDebug() << "IosController::connectVpn" << tunnelName; - - m_currentTunnel = nullptr; - - dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - __block bool ok = true; - __block bool isNewTunnelCreated = false; - - [NETunnelProviderManager loadAllFromPreferencesWithCompletionHandler:^(NSArray * _Nullable managers, NSError * _Nullable error) { - @try { - if (error) { - qDebug() << "IosController::connectVpn : VPNC: loadAllFromPreferences error:" << [error.localizedDescription UTF8String]; - emit connectionStateChanged(Vpn::ConnectionState::Error); - ok = false; - return; - } - - NSInteger managerCount = managers.count; - qDebug() << "IosController::connectVpn : We have received managers:" << (long)managerCount; - - - for (NETunnelProviderManager *manager in managers) { - if ([manager.localizedDescription isEqualToString:tunnelName.toNSString()]) { - m_currentTunnel = manager; - qDebug() << "IosController::connectVpn : Using existing tunnel:" << manager.localizedDescription; - if (manager.connection.status == NEVPNStatusConnected) { - emit connectionStateChanged(Vpn::ConnectionState::Connected); - return; - } - - break; - } - } - - if (!m_currentTunnel) { - isNewTunnelCreated = true; - m_currentTunnel = [[NETunnelProviderManager alloc] init]; - m_currentTunnel.localizedDescription = [NSString stringWithUTF8String:tunnelName.toStdString().c_str()]; - qDebug() << "IosController::connectVpn : Creating new tunnel" << m_currentTunnel.localizedDescription; - } - - } - @catch (NSException *exception) { - qDebug() << "IosController::connectVpn : exception" << QString::fromNSString(exception.reason); - ok = false; - m_currentTunnel = nullptr; - } - @finally { - dispatch_semaphore_signal(semaphore); - } - }]; - - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); - if (!ok) return false; - - [[NSNotificationCenter defaultCenter] - removeObserver:(__bridge NSObject *)m_iosControllerWrapper]; - - [[NSNotificationCenter defaultCenter] - addObserver:(__bridge NSObject *)m_iosControllerWrapper - selector:@selector(vpnStatusDidChange:) - name:NEVPNStatusDidChangeNotification - object:m_currentTunnel.connection]; - - - if (proto == amnezia::Proto::OpenVpn) { - return setupOpenVPN(); - } - if (proto == amnezia::Proto::Cloak) { - return setupCloak(); - } - if (proto == amnezia::Proto::WireGuard) { - return setupWireGuard(); - } - if (proto == amnezia::Proto::Awg) { - return setupAwg(); - } - if (proto == amnezia::Proto::Xray) { - return setupXray(); - } - if (proto == amnezia::Proto::SSXray) { - return setupSSXray(); - } - - return false; -} - -void IosController::disconnectVpn() -{ - if (!m_currentTunnel) { - return; - } - - if ([m_currentTunnel.connection isKindOfClass:[NETunnelProviderSession class]]) { - [(NETunnelProviderSession *)m_currentTunnel.connection stopTunnel]; - } -} - - -void IosController::checkStatus() -{ - NSString *actionKey = [NSString stringWithUTF8String:MessageKey::action]; - NSString *actionValue = [NSString stringWithUTF8String:Action::getStatus]; - NSString *tunnelIdKey = [NSString stringWithUTF8String:MessageKey::tunnelId]; - NSString *tunnelIdValue = !m_tunnelId.isEmpty() ? m_tunnelId.toNSString() : @""; - - NSDictionary* message = @{actionKey: actionValue, tunnelIdKey: tunnelIdValue}; - sendVpnExtensionMessage(message, [&](NSDictionary* response){ - uint64_t txBytes = [response[@"tx_bytes"] intValue]; - uint64_t rxBytes = [response[@"rx_bytes"] intValue]; - emit bytesChanged(rxBytes - m_rxBytes, txBytes - m_txBytes); - m_rxBytes = rxBytes; - m_txBytes = txBytes; - }); -} - -void IosController::vpnStatusDidChange(void *pNotification) -{ - NETunnelProviderSession *session = (NETunnelProviderSession *)pNotification; - - if (session /* && session == TunnelManager.session */ ) { - qDebug() << "IosController::vpnStatusDidChange" << iosStatusToState(session.status) << session; - - if (session.status == NEVPNStatusDisconnected) { - if (@available(iOS 16.0, *)) { - [session fetchLastDisconnectErrorWithCompletionHandler:^(NSError * _Nullable error) { - if (error != nil) { - qDebug() << "Disconnect error" << error.domain << error.code << error.localizedDescription; - - if ([error.domain isEqualToString:NEVPNConnectionErrorDomain]) { - switch (error.code) { - case NEVPNConnectionErrorOverslept: - qDebug() << "Disconnect error info" << "The VPN connection was terminated because the system slept for an extended period of time."; - break; - case NEVPNConnectionErrorNoNetworkAvailable: - qDebug() << "Disconnect error info" << "The VPN connection could not be established because the system is not connected to a network."; - break; - case NEVPNConnectionErrorUnrecoverableNetworkChange: - qDebug() << "Disconnect error info" << "The VPN connection was terminated because the network conditions changed in such a way that the VPN connection could not be maintained."; - break; - case NEVPNConnectionErrorConfigurationFailed: - qDebug() << "Disconnect error info" << "The VPN connection could not be established because the configuration is invalid. "; - break; - case NEVPNConnectionErrorServerAddressResolutionFailed: - qDebug() << "Disconnect error info" << "The address of the VPN server could not be determined."; - break; - case NEVPNConnectionErrorServerNotResponding: - qDebug() << "Disconnect error info" << "Network communication with the VPN server has failed."; - break; - case NEVPNConnectionErrorServerDead: - qDebug() << "Disconnect error info" << "The VPN server is no longer functioning."; - break; - case NEVPNConnectionErrorAuthenticationFailed: - qDebug() << "Disconnect error info" << "The user credentials were rejected by the VPN server."; - break; - case NEVPNConnectionErrorClientCertificateInvalid: - qDebug() << "Disconnect error info" << "The client certificate is invalid."; - break; - case NEVPNConnectionErrorClientCertificateNotYetValid: - qDebug() << "Disconnect error info" << "The client certificate will not be valid until some future point in time."; - break; - case NEVPNConnectionErrorClientCertificateExpired: - qDebug() << "Disconnect error info" << "The validity period of the client certificate has passed."; - break; - case NEVPNConnectionErrorPluginFailed: - qDebug() << "Disconnect error info" << "The VPN plugin died unexpectedly."; - break; - case NEVPNConnectionErrorConfigurationNotFound: - qDebug() << "Disconnect error info" << "The VPN configuration could not be found."; - break; - case NEVPNConnectionErrorPluginDisabled: - qDebug() << "Disconnect error info" << "The VPN plugin could not be found or needed to be updated."; - break; - case NEVPNConnectionErrorNegotiationFailed: - qDebug() << "Disconnect error info" << "The VPN protocol negotiation failed."; - break; - case NEVPNConnectionErrorServerDisconnected: - qDebug() << "Disconnect error info" << "The VPN server terminated the connection."; - break; - case NEVPNConnectionErrorServerCertificateInvalid: - qDebug() << "Disconnect error info" << "The server certificate is invalid."; - break; - case NEVPNConnectionErrorServerCertificateNotYetValid: - qDebug() << "Disconnect error info" << "The server certificate will not be valid until some future point in time."; - break; - case NEVPNConnectionErrorServerCertificateExpired: - qDebug() << "Disconnect error info" << "The validity period of the server certificate has passed."; - break; - default: - qDebug() << "Disconnect error info" << "Unknown code."; - break; - } - } - - NSError *underlyingError = error.userInfo[@"NSUnderlyingError"]; - if (underlyingError != nil) { - qDebug() << "Disconnect underlying error" << underlyingError.domain << underlyingError.code << underlyingError.localizedDescription; - - if ([underlyingError.domain isEqualToString:@"NEAgentErrorDomain"]) { - switch (underlyingError.code) { - case 1: - qDebug() << "Disconnect underlying error" << "General. Use sysdiagnose."; - break; - case 2: - qDebug() << "Disconnect underlying error" << "Plug-in unavailable. Use sysdiagnose."; - break; - default: - qDebug() << "Disconnect underlying error" << "Unknown code. Use sysdiagnose."; - break; - } - } - } - } - }]; - } else { - qDebug() << "Disconnect error is unavailable on iOS < 16.0"; - } - } - - emit connectionStateChanged(iosStatusToState(session.status)); - } -} - -void IosController::vpnConfigurationDidChange(void *pNotification) -{ - qDebug() << "IosController::vpnConfigurationDidChange" << pNotification; -} - -bool IosController::setupOpenVPN() -{ - QJsonObject ovpn = m_rawConfig[ProtocolProps::key_proto_config_data(amnezia::Proto::OpenVpn)].toObject(); - QString ovpnConfig = ovpn[config_key::config].toString(); - - QJsonObject openVPNConfig {}; - openVPNConfig.insert(config_key::config, ovpnConfig); - - if (ovpn.contains(config_key::mtu)) { - openVPNConfig.insert(config_key::mtu, ovpn[config_key::mtu]); - } else { - openVPNConfig.insert(config_key::mtu, protocols::openvpn::defaultMtu); - } - - openVPNConfig.insert(config_key::splitTunnelType, m_rawConfig[config_key::splitTunnelType]); - - QJsonArray splitTunnelSites = m_rawConfig[config_key::splitTunnelSites].toArray(); - - for(int index = 0; index < splitTunnelSites.count(); index++) { - splitTunnelSites[index] = splitTunnelSites[index].toString().remove(" "); - } - - openVPNConfig.insert(config_key::splitTunnelSites, splitTunnelSites); - - QJsonDocument openVPNConfigDoc(openVPNConfig); - QString openVPNConfigStr(openVPNConfigDoc.toJson(QJsonDocument::Compact)); - - return startOpenVPN(openVPNConfigStr); -} - -bool IosController::setupCloak() -{ - m_serverAddress = @"127.0.0.1"; - QJsonObject ovpn = m_rawConfig[ProtocolProps::key_proto_config_data(amnezia::Proto::OpenVpn)].toObject(); - QString ovpnConfig = ovpn[config_key::config].toString(); - - QJsonObject cloak = m_rawConfig[ProtocolProps::key_proto_config_data(amnezia::Proto::Cloak)].toObject(); - - cloak["NumConn"] = 1; - if (cloak.contains("remote")) { - cloak["RemoteHost"] = cloak["remote"].toString(); - } - if (cloak.contains("port")) { - cloak["RemotePort"] = cloak["port"].toString(); - } - cloak.remove("remote"); - cloak.remove("port"); - cloak.remove("transport_proto"); - - QJsonObject jsonObject {}; - foreach(const QString& key, cloak.keys()) { - if(key == "NumConn" or key == "StreamTimeout"){ - jsonObject.insert(key, cloak.value(key).toInt()); - }else{ - jsonObject.insert(key, cloak.value(key).toString()); - } - } - QJsonDocument doc(jsonObject); - QString strJson(doc.toJson(QJsonDocument::Compact)); - QString cloakBase64 = strJson.toUtf8().toBase64(); - ovpnConfig.append("\n\n"); - ovpnConfig.append(cloakBase64); - ovpnConfig.append("\n\n"); - - QJsonObject openVPNConfig {}; - openVPNConfig.insert(config_key::config, ovpnConfig); - - if (ovpn.contains(config_key::mtu)) { - openVPNConfig.insert(config_key::mtu, ovpn[config_key::mtu]); - } else { - openVPNConfig.insert(config_key::mtu, protocols::openvpn::defaultMtu); - } - - openVPNConfig.insert(config_key::splitTunnelType, m_rawConfig[config_key::splitTunnelType]); - - QJsonArray splitTunnelSites = m_rawConfig[config_key::splitTunnelSites].toArray(); - - for(int index = 0; index < splitTunnelSites.count(); index++) { - splitTunnelSites[index] = splitTunnelSites[index].toString().remove(" "); - } - - openVPNConfig.insert(config_key::splitTunnelSites, splitTunnelSites); - - QJsonDocument openVPNConfigDoc(openVPNConfig); - QString openVPNConfigStr(openVPNConfigDoc.toJson(QJsonDocument::Compact)); - - return startOpenVPN(openVPNConfigStr); -} - -bool IosController::setupWireGuard() -{ - QJsonObject config = m_rawConfig[ProtocolProps::key_proto_config_data(amnezia::Proto::WireGuard)].toObject(); - - QJsonObject wgConfig {}; - wgConfig.insert(config_key::dns1, m_rawConfig[config_key::dns1]); - wgConfig.insert(config_key::dns2, m_rawConfig[config_key::dns2]); - - if (config.contains(config_key::mtu)) { - wgConfig.insert(config_key::mtu, config[config_key::mtu]); - } else { - wgConfig.insert(config_key::mtu, protocols::wireguard::defaultMtu); - } - - wgConfig.insert(config_key::hostName, config[config_key::hostName]); - wgConfig.insert(config_key::port, config[config_key::port]); - wgConfig.insert(config_key::client_ip, config[config_key::client_ip]); - wgConfig.insert(config_key::client_priv_key, config[config_key::client_priv_key]); - wgConfig.insert(config_key::server_pub_key, config[config_key::server_pub_key]); - wgConfig.insert(config_key::psk_key, config[config_key::psk_key]); - wgConfig.insert(config_key::splitTunnelType, m_rawConfig[config_key::splitTunnelType]); - - QJsonArray splitTunnelSites = m_rawConfig[config_key::splitTunnelSites].toArray(); - - for(int index = 0; index < splitTunnelSites.count(); index++) { - splitTunnelSites[index] = splitTunnelSites[index].toString().remove(" "); - } - - wgConfig.insert(config_key::splitTunnelSites, splitTunnelSites); - - if (config.contains(config_key::allowed_ips) && config[config_key::allowed_ips].isArray()) { - wgConfig.insert(config_key::allowed_ips, config[config_key::allowed_ips]); - } else { - QJsonArray allowed_ips { "0.0.0.0/0", "::/0" }; - wgConfig.insert(config_key::allowed_ips, allowed_ips); - } - - if (config.contains(config_key::persistent_keep_alive)) { - wgConfig.insert(config_key::persistent_keep_alive, config[config_key::persistent_keep_alive]); - } else { - wgConfig.insert(config_key::persistent_keep_alive, "25"); - } - - if (config.contains(config_key::isObfuscationEnabled) && config.value(config_key::isObfuscationEnabled).toBool()) { - wgConfig.insert(config_key::initPacketMagicHeader, config[config_key::initPacketMagicHeader]); - wgConfig.insert(config_key::responsePacketMagicHeader, config[config_key::responsePacketMagicHeader]); - wgConfig.insert(config_key::underloadPacketMagicHeader, config[config_key::underloadPacketMagicHeader]); - wgConfig.insert(config_key::transportPacketMagicHeader, config[config_key::transportPacketMagicHeader]); - - wgConfig.insert(config_key::initPacketJunkSize, config[config_key::initPacketJunkSize]); - wgConfig.insert(config_key::responsePacketJunkSize, config[config_key::responsePacketJunkSize]); - - wgConfig.insert(config_key::junkPacketCount, config[config_key::junkPacketCount]); - wgConfig.insert(config_key::junkPacketMinSize, config[config_key::junkPacketMinSize]); - wgConfig.insert(config_key::junkPacketMaxSize, config[config_key::junkPacketMaxSize]); - } - - QJsonDocument wgConfigDoc(wgConfig); - QString wgConfigDocStr(wgConfigDoc.toJson(QJsonDocument::Compact)); - - return startWireGuard(wgConfigDocStr); -} - -bool IosController::setupXray() -{ - QJsonObject config = m_rawConfig[ProtocolProps::key_proto_config_data(amnezia::Proto::Xray)].toObject(); - QJsonDocument xrayConfigDoc(config); - - QString xrayConfigStr(xrayConfigDoc.toJson(QJsonDocument::Compact)); - - QJsonObject finalConfig; - finalConfig.insert(config_key::dns1, m_rawConfig[config_key::dns1].toString()); - finalConfig.insert(config_key::dns2, m_rawConfig[config_key::dns2].toString()); - finalConfig.insert(config_key::config, xrayConfigStr); - - QJsonDocument finalConfigDoc(finalConfig); - QString finalConfigStr(finalConfigDoc.toJson(QJsonDocument::Compact)); - - return startXray(finalConfigStr); -} - -bool IosController::setupSSXray() -{ - QJsonObject config = m_rawConfig[ProtocolProps::key_proto_config_data(amnezia::Proto::SSXray)].toObject(); - QJsonDocument ssXrayConfigDoc(config); - - QString ssXrayConfigStr(ssXrayConfigDoc.toJson(QJsonDocument::Compact)); - - QJsonObject finalConfig; - finalConfig.insert(config_key::dns1, m_rawConfig[config_key::dns1]); - finalConfig.insert(config_key::dns2, m_rawConfig[config_key::dns2]); - finalConfig.insert(config_key::config, ssXrayConfigStr); - - QJsonDocument finalConfigDoc(finalConfig); - QString finalConfigStr(finalConfigDoc.toJson(QJsonDocument::Compact)); - - return startXray(finalConfigStr); -} - -bool IosController::setupAwg() -{ - QJsonObject config = m_rawConfig[ProtocolProps::key_proto_config_data(amnezia::Proto::Awg)].toObject(); - - QJsonObject wgConfig {}; - wgConfig.insert(config_key::dns1, m_rawConfig[config_key::dns1]); - wgConfig.insert(config_key::dns2, m_rawConfig[config_key::dns2]); - - if (config.contains(config_key::mtu)) { - wgConfig.insert(config_key::mtu, config[config_key::mtu]); - } else { - wgConfig.insert(config_key::mtu, protocols::awg::defaultMtu); - } - - wgConfig.insert(config_key::hostName, config[config_key::hostName]); - wgConfig.insert(config_key::port, config[config_key::port]); - wgConfig.insert(config_key::client_ip, config[config_key::client_ip]); - wgConfig.insert(config_key::client_priv_key, config[config_key::client_priv_key]); - wgConfig.insert(config_key::server_pub_key, config[config_key::server_pub_key]); - wgConfig.insert(config_key::psk_key, config[config_key::psk_key]); - wgConfig.insert(config_key::splitTunnelType, m_rawConfig[config_key::splitTunnelType]); - - QJsonArray splitTunnelSites = m_rawConfig[config_key::splitTunnelSites].toArray(); - - for(int index = 0; index < splitTunnelSites.count(); index++) { - splitTunnelSites[index] = splitTunnelSites[index].toString().remove(" "); - } - - wgConfig.insert(config_key::splitTunnelSites, splitTunnelSites); - - if (config.contains(config_key::allowed_ips) && config[config_key::allowed_ips].isArray()) { - wgConfig.insert(config_key::allowed_ips, config[config_key::allowed_ips]); - } else { - QJsonArray allowed_ips { "0.0.0.0/0", "::/0" }; - wgConfig.insert(config_key::allowed_ips, allowed_ips); - } - - if (config.contains(config_key::persistent_keep_alive)) { - wgConfig.insert(config_key::persistent_keep_alive, config[config_key::persistent_keep_alive]); - } else { - wgConfig.insert(config_key::persistent_keep_alive, "25"); - } - - wgConfig.insert(config_key::initPacketMagicHeader, config[config_key::initPacketMagicHeader]); - wgConfig.insert(config_key::responsePacketMagicHeader, config[config_key::responsePacketMagicHeader]); - wgConfig.insert(config_key::underloadPacketMagicHeader, config[config_key::underloadPacketMagicHeader]); - wgConfig.insert(config_key::transportPacketMagicHeader, config[config_key::transportPacketMagicHeader]); - - wgConfig.insert(config_key::initPacketJunkSize, config[config_key::initPacketJunkSize]); - wgConfig.insert(config_key::responsePacketJunkSize, config[config_key::responsePacketJunkSize]); - - wgConfig.insert(config_key::junkPacketCount, config[config_key::junkPacketCount]); - wgConfig.insert(config_key::junkPacketMinSize, config[config_key::junkPacketMinSize]); - wgConfig.insert(config_key::junkPacketMaxSize, config[config_key::junkPacketMaxSize]); - - QJsonDocument wgConfigDoc(wgConfig); - QString wgConfigDocStr(wgConfigDoc.toJson(QJsonDocument::Compact)); - - return startWireGuard(wgConfigDocStr); -} - -bool IosController::startOpenVPN(const QString &config) -{ - qDebug() << "IosController::startOpenVPN"; - - NETunnelProviderProtocol *tunnelProtocol = [[NETunnelProviderProtocol alloc] init]; - tunnelProtocol.providerBundleIdentifier = [NSString stringWithUTF8String:VPN_NE_BUNDLEID]; - tunnelProtocol.providerConfiguration = @{@"ovpn": [[NSString stringWithUTF8String:config.toStdString().c_str()] dataUsingEncoding:NSUTF8StringEncoding]}; - tunnelProtocol.serverAddress = m_serverAddress; - - m_currentTunnel.protocolConfiguration = tunnelProtocol; - - startTunnel(); -} - -bool IosController::startWireGuard(const QString &config) -{ - qDebug() << "IosController::startWireGuard"; - - NETunnelProviderProtocol *tunnelProtocol = [[NETunnelProviderProtocol alloc] init]; - tunnelProtocol.providerBundleIdentifier = [NSString stringWithUTF8String:VPN_NE_BUNDLEID]; - tunnelProtocol.providerConfiguration = @{@"wireguard": [[NSString stringWithUTF8String:config.toStdString().c_str()] dataUsingEncoding:NSUTF8StringEncoding]}; - tunnelProtocol.serverAddress = m_serverAddress; - - m_currentTunnel.protocolConfiguration = tunnelProtocol; - - startTunnel(); -} - -bool IosController::startXray(const QString &config) -{ - qDebug() << "IosController::startXray"; - - NETunnelProviderProtocol *tunnelProtocol = [[NETunnelProviderProtocol alloc] init]; - tunnelProtocol.providerBundleIdentifier = [NSString stringWithUTF8String:VPN_NE_BUNDLEID]; - tunnelProtocol.providerConfiguration = @{@"xray": [[NSString stringWithUTF8String:config.toStdString().c_str()] dataUsingEncoding:NSUTF8StringEncoding]}; - tunnelProtocol.serverAddress = m_serverAddress; - - m_currentTunnel.protocolConfiguration = tunnelProtocol; - - startTunnel(); -} - -void IosController::startTunnel() -{ - NSString *protocolName = @"Unknown"; - - NETunnelProviderProtocol *tunnelProtocol = (NETunnelProviderProtocol *)m_currentTunnel.protocolConfiguration; - if (tunnelProtocol.providerConfiguration[@"wireguard"] != nil) { - protocolName = @"WireGuard"; - } else if (tunnelProtocol.providerConfiguration[@"ovpn"] != nil) { - protocolName = @"OpenVPN"; - } - - m_rxBytes = 0; - m_txBytes = 0; - - [m_currentTunnel setEnabled:YES]; - - [m_currentTunnel saveToPreferencesWithCompletionHandler:^(NSError *saveError) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - - if (saveError) { - qDebug().nospace() << "IosController::startTunnel" << protocolName << ": Connect " << protocolName << " Tunnel Save Error" << saveError.localizedDescription.UTF8String; - emit connectionStateChanged(Vpn::ConnectionState::Error); - return; - } - - [m_currentTunnel loadFromPreferencesWithCompletionHandler:^(NSError *loadError) { - if (loadError) { - qDebug().nospace() << "IosController::startTunnel :" << m_currentTunnel.localizedDescription << protocolName << ": Connect " << protocolName << " Tunnel Load Error" << loadError.localizedDescription.UTF8String; - emit connectionStateChanged(Vpn::ConnectionState::Error); - return; - } - - NSError *startError = nil; - qDebug() << iosStatusToState(m_currentTunnel.connection.status); - - BOOL started = [m_currentTunnel.connection startVPNTunnelWithOptions:nil andReturnError:&startError]; - - if (!started || startError) { - qDebug().nospace() << "IosController::startTunnel :" << m_currentTunnel.localizedDescription << protocolName << " : Connect " << protocolName << " Tunnel Start Error" - << (startError ? startError.localizedDescription.UTF8String : ""); - emit connectionStateChanged(Vpn::ConnectionState::Error); - } else { - qDebug().nospace() << "IosController::startTunnel :" << m_currentTunnel.localizedDescription << protocolName << " : Starting the tunnel succeeded"; - } - }]; - }); - }]; -} - -bool IosController::isOurManager(NETunnelProviderManager* manager) { - NETunnelProviderProtocol* tunnelProto = (NETunnelProviderProtocol*)manager.protocolConfiguration; - - if (!tunnelProto) { - qDebug() << "Ignoring manager because the proto is invalid"; - return false; - } - - if (!tunnelProto.providerBundleIdentifier) { - qDebug() << "Ignoring manager because the bundle identifier is null"; - return false; - } - - if (![tunnelProto.providerBundleIdentifier isEqualToString:[NSString stringWithUTF8String:VPN_NE_BUNDLEID]]) { - qDebug() << "Ignoring manager because the bundle identifier doesn't match"; - return false; - } - - qDebug() << "Found the manager with the correct bundle identifier:" << QString::fromNSString(tunnelProto.providerBundleIdentifier); - - return true; -} - -void IosController::sendVpnExtensionMessage(NSDictionary* message, std::function callback) -{ - if (!m_currentTunnel) { - qDebug() << "Cannot set an extension callback without a tunnel manager"; - return; - } - - NSError *error = nil; - NSData *data = [NSJSONSerialization dataWithJSONObject:message options:0 error:&error]; - - if (!data || error) { - qDebug() << "Failed to serialize message to VpnExtension as JSON. Error:" - << [error.localizedDescription UTF8String]; - return; - } - - void (^completionHandler)(NSData *) = ^(NSData *responseData) { - if (!responseData) { - if (callback) callback(nil); - return; - } - - NSError *deserializeError = nil; - NSDictionary *response = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&deserializeError]; - - if (response && [response isKindOfClass:[NSDictionary class]]) { - if (callback) callback(response); - return; - } else if (deserializeError) { - qDebug() << "Failed to deserialize the VpnExtension response"; - } - - if (callback) callback(nil); - }; - - NETunnelProviderSession *session = (NETunnelProviderSession *)m_currentTunnel.connection; - - NSError *sendError = nil; - - if ([session respondsToSelector:@selector(sendProviderMessage:returnError:responseHandler:)]) { - [session sendProviderMessage:data returnError:&sendError responseHandler:completionHandler]; - } else { - qDebug() << "Method sendProviderMessage:responseHandler:error: does not exist"; - } - - if (sendError) { - qDebug() << "Failed to send message to VpnExtension. Error:" - << [sendError.localizedDescription UTF8String]; - } - -} - -bool IosController::shareText(const QStringList& filesToSend) { - NSMutableArray *sharingItems = [NSMutableArray new]; - - for (int i = 0; i < filesToSend.size(); i++) { - NSURL *logFileUrl = [[NSURL alloc] initFileURLWithPath:filesToSend[i].toNSString()]; - [sharingItems addObject:logFileUrl]; - } - - UIViewController *qtController = getViewController(); - if (!qtController) return; - - UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil]; - - __block bool isAccepted = false; - - [activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) { - isAccepted = completed; - emit finished(); - }]; - - [qtController presentViewController:activityController animated:YES completion:nil]; - UIPopoverPresentationController *popController = activityController.popoverPresentationController; - if (popController) { - popController.sourceView = qtController.view; - popController.sourceRect = CGRectMake(100, 100, 100, 100); - } - - QEventLoop wait; - QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit); - wait.exec(); - - return isAccepted; -} - -QString IosController::openFile() { - UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeOpen]; - - DocumentPickerDelegate *documentPickerDelegate = [[DocumentPickerDelegate alloc] init]; - documentPicker.delegate = documentPickerDelegate; - - UIViewController *qtController = getViewController(); - if (!qtController) return; - - [qtController presentViewController:documentPicker animated:YES completion:nil]; - - __block QString filePath; - - documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) { - if (path) { - filePath = QString::fromUtf8(path.UTF8String); - } else { - filePath = QString(); - } - emit finished(); - }; - - QEventLoop wait; - QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit); - wait.exec(); - - return filePath; -} - -void IosController::requestInetAccess() { - NSURL *url = [NSURL URLWithString:@"http://captive.apple.com/generate_204"]; - if (!url) { - qDebug() << "IosController::requestInetAccess URL error"; - return; - } - - NSURLSession *session = [NSURLSession sharedSession]; - NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { - if (error) { - qDebug() << "IosController::requestInetAccess error:" << error.localizedDescription; - } else { - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; - QString responseBody = QString::fromUtf8((const char*)data.bytes, data.length); - } - }]; - [task resume]; -}