From 22b14dff5f51584ac471a5af239b9693cbba9014 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Sun, 22 Oct 2023 16:21:59 +0300 Subject: [PATCH] iOS AWG/WG split tunnel --- client/platforms/ios/ios_controller.h | 2 + client/platforms/ios/ios_controller.mm | 22 +++++- client/platforms/ios/iostunnel.swift | 71 ++++++++++++++++++- client/settings.cpp | 4 -- .../ui/qml/Pages2/PageSettingsConnection.qml | 8 +-- 5 files changed, 97 insertions(+), 10 deletions(-) diff --git a/client/platforms/ios/ios_controller.h b/client/platforms/ios/ios_controller.h index 68f30ce8..2597e15d 100644 --- a/client/platforms/ios/ios_controller.h +++ b/client/platforms/ios/ios_controller.h @@ -28,6 +28,8 @@ struct MessageKey static const char *host; static const char *port; static const char *isOnDemand; + static const char *SplitTunnelType; + static const char *SplitTunnelSites; }; class IosController : public QObject diff --git a/client/platforms/ios/ios_controller.mm b/client/platforms/ios/ios_controller.mm index 5665ff1d..841ff1e0 100644 --- a/client/platforms/ios/ios_controller.mm +++ b/client/platforms/ios/ios_controller.mm @@ -29,6 +29,9 @@ 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"; + Vpn::ConnectionState iosStatusToState(NEVPNStatus status) { switch (status) { @@ -351,6 +354,15 @@ void IosController::startTunnel() { m_rxBytes = 0; m_txBytes = 0; + + qDebug() << "m_rawConfig " << m_rawConfig; + + int STT = m_rawConfig["splitTunnelType"].toInt(); + QJsonArray splitTunnelSites = m_rawConfig["splitTunnelSites"].toArray(); + QJsonDocument doc; + doc.setArray(splitTunnelSites); + QString STS(doc.toJson()); + [m_currentTunnel setEnabled:YES]; [m_currentTunnel saveToPreferencesWithCompletionHandler:^(NSError *saveError) { @@ -376,8 +388,16 @@ void IosController::startTunnel() NSString *actionValue = [NSString stringWithUTF8String:Action::start]; NSString *tunnelIdKey = [NSString stringWithUTF8String:MessageKey::tunnelId]; NSString *tunnelIdValue = !m_tunnelId.isEmpty() ? m_tunnelId.toNSString() : @""; + NSString *SplitTunnelTypeKey = [NSString stringWithUTF8String:MessageKey::SplitTunnelType]; + NSString *SplitTunnelTypeValue = [NSString stringWithFormat:@"%d",STT]; + NSString *SplitTunnelSitesKey = [NSString stringWithUTF8String:MessageKey::SplitTunnelSites]; + NSString *SplitTunnelSitesValue = STS.toNSString(); + - NSDictionary* message = @{actionKey: actionValue, tunnelIdKey: tunnelIdValue}; + NSDictionary* message = @{actionKey: actionValue, tunnelIdKey: tunnelIdValue, + SplitTunnelTypeKey: SplitTunnelTypeValue, SplitTunnelSitesKey: SplitTunnelSitesValue}; + + qDebug() << "sendVpnExtensionMessage " << message; sendVpnExtensionMessage(message); diff --git a/client/platforms/ios/iostunnel.swift b/client/platforms/ios/iostunnel.swift index 41d835ae..45eee6a3 100644 --- a/client/platforms/ios/iostunnel.swift +++ b/client/platforms/ios/iostunnel.swift @@ -29,6 +29,8 @@ struct Constants { static let kMessageKeyHost = "host" static let kMessageKeyPort = "port" static let kMessageKeyOnDemand = "is-on-demand" + static let kMessageKeySplitTunnelType = "SplitTunnelType" + static let kMessageKeySplitTunnelSites = "SplitTunnelSites" } class PacketTunnelProvider: NEPacketTunnelProvider { @@ -49,6 +51,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider { private let dispatchQueue = DispatchQueue(label: "PacketTunnel", qos: .utility) private var openVPNConfig: Data? = nil + private var SplitTunnelType: String? = nil + private var SplitTunnelSites: String? = nil let vpnReachability = OpenVPNReachability() @@ -63,6 +67,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider { } override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) { + + let tmpStr = String(data: messageData, encoding: .utf8)! + wg_log(.error, message: tmpStr) guard let message = try? JSONSerialization.jsonObject(with: messageData, options: []) as? [String: Any] else { Logger.global?.log(message: "Failed to serialize message from app") return @@ -83,6 +90,11 @@ class PacketTunnelProvider: NEPacketTunnelProvider { handleStatusAppMessage(messageData, completionHandler: completionHandler) } + if action == Constants.kActionStart { + SplitTunnelType = message[Constants.kMessageKeySplitTunnelType] as? String + SplitTunnelSites = message[Constants.kMessageKeySplitTunnelSites] as? String + } + let callbackWrapper: (NSNumber?) -> Void = { errorCode in //let tunnelId = self.tunnelConfig?.id ?? "" let response: [String: Any] = [ @@ -175,6 +187,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { return } + let wgConfigStr = String(data: wgConfig, encoding: .utf8)! guard let tunnelConfiguration = try? TunnelConfiguration(fromWgQuickConfig: wgConfigStr) else { @@ -182,7 +195,63 @@ class PacketTunnelProvider: NEPacketTunnelProvider { completionHandler(nil) return } - + + wg_log(.error, message: tunnelConfiguration.peers.first!.allowedIPs.map { $0.stringRepresentation }.joined(separator: ", ")) + + if (tunnelConfiguration.peers.first!.allowedIPs.map { $0.stringRepresentation }.joined(separator: ", ") == "0.0.0.0/0, ::/0"){ + if (SplitTunnelType == "1") { + wg_log(.error, message: SplitTunnelSites!) + for index in tunnelConfiguration.peers.indices { + tunnelConfiguration.peers[index].allowedIPs.removeAll() + var allowedIPs = [IPAddressRange]() + + let data = Data(SplitTunnelSites!.utf8) + do { + let array = try JSONSerialization.jsonObject(with: data) as! [String] + for allowedIPString in array { + wg_log(.error,message: allowedIPString) + guard let allowedIP = IPAddressRange(from: allowedIPString) else { + wg_log(.error,message: "Parse SplitTunnelSites Error") + return + } + allowedIPs.append(allowedIP) + } + + } catch { + wg_log(.error,message: "Parse JSONSerialization Error") + } + tunnelConfiguration.peers[index].allowedIPs = allowedIPs + } + } else { + if (SplitTunnelType == "2") + { + wg_log(.error, message: SplitTunnelSites!) + for index in tunnelConfiguration.peers.indices { + var excludeIPs = [IPAddressRange]() + + let data = Data(SplitTunnelSites!.utf8) + do { + let array = try JSONSerialization.jsonObject(with: data) as! [String] + for excludeIPString in array { + wg_log(.error,message: excludeIPString) + guard let excludeIP = IPAddressRange(from: excludeIPString) else { + wg_log(.error,message: "Parse SplitTunnelSites Error") + return + } + excludeIPs.append(excludeIP) + } + + } catch { + wg_log(.error,message: "Parse JSONSerialization Error") + } + tunnelConfiguration.peers[index].excludeIPs = excludeIPs + } + } + } + } + + wg_log(.error, message: tunnelConfiguration.peers.first!.allowedIPs.map { $0.stringRepresentation }.joined(separator: ", ")) + wg_log(.info, message: "Starting wireguard tunnel from the " + (activationAttemptId == nil ? "OS directly, rather than the app" : "app")) // Start the tunnel diff --git a/client/settings.cpp b/client/settings.cpp index c6db0b63..4757dba6 100644 --- a/client/settings.cpp +++ b/client/settings.cpp @@ -233,10 +233,6 @@ QString Settings::routeModeString(RouteMode mode) const Settings::RouteMode Settings::routeMode() const { -// TODO implement for mobiles -#if defined(Q_OS_IOS) - return RouteMode::VpnAllSites; -#endif return static_cast(m_settings.value("Conf/routeMode", 0).toInt()); } diff --git a/client/ui/qml/Pages2/PageSettingsConnection.qml b/client/ui/qml/Pages2/PageSettingsConnection.qml index c4f3b64a..c12c335d 100644 --- a/client/ui/qml/Pages2/PageSettingsConnection.qml +++ b/client/ui/qml/Pages2/PageSettingsConnection.qml @@ -94,7 +94,7 @@ PageType { DividerType {} LabelWithButtonType { - visible: GC.isDesktop() || Qt.platform.os === "android" + visible: true Layout.fillWidth: true @@ -108,11 +108,11 @@ PageType { } DividerType { - visible: !GC.isMobile() + visible: GC.isDesktop() } LabelWithButtonType { - visible: false//!GC.isMobile() + visible: false Layout.fillWidth: true @@ -125,7 +125,7 @@ PageType { } DividerType { - visible: false//!GC.isMobile() + visible: false } } }