diff --git a/client/android/awg/src/main/kotlin/Awg.kt b/client/android/awg/src/main/kotlin/Awg.kt index f4175763b..fbd1cce04 100644 --- a/client/android/awg/src/main/kotlin/Awg.kt +++ b/client/android/awg/src/main/kotlin/Awg.kt @@ -1,81 +1,28 @@ package org.amnezia.vpn.protocol.awg import org.amnezia.vpn.protocol.wireguard.Wireguard +import org.amnezia.vpn.util.optStringOrNull import org.json.JSONObject -/** - * Config example: - * { - * "protocol": "awg", - * "description": "Server 1", - * "dns1": "1.1.1.1", - * "dns2": "1.0.0.1", - * "hostName": "100.100.100.0", - * "splitTunnelSites": [ - * ], - * "splitTunnelType": 0, - * "awg_config_data": { - * "H1": "969537490", - * "H2": "481688153", - * "H3": "2049399200", - * "H4": "52029755", - * "Jc": "3", - * "Jmax": "1000", - * "Jmin": "50", - * "S1": "49", - * "S2": "60", - * "client_ip": "10.8.1.1", - * "hostName": "100.100.100.0", - * "port": 12345, - * "client_pub_key": "clientPublicKeyBase64", - * "client_priv_key": "privateKeyBase64", - * "psk_key": "presharedKeyBase64", - * "server_pub_key": "publicKeyBase64", - * "config": "[Interface] - * Address = 10.8.1.1/32 - * DNS = 1.1.1.1, 1.0.0.1 - * PrivateKey = privateKeyBase64 - * Jc = 3 - * Jmin = 50 - * Jmax = 1000 - * S1 = 49 - * S2 = 60 - * H1 = 969537490 - * H2 = 481688153 - * H3 = 2049399200 - * H4 = 52029755 - * - * [Peer] - * PublicKey = publicKeyBase64 - * PresharedKey = presharedKeyBase64 - * AllowedIPs = 0.0.0.0/0, ::/0 - * Endpoint = 100.100.100.0:12345 - * PersistentKeepalive = 25 - * " - * } - * } - */ - class Awg : Wireguard() { override val ifName: String = "awg0" override fun parseConfig(config: JSONObject): AwgConfig { - val configDataJson = config.getJSONObject("awg_config_data") - val configData = parseConfigData(configDataJson.getString("config")) + val configData = config.getJSONObject("awg_config_data") return AwgConfig.build { - configWireguard(configData, configDataJson) + configWireguard(config, configData) configSplitTunneling(config) configAppSplitTunneling(config) - configData["Jc"]?.let { setJc(it.toInt()) } - configData["Jmin"]?.let { setJmin(it.toInt()) } - configData["Jmax"]?.let { setJmax(it.toInt()) } - configData["S1"]?.let { setS1(it.toInt()) } - configData["S2"]?.let { setS2(it.toInt()) } - configData["H1"]?.let { setH1(it.toLong()) } - configData["H2"]?.let { setH2(it.toLong()) } - configData["H3"]?.let { setH3(it.toLong()) } - configData["H4"]?.let { setH4(it.toLong()) } + configData.optStringOrNull("Jc")?.let { setJc(it.toInt()) } + configData.optStringOrNull("Jmin")?.let { setJmin(it.toInt()) } + configData.optStringOrNull("Jmax")?.let { setJmax(it.toInt()) } + configData.optStringOrNull("S1")?.let { setS1(it.toInt()) } + configData.optStringOrNull("S2")?.let { setS2(it.toInt()) } + configData.optStringOrNull("H1")?.let { setH1(it.toLong()) } + configData.optStringOrNull("H2")?.let { setH2(it.toLong()) } + configData.optStringOrNull("H3")?.let { setH3(it.toLong()) } + configData.optStringOrNull("H4")?.let { setH4(it.toLong()) } } } } diff --git a/client/android/cloak/src/main/kotlin/Cloak.kt b/client/android/cloak/src/main/kotlin/Cloak.kt index 5a5491308..18a5e6c7e 100644 --- a/client/android/cloak/src/main/kotlin/Cloak.kt +++ b/client/android/cloak/src/main/kotlin/Cloak.kt @@ -5,36 +5,6 @@ import net.openvpn.ovpn3.ClientAPI_Config import org.amnezia.vpn.protocol.openvpn.OpenVpn import org.json.JSONObject -/** - * Config Example: - * { - * "protocol": "cloak", - * "description": "Server 1", - * "dns1": "1.1.1.1", - * "dns2": "1.0.0.1", - * "hostName": "100.100.100.0", - * "splitTunnelSites": [ - * ], - * "splitTunnelType": 0, - * "openvpn_config_data": { - * "config": "openVpnConfig" - * } - * "cloak_config_data": { - * "BrowserSig": "chrome", - * "EncryptionMethod": "aes-gcm", - * "NumConn": 1, - * "ProxyMethod": "openvpn", - * "PublicKey": "PublicKey=", - * "RemoteHost": "100.100.100.0", - * "RemotePort": "443", - * "ServerName": "servername", - * "StreamTimeout": 300, - * "Transport": "direct", - * "UID": "UID=" - * } - * } - */ - class Cloak : OpenVpn() { override fun parseConfig(config: JSONObject): ClientAPI_Config { diff --git a/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt b/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt index abe46245e..b93d10ac9 100644 --- a/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt +++ b/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt @@ -16,23 +16,6 @@ import org.amnezia.vpn.util.net.getLocalNetworks import org.amnezia.vpn.util.net.parseInetAddress import org.json.JSONObject -/** - * Config Example: - * { - * "protocol": "openvpn", - * "description": "Server 1", - * "dns1": "1.1.1.1", - * "dns2": "1.0.0.1", - * "hostName": "100.100.100.0", - * "splitTunnelSites": [ - * ], - * "splitTunnelType": 0, - * "openvpn_config_data": { - * "config": "openVpnConfig" - * } - * } - */ - open class OpenVpn : Protocol() { private var openVpnClient: OpenVpnClient? = null diff --git a/client/android/utils/src/main/kotlin/JsonExt.kt b/client/android/utils/src/main/kotlin/JsonExt.kt new file mode 100644 index 000000000..45c5bacd9 --- /dev/null +++ b/client/android/utils/src/main/kotlin/JsonExt.kt @@ -0,0 +1,9 @@ +package org.amnezia.vpn.util + +import org.json.JSONArray +import org.json.JSONObject + +inline fun JSONArray.asSequence(): Sequence = + (0.., configDataJson: JSONObject) { - configData["Address"]?.split(",")?.map { address -> + protected fun WireguardConfig.Builder.configWireguard(config: JSONObject, configData: JSONObject) { + configData.getString("client_ip").split(",").map { address -> InetNetwork.parse(address.trim()) - }?.forEach(::addAddress) + }.forEach(::addAddress) + + config.optStringOrNull("dns1")?.let { dns -> + addDnsServer(parseInetAddress(dns.trim())) + } - configData["DNS"]?.split(",")?.map { dns -> - parseInetAddress(dns.trim()) - }?.forEach(::addDnsServer) + config.optStringOrNull("dns2")?.let { dns -> + addDnsServer(parseInetAddress(dns.trim())) + } val defRoutes = hashSetOf( InetNetwork("0.0.0.0", 0), InetNetwork("::", 0) ) val routes = hashSetOf() - configData["AllowedIPs"]?.split(",")?.map { route -> + configData.getJSONArray("allowed_ips").asSequence().map { route -> InetNetwork.parse(route.trim()) - }?.forEach(routes::add) + }.forEach(routes::add) // if the allowed IPs list contains at least one non-default route, disable global split tunneling if (routes.any { it !in defRoutes }) disableSplitTunneling() addRoutes(routes) - configDataJson.optString("mtu").let { mtu -> - if (mtu.isNotEmpty()) { - setMtu(mtu.toInt()) - } else { - configData["MTU"]?.let { setMtu(it.toInt()) } - } - } + configData.optStringOrNull("mtu")?.let { setMtu(it.toInt()) } - configData["Endpoint"]?.let { setEndpoint(InetEndpoint.parse(it)) } - configData["PersistentKeepalive"]?.let { setPersistentKeepalive(it.toInt()) } - configData["PrivateKey"]?.let { setPrivateKeyHex(it.base64ToHex()) } - configData["PublicKey"]?.let { setPublicKeyHex(it.base64ToHex()) } - configData["PresharedKey"]?.let { setPreSharedKeyHex(it.base64ToHex()) } - } + val host = configData.getString("hostName").let { parseInetAddress(it.trim()) } + val port = configData.getInt("port") + setEndpoint(InetEndpoint(host, port)) - protected fun parseConfigData(data: String): Map { - val parsedData = TreeMap(String.CASE_INSENSITIVE_ORDER) - data.lineSequence() - .filter { it.isNotEmpty() && !it.startsWith('[') } - .forEach { line -> - val attr = line.split("=", limit = 2) - parsedData[attr.first().trim()] = attr.last().trim() - } - return parsedData + configData.optStringOrNull("persistent_keep_alive")?.let { setPersistentKeepalive(it.toInt()) } + configData.getString("client_priv_key").let { setPrivateKeyHex(it.base64ToHex()) } + configData.getString("server_pub_key").let { setPublicKeyHex(it.base64ToHex()) } + configData.optStringOrNull("psk_key")?.let { setPreSharedKeyHex(it.base64ToHex()) } } private fun start(config: WireguardConfig, vpnBuilder: Builder, protect: (Int) -> Boolean) { diff --git a/client/android/xray/src/main/kotlin/Xray.kt b/client/android/xray/src/main/kotlin/Xray.kt index 3e5f9f7c9..b092c40ae 100644 --- a/client/android/xray/src/main/kotlin/Xray.kt +++ b/client/android/xray/src/main/kotlin/Xray.kt @@ -20,69 +20,6 @@ import org.amnezia.vpn.util.net.InetNetwork import org.amnezia.vpn.util.net.parseInetAddress import org.json.JSONObject -/** - * Config example: - * { - * "appSplitTunnelType": 0, - * "config_version": 0, - * "description": "Server 1", - * "dns1": "1.1.1.1", - * "dns2": "1.0.0.1", - * "hostName": "100.100.100.0", - * "protocol": "xray", - * "splitTunnelApps": [], - * "splitTunnelSites": [], - * "splitTunnelType": 0, - * "xray_config_data": { - * "inbounds": [ - * { - * "listen": "127.0.0.1", - * "port": 8080, - * "protocol": "socks", - * "settings": { - * "udp": true - * } - * } - * ], - * "log": { - * "loglevel": "error" - * }, - * "outbounds": [ - * { - * "protocol": "vless", - * "settings": { - * "vnext": [ - * { - * "address": "100.100.100.0", - * "port": 443, - * "users": [ - * { - * "encryption": "none", - * "flow": "xtls-rprx-vision", - * "id": "id" - * } - * ] - * } - * ] - * }, - * "streamSettings": { - * "network": "tcp", - * "realitySettings": { - * "fingerprint": "chrome", - * "publicKey": "publicKey", - * "serverName": "google.com", - * "shortId": "id", - * "spiderX": "" - * }, - * "security": "reality" - * } - * } - * ] - * } - * } - * - */ - private const val TAG = "Xray" private const val LIBXRAY_TAG = "libXray" diff --git a/client/configurators/wireguard_configurator.cpp b/client/configurators/wireguard_configurator.cpp index f7faaa523..d594312db 100644 --- a/client/configurators/wireguard_configurator.cpp +++ b/client/configurators/wireguard_configurator.cpp @@ -187,6 +187,10 @@ QString WireguardConfigurator::createConfig(const ServerCredentials &credentials jConfig[config_key::server_pub_key] = connData.serverPubKey; jConfig[config_key::mtu] = wireguarConfig.value(config_key::mtu).toString(protocols::wireguard::defaultMtu); + jConfig[config_key::persistent_keep_alive] = 25; + QJsonArray allowedIps { "0.0.0.0/0" }; + jConfig[config_key::allowed_ips] = allowedIps; + jConfig[config_key::clientId] = connData.clientPubKey; return QJsonDocument(jConfig).toJson(); diff --git a/client/mozilla/localsocketcontroller.cpp b/client/mozilla/localsocketcontroller.cpp index 0502facc5..b98972d2e 100644 --- a/client/mozilla/localsocketcontroller.cpp +++ b/client/mozilla/localsocketcontroller.cpp @@ -149,9 +149,13 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) { QJsonArray jsAllowedIPAddesses; QJsonArray plainAllowedIP = wgConfig.value(amnezia::config_key::allowed_ips).toArray(); - QJsonArray defaultAllowedIP = QJsonArray::fromStringList(QString("0.0.0.0/0, ::/0").split(",")); - if (plainAllowedIP != defaultAllowedIP && !plainAllowedIP.isEmpty()) { + bool allowSplitTunnelFromAppSettings = false; + if (!plainAllowedIP.isEmpty() && plainAllowedIP.contains("0.0.0.0/0")) { + allowSplitTunnelFromAppSettings = true; + } + + if (!allowSplitTunnelFromAppSettings) { // Use AllowedIP list from WG config because of higher priority for (auto v : plainAllowedIP) { QString ipRange = v.toString(); @@ -170,7 +174,6 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) { } } } else { - // Use APP split tunnel if (splitTunnelType == 0 || splitTunnelType == 2) { QJsonObject range_ipv4; diff --git a/client/platforms/ios/PacketTunnelProvider+WireGuard.swift b/client/platforms/ios/PacketTunnelProvider+WireGuard.swift index 18200c7fc..17fbbc078 100644 --- a/client/platforms/ios/PacketTunnelProvider+WireGuard.swift +++ b/client/platforms/ios/PacketTunnelProvider+WireGuard.swift @@ -22,7 +22,7 @@ extension PacketTunnelProvider { if tunnelConfiguration.peers.first!.allowedIPs .map({ $0.stringRepresentation }) - .joined(separator: ", ") == "0.0.0.0/0, ::/0" { + .contains("0.0.0.0/0") { if wgConfig.splitTunnelType == 1 { for index in tunnelConfiguration.peers.indices { tunnelConfiguration.peers[index].allowedIPs.removeAll() diff --git a/client/platforms/ios/ios_controller.mm b/client/platforms/ios/ios_controller.mm index f4ba2798e..c56ac34ba 100644 --- a/client/platforms/ios/ios_controller.mm +++ b/client/platforms/ios/ios_controller.mm @@ -491,7 +491,7 @@ 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" }; + QJsonArray allowed_ips { "0.0.0.0/0" }; wgConfig.insert(config_key::allowed_ips, allowed_ips); } @@ -576,7 +576,7 @@ 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" }; + QJsonArray allowed_ips { "0.0.0.0/0" }; wgConfig.insert(config_key::allowed_ips, allowed_ips); } diff --git a/client/server_scripts/awg/template.conf b/client/server_scripts/awg/template.conf index 79932806b..086ff98bf 100644 --- a/client/server_scripts/awg/template.conf +++ b/client/server_scripts/awg/template.conf @@ -15,6 +15,6 @@ H4 = $TRANSPORT_PACKET_MAGIC_HEADER [Peer] PublicKey = $WIREGUARD_SERVER_PUBLIC_KEY PresharedKey = $WIREGUARD_PSK -AllowedIPs = 0.0.0.0/0, ::/0 +AllowedIPs = 0.0.0.0/0 Endpoint = $SERVER_IP_ADDRESS:$AWG_SERVER_PORT PersistentKeepalive = 25 diff --git a/client/server_scripts/wireguard/template.conf b/client/server_scripts/wireguard/template.conf index 392746518..dfaa4c552 100644 --- a/client/server_scripts/wireguard/template.conf +++ b/client/server_scripts/wireguard/template.conf @@ -6,6 +6,6 @@ PrivateKey = $WIREGUARD_CLIENT_PRIVATE_KEY [Peer] PublicKey = $WIREGUARD_SERVER_PUBLIC_KEY PresharedKey = $WIREGUARD_PSK -AllowedIPs = 0.0.0.0/0, ::/0 +AllowedIPs = 0.0.0.0/0 Endpoint = $SERVER_IP_ADDRESS:$WIREGUARD_SERVER_PORT PersistentKeepalive = 25 diff --git a/client/ui/controllers/importController.cpp b/client/ui/controllers/importController.cpp index 7ffcedd79..d84e1a6c9 100644 --- a/client/ui/controllers/importController.cpp +++ b/client/ui/controllers/importController.cpp @@ -395,7 +395,11 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data) lastConfig[config_key::mtu] = configMap.value("MTU"); } - QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList(configMap.value("AllowedIPs").split(",")); + if (!configMap.value("PersistentKeepalive").isEmpty()) { + lastConfig[config_key::persistent_keep_alive] = configMap.value("PersistentKeepalive"); + } + + QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList(configMap.value("AllowedIPs").split(", ")); lastConfig[config_key::allowed_ips] = allowedIpsJsonArray; diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index 85e5dae2d..b82a49ce8 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -694,7 +694,16 @@ bool ServersModel::isDefaultServerDefaultContainerHasSplitTunneling() QJsonObject serverProtocolConfig = container.value(ContainerProps::containerTypeToString(defaultContainer)).toObject(); QString clientProtocolConfigString = serverProtocolConfig.value(config_key::last_config).toString(); QJsonObject clientProtocolConfig = QJsonDocument::fromJson(clientProtocolConfigString.toUtf8()).object(); - return (clientProtocolConfigString.contains("AllowedIPs") && !clientProtocolConfigString.contains("AllowedIPs = 0.0.0.0/0, ::/0")) + QString nativeProtocolConfigString = clientProtocolConfig.value(config_key::config).toString(); + + const static QRegularExpression allowedIpsRegExp("AllowedIPs\\s*=\\s*([^\n]*)"); + QRegularExpressionMatch allowedIpsMatch = allowedIpsRegExp.match(nativeProtocolConfigString); + QString allowedIpsString; + if (allowedIpsMatch.hasCaptured(1)) { + allowedIpsString = allowedIpsMatch.captured(1); + } + + return !allowedIpsString.contains("0.0.0.0/0") || (!clientProtocolConfig.value(config_key::allowed_ips).toArray().isEmpty() && !clientProtocolConfig.value(config_key::allowed_ips).toArray().contains("0.0.0.0/0")); } else if (defaultContainer == DockerContainer::Cloak || defaultContainer == DockerContainer::OpenVpn diff --git a/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml b/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml index 405d4eda6..b9ec02694 100644 --- a/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml +++ b/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml @@ -11,7 +11,7 @@ import "../Config" DrawerType2 { id: root - property bool isAppSplitTinnelingEnabled: Qt.platform.os === "windows" || Qt.platform.os === "android" + property bool isAppSplitTunnelingEnabled: Qt.platform.os === "windows" || Qt.platform.os === "android" anchors.fill: parent expandedHeight: parent.height * 0.9 @@ -24,6 +24,8 @@ DrawerType2 { anchors.right: parent.right spacing: 0 + property bool isServerSplitTunnelingEnabled: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling + Connections { target: root enabled: !GC.isMobile() @@ -53,7 +55,7 @@ DrawerType2 { Layout.fillWidth: true Layout.topMargin: 16 - visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling + visible: isServerSplitTunnelingEnabled text: qsTr("Split tunneling on the server") descriptionText: qsTr("Enabled \nCan't be disabled for current server") @@ -68,7 +70,7 @@ DrawerType2 { } DividerType { - visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling + visible: isServerSplitTunnelingEnabled } LabelWithButtonType { @@ -95,7 +97,7 @@ DrawerType2 { LabelWithButtonType { id: appSplitTunnelingSwitch - visible: isAppSplitTinnelingEnabled + visible: isAppSplitTunnelingEnabled Layout.fillWidth: true @@ -112,7 +114,7 @@ DrawerType2 { } DividerType { - visible: isAppSplitTinnelingEnabled + visible: isAppSplitTunnelingEnabled } } } diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index daff1187a..9c4cc80cc 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -291,43 +291,68 @@ void VpnConnection::appendKillSwitchConfig() void VpnConnection::appendSplitTunnelingConfig() { - if (m_vpnConfiguration.value(config_key::configVersion).toInt()) { - auto protocolName = m_vpnConfiguration.value(config_key::vpnproto).toString(); - if (protocolName == ProtocolProps::protoToString(Proto::Awg)) { - auto configData = m_vpnConfiguration.value(protocolName + "_config_data").toObject(); - QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList(configData.value("allowed_ips").toString().split(",")); - QJsonArray defaultAllowedIP = QJsonArray::fromStringList(QString("0.0.0.0/0, ::/0").split(",")); - - if (allowedIpsJsonArray != defaultAllowedIP) { - allowedIpsJsonArray.append(m_vpnConfiguration.value(config_key::dns1).toString()); - allowedIpsJsonArray.append(m_vpnConfiguration.value(config_key::dns2).toString()); - - m_vpnConfiguration.insert(config_key::splitTunnelType, Settings::RouteMode::VpnOnlyForwardSites); - m_vpnConfiguration.insert(config_key::splitTunnelSites, allowedIpsJsonArray); + // this block is for old native configs and for old self-hosted configs + auto protocolName = m_vpnConfiguration.value(config_key::vpnproto).toString(); + if (protocolName == ProtocolProps::protoToString(Proto::Awg) || protocolName == ProtocolProps::protoToString(Proto::WireGuard)) { + auto configData = m_vpnConfiguration.value(protocolName + "_config_data").toObject(); + if (configData.value(config_key::allowed_ips).isString()) { + QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList(configData.value(config_key::allowed_ips).toString().split(", ")); + configData.insert(config_key::allowed_ips, allowedIpsJsonArray); + m_vpnConfiguration.insert(protocolName + "_config_data", configData); + } else if (configData.value(config_key::allowed_ips).isUndefined()) { + auto nativeConfig = configData.value(config_key::config).toString(); + auto nativeConfigLines = nativeConfig.split("\n"); + for (auto &line : nativeConfigLines) { + if (line.contains("AllowedIPs")) { + auto allowedIpsString = line.split(" = "); + if (allowedIpsString.size() < 1) { + break; + } + QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList(allowedIpsString.at(1).split(", ")); + configData.insert(config_key::allowed_ips, allowedIpsJsonArray); + m_vpnConfiguration.insert(protocolName + "_config_data", configData); + break; + } } } - } else { - Settings::RouteMode routeMode = Settings::RouteMode::VpnAllSites; - QJsonArray sitesJsonArray; - if (m_settings->isSitesSplitTunnelingEnabled()) { - routeMode = m_settings->routeMode(); - - auto sites = m_settings->getVpnIps(routeMode); - for (const auto &site : sites) { - sitesJsonArray.append(site); - } - // Allow traffic to Amnezia DNS - if (routeMode == Settings::VpnOnlyForwardSites) { - sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns1).toString()); - sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns2).toString()); + if (configData.value(config_key::persistent_keep_alive).isUndefined()) { + auto nativeConfig = configData.value(config_key::config).toString(); + auto nativeConfigLines = nativeConfig.split("\n"); + for (auto &line : nativeConfigLines) { + if (line.contains("PersistentKeepalive")) { + auto persistentKeepaliveString = line.split(" = "); + if (persistentKeepaliveString.size() < 1) { + break; + } + configData.insert(config_key::persistent_keep_alive, persistentKeepaliveString.at(1)); + m_vpnConfiguration.insert(protocolName + "_config_data", configData); + break; + } } } + } - m_vpnConfiguration.insert(config_key::splitTunnelType, routeMode); - m_vpnConfiguration.insert(config_key::splitTunnelSites, sitesJsonArray); + Settings::RouteMode routeMode = Settings::RouteMode::VpnAllSites; + QJsonArray sitesJsonArray; + if (m_settings->isSitesSplitTunnelingEnabled()) { + routeMode = m_settings->routeMode(); + + auto sites = m_settings->getVpnIps(routeMode); + for (const auto &site : sites) { + sitesJsonArray.append(site); + } + + // Allow traffic to Amnezia DNS + if (routeMode == Settings::VpnOnlyForwardSites) { + sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns1).toString()); + sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns2).toString()); + } } + m_vpnConfiguration.insert(config_key::splitTunnelType, routeMode); + m_vpnConfiguration.insert(config_key::splitTunnelSites, sitesJsonArray); + Settings::AppsRouteMode appsRouteMode = Settings::AppsRouteMode::VpnAllApps; QJsonArray appsJsonArray; if (m_settings->isAppsSplitTunnelingEnabled()) {