mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-20 02:00:55 +07:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b9ee600fa8 | |||
| 407b2e8bf7 | |||
| 9c30b0f034 |
@@ -127,56 +127,6 @@ jobs:
|
||||
path: deploy\\build_${{ env.BUILD_ARCH }}\\client\\Release
|
||||
retention-days: 7
|
||||
|
||||
# ------------------------------------------------------
|
||||
|
||||
Build-Windows-x32:
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
BUILD_ARCH: 32
|
||||
steps:
|
||||
- name: 'Get sources'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'true'
|
||||
fetch-depth: 10
|
||||
- name: 'Setup ccache'
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
- name: 'Install Qt'
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
host: 'windows'
|
||||
target: 'desktop'
|
||||
arch: 'win32_msvc2019_32'
|
||||
modules: 'qtremoteobjects qt5compat qtshadertools'
|
||||
dir: ${{ runner.temp }}
|
||||
setup-python: 'true'
|
||||
tools: 'tools_ifw'
|
||||
set-env: 'true'
|
||||
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||
- name: 'Setup mvsc'
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
arch: 'x86'
|
||||
- name: 'Build project'
|
||||
shell: cmd
|
||||
run: |
|
||||
set BUILD_ARCH=${{ env.BUILD_ARCH }}
|
||||
set QT_BIN_DIR="${{ runner.temp }}\\Qt\\${{ env.QT_VERSION }}\\msvc2019_32\\bin"
|
||||
set QIF_BIN_DIR="${{ runner.temp }}\\Qt\\Tools\\QtInstallerFramework\\${{ env.QIF_VERSION }}\\bin"
|
||||
call deploy\\build_windows.bat
|
||||
- name: 'Upload installer artifact'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: AmneziaVPN_Windows_installer
|
||||
path: AmneziaVPN_x${{ env.BUILD_ARCH }}.exe
|
||||
retention-days: 7
|
||||
- name: 'Upload unpacked artifact'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: AmneziaVPN_Windows_unpacked
|
||||
path: deploy\\build_${{ env.BUILD_ARCH }}\\client\\Release
|
||||
retention-days: 7
|
||||
# ------------------------------------------------------
|
||||
|
||||
Build-iOS:
|
||||
@@ -333,6 +283,7 @@ jobs:
|
||||
ANDROID_BUILD_PLATFORM: android-34
|
||||
QT_VERSION: 6.6.1
|
||||
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
|
||||
BUILD_AAB: ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/') }}
|
||||
|
||||
steps:
|
||||
- name: 'Install desktop Qt'
|
||||
@@ -431,7 +382,7 @@ jobs:
|
||||
ANDROID_KEYSTORE_KEY_ALIAS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_ALIAS }}
|
||||
ANDROID_KEYSTORE_KEY_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }}
|
||||
shell: bash
|
||||
run: ./deploy/build_android.sh --aab --apk all --build-platform ${{ env.ANDROID_BUILD_PLATFORM }}
|
||||
run: ./deploy/build_android.sh ${{ env.BUILD_AAB == 'true' && '--aab' || '' }} --apk all --build-platform ${{ env.ANDROID_BUILD_PLATFORM }}
|
||||
|
||||
- name: 'Upload x86_64 apk'
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -466,6 +417,7 @@ jobs:
|
||||
retention-days: 7
|
||||
|
||||
- name: 'Upload aab'
|
||||
if: ${{ env.BUILD_AAB == 'true' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AmneziaVPN-android
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||
|
||||
set(PROJECT AmneziaVPN)
|
||||
|
||||
project(${PROJECT} VERSION 4.2.1.1
|
||||
project(${PROJECT} VERSION 4.2.1.0
|
||||
DESCRIPTION "AmneziaVPN"
|
||||
HOMEPAGE_URL "https://amnezia.org/"
|
||||
)
|
||||
@@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
||||
set(RELEASE_DATE "${CURRENT_DATE}")
|
||||
|
||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||
set(APP_ANDROID_VERSION_CODE 43)
|
||||
set(APP_ANDROID_VERSION_CODE 42)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(MZ_PLATFORM_NAME "linux")
|
||||
|
||||
@@ -18,6 +18,7 @@ Amnezia is an open-source VPN client, with a key feature that enables you to dep
|
||||
[https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit
|
||||
[https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Telegram support channel (English)
|
||||
[https://t.me/amnezia_vpn](https://t.me/amnezia_vpn) - Telegram support channel (Russian)
|
||||
[https://signal.group/...](https://signal.group/#CjQKIB2gUf8QH_IXnOJMGQWMDjYz9cNfmRQipGWLFiIgc4MwEhAKBONrSiWHvoUFbbD0xwdh) - Signal channel
|
||||
|
||||
## Tech
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ class AwgConfig private constructor(
|
||||
fun setH3(h3: Long) = apply { this.h3 = h3 }
|
||||
fun setH4(h4: Long) = apply { this.h4 = h4 }
|
||||
|
||||
override fun build(): AwgConfig = configBuild().run { AwgConfig(this@Builder) }
|
||||
override fun build(): AwgConfig = AwgConfig(this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -3,9 +3,6 @@ package org.amnezia.vpn.protocol.cloak
|
||||
import android.util.Base64
|
||||
import net.openvpn.ovpn3.ClientAPI_Config
|
||||
import org.amnezia.vpn.protocol.openvpn.OpenVpn
|
||||
import org.amnezia.vpn.protocol.openvpn.OpenVpnConfig
|
||||
import org.amnezia.vpn.util.net.InetNetwork
|
||||
import org.amnezia.vpn.util.net.parseInetAddress
|
||||
import org.json.JSONObject
|
||||
|
||||
/**
|
||||
@@ -54,13 +51,6 @@ class Cloak : OpenVpn() {
|
||||
return openVpnConfig
|
||||
}
|
||||
|
||||
override fun configPluggableTransport(configBuilder: OpenVpnConfig.Builder, config: JSONObject) {
|
||||
// exclude remote server ip from vpn routes
|
||||
val remoteServer = config.getString("hostName")
|
||||
val remoteServerAddress = InetNetwork(parseInetAddress(remoteServer))
|
||||
configBuilder.excludeRoute(remoteServerAddress)
|
||||
}
|
||||
|
||||
private fun checkCloakJson(cloakConfigJson: JSONObject): JSONObject {
|
||||
cloakConfigJson.put("NumConn", 1)
|
||||
cloakConfigJson.put("ProxyMethod", "openvpn")
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.amnezia.vpn.protocol.openvpn
|
||||
|
||||
import android.content.Context
|
||||
import android.net.VpnService.Builder
|
||||
import android.os.Build
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -13,6 +14,7 @@ import org.amnezia.vpn.protocol.ProtocolState
|
||||
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
||||
import org.amnezia.vpn.protocol.Statistics
|
||||
import org.amnezia.vpn.protocol.VpnStartException
|
||||
import org.amnezia.vpn.util.net.InetNetwork
|
||||
import org.amnezia.vpn.util.net.getLocalNetworks
|
||||
import org.json.JSONObject
|
||||
|
||||
@@ -77,8 +79,16 @@ open class OpenVpn : Protocol() {
|
||||
if (evalConfig.error) {
|
||||
throw BadConfigException("OpenVPN config parse error: ${evalConfig.message}")
|
||||
}
|
||||
configPluggableTransport(configBuilder, config)
|
||||
configBuilder.configSplitTunneling(config)
|
||||
configBuilder.apply {
|
||||
// fix for split tunneling
|
||||
// The exclude split tunneling OpenVpn configuration does not contain a default route.
|
||||
// It is required for split tunneling in newer versions of Android.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
addRoute(InetNetwork("0.0.0.0", 0))
|
||||
addRoute(InetNetwork("::", 0))
|
||||
}
|
||||
configSplitTunneling(config)
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
val status = client.connect()
|
||||
@@ -112,8 +122,6 @@ open class OpenVpn : Protocol() {
|
||||
return openVpnConfig
|
||||
}
|
||||
|
||||
protected open fun configPluggableTransport(configBuilder: OpenVpnConfig.Builder, config: JSONObject) {}
|
||||
|
||||
private fun makeEstablish(vpnBuilder: Builder): (OpenVpnConfig.Builder) -> Int = { configBuilder ->
|
||||
val openVpnConfig = configBuilder.build()
|
||||
buildVpnInterface(openVpnConfig, vpnBuilder)
|
||||
|
||||
+3
-1
@@ -91,7 +91,9 @@ class OpenVpnClient(
|
||||
// metric is optional and should be ignored if < 0
|
||||
override fun tun_builder_exclude_route(address: String, prefix_length: Int, metric: Int, ipv6: Boolean): Boolean {
|
||||
Log.d(TAG, "tun_builder_exclude_route: $address, $prefix_length, $metric, $ipv6")
|
||||
configBuilder.excludeRoute(InetNetwork(address, prefix_length))
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
configBuilder.excludeRoute(InetNetwork(address, prefix_length))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ class OpenVpnConfig private constructor(
|
||||
class Builder : ProtocolConfig.Builder(false) {
|
||||
override var mtu: Int = OPENVPN_DEFAULT_MTU
|
||||
|
||||
override fun build(): OpenVpnConfig = configBuild().run { OpenVpnConfig(this@Builder) }
|
||||
override fun build(): OpenVpnConfig = OpenVpnConfig(this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -14,6 +14,8 @@ import java.util.zip.ZipFile
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import org.amnezia.vpn.util.Log
|
||||
import org.amnezia.vpn.util.net.InetNetwork
|
||||
import org.amnezia.vpn.util.net.IpRange
|
||||
import org.amnezia.vpn.util.net.IpRangeSet
|
||||
import org.json.JSONObject
|
||||
|
||||
private const val TAG = "Protocol"
|
||||
@@ -51,16 +53,40 @@ abstract class Protocol {
|
||||
val splitTunnelType = config.optInt("splitTunnelType")
|
||||
if (splitTunnelType == SPLIT_TUNNEL_DISABLE) return
|
||||
val splitTunnelSites = config.getJSONArray("splitTunnelSites")
|
||||
val addressHandlerFunc = when (splitTunnelType) {
|
||||
SPLIT_TUNNEL_INCLUDE -> ::includeAddress
|
||||
SPLIT_TUNNEL_EXCLUDE -> ::excludeAddress
|
||||
when (splitTunnelType) {
|
||||
SPLIT_TUNNEL_INCLUDE -> {
|
||||
// remove default routes, if any
|
||||
removeRoute(InetNetwork("0.0.0.0", 0))
|
||||
removeRoute(InetNetwork("::", 0))
|
||||
// add routes from config
|
||||
for (i in 0 until splitTunnelSites.length()) {
|
||||
val address = InetNetwork.parse(splitTunnelSites.getString(i))
|
||||
addRoute(address)
|
||||
}
|
||||
}
|
||||
|
||||
else -> throw BadConfigException("Unexpected value of the 'splitTunnelType' parameter: $splitTunnelType")
|
||||
}
|
||||
|
||||
for (i in 0 until splitTunnelSites.length()) {
|
||||
val address = InetNetwork.parse(splitTunnelSites.getString(i))
|
||||
addressHandlerFunc(address)
|
||||
SPLIT_TUNNEL_EXCLUDE -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
// exclude routes from config
|
||||
for (i in 0 until splitTunnelSites.length()) {
|
||||
val address = InetNetwork.parse(splitTunnelSites.getString(i))
|
||||
excludeRoute(address)
|
||||
}
|
||||
} else {
|
||||
// For older versions of Android, build a list of subnets without excluded addresses
|
||||
val ipRangeSet = IpRangeSet()
|
||||
ipRangeSet.remove(IpRange("127.0.0.0", 8))
|
||||
for (i in 0 until splitTunnelSites.length()) {
|
||||
val address = InetNetwork.parse(splitTunnelSites.getString(i))
|
||||
ipRangeSet.remove(IpRange(address))
|
||||
}
|
||||
// remove default routes, if any
|
||||
removeRoute(InetNetwork("0.0.0.0", 0))
|
||||
removeRoute(InetNetwork("::", 0))
|
||||
ipRangeSet.subnets().forEach(::addRoute)
|
||||
addRoute(InetNetwork("2000::", 3))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@ import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import java.net.InetAddress
|
||||
import org.amnezia.vpn.util.net.InetNetwork
|
||||
import org.amnezia.vpn.util.net.IpRange
|
||||
import org.amnezia.vpn.util.net.IpRangeSet
|
||||
|
||||
open class ProtocolConfig protected constructor(
|
||||
val addresses: Set<InetNetwork>,
|
||||
@@ -14,8 +12,6 @@ open class ProtocolConfig protected constructor(
|
||||
val searchDomain: String?,
|
||||
val routes: Set<InetNetwork>,
|
||||
val excludedRoutes: Set<InetNetwork>,
|
||||
val includedAddresses: Set<InetNetwork>,
|
||||
val excludedAddresses: Set<InetNetwork>,
|
||||
val excludedApplications: Set<String>,
|
||||
val httpProxy: ProxyInfo?,
|
||||
val allowAllAF: Boolean,
|
||||
@@ -29,8 +25,6 @@ open class ProtocolConfig protected constructor(
|
||||
builder.searchDomain,
|
||||
builder.routes,
|
||||
builder.excludedRoutes,
|
||||
builder.includedAddresses,
|
||||
builder.excludedAddresses,
|
||||
builder.excludedApplications,
|
||||
builder.httpProxy,
|
||||
builder.allowAllAF,
|
||||
@@ -43,8 +37,6 @@ open class ProtocolConfig protected constructor(
|
||||
internal val dnsServers: MutableSet<InetAddress> = hashSetOf()
|
||||
internal val routes: MutableSet<InetNetwork> = hashSetOf()
|
||||
internal val excludedRoutes: MutableSet<InetNetwork> = hashSetOf()
|
||||
internal val includedAddresses: MutableSet<InetNetwork> = hashSetOf()
|
||||
internal val excludedAddresses: MutableSet<InetNetwork> = hashSetOf()
|
||||
internal val excludedApplications: MutableSet<String> = hashSetOf()
|
||||
|
||||
internal var searchDomain: String? = null
|
||||
@@ -79,15 +71,12 @@ open class ProtocolConfig protected constructor(
|
||||
fun removeRoute(route: InetNetwork) = apply { this.routes.remove(route) }
|
||||
fun clearRoutes() = apply { this.routes.clear() }
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||
fun excludeRoute(route: InetNetwork) = apply { this.excludedRoutes += route }
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||
fun excludeRoutes(routes: Collection<InetNetwork>) = apply { this.excludedRoutes += routes }
|
||||
|
||||
fun includeAddress(addr: InetNetwork) = apply { this.includedAddresses += addr }
|
||||
fun includeAddresses(addresses: Collection<InetNetwork>) = apply { this.includedAddresses += addresses }
|
||||
|
||||
fun excludeAddress(addr: InetNetwork) = apply { this.excludedAddresses += addr }
|
||||
fun excludeAddresses(addresses: Collection<InetNetwork>) = apply { this.excludedAddresses += addresses }
|
||||
|
||||
fun excludeApplication(application: String) = apply { this.excludedApplications += application }
|
||||
fun excludeApplications(applications: Collection<String>) = apply { this.excludedApplications += applications }
|
||||
|
||||
@@ -102,48 +91,6 @@ open class ProtocolConfig protected constructor(
|
||||
|
||||
fun setMtu(mtu: Int) = apply { this.mtu = mtu }
|
||||
|
||||
private fun processSplitTunneling() {
|
||||
if (includedAddresses.isNotEmpty() && excludedAddresses.isNotEmpty()) {
|
||||
throw BadConfigException("Config contains addresses for inclusive and exclusive split tunneling at the same time")
|
||||
}
|
||||
|
||||
if (includedAddresses.isNotEmpty()) {
|
||||
// remove default routes, if any
|
||||
removeRoute(InetNetwork("0.0.0.0", 0))
|
||||
removeRoute(InetNetwork("::", 0))
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
// for older versions of Android, add the default route to the excluded routes
|
||||
// to correctly build the excluded subnets list later
|
||||
excludeRoute(InetNetwork("0.0.0.0", 0))
|
||||
}
|
||||
addRoutes(includedAddresses)
|
||||
} else if (excludedAddresses.isNotEmpty()) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
// default routes are required for split tunneling in newer versions of Android
|
||||
addRoute(InetNetwork("0.0.0.0", 0))
|
||||
addRoute(InetNetwork("::", 0))
|
||||
}
|
||||
excludeRoutes(excludedAddresses)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processExcludedRoutes() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
// for older versions of Android, build a list of subnets without excluded routes
|
||||
// and add them to routes
|
||||
val ipRangeSet = IpRangeSet()
|
||||
ipRangeSet.remove(IpRange("127.0.0.0", 8))
|
||||
excludedRoutes.forEach {
|
||||
ipRangeSet.remove(IpRange(it))
|
||||
}
|
||||
// remove default routes, if any
|
||||
removeRoute(InetNetwork("0.0.0.0", 0))
|
||||
removeRoute(InetNetwork("::", 0))
|
||||
ipRangeSet.subnets().forEach(::addRoute)
|
||||
addRoute(InetNetwork("2000::", 3))
|
||||
}
|
||||
}
|
||||
|
||||
private fun validate() {
|
||||
val errorMessage = StringBuilder()
|
||||
|
||||
@@ -156,13 +103,7 @@ open class ProtocolConfig protected constructor(
|
||||
if (errorMessage.isNotEmpty()) throw BadConfigException(errorMessage.toString())
|
||||
}
|
||||
|
||||
protected fun configBuild() {
|
||||
processSplitTunneling()
|
||||
processExcludedRoutes()
|
||||
validate()
|
||||
}
|
||||
|
||||
open fun build(): ProtocolConfig = configBuild().run { ProtocolConfig(this@Builder) }
|
||||
open fun build(): ProtocolConfig = validate().run { ProtocolConfig(this@Builder) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -64,7 +64,6 @@ class AmneziaActivity : QtActivity() {
|
||||
|
||||
ServiceEvent.DISCONNECTED -> {
|
||||
QtAndroidController.onVpnDisconnected()
|
||||
doUnbindService()
|
||||
}
|
||||
|
||||
ServiceEvent.RECONNECTING -> {
|
||||
|
||||
+1
-4
@@ -99,10 +99,7 @@ open class Wireguard : Protocol() {
|
||||
}
|
||||
|
||||
protected fun WireguardConfig.Builder.configWireguard(configData: Map<String, String>) {
|
||||
configData["Address"]?.split(",")?.map { address ->
|
||||
InetNetwork.parse(address.trim())
|
||||
}?.forEach(::addAddress)
|
||||
|
||||
configData["Address"]?.let { addAddress(InetNetwork.parse(it)) }
|
||||
configData["DNS"]?.split(",")?.map { dns ->
|
||||
parseInetAddress(dns.trim())
|
||||
}?.forEach(::addDnsServer)
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ open class WireguardConfig protected constructor(
|
||||
|
||||
fun setPrivateKeyHex(privateKeyHex: String) = apply { this.privateKeyHex = privateKeyHex }
|
||||
|
||||
override fun build(): WireguardConfig = configBuild().run { WireguardConfig(this@Builder) }
|
||||
override fun build(): WireguardConfig = WireguardConfig(this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, cons
|
||||
}
|
||||
|
||||
// Skip annoying messages from Qt
|
||||
if (msg.startsWith("Unknown property") || msg.startsWith("Could not create pixmap") || msg.startsWith("Populating font") || msg.startsWith("stale focus object")) {
|
||||
if (msg.startsWith("Unknown property") || msg.startsWith("Could not create pixmap") || msg.startsWith("Populating font")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@ bool MobileUtils::shareText(const QStringList& filesToSend) {
|
||||
UIPopoverPresentationController *popController = activityController.popoverPresentationController;
|
||||
if (popController) {
|
||||
popController.sourceView = qtController.view;
|
||||
popController.sourceRect = CGRectMake(100, 100, 100, 100);
|
||||
}
|
||||
|
||||
QEventLoop wait;
|
||||
|
||||
@@ -249,107 +249,8 @@ 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 absent";
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
qDebug() << "Disconnect error is unavailable on iOS < 16.0";
|
||||
}
|
||||
}
|
||||
|
||||
emit connectionStateChanged(iosStatusToState(session.status));
|
||||
qDebug() << "IosController::vpnStatusDidChange" << iosStatusToState(session.status) << session;
|
||||
emit connectionStateChanged(iosStatusToState(session.status));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,15 +352,6 @@ bool IosController::startWireGuard(const QString &config)
|
||||
|
||||
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;
|
||||
|
||||
@@ -481,7 +373,7 @@ void IosController::startTunnel()
|
||||
|
||||
[m_currentTunnel loadFromPreferencesWithCompletionHandler:^(NSError *loadError) {
|
||||
if (loadError) {
|
||||
qDebug().nospace() << "IosController::start" << protocolName << ": Connect " << protocolName << " Tunnel Load Error" << loadError.localizedDescription.UTF8String;
|
||||
qDebug() << "IosController::startOpenVPN : Connect OpenVPN Tunnel Load Error" << loadError.localizedDescription.UTF8String;
|
||||
emit connectionStateChanged(Vpn::ConnectionState::Error);
|
||||
return;
|
||||
}
|
||||
@@ -509,11 +401,11 @@ void IosController::startTunnel()
|
||||
BOOL started = [m_currentTunnel.connection startVPNTunnelWithOptions:nil andReturnError:&startError];
|
||||
|
||||
if (!started || startError) {
|
||||
qDebug().nospace() << "IosController::start" << protocolName << " : Connect " << protocolName << " Tunnel Start Error"
|
||||
qDebug() << "IosController::startOpenVPN : Connect OpenVPN Tunnel Start Error"
|
||||
<< (startError ? startError.localizedDescription.UTF8String : "");
|
||||
emit connectionStateChanged(Vpn::ConnectionState::Error);
|
||||
} else {
|
||||
qDebug().nospace() << "IosController::start" << protocolName << " : Starting the tunnel succeeded";
|
||||
qDebug() << "IosController::startOpenVPN : Starting the tunnel succeeded";
|
||||
}
|
||||
}];
|
||||
});
|
||||
|
||||
@@ -379,7 +379,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolAwgSettings.qml" line="311"/>
|
||||
<source>Save and Restart Amnezia</source>
|
||||
<translation>Сохранить и перезагрузить Amnezia</translation>
|
||||
<translation>Сохранить и пререзагрузить Amnezia</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -416,7 +416,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="77"/>
|
||||
<source>OpenVPN settings</source>
|
||||
<translation>OpenVPN настройки</translation>
|
||||
<translation>Настройки OpenVPN</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="84"/>
|
||||
@@ -1192,7 +1192,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsConnection.qml" line="86"/>
|
||||
<source>If AmneziaDNS is not used or installed</source>
|
||||
<translation>Эти адреса будут использоваться, если не включен AmneziaDNS</translation>
|
||||
<translation>Эти серверы будут использоваться, если не включен AmneziaDNS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsConnection.qml" line="101"/>
|
||||
@@ -2047,7 +2047,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="598"/>
|
||||
<source>Rename</source>
|
||||
<translation type="unfinished">Переименовать</translation>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="627"/>
|
||||
@@ -2062,12 +2062,12 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="668"/>
|
||||
<source>Revoke</source>
|
||||
<translation type="unfinished">Отозвать</translation>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="671"/>
|
||||
<source>Revoke the config for a user - %1?</source>
|
||||
<translation type="unfinished">Отозвать доступ для пользователя - %1?</translation>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="672"/>
|
||||
|
||||
@@ -251,9 +251,10 @@ void ServersModel::addServer(const QJsonObject &server)
|
||||
|
||||
void ServersModel::editServer(const QJsonObject &server)
|
||||
{
|
||||
beginResetModel();
|
||||
m_settings->editServer(m_currentlyProcessedServerIndex, server);
|
||||
m_servers.replace(m_currentlyProcessedServerIndex, m_settings->serversArray().at(m_currentlyProcessedServerIndex));
|
||||
emit dataChanged(index(m_currentlyProcessedServerIndex, 0), index(m_currentlyProcessedServerIndex, 0));
|
||||
m_servers = m_settings->serversArray();
|
||||
endResetModel();
|
||||
updateContainersModel();
|
||||
}
|
||||
|
||||
|
||||
@@ -124,13 +124,8 @@ void Autostart::setAutostart(bool autostart) {
|
||||
if (file.open(QIODevice::ReadWrite)) {
|
||||
QTextStream stream(&file);
|
||||
stream << "[Desktop Entry]" << Qt::endl;
|
||||
stream << "Exec=AmneziaVPN" << Qt::endl;
|
||||
stream << "Exec=" << appPath() << Qt::endl;
|
||||
stream << "Type=Application" << Qt::endl;
|
||||
stream << "Name=AmneziaVPN" << Qt::endl;
|
||||
stream << "Comment=Client of your self-hosted VPN" << Qt::endl;
|
||||
stream << "Icon=/usr/share/pixmaps/AmneziaVPN.png" << Qt::endl;
|
||||
stream << "Categories=Network;Qt;Security;" << Qt::endl;
|
||||
stream << "Terminal=false" << Qt::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,9 +30,7 @@ ListView {
|
||||
target: ServersModel
|
||||
|
||||
function onCurrentlyProcessedServerIndexChanged() {
|
||||
if (ContainersModel.getDefaultContainer()) {
|
||||
menuContent.checkCurrentItem()
|
||||
}
|
||||
menuContent.checkCurrentItem()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,8 +76,9 @@ ListView {
|
||||
}
|
||||
|
||||
if (checked) {
|
||||
containersDropDown.menuVisible = false
|
||||
ServersModel.setDefaultContainer(proxyContainersModel.mapToSource(index))
|
||||
|
||||
containersDropDown.menuVisible = false
|
||||
} else {
|
||||
if (!isSupported && isInstalled) {
|
||||
PageController.showErrorMessage(qsTr("The selected protocol is not supported on the current platform"))
|
||||
|
||||
@@ -25,7 +25,7 @@ DrawerType {
|
||||
|
||||
property string configExtension: ".vpn"
|
||||
property string configCaption: qsTr("Save AmneziaVPN config")
|
||||
property string configFileName: "amnezia_config"
|
||||
property string configFileName: "amnezia_config.vpn"
|
||||
|
||||
width: parent.width
|
||||
height: parent.height * 0.9
|
||||
|
||||
@@ -66,8 +66,7 @@ PageType {
|
||||
|
||||
ColumnLayout {
|
||||
Layout.alignment: Qt.AlignBaseline
|
||||
Layout.preferredWidth: GC.isMobile() ? 0 : root.width / 3
|
||||
visible: !GC.isMobile()
|
||||
Layout.preferredWidth: root.width / 3
|
||||
|
||||
ImageButtonType {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
@@ -91,7 +90,7 @@ PageType {
|
||||
|
||||
ColumnLayout {
|
||||
Layout.alignment: Qt.AlignBaseline
|
||||
Layout.preferredWidth: root.width / ( GC.isMobile() ? 2 : 3 )
|
||||
Layout.preferredWidth: root.width / 3
|
||||
|
||||
ImageButtonType {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
@@ -132,7 +131,7 @@ PageType {
|
||||
|
||||
ColumnLayout {
|
||||
Layout.alignment: Qt.AlignBaseline
|
||||
Layout.preferredWidth: root.width / ( GC.isMobile() ? 2 : 3 )
|
||||
Layout.preferredWidth: root.width / 3
|
||||
|
||||
ImageButtonType {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
@@ -112,7 +112,7 @@ PageType {
|
||||
var defaultContainerProto = ContainerProps.defaultProtocol(dockerContainer)
|
||||
|
||||
containers.dockerContainer = dockerContainer
|
||||
containers.containerDefaultPort = ProtocolProps.getPortForInstall(defaultContainerProto)
|
||||
containers.containerDefaultPort = ProtocolProps.defaultPort(defaultContainerProto)
|
||||
containers.containerDefaultTransportProto = ProtocolProps.defaultTransportProto(defaultContainerProto)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,13 +253,10 @@ QString VpnConnection::createVpnConfigurationForProto(int serverIndex, const Ser
|
||||
m_settings->setProtocolConfig(serverIndex, container, proto, protoObject);
|
||||
}
|
||||
|
||||
if ((container != DockerContainer::Cloak && container != DockerContainer::ShadowSocks) ||
|
||||
((container == DockerContainer::Cloak || container == DockerContainer::ShadowSocks) && proto == Proto::OpenVpn)) {
|
||||
QEventLoop wait;
|
||||
emit m_configurator->newVpnConfigCreated(clientId, QString("Admin [%1]").arg(QSysInfo::prettyProductName()), container, credentials);
|
||||
QObject::connect(m_configurator.get(), &VpnConfigurator::clientModelUpdated, &wait, &QEventLoop::quit);
|
||||
wait.exec();
|
||||
}
|
||||
QEventLoop wait;
|
||||
emit m_configurator->newVpnConfigCreated(clientId, QString("Admin [%1]").arg(QSysInfo::prettyProductName()), container, credentials);
|
||||
QObject::connect(m_configurator.get(), &VpnConfigurator::clientModelUpdated, &wait, &QEventLoop::quit);
|
||||
wait.exec();
|
||||
}
|
||||
|
||||
return configData;
|
||||
|
||||
Reference in New Issue
Block a user