diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 0c9dfb32..ae8768a6 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -20,8 +20,6 @@ jobs: DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} - FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} - PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Install Qt' @@ -92,8 +90,6 @@ jobs: DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} - FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} - PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Get sources' @@ -160,8 +156,6 @@ jobs: DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} - FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} - PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Setup xcode' @@ -249,82 +243,18 @@ jobs: # ------------------------------------------------------ - Build-MacOS-old: + Build-MacOS: runs-on: macos-latest env: # Keep compat with MacOS 10.15 aka Catalina by Qt 6.4 QT_VERSION: 6.4.3 + QIF_VERSION: 4.6 PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} - FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} - PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} - - steps: - - name: 'Setup xcode' - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: '15.4.0' - - - name: 'Install Qt' - uses: jurplel/install-qt-action@v3 - with: - version: ${{ env.QT_VERSION }} - host: 'mac' - target: 'desktop' - arch: 'clang_64' - modules: 'qtremoteobjects qt5compat qtshadertools' - dir: ${{ runner.temp }} - setup-python: 'true' - set-env: 'true' - extra: '--external 7z --base ${{ env.QT_MIRROR }}' - - - - name: 'Get sources' - uses: actions/checkout@v4 - with: - submodules: 'true' - fetch-depth: 10 - - - name: 'Setup ccache' - uses: hendrikmuhs/ccache-action@v1.2 - - - name: 'Build project' - run: | - export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin" - bash deploy/build_macos.sh - - - name: 'Upload installer artifact' - uses: actions/upload-artifact@v4 - with: - name: AmneziaVPN_MacOS_old_installer - path: deploy/build/pkg/AmneziaVPN.pkg - retention-days: 7 - - - name: 'Upload unpacked artifact' - uses: actions/upload-artifact@v4 - with: - name: AmneziaVPN_MacOS_old_unpacked - path: deploy/build/client/AmneziaVPN.app - retention-days: 7 - -# ------------------------------------------------------ - - Build-MacOS: - runs-on: macos-latest - - env: - QT_VERSION: 6.8.0 - PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} - PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} - DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} - DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} - DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} - FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} - PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Setup xcode' @@ -345,6 +275,11 @@ jobs: set-env: 'true' extra: '--external 7z --base ${{ env.QT_MIRROR }}' + - name: 'Install Qt Installer Framework ${{ env.QIF_VERSION }}' + run: | + mkdir -pv ${{ runner.temp }}/Qt/Tools/QtInstallerFramework + wget https://qt.amzsvc.com/tools/ifw/${{ env.QIF_VERSION }}.zip + unzip ${{ env.QIF_VERSION }}.zip -d ${{ runner.temp }}/Qt/Tools/QtInstallerFramework/ - name: 'Get sources' uses: actions/checkout@v4 @@ -358,13 +293,14 @@ jobs: - name: 'Build project' run: | export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin" + export QIF_BIN_DIR="${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin" bash deploy/build_macos.sh - name: 'Upload installer artifact' uses: actions/upload-artifact@v4 with: name: AmneziaVPN_MacOS_installer - path: deploy/build/pkg/AmneziaVPN.pkg + path: AmneziaVPN.dmg retention-days: 7 - name: 'Upload unpacked artifact' @@ -388,8 +324,6 @@ jobs: DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} - FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} - PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Install desktop Qt' diff --git a/.github/workflows/tag-deploy.yml b/.github/workflows/tag-deploy.yml index 31c334bf..2bcbd8c6 100644 --- a/.github/workflows/tag-deploy.yml +++ b/.github/workflows/tag-deploy.yml @@ -20,8 +20,6 @@ jobs: DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} - FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} - PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Install desktop Qt' diff --git a/.gitignore b/.gitignore index 503adc2d..5b90fd55 100644 --- a/.gitignore +++ b/.gitignore @@ -133,8 +133,4 @@ client/3rd/ShadowSocks/ss_ios.xcconfig out/ # CMake files -CMakeFiles/ - -ios-ne-build.sh -macos-ne-build.sh -macos-signed-build.sh +CMakeFiles/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 90edb582..decab9b7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,7 +7,6 @@ [submodule "client/3rd-prebuilt"] path = client/3rd-prebuilt url = https://github.com/amnezia-vpn/3rd-prebuilt - branch = feature/special-handshake [submodule "client/3rd/amneziawg-apple"] path = client/3rd/amneziawg-apple url = https://github.com/amnezia-vpn/amneziawg-apple diff --git a/CMakeLists.txt b/CMakeLists.txt index fec613de..0896973e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR) set(PROJECT AmneziaVPN) -project(${PROJECT} VERSION 4.8.8.1 +project(${PROJECT} VERSION 4.8.6.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 2087) +set(APP_ANDROID_VERSION_CODE 2083) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(MZ_PLATFORM_NAME "linux") diff --git a/client/3rd-prebuilt b/client/3rd-prebuilt index 840b7b07..efad1a5b 160000 --- a/client/3rd-prebuilt +++ b/client/3rd-prebuilt @@ -1 +1 @@ -Subproject commit 840b7b070e6ac8b90dda2fac6e98859b23727c0c +Subproject commit efad1a5b5cb8e8ab61e49ccdca18c9090a0da8d3 diff --git a/client/3rd/amneziawg-apple b/client/3rd/amneziawg-apple index 811af0a8..76e7db55 160000 --- a/client/3rd/amneziawg-apple +++ b/client/3rd/amneziawg-apple @@ -1 +1 @@ -Subproject commit 811af0a83b3faeade89a9093a588595666d32066 +Subproject commit 76e7db556a6d7e2582f9481df91db188a46c009c diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index a454142d..b3f775a0 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -31,9 +31,6 @@ add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}") add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}") add_definitions(-DDEV_S3_ENDPOINT="$ENV{DEV_S3_ENDPOINT}") -add_definitions(-DFREE_V2_ENDPOINT="$ENV{FREE_V2_ENDPOINT}") -add_definitions(-DPREM_V1_ENDPOINT="$ENV{PREM_V1_ENDPOINT}") - if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID)) set(PACKAGES ${PACKAGES} Widgets) endif() diff --git a/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt index 42a27de4..80cab96d 100644 --- a/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt +++ b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt @@ -120,21 +120,10 @@ open class Wireguard : Protocol() { configData.optStringOrNull("Jmax")?.let { setJmax(it.toInt()) } configData.optStringOrNull("S1")?.let { setS1(it.toInt()) } configData.optStringOrNull("S2")?.let { setS2(it.toInt()) } - configData.optStringOrNull("S3")?.let { setS3(it.toInt()) } - configData.optStringOrNull("S4")?.let { setS4(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()) } - configData.optStringOrNull("I1")?.let { setI1(it) } - configData.optStringOrNull("I2")?.let { setI2(it) } - configData.optStringOrNull("I3")?.let { setI3(it) } - configData.optStringOrNull("I4")?.let { setI4(it) } - configData.optStringOrNull("I5")?.let { setI5(it) } - configData.optStringOrNull("J1")?.let { setJ1(it) } - configData.optStringOrNull("J2")?.let { setJ2(it) } - configData.optStringOrNull("J3")?.let { setJ3(it) } - configData.optStringOrNull("Itime")?.let { setItime(it.toInt()) } } private fun start(config: WireguardConfig, vpnBuilder: Builder, protect: (Int) -> Boolean) { diff --git a/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/WireguardConfig.kt b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/WireguardConfig.kt index 2dfbbae8..7ae3d43b 100644 --- a/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/WireguardConfig.kt +++ b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/WireguardConfig.kt @@ -20,21 +20,10 @@ open class WireguardConfig protected constructor( val jmax: Int?, val s1: Int?, val s2: Int?, - val s3: Int?, - val s4: Int?, val h1: Long?, val h2: Long?, val h3: Long?, - val h4: Long?, - var i1: String?, - var i2: String?, - var i3: String?, - var i4: String?, - var i5: String?, - var j1: String?, - var j2: String?, - var j3: String?, - var itime: Int? + val h4: Long? ) : ProtocolConfig(protocolConfigBuilder) { protected constructor(builder: Builder) : this( @@ -50,21 +39,10 @@ open class WireguardConfig protected constructor( builder.jmax, builder.s1, builder.s2, - builder.s3, - builder.s4, builder.h1, builder.h2, builder.h3, - builder.h4, - builder.i1, - builder.i2, - builder.i3, - builder.i4, - builder.i5, - builder.j1, - builder.j2, - builder.j3, - builder.itime + builder.h4 ) fun toWgUserspaceString(): String = with(StringBuilder()) { @@ -83,21 +61,10 @@ open class WireguardConfig protected constructor( appendLine("jmax=$jmax") appendLine("s1=$s1") appendLine("s2=$s2") - s3?.let { appendLine("s3=$it") } - s4?.let { appendLine("s4=$it") } appendLine("h1=$h1") appendLine("h2=$h2") appendLine("h3=$h3") appendLine("h4=$h4") - i1?.let { appendLine("i1=$it") } - i2?.let { appendLine("i2=$it") } - i3?.let { appendLine("i3=$it") } - i4?.let { appendLine("i4=$it") } - i5?.let { appendLine("i5=$it") } - j1?.let { appendLine("j1=$it") } - j2?.let { appendLine("j2=$it") } - j3?.let { appendLine("j3=$it") } - itime?.let { appendLine("itime=$it") } } } @@ -150,21 +117,10 @@ open class WireguardConfig protected constructor( internal var jmax: Int? = null internal var s1: Int? = null internal var s2: Int? = null - internal var s3: Int? = null - internal var s4: Int? = null internal var h1: Long? = null internal var h2: Long? = null internal var h3: Long? = null internal var h4: Long? = null - internal var i1: String? = null - internal var i2: String? = null - internal var i3: String? = null - internal var i4: String? = null - internal var i5: String? = null - internal var j1: String? = null - internal var j2: String? = null - internal var j3: String? = null - internal var itime: Int? = null fun setEndpoint(endpoint: InetEndpoint) = apply { this.endpoint = endpoint } @@ -183,21 +139,10 @@ open class WireguardConfig protected constructor( fun setJmax(jmax: Int) = apply { this.jmax = jmax } fun setS1(s1: Int) = apply { this.s1 = s1 } fun setS2(s2: Int) = apply { this.s2 = s2 } - fun setS3(s3: Int) = apply { this.s3 = s3 } - fun setS4(s4: Int) = apply { this.s4 = s4 } fun setH1(h1: Long) = apply { this.h1 = h1 } fun setH2(h2: Long) = apply { this.h2 = h2 } fun setH3(h3: Long) = apply { this.h3 = h3 } fun setH4(h4: Long) = apply { this.h4 = h4 } - fun setI1(i1: String) = apply { this.i1 = i1 } - fun setI2(i2: String) = apply { this.i2 = i2 } - fun setI3(i3: String) = apply { this.i3 = i3 } - fun setI4(i4: String) = apply { this.i4 = i4 } - fun setI5(i5: String) = apply { this.i5 = i5 } - fun setJ1(j1: String) = apply { this.j1 = j1 } - fun setJ2(j2: String) = apply { this.j2 = j2 } - fun setJ3(j3: String) = apply { this.j3 = j3 } - fun setItime(itime: Int) = apply { this.itime = itime } override fun build(): WireguardConfig = configBuild().run { WireguardConfig(this@Builder) } } diff --git a/client/cmake/ios.cmake b/client/cmake/ios.cmake index a498a5b1..58192237 100644 --- a/client/cmake/ios.cmake +++ b/client/cmake/ios.cmake @@ -76,22 +76,8 @@ set_target_properties(${PROJECT} PROPERTIES XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks" XCODE_EMBED_APP_EXTENSIONS networkextension + XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic ) - -if(DEFINED DEPLOY) - set_target_properties(${PROJECT} PROPERTIES - XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution" - XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development" - XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual - XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr ios.org.amnezia.AmneziaVPN" - XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev ios.org.amnezia.AmneziaVPN" - ) -else() - set_target_properties(${PROJECT} PROPERTIES - XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic - ) -endif() - set_target_properties(${PROJECT} PROPERTIES XCODE_ATTRIBUTE_SWIFT_VERSION "5.0" XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES" diff --git a/client/configurators/awg_configurator.cpp b/client/configurators/awg_configurator.cpp index f83acb19..21b61ba4 100644 --- a/client/configurators/awg_configurator.cpp +++ b/client/configurators/awg_configurator.cpp @@ -1,5 +1,4 @@ #include "awg_configurator.h" -#include "protocols/protocols_defs.h" #include #include @@ -40,20 +39,6 @@ QString AwgConfigurator::createConfig(const ServerCredentials &credentials, Dock jsonConfig[config_key::responsePacketMagicHeader] = configMap.value(config_key::responsePacketMagicHeader); jsonConfig[config_key::underloadPacketMagicHeader] = configMap.value(config_key::underloadPacketMagicHeader); jsonConfig[config_key::transportPacketMagicHeader] = configMap.value(config_key::transportPacketMagicHeader); - - // jsonConfig[config_key::cookieReplyPacketJunkSize] = configMap.value(config_key::cookieReplyPacketJunkSize); - // jsonConfig[config_key::transportPacketJunkSize] = configMap.value(config_key::transportPacketJunkSize); - - // jsonConfig[config_key::specialJunk1] = configMap.value(amnezia::config_key::specialJunk1); - // jsonConfig[config_key::specialJunk2] = configMap.value(amnezia::config_key::specialJunk2); - // jsonConfig[config_key::specialJunk3] = configMap.value(amnezia::config_key::specialJunk3); - // jsonConfig[config_key::specialJunk4] = configMap.value(amnezia::config_key::specialJunk4); - // jsonConfig[config_key::specialJunk5] = configMap.value(amnezia::config_key::specialJunk5); - // jsonConfig[config_key::controlledJunk1] = configMap.value(amnezia::config_key::controlledJunk1); - // jsonConfig[config_key::controlledJunk2] = configMap.value(amnezia::config_key::controlledJunk2); - // jsonConfig[config_key::controlledJunk3] = configMap.value(amnezia::config_key::controlledJunk3); - // jsonConfig[config_key::specialHandshakeTimeout] = configMap.value(amnezia::config_key::specialHandshakeTimeout); - jsonConfig[config_key::mtu] = containerConfig.value(ProtocolProps::protoToString(Proto::Awg)).toObject().value(config_key::mtu).toString(protocols::awg::defaultMtu); diff --git a/client/configurators/openvpn_configurator.cpp b/client/configurators/openvpn_configurator.cpp index f6996320..eca81afd 100644 --- a/client/configurators/openvpn_configurator.cpp +++ b/client/configurators/openvpn_configurator.cpp @@ -118,14 +118,15 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPairisSitesSplitTunnelingEnabled()) { config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n"); + +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) + // Prevent ipv6 leak + if (NetworkUtilities::checkIpv6Enabled()) { + config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n"); + } +#endif config.append("block-ipv6\n"); } else if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) { @@ -134,6 +135,7 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair ContainerProps::containerDetailedDescriptions() { return { { DockerContainer::OpenVpn, - QObject::tr("OpenVPN is one of the most popular and reliable VPN protocols. " - "It uses SSL/TLS encryption, supports a wide variety of devices and operating systems, " - "and is continuously improved by the community due to its open-source nature. " - "It provides a good balance between speed and security but is easily recognized by DPI systems, " - "making it susceptible to blocking.\n" - "\nFeatures:\n" - "* Available on all AmneziaVPN platforms\n" - "* Normal battery consumption on mobile devices\n" - "* Flexible customization for various devices and OS\n" - "* Operates over both TCP and UDP protocols") }, + QObject::tr( + "OpenVPN stands as one of the most popular and time-tested VPN protocols available.\n" + "It employs its unique security protocol, " + "leveraging the strength of SSL/TLS for encryption and key exchange. " + "Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, " + "catering to a wide range of devices and operating systems. " + "Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, " + "which continually reinforces its security. " + "With a strong balance of performance, security, and compatibility, " + "OpenVPN remains a top choice for privacy-conscious individuals and businesses alike.\n\n" + "* Available in the AmneziaVPN across all platforms\n" + "* Normal power consumption on mobile devices\n" + "* Flexible customisation to suit user needs to work with different operating systems and devices\n" + "* Recognised by DPI systems and therefore susceptible to blocking\n" + "* Can operate over both TCP and UDP network protocols.") }, { DockerContainer::ShadowSocks, - QObject::tr("Shadowsocks is based on the SOCKS5 protocol and encrypts connections using AEAD cipher. " - "Although designed to be discreet, it doesn't mimic a standard HTTPS connection and can be detected by some DPI systems. " - "Due to limited support in Amnezia, we recommend using the AmneziaWG protocol.\n" - "\nFeatures:\n" - "* Available in AmneziaVPN only on desktop platforms\n" - "* Customizable encryption protocol\n" + QObject::tr("Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. " + "Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection." + "However, certain traffic analysis systems might still detect a Shadowsocks connection. " + "Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol.\n\n" + "* Available in the AmneziaVPN only on desktop platforms\n" + "* Configurable encryption protocol\n" "* Detectable by some DPI systems\n" - "* Operates over TCP protocol\n") }, + "* Works over TCP network protocol.") }, { DockerContainer::Cloak, - QObject::tr("This combination includes the OpenVPN protocol and the Cloak plugin, specifically designed to protect against blocking.\n" - "\nOpenVPN securely encrypts all internet traffic between your device and the server.\n" - "\nThe Cloak plugin further protects the connection from DPI detection. " - "It modifies traffic metadata to disguise VPN traffic as regular web traffic and prevents detection through active probing. " - "If an incoming connection fails authentication, Cloak serves a fake website, making your VPN invisible to traffic analysis systems.\n" - "\nIn regions with heavy internet censorship, we strongly recommend using OpenVPN with Cloak from your first connection.\n" - "\nFeatures:\n" - "* Available on all AmneziaVPN platforms\n" + QObject::tr("This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for " + "protecting against detection.\n\n" + "OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client " + "and the server.\n\n" + "Cloak protects OpenVPN from detection. \n\n" + "Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, " + "and also protects the VPN from detection by Active Probing. This makes it very resistant to " + "being detected\n\n" + "Immediately after receiving the first data packet, Cloak authenticates the incoming connection. " + "If authentication fails, the plugin masks the server as a fake website and your VPN becomes " + "invisible to analysis systems.\n\n" + "* Available in the AmneziaVPN across all platforms\n" "* High power consumption on mobile devices\n" - "* Flexible configuration options\n" - "* Undetectable by DPI systems\n" - "* Operates over TCP protocol on port 443") }, + "* Flexible settings\n" + "* Not recognised by detection systems\n" + "* Works over TCP network protocol, 443 port.\n") }, { DockerContainer::WireGuard, - QObject::tr("WireGuard is a modern, streamlined VPN protocol offering stable connectivity and excellent performance across all devices. " - "It uses fixed encryption settings, delivering lower latency and higher data transfer speeds compared to OpenVPN. " - "However, WireGuard is easily identifiable by DPI systems due to its distinctive packet signatures, making it susceptible to blocking.\n" - "\nFeatures:\n" - "* Available on all AmneziaVPN platforms\n" - "* Low power consumption on mobile devices\n" - "* Minimal configuration required\n" - "* Easily detected by DPI systems (susceptible to blocking)\n" - "* Operates over UDP protocol") }, + QObject::tr("A relatively new popular VPN protocol with a simplified architecture.\n" + "WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption " + "settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput.\n" + "WireGuard is very susceptible to detection and blocking due to its distinct packet signatures. " + "Unlike some other VPN protocols that employ obfuscation techniques, " + "the consistent signature patterns of WireGuard packets can be more easily identified and " + "thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools.\n\n" + "* Available in the AmneziaVPN across all platforms\n" + "* Low power consumption\n" + "* Minimum number of settings\n" + "* Easily recognised by DPI analysis systems, susceptible to blocking\n" + "* Works over UDP network protocol.") }, { DockerContainer::Awg, - QObject::tr("AmneziaWG is a modern VPN protocol based on WireGuard, " - "combining simplified architecture with high performance across all devices. " - "It addresses WireGuard's main vulnerability (easy detection by DPI systems) through advanced obfuscation techniques, " - "making VPN traffic indistinguishable from regular internet traffic.\n" - "\nAmneziaWG is an excellent choice for those seeking a fast, stealthy VPN connection.\n" - "\nFeatures:\n" - "* Available on all AmneziaVPN platforms\n" - "* Low battery consumption on mobile devices\n" - "* Minimal settings required\n" - "* Undetectable by traffic analysis systems (DPI)\n" - "* Operates over UDP protocol") }, + QObject::tr("A modern iteration of the popular VPN protocol, " + "AmneziaWG builds upon the foundation set by WireGuard, " + "retaining its simplified architecture and high-performance capabilities across devices.\n" + "While WireGuard is known for its efficiency, " + "it had issues with being easily detected due to its distinct packet signatures. " + "AmneziaWG solves this problem by using better obfuscation methods, " + "making its traffic blend in with regular internet traffic.\n" + "This means that AmneziaWG keeps the fast performance of the original " + "while adding an extra layer of stealth, " + "making it a great choice for those wanting a fast and discreet VPN connection.\n\n" + "* Available in the AmneziaVPN across all platforms\n" + "* Low power consumption\n" + "* Minimum number of settings\n" + "* Not recognised by traffic analysis systems\n" + "* Works over UDP network protocol.") }, { DockerContainer::Xray, - QObject::tr("REALITY is an innovative protocol developed by the creators of XRay, designed specifically to combat high levels of internet censorship. " - "REALITY identifies censorship systems during the TLS handshake, " - "redirecting suspicious traffic seamlessly to legitimate websites like google.com while providing genuine TLS certificates. " - "This allows VPN traffic to blend indistinguishably with regular web traffic without special configuration." - "\nUnlike older protocols such as VMess, VLESS, and XTLS-Vision, REALITY incorporates an advanced built-in \"friend-or-foe\" detection mechanism, " - "effectively protecting against DPI and other traffic analysis methods.\n" - "\nFeatures:\n" - "* Resistant to active probing and DPI detection\n" - "* No special configuration required to disguise traffic\n" - "* Highly effective in heavily censored regions\n" - "* Minimal battery consumption on devices\n" - "* Operates over TCP protocol") }, + QObject::tr("The REALITY protocol, a pioneering development by the creators of XRay, " + "is designed to provide the highest level of protection against detection through its innovative approach to security and privacy.\n" + "It uniquely identifies attackers during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting attackers to genuine websites, " + "thus presenting an authentic TLS certificate and data. \n" + "This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, " + "legitimate sites without the need for specific configurations. \n" + "Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, " + "REALITY's innovative \"friend or foe\" recognition at the TLS handshake enhances security. " + "This makes REALITY a robust solution for maintaining internet freedom.") + }, { DockerContainer::Ipsec, - QObject::tr("IKEv2, combined with IPSec encryption, is a modern and reliable VPN protocol. " - "It reconnects quickly when switching networks or devices, making it ideal for dynamic network environments. " - "While it provides good security and speed, it's easily recognized by DPI systems and susceptible to blocking.\n" - "\nFeatures:\n" - "* Available in AmneziaVPN only on Windows\n" - "* Low battery consumption on mobile devices\n" - "* Minimal configuration required\n" - "* Detectable by DPI analysis systems(easily blocked)\n" - "* Operates over UDP protocol(ports 500 and 4500)") }, + QObject::tr("IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol.\n" + "One of its distinguishing features is its ability to swiftly switch between networks and devices, " + "making it particularly adaptive in dynamic network environments. \n" + "While it offers a blend of security, stability, and speed, " + "it's essential to note that IKEv2 can be easily detected and is susceptible to blocking.\n\n" + "* Available in the AmneziaVPN only on Windows\n" + "* Low power consumption, on mobile devices\n" + "* Minimal configuration\n" + "* Recognised by DPI analysis systems\n" + "* Works over UDP network protocol, ports 500 and 4500.") }, { DockerContainer::TorWebSite, QObject::tr("Website in Tor network") }, { DockerContainer::Dns, QObject::tr("DNS Service") }, diff --git a/client/core/api/apiDefs.h b/client/core/api/apiDefs.h index 12c8051f..d1a92d9d 100644 --- a/client/core/api/apiDefs.h +++ b/client/core/api/apiDefs.h @@ -22,21 +22,12 @@ namespace apiDefs namespace key { constexpr QLatin1String configVersion("config_version"); - constexpr QLatin1String apiEndpoint("api_endpoint"); - constexpr QLatin1String apiKey("api_key"); - constexpr QLatin1String description("description"); - constexpr QLatin1String name("name"); - constexpr QLatin1String protocol("protocol"); constexpr QLatin1String apiConfig("api_config"); constexpr QLatin1String stackType("stack_type"); constexpr QLatin1String serviceType("service_type"); - constexpr QLatin1String cliVersion("cli_version"); - constexpr QLatin1String supportedProtocols("supported_protocols"); constexpr QLatin1String vpnKey("vpn_key"); - constexpr QLatin1String config("config"); - constexpr QLatin1String configs("configs"); constexpr QLatin1String installationUuid("installation_uuid"); constexpr QLatin1String workerLastUpdated("worker_last_updated"); @@ -60,10 +51,6 @@ namespace apiDefs constexpr QLatin1String website("website"); constexpr QLatin1String websiteName("website_name"); constexpr QLatin1String telegram("telegram"); - - constexpr QLatin1String id("id"); - constexpr QLatin1String orderId("order_id"); - constexpr QLatin1String migrationCode("migration_code"); } const int requestTimeoutMsecs = 12 * 1000; // 12 secs diff --git a/client/core/api/apiUtils.cpp b/client/core/api/apiUtils.cpp index 7f3e6db3..f5f575c5 100644 --- a/client/core/api/apiUtils.cpp +++ b/client/core/api/apiUtils.cpp @@ -3,24 +3,6 @@ #include #include -namespace -{ - const QByteArray AMNEZIA_CONFIG_SIGNATURE = QByteArray::fromHex("000000ff"); - - QString escapeUnicode(const QString &input) - { - QString output; - for (QChar c : input) { - if (c.unicode() < 0x20 || c.unicode() > 0x7E) { - output += QString("\\u%1").arg(QString::number(c.unicode(), 16).rightJustified(4, '0')); - } else { - output += c; - } - } - return output; - } -} - bool apiUtils::isSubscriptionExpired(const QString &subscriptionEndDate) { QDateTime now = QDateTime::currentDateTime(); @@ -41,21 +23,13 @@ bool apiUtils::isServerFromApi(const QJsonObject &serverConfigObject) apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObject) { auto configVersion = serverConfigObject.value(apiDefs::key::configVersion).toInt(); - switch (configVersion) { case apiDefs::ConfigSource::Telegram: { - constexpr QLatin1String freeV2Endpoint(FREE_V2_ENDPOINT); - constexpr QLatin1String premiumV1Endpoint(PREM_V1_ENDPOINT); - - auto apiEndpoint = serverConfigObject.value(apiDefs::key::apiEndpoint).toString(); - - if (apiEndpoint.contains(premiumV1Endpoint)) { - return apiDefs::ConfigType::AmneziaPremiumV1; - } else if (apiEndpoint.contains(freeV2Endpoint)) { - return apiDefs::ConfigType::AmneziaFreeV2; - } }; case apiDefs::ConfigSource::AmneziaGateway: { + constexpr QLatin1String stackPremium("prem"); + constexpr QLatin1String stackFree("free"); + constexpr QLatin1String servicePremium("amnezia-premium"); constexpr QLatin1String serviceFree("amnezia-free"); constexpr QLatin1String serviceExternalPremium("external-premium"); @@ -96,9 +70,6 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList &ssl || reply->error() == QNetworkReply::NetworkError::TimeoutError) { qDebug() << reply->error(); return amnezia::ErrorCode::ApiConfigTimeoutError; - } else if (reply->error() == QNetworkReply::NetworkError::OperationNotImplementedError) { - qDebug() << reply->error(); - return amnezia::ErrorCode::ApiUpdateRequestError; } else { QString err = reply->errorString(); int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); @@ -124,41 +95,3 @@ bool apiUtils::isPremiumServer(const QJsonObject &serverConfigObject) apiDefs::ConfigType::ExternalPremium }; return premiumTypes.contains(getConfigType(serverConfigObject)); } - -QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject) -{ - if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV1) { - return {}; - } - - QList> orderedFields; - orderedFields.append(qMakePair(apiDefs::key::name, serverConfigObject[apiDefs::key::name].toString())); - orderedFields.append(qMakePair(apiDefs::key::description, serverConfigObject[apiDefs::key::description].toString())); - orderedFields.append(qMakePair(apiDefs::key::configVersion, serverConfigObject[apiDefs::key::configVersion].toDouble())); - orderedFields.append(qMakePair(apiDefs::key::protocol, serverConfigObject[apiDefs::key::protocol].toString())); - orderedFields.append(qMakePair(apiDefs::key::apiEndpoint, serverConfigObject[apiDefs::key::apiEndpoint].toString())); - orderedFields.append(qMakePair(apiDefs::key::apiKey, serverConfigObject[apiDefs::key::apiKey].toString())); - - QString vpnKeyStr = "{"; - for (int i = 0; i < orderedFields.size(); ++i) { - const auto &pair = orderedFields[i]; - if (pair.second.typeId() == QMetaType::Type::QString) { - vpnKeyStr += "\"" + pair.first + "\": \"" + pair.second.toString() + "\""; - } else if (pair.second.typeId() == QMetaType::Type::Double || pair.second.typeId() == QMetaType::Type::Int) { - vpnKeyStr += "\"" + pair.first + "\": " + QString::number(pair.second.toDouble(), 'f', 1); - } - - if (i < orderedFields.size() - 1) { - vpnKeyStr += ", "; - } - } - vpnKeyStr += "}"; - - QByteArray vpnKeyCompressed = escapeUnicode(vpnKeyStr).toUtf8(); - vpnKeyCompressed = qCompress(vpnKeyCompressed, 6); - vpnKeyCompressed = vpnKeyCompressed.mid(4); - - QByteArray signedData = AMNEZIA_CONFIG_SIGNATURE + vpnKeyCompressed; - - return QString("vpn://%1").arg(QString(signedData.toBase64(QByteArray::Base64UrlEncoding))); -} diff --git a/client/core/api/apiUtils.h b/client/core/api/apiUtils.h index 45eaf2de..47006e80 100644 --- a/client/core/api/apiUtils.h +++ b/client/core/api/apiUtils.h @@ -19,8 +19,6 @@ namespace apiUtils apiDefs::ConfigSource getConfigSource(const QJsonObject &serverConfigObject); amnezia::ErrorCode checkNetworkReplyErrors(const QList &sslErrors, QNetworkReply *reply); - - QString getPremiumV1VpnKey(const QJsonObject &serverConfigObject); } #endif // APIUTILS_H diff --git a/client/core/controllers/coreController.cpp b/client/core/controllers/coreController.cpp index 0e72ef1a..f2c09d45 100644 --- a/client/core/controllers/coreController.cpp +++ b/client/core/controllers/coreController.cpp @@ -148,9 +148,6 @@ void CoreController::initControllers() m_apiConfigsController.reset(new ApiConfigsController(m_serversModel, m_apiServicesModel, m_settings)); m_engine->rootContext()->setContextProperty("ApiConfigsController", m_apiConfigsController.get()); - - m_apiPremV1MigrationController.reset(new ApiPremV1MigrationController(m_serversModel, m_settings, this)); - m_engine->rootContext()->setContextProperty("ApiPremV1MigrationController", m_apiPremV1MigrationController.get()); } void CoreController::initAndroidController() @@ -223,8 +220,6 @@ void CoreController::initSignalHandlers() initAutoConnectHandler(); initAmneziaDnsToggledHandler(); initPrepareConfigHandler(); - initImportPremiumV2VpnKeyHandler(); - initShowMigrationDrawerHandler(); initStrictKillSwitchHandler(); } @@ -368,29 +363,10 @@ void CoreController::initPrepareConfigHandler() }); } -void CoreController::initImportPremiumV2VpnKeyHandler() -{ - connect(m_apiPremV1MigrationController.get(), &ApiPremV1MigrationController::importPremiumV2VpnKey, this, [this](const QString &vpnKey) { - m_importController->extractConfigFromData(vpnKey); - m_importController->importConfig(); - - emit m_apiPremV1MigrationController->migrationFinished(); - }); -} - -void CoreController::initShowMigrationDrawerHandler() -{ - QTimer::singleShot(1000, this, [this]() { - if (m_apiPremV1MigrationController->isPremV1MigrationReminderActive() && m_apiPremV1MigrationController->hasConfigsToMigration()) { - m_apiPremV1MigrationController->showMigrationDrawer(); - } - }); -} - void CoreController::initStrictKillSwitchHandler() { - connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged, m_vpnConnection.get(), - &VpnConnection::onKillSwitchModeChanged); + connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged, + m_vpnConnection.get(), &VpnConnection::onKillSwitchModeChanged); } QSharedPointer CoreController::pageController() const diff --git a/client/core/controllers/coreController.h b/client/core/controllers/coreController.h index 9ae53562..6342d738 100644 --- a/client/core/controllers/coreController.h +++ b/client/core/controllers/coreController.h @@ -7,7 +7,6 @@ #include "ui/controllers/api/apiConfigsController.h" #include "ui/controllers/api/apiSettingsController.h" -#include "ui/controllers/api/apiPremV1MigrationController.h" #include "ui/controllers/appSplitTunnelingController.h" #include "ui/controllers/allowedDnsController.h" #include "ui/controllers/connectionController.h" @@ -83,8 +82,6 @@ private: void initAutoConnectHandler(); void initAmneziaDnsToggledHandler(); void initPrepareConfigHandler(); - void initImportPremiumV2VpnKeyHandler(); - void initShowMigrationDrawerHandler(); void initStrictKillSwitchHandler(); QQmlApplicationEngine *m_engine {}; // TODO use parent child system here? @@ -112,7 +109,6 @@ private: QScopedPointer m_apiSettingsController; QScopedPointer m_apiConfigsController; - QScopedPointer m_apiPremV1MigrationController; QSharedPointer m_containersModel; QSharedPointer m_defaultServerContainersModel; diff --git a/client/core/controllers/gatewayController.cpp b/client/core/controllers/gatewayController.cpp index 26855ae6..0d86b9d5 100644 --- a/client/core/controllers/gatewayController.cpp +++ b/client/core/controllers/gatewayController.cpp @@ -14,8 +14,8 @@ #include "amnezia_application.h" #include "core/api/apiUtils.h" -#include "core/networkUtilities.h" #include "utilities.h" +#include "core/networkUtilities.h" #ifdef AMNEZIA_DESKTOP #include "core/ipcclient.h" @@ -36,17 +36,10 @@ namespace constexpr QLatin1String errorResponsePattern1("No active configuration found for"); constexpr QLatin1String errorResponsePattern2("No non-revoked public key found for"); constexpr QLatin1String errorResponsePattern3("Account not found."); - - constexpr QLatin1String updateRequestResponsePattern("client version update is required"); } -GatewayController::GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs, - const bool isStrictKillSwitchEnabled, QObject *parent) - : QObject(parent), - m_gatewayEndpoint(gatewayEndpoint), - m_isDevEnvironment(isDevEnvironment), - m_requestTimeoutMsecs(requestTimeoutMsecs), - m_isStrictKillSwitchEnabled(isStrictKillSwitchEnabled) +GatewayController::GatewayController(const QString &gatewayEndpoint, bool isDevEnvironment, int requestTimeoutMsecs, QObject *parent) + : QObject(parent), m_gatewayEndpoint(gatewayEndpoint), m_isDevEnvironment(isDevEnvironment), m_requestTimeoutMsecs(requestTimeoutMsecs) { } @@ -65,11 +58,11 @@ ErrorCode GatewayController::get(const QString &endpoint, QByteArray &responseBo // bypass killSwitch exceptions for API-gateway #ifdef AMNEZIA_DESKTOP - if (m_isStrictKillSwitchEnabled) { + { QString host = QUrl(request.url()).host(); QString ip = NetworkUtilities::getIPAddress(host); if (!ip.isEmpty()) { - IpcClient::Interface()->addKillSwitchAllowedRange(QStringList { ip }); + IpcClient::Interface()->addKillSwitchAllowedRange(QStringList{ip}); } } #endif @@ -127,11 +120,11 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api // bypass killSwitch exceptions for API-gateway #ifdef AMNEZIA_DESKTOP - if (m_isStrictKillSwitchEnabled) { + { QString host = QUrl(request.url()).host(); QString ip = NetworkUtilities::getIPAddress(host); if (!ip.isEmpty()) { - IpcClient::Interface()->addKillSwitchAllowedRange(QStringList { ip }); + IpcClient::Interface()->addKillSwitchAllowedRange(QStringList{ip}); } } #endif @@ -313,13 +306,6 @@ bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray qDebug() << reply->error(); return true; } - } else if (reply->error() == QNetworkReply::NetworkError::OperationNotImplementedError) { - if (responseBody.contains(updateRequestResponsePattern)) { - return false; - } else { - qDebug() << reply->error(); - return true; - } } else if (reply->error() != QNetworkReply::NetworkError::NoError) { qDebug() << reply->error(); return true; diff --git a/client/core/controllers/gatewayController.h b/client/core/controllers/gatewayController.h index 9f91df53..45d989f0 100644 --- a/client/core/controllers/gatewayController.h +++ b/client/core/controllers/gatewayController.h @@ -15,8 +15,7 @@ class GatewayController : public QObject Q_OBJECT public: - explicit GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs, - const bool isStrictKillSwitchEnabled, QObject *parent = nullptr); + explicit GatewayController(const QString &gatewayEndpoint, bool isDevEnvironment, int requestTimeoutMsecs, QObject *parent = nullptr); amnezia::ErrorCode get(const QString &endpoint, QByteArray &responseBody); amnezia::ErrorCode post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody); @@ -31,7 +30,6 @@ private: int m_requestTimeoutMsecs; QString m_gatewayEndpoint; bool m_isDevEnvironment = false; - bool m_isStrictKillSwitchEnabled = false; }; #endif // GATEWAYCONTROLLER_H diff --git a/client/core/controllers/serverController.cpp b/client/core/controllers/serverController.cpp index 3c24edea..9ac62759 100644 --- a/client/core/controllers/serverController.cpp +++ b/client/core/controllers/serverController.cpp @@ -138,7 +138,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, if (overwriteMode == libssh::ScpOverwriteMode::ScpOverwriteExisting) { e = runScript(credentials, - replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, path), + replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path), genVarsForScript(credentials, container)), cbReadStd, cbReadStd); @@ -146,7 +146,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, return e; } else if (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting) { e = runScript(credentials, - replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, tmpFileName), + replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(tmpFileName), genVarsForScript(credentials, container)), cbReadStd, cbReadStd); @@ -154,7 +154,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, return e; e = runScript(credentials, - replaceVars(QStringLiteral("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName, path), + replaceVars(QString("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName).arg(path), genVarsForScript(credentials, container)), cbReadStd, cbReadStd); @@ -177,7 +177,7 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container, errorCode = ErrorCode::NoError; - QString script = QStringLiteral("sudo docker exec -i %1 sh -c \"xxd -p '%2'\"").arg(ContainerProps::containerToString(container), path); + QString script = QString("sudo docker exec -i %1 sh -c \"xxd -p \'%2\'\"").arg(ContainerProps::containerToString(container)).arg(path); QString stdOut; auto cbReadStdOut = [&](const QString &data, libssh::Client &) { @@ -349,7 +349,7 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c if ((oldProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress) != newProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress)) || (oldProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort) - != newProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort)) + != newProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort)) || (oldProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount) != newProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount)) || (oldProtoConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize) @@ -366,13 +366,8 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c != newProtoConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader)) || (oldProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader) != newProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader)) - || (oldProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader)) - != newProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader)) - // || (oldProtoConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize) - // != newProtoConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize)) - // || (oldProtoConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize) - // != newProtoConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize)) - + || (oldProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader) + != newProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader))) return true; } @@ -380,7 +375,7 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c if ((oldProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress) != newProtoConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress)) || (oldProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort) - != newProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort))) + != newProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort))) return true; } @@ -388,13 +383,6 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c return true; } - if (container == DockerContainer::Xray) { - if (oldProtoConfig.value(config_key::port).toString(protocols::xray::defaultPort) - != newProtoConfig.value(config_key::port).toString(protocols::xray::defaultPort)) { - return true; - } - } - return false; } @@ -460,13 +448,11 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::build_container), genVarsForScript(credentials, container, config)), cbReadStdOut, cbReadStdErr); - + if (stdOut.contains("doesn't work on cgroups v2")) return ErrorCode::ServerDockerOnCgroupsV2; if (stdOut.contains("cgroup mountpoint does not exist")) return ErrorCode::ServerCgroupMountpoint; - if (stdOut.contains("have reached") && stdOut.contains("pull rate limit")) - return ErrorCode::DockerPullRateLimit; return error; } @@ -646,9 +632,6 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential vars.append({ { "$UNDERLOAD_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::underloadPacketMagicHeader).toString() } }); vars.append({ { "$TRANSPORT_PACKET_MAGIC_HEADER", amneziaWireguarConfig.value(config_key::transportPacketMagicHeader).toString() } }); - vars.append({ { "$COOKIE_REPLY_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::cookieReplyPacketJunkSize).toString() } }); - vars.append({ { "$TRANSPORT_PACKET_JUNK_SIZE", amneziaWireguarConfig.value(config_key::transportPacketJunkSize).toString() } }); - // Socks5 proxy vars vars.append({ { "$SOCKS5_PROXY_PORT", socks5ProxyConfig.value(config_key::port).toString(protocols::socks5Proxy::defaultPort) } }); auto username = socks5ProxyConfig.value(config_key::userName).toString(); @@ -835,7 +818,7 @@ ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credential if (stdOut.contains("Packet manager not found")) return ErrorCode::ServerPacketManagerError; - if (stdOut.contains("fuser not installed") || stdOut.contains("cat not installed")) + if (stdOut.contains("fuser not installed")) return ErrorCode::NoError; if (stdOut.isEmpty()) { diff --git a/client/core/defs.h b/client/core/defs.h index 64f52ce6..eff3df3b 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -60,7 +60,6 @@ namespace amnezia ServerUserPasswordRequired = 210, ServerDockerOnCgroupsV2 = 211, ServerCgroupMountpoint = 212, - DockerPullRateLimit = 213, // Ssh connection errors SshRequestDeniedError = 300, @@ -118,8 +117,6 @@ namespace amnezia ApiServicesMissingError = 1107, ApiConfigLimitError = 1108, ApiNotFoundError = 1109, - ApiMigrationError = 1110, - ApiUpdateRequestError = 1111, // QFile errors OpenError = 1200, diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp index bd5ccaba..6abab0e0 100644 --- a/client/core/errorstrings.cpp +++ b/client/core/errorstrings.cpp @@ -28,7 +28,6 @@ QString errorString(ErrorCode code) { case(ErrorCode::ServerUserPasswordRequired): errorMessage = QObject::tr("The user's password is required"); break; case(ErrorCode::ServerDockerOnCgroupsV2): errorMessage = QObject::tr("Docker error: runc doesn't work on cgroups v2"); break; case(ErrorCode::ServerCgroupMountpoint): errorMessage = QObject::tr("Server error: cgroup mountpoint does not exist"); break; - case(ErrorCode::DockerPullRateLimit): errorMessage = QObject::tr("Docker error: The pull rate limit has been reached"); break; // Libssh errors case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break; @@ -75,8 +74,6 @@ QString errorString(ErrorCode code) { case (ErrorCode::ApiServicesMissingError): errorMessage = QObject::tr("Missing list of available services"); break; case (ErrorCode::ApiConfigLimitError): errorMessage = QObject::tr("The limit of allowed configurations per subscription has been exceeded"); break; case (ErrorCode::ApiNotFoundError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break; - case (ErrorCode::ApiMigrationError): errorMessage = QObject::tr("A migration error has occurred. Please contact our technical support"); break; - case (ErrorCode::ApiUpdateRequestError): errorMessage = QObject::tr("Please update the application to use this feature"); break; // QFile errors case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break; diff --git a/client/daemon/daemon.cpp b/client/daemon/daemon.cpp index 2faff0ef..e4b0ab3d 100644 --- a/client/daemon/daemon.cpp +++ b/client/daemon/daemon.cpp @@ -149,7 +149,8 @@ bool Daemon::activate(const InterfaceConfig& config) { // set routing for (const IPAddress& ip : config.m_allowedIPAddressRanges) { if (!wgutils()->updateRoutePrefix(ip)) { - logger.debug() << "Routing configuration failed for" << ip.toString(); + logger.debug() << "Routing configuration failed for" + << logger.sensitive(ip.toString()); return false; } } @@ -169,14 +170,11 @@ bool Daemon::maybeUpdateResolvers(const InterfaceConfig& config) { if ((config.m_hopType == InterfaceConfig::MultiHopExit) || (config.m_hopType == InterfaceConfig::SingleHop)) { QList resolvers; - resolvers.append(QHostAddress(config.m_primaryDnsServer)); - if (!config.m_secondaryDnsServer.isEmpty()) { - resolvers.append(QHostAddress(config.m_secondaryDnsServer)); - } + resolvers.append(QHostAddress(config.m_dnsServer)); // If the DNS is not the Gateway, it's a user defined DNS // thus, not add any other :) - if (config.m_primaryDnsServer == config.m_serverIpv4Gateway) { + if (config.m_dnsServer == config.m_serverIpv4Gateway) { resolvers.append(QHostAddress(config.m_serverIpv6Gateway)); } @@ -282,26 +280,15 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) { config.m_serverIpv4Gateway = obj.value("serverIpv4Gateway").toString(); config.m_serverIpv6Gateway = obj.value("serverIpv6Gateway").toString(); - if (!obj.contains("primaryDnsServer")) { - config.m_primaryDnsServer = QString(); + if (!obj.contains("dnsServer")) { + config.m_dnsServer = QString(); } else { - QJsonValue value = obj.value("primaryDnsServer"); + QJsonValue value = obj.value("dnsServer"); if (!value.isString()) { logger.error() << "dnsServer is not a string"; return false; } - config.m_primaryDnsServer = value.toString(); - } - - if (!obj.contains("secondaryDnsServer")) { - config.m_secondaryDnsServer = QString(); - } else { - QJsonValue value = obj.value("secondaryDnsServer"); - if (!value.isString()) { - logger.error() << "dnsServer is not a string"; - return false; - } - config.m_secondaryDnsServer = value.toString(); + config.m_dnsServer = value.toString(); } if (!obj.contains("hopType")) { @@ -405,13 +392,6 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) { if (!obj.value("S2").isNull()) { config.m_responsePacketJunkSize = obj.value("S2").toString(); } - if (!obj.value("S3").isNull()) { - config.m_cookieReplyPacketJunkSize = obj.value("S3").toString(); - } - if (!obj.value("S4").isNull()) { - config.m_transportPacketJunkSize = obj.value("S4").toString(); - } - if (!obj.value("H1").isNull()) { config.m_initPacketMagicHeader = obj.value("H1").toString(); } @@ -425,34 +405,6 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) { config.m_transportPacketMagicHeader = obj.value("H4").toString(); } - if (!obj.value("I1").isNull()) { - config.m_specialJunk["I1"] = obj.value("I1").toString(); - } - if (!obj.value("I2").isNull()) { - config.m_specialJunk["I2"] = obj.value("I2").toString(); - } - if (!obj.value("I3").isNull()) { - config.m_specialJunk["I3"] = obj.value("I3").toString(); - } - if (!obj.value("I4").isNull()) { - config.m_specialJunk["I4"] = obj.value("I4").toString(); - } - if (!obj.value("I5").isNull()) { - config.m_specialJunk["I5"] = obj.value("I5").toString(); - } - if (!obj.value("J1").isNull()) { - config.m_controlledJunk["J1"] = obj.value("J1").toString(); - } - if (!obj.value("J2").isNull()) { - config.m_controlledJunk["J2"] = obj.value("J2").toString(); - } - if (!obj.value("J3").isNull()) { - config.m_controlledJunk["J3"] = obj.value("J3").toString(); - } - if (!obj.value("Itime").isNull()) { - config.m_specialHandshakeTimeout = obj.value("Itime").toString(); - } - return true; } @@ -495,7 +447,7 @@ bool Daemon::deactivate(bool emitSignals) { m_connections.clear(); // Delete the interface - return wgutils()->deleteInterface(); + return wgutils()->deleteInterface(); } QString Daemon::logs() { diff --git a/client/daemon/interfaceconfig.cpp b/client/daemon/interfaceconfig.cpp index 53da5d36..f0adcc92 100644 --- a/client/daemon/interfaceconfig.cpp +++ b/client/daemon/interfaceconfig.cpp @@ -28,8 +28,7 @@ QJsonObject InterfaceConfig::toJson() const { (m_hopType == InterfaceConfig::SingleHop)) { json.insert("serverIpv4Gateway", QJsonValue(m_serverIpv4Gateway)); json.insert("serverIpv6Gateway", QJsonValue(m_serverIpv6Gateway)); - json.insert("primaryDnsServer", QJsonValue(m_primaryDnsServer)); - json.insert("secondaryDnsServer", QJsonValue(m_secondaryDnsServer)); + json.insert("dnsServer", QJsonValue(m_dnsServer)); } QJsonArray allowedIPAddesses; @@ -101,15 +100,11 @@ QString InterfaceConfig::toWgConf(const QMap& extra) const { out << "MTU = " << m_deviceMTU << "\n"; } - if (!m_primaryDnsServer.isNull()) { - QStringList dnsServers; - dnsServers.append(m_primaryDnsServer); - if (!m_secondaryDnsServer.isNull()) { - dnsServers.append(m_secondaryDnsServer); - } + if (!m_dnsServer.isNull()) { + QStringList dnsServers(m_dnsServer); // If the DNS is not the Gateway, it's a user defined DNS // thus, not add any other :) - if (m_primaryDnsServer == m_serverIpv4Gateway) { + if (m_dnsServer == m_serverIpv4Gateway) { dnsServers.append(m_serverIpv6Gateway); } out << "DNS = " << dnsServers.join(", ") << "\n"; @@ -130,12 +125,6 @@ QString InterfaceConfig::toWgConf(const QMap& extra) const { if (!m_responsePacketJunkSize.isNull()) { out << "S2 = " << m_responsePacketJunkSize << "\n"; } - if (!m_cookieReplyPacketJunkSize.isNull()) { - out << "S3 = " << m_cookieReplyPacketJunkSize << "\n"; - } - if (!m_transportPacketJunkSize.isNull()) { - out << "S4 = " << m_transportPacketJunkSize << "\n"; - } if (!m_initPacketMagicHeader.isNull()) { out << "H1 = " << m_initPacketMagicHeader << "\n"; } @@ -149,16 +138,6 @@ QString InterfaceConfig::toWgConf(const QMap& extra) const { out << "H4 = " << m_transportPacketMagicHeader << "\n"; } - for (const QString& key : m_specialJunk.keys()) { - out << key << " = " << m_specialJunk[key] << "\n"; - } - for (const QString& key : m_controlledJunk.keys()) { - out << key << " = " << m_controlledJunk[key] << "\n"; - } - if (!m_specialHandshakeTimeout.isNull()) { - out << "Itime = " << m_specialHandshakeTimeout << "\n"; - } - // If any extra config was provided, append it now. for (const QString& key : extra.keys()) { out << key << " = " << extra[key] << "\n"; diff --git a/client/daemon/interfaceconfig.h b/client/daemon/interfaceconfig.h index 06288e80..ee43a253 100644 --- a/client/daemon/interfaceconfig.h +++ b/client/daemon/interfaceconfig.h @@ -32,8 +32,7 @@ class InterfaceConfig { QString m_serverIpv4AddrIn; QString m_serverPskKey; QString m_serverIpv6AddrIn; - QString m_primaryDnsServer; - QString m_secondaryDnsServer; + QString m_dnsServer; int m_serverPort = 0; int m_deviceMTU = 1420; QList m_allowedIPAddressRanges; @@ -50,15 +49,10 @@ class InterfaceConfig { QString m_junkPacketMaxSize; QString m_initPacketJunkSize; QString m_responsePacketJunkSize; - QString m_cookieReplyPacketJunkSize; - QString m_transportPacketJunkSize; QString m_initPacketMagicHeader; QString m_responsePacketMagicHeader; QString m_underloadPacketMagicHeader; QString m_transportPacketMagicHeader; - QMap m_specialJunk; - QMap m_controlledJunk; - QString m_specialHandshakeTimeout; QJsonObject toJson() const; QString toWgConf( diff --git a/client/ios/networkextension/CMakeLists.txt b/client/ios/networkextension/CMakeLists.txt index 64b1c3c4..dde03b3b 100644 --- a/client/ios/networkextension/CMakeLists.txt +++ b/client/ios/networkextension/CMakeLists.txt @@ -26,21 +26,9 @@ set_target_properties(networkextension PROPERTIES XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2" XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../Frameworks" -) -if(DEPLOY) - set_target_properties(networkextension PROPERTIES - XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution" - XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development" - XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual - XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr ios.org.amnezia.AmneziaVPN" - XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev ios.org.amnezia.AmneziaVPN" - ) -else() - set_target_properties(networkextension PROPERTIES - XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic - ) -endif() + XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic +) set_target_properties(networkextension PROPERTIES XCODE_ATTRIBUTE_SWIFT_VERSION "5.0" diff --git a/client/mozilla/localsocketcontroller.cpp b/client/mozilla/localsocketcontroller.cpp index 9abab81c..afa29c47 100644 --- a/client/mozilla/localsocketcontroller.cpp +++ b/client/mozilla/localsocketcontroller.cpp @@ -38,7 +38,7 @@ LocalSocketController::LocalSocketController() { m_socket = new QLocalSocket(this); connect(m_socket, &QLocalSocket::connected, this, &LocalSocketController::daemonConnected); - connect(m_socket, &QLocalSocket::disconnected, this, + connect(m_socket, &QLocalSocket::disconnected, this, [&] { errorOccurred(QLocalSocket::PeerClosedError); }); connect(m_socket, &QLocalSocket::errorOccurred, this, &LocalSocketController::errorOccurred); @@ -135,7 +135,7 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) { // set up IPv6 unique-local-address, ULA, with "fd00::/8" prefix, not globally routable. // this will be default IPv6 gateway, OS recognizes that IPv6 link is local and switches to IPv4. - // Otherwise some OSes (Linux) try IPv6 forever and hang. + // Otherwise some OSes (Linux) try IPv6 forever and hang. // https://en.wikipedia.org/wiki/Unique_local_address (RFC 4193) // https://man7.org/linux/man-pages/man5/gai.conf.5.html json.insert("deviceIpv6Address", "fd58:baa6:dead::1"); // simply "dead::1" is globally-routable, don't use it @@ -149,14 +149,7 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) { json.insert("serverPort", wgConfig.value(amnezia::config_key::port).toInt()); json.insert("serverIpv4Gateway", wgConfig.value(amnezia::config_key::hostName)); // json.insert("serverIpv6Gateway", QJsonValue(hop.m_server.ipv6Gateway())); - - json.insert("primaryDnsServer", rawConfig.value(amnezia::config_key::dns1)); - - // We don't use secondary DNS if primary DNS is AmneziaDNS - if (!rawConfig.value(amnezia::config_key::dns1).toString(). - contains(amnezia::protocols::dns::amneziaDnsIp)) { - json.insert("secondaryDnsServer", rawConfig.value(amnezia::config_key::dns2)); - } + json.insert("dnsServer", rawConfig.value(amnezia::config_key::dns1)); QJsonArray jsAllowedIPAddesses; @@ -244,61 +237,28 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) { json.insert(amnezia::config_key::junkPacketMaxSize, wgConfig.value(amnezia::config_key::junkPacketMaxSize)); json.insert(amnezia::config_key::initPacketJunkSize, wgConfig.value(amnezia::config_key::initPacketJunkSize)); json.insert(amnezia::config_key::responsePacketJunkSize, wgConfig.value(amnezia::config_key::responsePacketJunkSize)); - json.insert(amnezia::config_key::cookieReplyPacketJunkSize, wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize)); - json.insert(amnezia::config_key::transportPacketJunkSize, wgConfig.value(amnezia::config_key::transportPacketJunkSize)); json.insert(amnezia::config_key::initPacketMagicHeader, wgConfig.value(amnezia::config_key::initPacketMagicHeader)); json.insert(amnezia::config_key::responsePacketMagicHeader, wgConfig.value(amnezia::config_key::responsePacketMagicHeader)); json.insert(amnezia::config_key::underloadPacketMagicHeader, wgConfig.value(amnezia::config_key::underloadPacketMagicHeader)); json.insert(amnezia::config_key::transportPacketMagicHeader, wgConfig.value(amnezia::config_key::transportPacketMagicHeader)); - json.insert(amnezia::config_key::specialJunk1, wgConfig.value(amnezia::config_key::specialJunk1)); - json.insert(amnezia::config_key::specialJunk2, wgConfig.value(amnezia::config_key::specialJunk2)); - json.insert(amnezia::config_key::specialJunk3, wgConfig.value(amnezia::config_key::specialJunk3)); - json.insert(amnezia::config_key::specialJunk4, wgConfig.value(amnezia::config_key::specialJunk4)); - json.insert(amnezia::config_key::specialJunk5, wgConfig.value(amnezia::config_key::specialJunk5)); - json.insert(amnezia::config_key::controlledJunk1, wgConfig.value(amnezia::config_key::controlledJunk1)); - json.insert(amnezia::config_key::controlledJunk2, wgConfig.value(amnezia::config_key::controlledJunk2)); - json.insert(amnezia::config_key::controlledJunk3, wgConfig.value(amnezia::config_key::controlledJunk3)); - json.insert(amnezia::config_key::specialHandshakeTimeout, wgConfig.value(amnezia::config_key::specialHandshakeTimeout)); } else if (!wgConfig.value(amnezia::config_key::junkPacketCount).isUndefined() && !wgConfig.value(amnezia::config_key::junkPacketMinSize).isUndefined() && !wgConfig.value(amnezia::config_key::junkPacketMaxSize).isUndefined() && !wgConfig.value(amnezia::config_key::initPacketJunkSize).isUndefined() && !wgConfig.value(amnezia::config_key::responsePacketJunkSize).isUndefined() - && !wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize).isUndefined() - && !wgConfig.value(amnezia::config_key::transportPacketJunkSize).isUndefined() && !wgConfig.value(amnezia::config_key::initPacketMagicHeader).isUndefined() && !wgConfig.value(amnezia::config_key::responsePacketMagicHeader).isUndefined() && !wgConfig.value(amnezia::config_key::underloadPacketMagicHeader).isUndefined() - && !wgConfig.value(amnezia::config_key::transportPacketMagicHeader).isUndefined() - && !wgConfig.value(amnezia::config_key::specialJunk1).isUndefined() - && !wgConfig.value(amnezia::config_key::specialJunk2).isUndefined() - && !wgConfig.value(amnezia::config_key::specialJunk3).isUndefined() - && !wgConfig.value(amnezia::config_key::specialJunk4).isUndefined() - && !wgConfig.value(amnezia::config_key::specialJunk5).isUndefined() - && !wgConfig.value(amnezia::config_key::controlledJunk1).isUndefined() - && !wgConfig.value(amnezia::config_key::controlledJunk2).isUndefined() - && !wgConfig.value(amnezia::config_key::controlledJunk3).isUndefined() - && !wgConfig.value(amnezia::config_key::specialHandshakeTimeout).isUndefined()) { + && !wgConfig.value(amnezia::config_key::transportPacketMagicHeader).isUndefined()) { json.insert(amnezia::config_key::junkPacketCount, wgConfig.value(amnezia::config_key::junkPacketCount)); json.insert(amnezia::config_key::junkPacketMinSize, wgConfig.value(amnezia::config_key::junkPacketMinSize)); json.insert(amnezia::config_key::junkPacketMaxSize, wgConfig.value(amnezia::config_key::junkPacketMaxSize)); json.insert(amnezia::config_key::initPacketJunkSize, wgConfig.value(amnezia::config_key::initPacketJunkSize)); json.insert(amnezia::config_key::responsePacketJunkSize, wgConfig.value(amnezia::config_key::responsePacketJunkSize)); - json.insert(amnezia::config_key::cookieReplyPacketJunkSize, wgConfig.value(amnezia::config_key::cookieReplyPacketJunkSize)); - json.insert(amnezia::config_key::transportPacketJunkSize, wgConfig.value(amnezia::config_key::transportPacketJunkSize)); json.insert(amnezia::config_key::initPacketMagicHeader, wgConfig.value(amnezia::config_key::initPacketMagicHeader)); json.insert(amnezia::config_key::responsePacketMagicHeader, wgConfig.value(amnezia::config_key::responsePacketMagicHeader)); json.insert(amnezia::config_key::underloadPacketMagicHeader, wgConfig.value(amnezia::config_key::underloadPacketMagicHeader)); json.insert(amnezia::config_key::transportPacketMagicHeader, wgConfig.value(amnezia::config_key::transportPacketMagicHeader)); - json.insert(amnezia::config_key::specialJunk1, wgConfig.value(amnezia::config_key::specialJunk1)); - json.insert(amnezia::config_key::specialJunk2, wgConfig.value(amnezia::config_key::specialJunk2)); - json.insert(amnezia::config_key::specialJunk3, wgConfig.value(amnezia::config_key::specialJunk3)); - json.insert(amnezia::config_key::specialJunk4, wgConfig.value(amnezia::config_key::specialJunk4)); - json.insert(amnezia::config_key::specialJunk5, wgConfig.value(amnezia::config_key::specialJunk5)); - json.insert(amnezia::config_key::controlledJunk1, wgConfig.value(amnezia::config_key::controlledJunk1)); - json.insert(amnezia::config_key::controlledJunk2, wgConfig.value(amnezia::config_key::controlledJunk2)); - json.insert(amnezia::config_key::controlledJunk3, wgConfig.value(amnezia::config_key::controlledJunk3)); - json.insert(amnezia::config_key::specialHandshakeTimeout, wgConfig.value(amnezia::config_key::specialHandshakeTimeout)); } write(json); diff --git a/client/platforms/ios/WGConfig.swift b/client/platforms/ios/WGConfig.swift index 537687f1..e3b67efe 100644 --- a/client/platforms/ios/WGConfig.swift +++ b/client/platforms/ios/WGConfig.swift @@ -4,10 +4,7 @@ struct WGConfig: Decodable { let initPacketMagicHeader, responsePacketMagicHeader: String? let underloadPacketMagicHeader, transportPacketMagicHeader: String? let junkPacketCount, junkPacketMinSize, junkPacketMaxSize: String? - let initPacketJunkSize, responsePacketJunkSize, cookieReplyPacketJunkSize, transportPacketJunkSize: String? - let specialJunk1, specialJunk2, specialJunk3, specialJunk4, specialJunk5: String? - let controlledJunk1, controlledJunk2, controlledJunk3: String? - let specialHandshakeTimeout: String? + let initPacketJunkSize, responsePacketJunkSize: String? let dns1: String let dns2: String let mtu: String @@ -26,10 +23,7 @@ struct WGConfig: Decodable { case initPacketMagicHeader = "H1", responsePacketMagicHeader = "H2" case underloadPacketMagicHeader = "H3", transportPacketMagicHeader = "H4" case junkPacketCount = "Jc", junkPacketMinSize = "Jmin", junkPacketMaxSize = "Jmax" - case initPacketJunkSize = "S1", responsePacketJunkSize = "S2", cookieReplyPacketJunkSize = "S3", transportPacketJunkSize = "S4" - case specialJunk1 = "I1", specialJunk2 = "I2", specialJunk3 = "I3", specialJunk4 = "I4", specialJunk5 = "I5" - case controlledJunk1 = "J1", controlledJunk2 = "J2", controlledJunk3 = "J3" - case specialHandshakeTimeout = "Itime" + case initPacketJunkSize = "S1", responsePacketJunkSize = "S2" case dns1 case dns2 case mtu @@ -46,59 +40,19 @@ struct WGConfig: Decodable { } var settings: String { - guard junkPacketCount != nil else { return "" } - - var settingsLines: [String] = [] - - // Required parameters when junkPacketCount is present - settingsLines.append("Jc = \(junkPacketCount!)") - settingsLines.append("Jmin = \(junkPacketMinSize!)") - settingsLines.append("Jmax = \(junkPacketMaxSize!)") - settingsLines.append("S1 = \(initPacketJunkSize!)") - settingsLines.append("S2 = \(responsePacketJunkSize!)") - - settingsLines.append("H1 = \(initPacketMagicHeader!)") - settingsLines.append("H2 = \(responsePacketMagicHeader!)") - settingsLines.append("H3 = \(underloadPacketMagicHeader!)") - settingsLines.append("H4 = \(transportPacketMagicHeader!)") + junkPacketCount == nil ? "" : + """ + Jc = \(junkPacketCount!) + Jmin = \(junkPacketMinSize!) + Jmax = \(junkPacketMaxSize!) + S1 = \(initPacketJunkSize!) + S2 = \(responsePacketJunkSize!) + H1 = \(initPacketMagicHeader!) + H2 = \(responsePacketMagicHeader!) + H3 = \(underloadPacketMagicHeader!) + H4 = \(transportPacketMagicHeader!) - // Optional parameters - only add if not nil and not empty - if let s3 = cookieReplyPacketJunkSize, !s3.isEmpty { - settingsLines.append("S3 = \(s3)") - } - if let s4 = transportPacketJunkSize, !s4.isEmpty { - settingsLines.append("S4 = \(s4)") - } - - if let i1 = specialJunk1, !i1.isEmpty { - settingsLines.append("I1 = \(i1)") - } - if let i2 = specialJunk2, !i2.isEmpty { - settingsLines.append("I2 = \(i2)") - } - if let i3 = specialJunk3, !i3.isEmpty { - settingsLines.append("I3 = \(i3)") - } - if let i4 = specialJunk4, !i4.isEmpty { - settingsLines.append("I4 = \(i4)") - } - if let i5 = specialJunk5, !i5.isEmpty { - settingsLines.append("I5 = \(i5)") - } - if let j1 = controlledJunk1, !j1.isEmpty { - settingsLines.append("J1 = \(j1)") - } - if let j2 = controlledJunk2, !j2.isEmpty { - settingsLines.append("J2 = \(j2)") - } - if let j3 = controlledJunk3, !j3.isEmpty { - settingsLines.append("J3 = \(j3)") - } - if let itime = specialHandshakeTimeout, !itime.isEmpty { - settingsLines.append("Itime = \(itime)") - } - - return settingsLines.joined(separator: "\n") + """ } var str: String { diff --git a/client/platforms/ios/ios_controller.mm b/client/platforms/ios/ios_controller.mm index e64c6dce..85fb50b7 100644 --- a/client/platforms/ios/ios_controller.mm +++ b/client/platforms/ios/ios_controller.mm @@ -507,8 +507,6 @@ bool IosController::setupWireGuard() wgConfig.insert(config_key::initPacketJunkSize, config[config_key::initPacketJunkSize]); wgConfig.insert(config_key::responsePacketJunkSize, config[config_key::responsePacketJunkSize]); - wgConfig.insert(config_key::cookieReplyPacketJunkSize, config[config_key::cookieReplyPacketJunkSize]); - wgConfig.insert(config_key::transportPacketJunkSize, config[config_key::transportPacketJunkSize]); wgConfig.insert(config_key::junkPacketCount, config[config_key::junkPacketCount]); wgConfig.insert(config_key::junkPacketMinSize, config[config_key::junkPacketMinSize]); @@ -607,23 +605,11 @@ bool IosController::setupAwg() wgConfig.insert(config_key::initPacketJunkSize, config[config_key::initPacketJunkSize]); wgConfig.insert(config_key::responsePacketJunkSize, config[config_key::responsePacketJunkSize]); - wgConfig.insert(config_key::cookieReplyPacketJunkSize, config[config_key::cookieReplyPacketJunkSize]); - wgConfig.insert(config_key::transportPacketJunkSize, config[config_key::transportPacketJunkSize]); 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]); - wgConfig.insert(config_key::specialJunk1, config[config_key::specialJunk1]); - wgConfig.insert(config_key::specialJunk2, config[config_key::specialJunk2]); - wgConfig.insert(config_key::specialJunk3, config[config_key::specialJunk3]); - wgConfig.insert(config_key::specialJunk4, config[config_key::specialJunk4]); - wgConfig.insert(config_key::specialJunk5, config[config_key::specialJunk5]); - wgConfig.insert(config_key::controlledJunk1, config[config_key::controlledJunk1]); - wgConfig.insert(config_key::controlledJunk2, config[config_key::controlledJunk2]); - wgConfig.insert(config_key::controlledJunk3, config[config_key::controlledJunk3]); - wgConfig.insert(config_key::specialHandshakeTimeout, config[config_key::specialHandshakeTimeout]); - QJsonDocument wgConfigDoc(wgConfig); QString wgConfigDocStr(wgConfigDoc.toJson(QJsonDocument::Compact)); @@ -808,9 +794,9 @@ bool IosController::shareText(const QStringList& filesToSend) { 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(); @@ -822,11 +808,11 @@ bool IosController::shareText(const QStringList& filesToSend) { 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; } @@ -840,7 +826,7 @@ QString IosController::openFile() { if (!qtController) return; [qtController presentViewController:documentPicker animated:YES completion:nil]; - + __block QString filePath; documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) { @@ -855,7 +841,7 @@ QString IosController::openFile() { QEventLoop wait; QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit); wait.exec(); - + return filePath; } diff --git a/client/platforms/linux/daemon/iputilslinux.cpp b/client/platforms/linux/daemon/iputilslinux.cpp index 25d4f631..63bd92f9 100644 --- a/client/platforms/linux/daemon/iputilslinux.cpp +++ b/client/platforms/linux/daemon/iputilslinux.cpp @@ -97,7 +97,7 @@ bool IPUtilsLinux::addIP4AddressToDevice(const InterfaceConfig& config) { // Set ifr to interface int ret = ioctl(sockfd, SIOCSIFADDR, &ifr); if (ret) { - logger.error() << "Failed to set IPv4: " << deviceAddr + logger.error() << "Failed to set IPv4: " << logger.sensitive(deviceAddr) << "error:" << strerror(errno); return false; } @@ -138,7 +138,7 @@ bool IPUtilsLinux::addIP6AddressToDevice(const InterfaceConfig& config) { // Set ifr6 to the interface ret = ioctl(sockfd, SIOCSIFADDR, &ifr6); if (ret && (errno != EEXIST)) { - logger.error() << "Failed to set IPv6: " << deviceAddr + logger.error() << "Failed to set IPv6: " << logger.sensitive(deviceAddr) << "error:" << strerror(errno); return false; } diff --git a/client/platforms/linux/daemon/wireguardutilslinux.cpp b/client/platforms/linux/daemon/wireguardutilslinux.cpp index cfde73e2..0fbb65a8 100644 --- a/client/platforms/linux/daemon/wireguardutilslinux.cpp +++ b/client/platforms/linux/daemon/wireguardutilslinux.cpp @@ -121,12 +121,6 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) { if (!config.m_responsePacketJunkSize.isEmpty()) { out << "s2=" << config.m_responsePacketJunkSize << "\n"; } - if (!config.m_cookieReplyPacketJunkSize.isEmpty()) { - out << "s3=" << config.m_cookieReplyPacketJunkSize << "\n"; - } - if (!config.m_transportPacketJunkSize.isEmpty()) { - out << "s4=" << config.m_transportPacketJunkSize << "\n"; - } if (!config.m_initPacketMagicHeader.isEmpty()) { out << "h1=" << config.m_initPacketMagicHeader << "\n"; } @@ -140,26 +134,13 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) { out << "h4=" << config.m_transportPacketMagicHeader << "\n"; } - for (const QString& key : config.m_specialJunk.keys()) { - out << key.toLower() << "=" << config.m_specialJunk.value(key) << "\n"; - } - for (const QString& key : config.m_controlledJunk.keys()) { - out << key.toLower() << "=" << config.m_controlledJunk.value(key) << "\n"; - } - if (!config.m_specialHandshakeTimeout.isEmpty()) { - out << "itime=" << config.m_specialHandshakeTimeout << "\n"; - } - int err = uapiErrno(uapiCommand(message)); if (err != 0) { logger.error() << "Interface configuration failed:" << strerror(err); } else { if (config.m_killSwitchEnabled) { FirewallParams params { }; - params.dnsServers.append(config.m_primaryDnsServer); - if (!config.m_secondaryDnsServer.isEmpty()) { - params.dnsServers.append(config.m_secondaryDnsServer); - } + params.dnsServers.append(config.m_dnsServer); if (config.m_allowedIPAddressRanges.contains(IPAddress("0.0.0.0/0"))) { params.blockAll = true; if (config.m_excludedAddresses.size()) { diff --git a/client/platforms/macos/daemon/iputilsmacos.cpp b/client/platforms/macos/daemon/iputilsmacos.cpp index 901436ae..0599e2eb 100644 --- a/client/platforms/macos/daemon/iputilsmacos.cpp +++ b/client/platforms/macos/daemon/iputilsmacos.cpp @@ -122,7 +122,7 @@ bool IPUtilsMacos::addIP4AddressToDevice(const InterfaceConfig& config) { // Set ifr to interface int ret = ioctl(sockfd, SIOCAIFADDR, &ifr); if (ret) { - logger.error() << "Failed to set IPv4: " << deviceAddr + logger.error() << "Failed to set IPv4: " << logger.sensitive(deviceAddr) << "error:" << strerror(errno); return false; } @@ -162,7 +162,7 @@ bool IPUtilsMacos::addIP6AddressToDevice(const InterfaceConfig& config) { // Set ifr to interface int ret = ioctl(sockfd, SIOCAIFADDR_IN6, &ifr6); if (ret) { - logger.error() << "Failed to set IPv6: " << deviceAddr + logger.error() << "Failed to set IPv6: " << logger.sensitive(deviceAddr) << "error:" << strerror(errno); return false; } diff --git a/client/platforms/macos/daemon/macosfirewall.cpp b/client/platforms/macos/daemon/macosfirewall.cpp index 5211c440..0fe51f23 100644 --- a/client/platforms/macos/daemon/macosfirewall.cpp +++ b/client/platforms/macos/daemon/macosfirewall.cpp @@ -43,16 +43,8 @@ namespace { #include "macosfirewall.h" -#include -#include - -// Read-only rules bundled with the application. -#define ResourceDir (qApp->applicationDirPath() + "/pf") - -// Writable location that does NOT live inside the signed bundle. Using a -// constant path under /Library/Application Support keeps the signature intact -// and is accessible to the root helper. -#define DaemonDataDir QStringLiteral("/Library/Application Support/AmneziaVPN/pf") +#define ResourceDir qApp->applicationDirPath() + "/pf" +#define DaemonDataDir qApp->applicationDirPath() + "/pf" #include @@ -129,8 +121,6 @@ void MacOSFirewall::install() logger.info() << "Installing PF root anchor"; installRootAnchors(); - // Ensure writable directory exists, then store the token there. - QDir().mkpath(DaemonDataDir); execute(QStringLiteral("pfctl -E 2>&1 | grep -F 'Token : ' | cut -c9- > '%1/pf.token'").arg(DaemonDataDir)); } diff --git a/client/platforms/macos/daemon/macosroutemonitor.cpp b/client/platforms/macos/daemon/macosroutemonitor.cpp index 062f97f3..bd991c01 100644 --- a/client/platforms/macos/daemon/macosroutemonitor.cpp +++ b/client/platforms/macos/daemon/macosroutemonitor.cpp @@ -144,7 +144,7 @@ void MacosRouteMonitor::handleRtmDelete(const struct rt_msghdr* rtm, for (const IPAddress& prefix : m_exclusionRoutes) { if (prefix.address().protocol() == protocol) { logger.debug() << "Removing exclusion route to" - << prefix.toString(); + << logger.sensitive(prefix.toString()); rtmSendRoute(RTM_DELETE, prefix, rtm->rtm_index, nullptr); } } @@ -259,7 +259,7 @@ void MacosRouteMonitor::handleRtmUpdate(const struct rt_msghdr* rtm, for (const IPAddress& prefix : m_exclusionRoutes) { if (prefix.address().protocol() == protocol) { logger.debug() << "Updating exclusion route to" - << prefix.toString(); + << logger.sensitive(prefix.toString()); rtmSendRoute(rtm_type, prefix, ifindex, addrlist[1].constData()); } } @@ -510,7 +510,8 @@ bool MacosRouteMonitor::deleteRoute(const IPAddress& prefix, int flags) { } bool MacosRouteMonitor::addExclusionRoute(const IPAddress& prefix) { - logger.debug() << "Adding exclusion route for" << prefix.toString(); + logger.debug() << "Adding exclusion route for" + << logger.sensitive(prefix.toString()); if (m_exclusionRoutes.contains(prefix)) { logger.warning() << "Exclusion route already exists"; @@ -535,7 +536,8 @@ bool MacosRouteMonitor::addExclusionRoute(const IPAddress& prefix) { } bool MacosRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) { - logger.debug() << "Deleting exclusion route for" << prefix.toString(); + logger.debug() << "Deleting exclusion route for" + << logger.sensitive(prefix.toString()); m_exclusionRoutes.removeAll(prefix); if (prefix.address().protocol() == QAbstractSocket::IPv4Protocol) { diff --git a/client/platforms/macos/daemon/wireguardutilsmacos.cpp b/client/platforms/macos/daemon/wireguardutilsmacos.cpp index cce4afab..1d8aa6e0 100644 --- a/client/platforms/macos/daemon/wireguardutilsmacos.cpp +++ b/client/platforms/macos/daemon/wireguardutilsmacos.cpp @@ -119,12 +119,6 @@ bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) { if (!config.m_responsePacketJunkSize.isEmpty()) { out << "s2=" << config.m_responsePacketJunkSize << "\n"; } - if (!config.m_cookieReplyPacketJunkSize.isEmpty()) { - out << "s3=" << config.m_cookieReplyPacketJunkSize << "\n"; - } - if (!config.m_transportPacketJunkSize.isEmpty()) { - out << "s4=" << config.m_transportPacketJunkSize << "\n"; - } if (!config.m_initPacketMagicHeader.isEmpty()) { out << "h1=" << config.m_initPacketMagicHeader << "\n"; } @@ -138,43 +132,30 @@ bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) { out << "h4=" << config.m_transportPacketMagicHeader << "\n"; } - for (const QString& key : config.m_specialJunk.keys()) { - out << key.toLower() << "=" << config.m_specialJunk.value(key) << "\n"; - } - for (const QString& key : config.m_controlledJunk.keys()) { - out << key.toLower() << "=" << config.m_controlledJunk.value(key) << "\n"; - } - if (!config.m_specialHandshakeTimeout.isEmpty()) { - out << "itime=" << config.m_specialHandshakeTimeout << "\n"; - } - int err = uapiErrno(uapiCommand(message)); if (err != 0) { logger.error() << "Interface configuration failed:" << strerror(err); } else { - if (config.m_killSwitchEnabled) { - FirewallParams params { }; - params.dnsServers.append(config.m_primaryDnsServer); - if (!config.m_secondaryDnsServer.isEmpty()) { - params.dnsServers.append(config.m_secondaryDnsServer); - } + if (config.m_killSwitchEnabled) { + FirewallParams params { }; + params.dnsServers.append(config.m_dnsServer); - if (config.m_allowedIPAddressRanges.contains(IPAddress("0.0.0.0/0"))) { + if (config.m_allowedIPAddressRanges.contains(IPAddress("0.0.0.0/0"))) { params.blockAll = true; if (config.m_excludedAddresses.size()) { - params.allowNets = true; - foreach (auto net, config.m_excludedAddresses) { - params.allowAddrs.append(net.toUtf8()); - } + params.allowNets = true; + foreach (auto net, config.m_excludedAddresses) { + params.allowAddrs.append(net.toUtf8()); + } } - } else { + } else { params.blockNets = true; foreach (auto net, config.m_allowedIPAddressRanges) { - params.blockAddrs.append(net.toString()); + params.blockAddrs.append(net.toString()); } + } + applyFirewallRules(params); } - applyFirewallRules(params); - } } return (err == 0); } diff --git a/client/platforms/windows/daemon/windowsfirewall.cpp b/client/platforms/windows/daemon/windowsfirewall.cpp index 2556c417..85a2a155 100644 --- a/client/platforms/windows/daemon/windowsfirewall.cpp +++ b/client/platforms/windows/daemon/windowsfirewall.cpp @@ -256,7 +256,7 @@ bool WindowsFirewall::allowTrafficRange(const QStringList& ranges) { for (const QString& addr : ranges) { logger.debug() << "Allow killswitch exclude: " << addr; - if (!allowTrafficTo(QHostAddress(addr), HIGH_WEIGHT, "Allow killswitch bypass traffic")) { + if (!allowTrafficTo(QHostAddress(addr), LOW_WEIGHT + 1, "Allow killswitch bypass traffic")) { return false; } } @@ -291,32 +291,15 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) { "Block Internet", config.m_serverPublicKey)) { return false; } - if (!config.m_primaryDnsServer.isEmpty()) { - if (!allowTrafficTo(QHostAddress(config.m_primaryDnsServer), 53, HIGH_WEIGHT, + if (!config.m_dnsServer.isEmpty()) { + if (!allowTrafficTo(QHostAddress(config.m_dnsServer), 53, HIGH_WEIGHT, "Allow DNS-Server", config.m_serverPublicKey)) { return false; } // In some cases, we might configure a 2nd DNS server for IPv6, however // this should probably be cleaned up by converting m_dnsServer into // a QStringList instead. - if (config.m_primaryDnsServer == config.m_serverIpv4Gateway) { - if (!allowTrafficTo(QHostAddress(config.m_serverIpv6Gateway), 53, - HIGH_WEIGHT, "Allow extra IPv6 DNS-Server", - config.m_serverPublicKey)) { - return false; - } - } - } - - if (!config.m_secondaryDnsServer.isEmpty()) { - if (!allowTrafficTo(QHostAddress(config.m_secondaryDnsServer), 53, HIGH_WEIGHT, - "Allow DNS-Server", config.m_serverPublicKey)) { - return false; - } - // In some cases, we might configure a 2nd DNS server for IPv6, however - // this should probably be cleaned up by converting m_dnsServer into - // a QStringList instead. - if (config.m_secondaryDnsServer == config.m_serverIpv4Gateway) { + if (config.m_dnsServer == config.m_serverIpv4Gateway) { if (!allowTrafficTo(QHostAddress(config.m_serverIpv6Gateway), 53, HIGH_WEIGHT, "Allow extra IPv6 DNS-Server", config.m_serverPublicKey)) { diff --git a/client/platforms/windows/daemon/windowsroutemonitor.cpp b/client/platforms/windows/daemon/windowsroutemonitor.cpp index 1d0ce4c2..fb0fbf7e 100644 --- a/client/platforms/windows/daemon/windowsroutemonitor.cpp +++ b/client/platforms/windows/daemon/windowsroutemonitor.cpp @@ -303,7 +303,8 @@ void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) { data->Age++; continue; } - logger.debug() << "Capturing route to" << prefix.toString(); + logger.debug() << "Capturing route to" + << logger.sensitive(prefix.toString()); // Clone the route and direct it into the VPN tunnel. data = new MIB_IPFORWARD_ROW2; @@ -353,7 +354,8 @@ void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) { continue; } - logger.debug() << "Removing route capture for" << i.key().toString(); + logger.debug() << "Removing route capture for" + << logger.sensitive(i.key().toString()); // Otherwise, this route is no longer in use. DWORD result = DeleteIpForwardEntry2(data); @@ -366,7 +368,8 @@ void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) { } bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) { - logger.debug() << "Adding exclusion route for" << prefix.toString(); + logger.debug() << "Adding exclusion route for" + << logger.sensitive(prefix.toString()); // Silently ignore non-routeable addresses. QHostAddress addr = prefix.address(); @@ -434,7 +437,7 @@ bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) { bool WindowsRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) { logger.debug() << "Deleting exclusion route for" - << prefix.address().toString(); + << logger.sensitive(prefix.address().toString()); MIB_IPFORWARD_ROW2* data = m_exclusionRoutes.take(prefix); if (data == nullptr) { @@ -444,7 +447,7 @@ bool WindowsRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) { DWORD result = DeleteIpForwardEntry2(data); if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) { logger.error() << "Failed to delete route to" - << prefix.toString() + << logger.sensitive(prefix.toString()) << "result:" << result; } @@ -462,7 +465,7 @@ void WindowsRouteMonitor::flushRouteTable( DWORD result = DeleteIpForwardEntry2(data); if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) { logger.error() << "Failed to delete route to" - << i.key().toString() + << logger.sensitive(i.key().toString()) << "result:" << result; } delete data; diff --git a/client/platforms/windows/daemon/wireguardutilswindows.cpp b/client/platforms/windows/daemon/wireguardutilswindows.cpp index a5c9c84d..d01ef54a 100644 --- a/client/platforms/windows/daemon/wireguardutilswindows.cpp +++ b/client/platforms/windows/daemon/wireguardutilswindows.cpp @@ -130,7 +130,6 @@ bool WireguardUtilsWindows::addInterface(const InterfaceConfig& config) { // Enable the windows firewall NET_IFINDEX ifindex; ConvertInterfaceLuidToIndex(&luid, &ifindex); - m_firewall->allowAllTraffic(); m_firewall->enableInterface(ifindex); } diff --git a/client/protocols/openvpnprotocol.cpp b/client/protocols/openvpnprotocol.cpp index 0bbdbd07..0c8f5907 100644 --- a/client/protocols/openvpnprotocol.cpp +++ b/client/protocols/openvpnprotocol.cpp @@ -171,7 +171,7 @@ ErrorCode OpenVpnProtocol::start() return lastError(); } -#ifdef AMNEZIA_DESKTOP +#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) IpcClient::Interface()->addKillSwitchAllowedRange(QStringList(NetworkUtilities::getIPAddress( m_configData.value(amnezia::config_key::hostName).toString()))); #endif @@ -343,7 +343,7 @@ void OpenVpnProtocol::updateVpnGateway(const QString &line) // killSwitch toggle if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) { if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) { - IpcClient::Interface()->enableKillSwitch(m_configData, netInterfaces.at(i).index()); + IpcClient::Interface()->enableKillSwitch(QJsonObject(), netInterfaces.at(i).index()); } m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index()); m_configData.insert("vpnGateway", m_vpnGateway); diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index b4cbb6de..feeabb2f 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -72,21 +72,10 @@ namespace amnezia constexpr char junkPacketMaxSize[] = "Jmax"; constexpr char initPacketJunkSize[] = "S1"; constexpr char responsePacketJunkSize[] = "S2"; - constexpr char cookieReplyPacketJunkSize[] = "S3"; - constexpr char transportPacketJunkSize[] = "S4"; constexpr char initPacketMagicHeader[] = "H1"; constexpr char responsePacketMagicHeader[] = "H2"; constexpr char underloadPacketMagicHeader[] = "H3"; constexpr char transportPacketMagicHeader[] = "H4"; - constexpr char specialJunk1[] = "I1"; - constexpr char specialJunk2[] = "I2"; - constexpr char specialJunk3[] = "I3"; - constexpr char specialJunk4[] = "I4"; - constexpr char specialJunk5[] = "I5"; - constexpr char controlledJunk1[] = "J1"; - constexpr char controlledJunk2[] = "J2"; - constexpr char controlledJunk3[] = "J3"; - constexpr char specialHandshakeTimeout[] = "Itime"; constexpr char openvpn[] = "openvpn"; constexpr char wireguard[] = "wireguard"; @@ -114,8 +103,6 @@ namespace amnezia constexpr char clientId[] = "clientId"; - constexpr char nameOverriddenByUser[] = "nameOverriddenByUser"; - } namespace protocols @@ -227,22 +214,10 @@ namespace amnezia constexpr char defaultJunkPacketMaxSize[] = "30"; constexpr char defaultInitPacketJunkSize[] = "15"; constexpr char defaultResponsePacketJunkSize[] = "18"; - constexpr char defaultCookieReplyPacketJunkSize[] = "20"; - constexpr char defaultTransportPacketJunkSize[] = "23"; - constexpr char defaultInitPacketMagicHeader[] = "1020325451"; constexpr char defaultResponsePacketMagicHeader[] = "3288052141"; constexpr char defaultTransportPacketMagicHeader[] = "2528465083"; constexpr char defaultUnderloadPacketMagicHeader[] = "1766607858"; - constexpr char defaultSpecialJunk1[] = ""; - constexpr char defaultSpecialJunk2[] = ""; - constexpr char defaultSpecialJunk3[] = ""; - constexpr char defaultSpecialJunk4[] = ""; - constexpr char defaultSpecialJunk5[] = ""; - constexpr char defaultControlledJunk1[] = ""; - constexpr char defaultControlledJunk2[] = ""; - constexpr char defaultControlledJunk3[] = ""; - constexpr char defaultSpecialHandshakeTimeout[] = ""; } namespace socks5Proxy diff --git a/client/protocols/xrayprotocol.cpp b/client/protocols/xrayprotocol.cpp index 84922634..faad8e94 100755 --- a/client/protocols/xrayprotocol.cpp +++ b/client/protocols/xrayprotocol.cpp @@ -98,13 +98,8 @@ ErrorCode XrayProtocol::startTun2Sock() if (vpnState == Vpn::ConnectionState::Connected) { setConnectionState(Vpn::ConnectionState::Connecting); QList dnsAddr; - dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns1).toString())); - // We don't use secondary DNS if primary DNS is AmneziaDNS - if (!m_configData.value(amnezia::config_key::dns1).toString(). - contains(amnezia::protocols::dns::amneziaDnsIp)) { - dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns2).toString())); - } + dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns2).toString())); #ifdef Q_OS_WIN QThread::msleep(8000); #endif @@ -139,7 +134,7 @@ ErrorCode XrayProtocol::startTun2Sock() // killSwitch toggle if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) { if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) { - IpcClient::Interface()->enableKillSwitch(m_configData, netInterfaces.at(i).index()); + IpcClient::Interface()->enableKillSwitch(QJsonObject(), netInterfaces.at(i).index()); } m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index()); m_configData.insert("vpnGateway", m_vpnGateway); diff --git a/client/resources.qrc b/client/resources.qrc index 54b5846c..a36b60d1 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -236,10 +236,6 @@ ui/qml/Pages2/PageSettingsApiNativeConfigs.qml ui/qml/Pages2/PageSettingsApiDevices.qml images/controls/monitor.svg - ui/qml/Components/ApiPremV1MigrationDrawer.qml - ui/qml/Components/ApiPremV1SubListDrawer.qml - ui/qml/Components/OtpCodeDrawer.qml - ui/qml/Components/AwgTextField.qml images/flagKit/ZW.svg diff --git a/client/server_scripts/awg/Dockerfile b/client/server_scripts/awg/Dockerfile index a6118a84..8c536fc7 100644 --- a/client/server_scripts/awg/Dockerfile +++ b/client/server_scripts/awg/Dockerfile @@ -10,7 +10,7 @@ RUN mkdir -p /opt/amnezia RUN echo -e "#!/bin/bash\ntail -f /dev/null" > /opt/amnezia/start.sh RUN chmod a+x /opt/amnezia/start.sh -# Tune network +# Tune network RUN echo -e " \n\ fs.file-max = 51200 \n\ \n\ @@ -40,8 +40,7 @@ RUN echo -e " \n\ echo -e " \n\ * soft nofile 51200 \n\ * hard nofile 51200 \n\ - " | sed -e 's/^\s\+//g' | tee -a /etc/security/limits.conf + " | sed -e 's/^\s\+//g' | tee -a /etc/security/limits.conf ENTRYPOINT [ "dumb-init", "/opt/amnezia/start.sh" ] CMD [ "" ] - diff --git a/client/server_scripts/awg/configure_container.sh b/client/server_scripts/awg/configure_container.sh index e327f080..2000c965 100644 --- a/client/server_scripts/awg/configure_container.sh +++ b/client/server_scripts/awg/configure_container.sh @@ -23,5 +23,4 @@ H1 = $INIT_PACKET_MAGIC_HEADER H2 = $RESPONSE_PACKET_MAGIC_HEADER H3 = $UNDERLOAD_PACKET_MAGIC_HEADER H4 = $TRANSPORT_PACKET_MAGIC_HEADER - EOF diff --git a/client/server_scripts/check_server_is_busy.sh b/client/server_scripts/check_server_is_busy.sh index feddfed3..4e6a2c26 100644 --- a/client/server_scripts/check_server_is_busy.sh +++ b/client/server_scripts/check_server_is_busy.sh @@ -1,7 +1,6 @@ -if which apt-get > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/lib/dpkg/lock-frontend";\ -elif which dnf > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/cache/dnf/* /var/run/dnf/* /var/lib/dnf/* /var/lib/rpm/*";\ -elif which yum > /dev/null 2>&1; then LOCK_CMD="cat"; LOCK_FILE="/var/run/yum.pid";\ -elif which zypper > /dev/null 2>&1; then LOCK_CMD="cat"; LOCK_FILE="/var/run/zypp.pid";\ -elif which pacman > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/lib/pacman/db.lck";\ +if which apt-get > /dev/null 2>&1; then LOCK_FILE="/var/lib/dpkg/lock-frontend";\ +elif which dnf > /dev/null 2>&1; then LOCK_FILE="/var/run/dnf.pid";\ +elif which yum > /dev/null 2>&1; then LOCK_FILE="/var/run/yum.pid";\ +elif which pacman > /dev/null 2>&1; then LOCK_FILE="/var/lib/pacman/db.lck";\ else echo "Packet manager not found"; echo "Internal error"; exit 1; fi;\ -if command -v $LOCK_CMD > /dev/null 2>&1; then sudo $LOCK_CMD $LOCK_FILE 2>/dev/null; else echo "$LOCK_CMD not installed"; fi +if command -v fuser > /dev/null 2>&1; then sudo fuser $LOCK_FILE 2>/dev/null; else echo "fuser not installed"; fi diff --git a/client/server_scripts/check_user_in_sudo.sh b/client/server_scripts/check_user_in_sudo.sh index f83f2fd7..685e6a18 100644 --- a/client/server_scripts/check_user_in_sudo.sh +++ b/client/server_scripts/check_user_in_sudo.sh @@ -1,7 +1,6 @@ if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); opt="--version";\ elif which dnf > /dev/null 2>&1; then pm=$(which dnf); opt="--version";\ elif which yum > /dev/null 2>&1; then pm=$(which yum); opt="--version";\ -elif which zypper > /dev/null 2>&1; then pm=$(which zypper); opt="--version";\ elif which pacman > /dev/null 2>&1; then pm=$(which pacman); opt="--version";\ else pm="uname"; opt="-a";\ fi;\ diff --git a/client/server_scripts/install_docker.sh b/client/server_scripts/install_docker.sh index 1e41bb5a..619b08d6 100644 --- a/client/server_scripts/install_docker.sh +++ b/client/server_scripts/install_docker.sh @@ -1,7 +1,6 @@ if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); silent_inst="-yq install"; check_pkgs="-yq update"; docker_pkg="docker.io"; dist="debian";\ elif which dnf > /dev/null 2>&1; then pm=$(which dnf); silent_inst="-yq install"; check_pkgs="-yq check-update"; docker_pkg="docker"; dist="fedora";\ elif which yum > /dev/null 2>&1; then pm=$(which yum); silent_inst="-y -q install"; check_pkgs="-y -q check-update"; docker_pkg="docker"; dist="centos";\ -elif which zypper > /dev/null 2>&1; then pm=$(which zypper); silent_inst="-nq install"; check_pkgs="-nq refresh"; docker_pkg="docker"; dist="opensuse";\ elif which pacman > /dev/null 2>&1; then pm=$(which pacman); silent_inst="-S --noconfirm --noprogressbar --quiet"; check_pkgs="-Sup"; docker_pkg="docker"; dist="archlinux";\ else echo "Packet manager not found"; exit 1; fi;\ echo "Dist: $dist, Packet manager: $pm, Install command: $silent_inst, Check pkgs command: $check_pkgs, Docker pkg: $docker_pkg";\ diff --git a/client/settings.cpp b/client/settings.cpp index fb9c72c1..9a0a32e5 100644 --- a/client/settings.cpp +++ b/client/settings.cpp @@ -559,16 +559,6 @@ void Settings::disableHomeAdLabel() setValue("Conf/homeAdLabelVisible", false); } -bool Settings::isPremV1MigrationReminderActive() -{ - return value("Conf/premV1MigrationReminderActive", true).toBool(); -} - -void Settings::disablePremV1MigrationReminder() -{ - setValue("Conf/premV1MigrationReminderActive", false); -} - QStringList Settings::allowedDnsServers() const { return value("Conf/allowedDnsServers").toStringList(); diff --git a/client/settings.h b/client/settings.h index 7c244cd4..01155c0c 100644 --- a/client/settings.h +++ b/client/settings.h @@ -174,12 +174,11 @@ public: QLocale getAppLanguage() { - QString localeStr = m_settings.value("Conf/appLanguage").toString(); - return QLocale(localeStr); + return value("Conf/appLanguage", QLocale()).toLocale(); }; void setAppLanguage(QLocale locale) { - setValue("Conf/appLanguage", locale.name()); + setValue("Conf/appLanguage", locale); }; bool isScreenshotsEnabled() const @@ -230,9 +229,6 @@ public: bool isHomeAdLabelVisible(); void disableHomeAdLabel(); - bool isPremV1MigrationReminderActive(); - void disablePremV1MigrationReminder(); - QStringList allowedDnsServers() const; void setAllowedDnsServers(const QStringList &servers); diff --git a/client/translations/amneziavpn_ru_RU.ts b/client/translations/amneziavpn_ru_RU.ts index 1d8766ac..c740a560 100644 --- a/client/translations/amneziavpn_ru_RU.ts +++ b/client/translations/amneziavpn_ru_RU.ts @@ -3,60 +3,16 @@ AdLabel + + Amnezia Premium - for access to any website + Amnezia Premium - для доступа к любым сайтам + Amnezia Premium - for access to all websites and online resources Amnezia Premium - доступ ко всем сайтам и онлайн ресурсам - - AllowedDnsController - - - The address does not look like a valid IP address - Адрес не похож на корректный IP-адрес - - - - New DNS server added: %1 - Добавлен новый DNS сервер: %1 - - - - DNS server already exists: %1 - DNS сервер уже существует: %1 - - - - DNS server removed: %1 - DNS сервер удален: %1 - - - - Can't open file: %1 - Невозможно открыть файл: %1 - - - - Failed to parse JSON data from file: %1 - Не удалось разобрать JSON-данные из файла: %1 - - - - The JSON data is not an array in file: %1 - JSON-данные не являются массивом в файле: %1 - - - - Import completed - Импорт завершён - - - - Export completed - Экспорт завершён - - ApiAccountInfoModel @@ -81,143 +37,75 @@ Классический VPN для комфортной работы, загрузки больших файлов и просмотра видео. Доступ ко всем сайтам и онлайн-ресурсам. Скорость — до 200 Мбит/с - + Free unlimited access to a basic set of websites such as Facebook, Instagram, Twitter (X), Discord, Telegram and more. YouTube is not included in the free plan. - Бесплатный неограниченный доступ к базовому набору сайтов и приложений, таким как Facebook, Instagram, Twitter (X), Discord, Telegram и другим. YouTube не включён в бесплатный тариф. + Бесплатный неограниченный доступ к базовому набору сайтов и приложений, таким как Facebook, Instagram, Twitter (X), Discord, Telegram и другим. YouTube не включен в бесплатный тариф. + + + + amnezia_free_support_bot + + + + + amnezia_premium_support_bot + ApiConfigsController - + %1 installed successfully. %1 успешно установлен. - + API config reloaded Конфигурация API перезагружена - + Successfully changed the country of connection to %1 Страна подключения изменена на %1 - - ApiPremV1MigrationDrawer - - - Switch to the new Amnezia Premium subscription - Перейдите на новый тип подписки Amnezia Premium - - - - We'll preserve all remaining days of your current subscription and give you an extra month as a thank you. - Мы сохраним все оставшиеся дни текущей подписки и подарим дополнительный месяц в благодарность за переход. - - - - This new subscription type will be actively developed with more locations and features added regularly. Currently available: - Именно новый тип подписки будет активно развиваться и пополняться новыми локациями и функциями. Уже доступны: - - - - <li>13 locations (with more coming soon)</li> - <li>13 локаций (их число будет расти)</li> - - - - <li>Easier switching between countries in the app</li> - <li>Удобное переключение между странами в приложении</li> - - - - <li>Personal dashboard to manage your subscription</li> - <li>Личный кабинет для управления подпиской</li> - - - - Old keys will be deactivated after switching. - После перехода старые ключи перестанут работать. - - - - Email - Email - - - - mail@example.com - mail@example.com - - - - No old format subscriptions for a given email - Для указанного адреса электронной почты нет подписок старого типа - - - - Enter the email you used for your current subscription - Укажите адрес почты, который использовали при заказе текущей подписки - - - - - Continue - Продолжить - - - - Remind me later - Напомнить позже - - - - Don't remind me again - Больше не напоминать - - - - No more reminders? You can always switch to the new format in the server settings - Отключить напоминания? Вы всегда сможете перейти на новый тип подписки в настройках сервера - - - - Cancel - Отменить - - - - ApiPremV1SubListDrawer - - - Choose Subscription - Выбрать подписку - - - - Order ID: - ID заказа: - - - - Purchase Date: - Дата покупки: - - ApiServicesModel + + Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s + Классический VPN для комфортной работы, загрузки больших файлов и просмотра видео. Работает для любых сайтов. Скорость до %1 Мбит/с + + + VPN to access blocked sites in regions with high levels of Internet censorship. + VPN для доступа к заблокированным сайтам в регионах с высоким уровнем интернет-цензуры. + <p><a style="color: #EB5757;">Not available in your region. If you have VPN enabled, disable it, return to the previous screen, and try again.</a> <p><a style="color: #EB5757;">Недоступно в вашем регионе. Если у вас включен VPN, отключите его, вернитесь на предыдущий экран и попробуйте снова.</a> + + Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship. + Amnezia Premium — классический VPN для комфортной работы, загрузки больших файлов и просмотра видео в высоком разрешении. Работает на всех сайтах, даже в странах с самым высоким уровнем интернет-цензуры. + + + Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship + Amnezia Free - это бесплатный VPN для обхода блокировок в странах с высоким уровнем интернет-цензуры + + + Amnezia Premium is VPN for comfortable work, downloading large files and watching videos in 8K resolution. Works for any sites with no restrictions. Speed up to %1 MBit/s. Unlimited traffic. + Amnezia Premium — VPN для комфортной работы, скачивания больших файлов и просмотра видео в высоком разрешении. Скорость до %1 Мбит/с. Безлимитный трафик. + - Amnezia Free provides unlimited, free access to a basic set of websites and apps, including Facebook, Instagram, Twitter (X), Discord, Telegram, and more. YouTube is not included in the free plan. - Amnezia Free позволяет бесплатно и без ограничений пользоваться базовым набором сайтов и приложений, включая Facebook, Instagram, Twitter (X), Discord, Telegram и другие. YouTube не входит в бесплатный тариф. + AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan. + AmneziaFree предоставляет бесплатный неограниченный доступ к базовому набору сайтов и приложений, таким как Facebook, Instagram, Twitter (X), Discord, Telegram и другим. YouTube не включен в бесплатный тариф. + + + Amnezia Premium is VPN for comfortable work, downloading large files and watching videos in 8K resolution. Works for any sites with no restrictions. + Amnezia Premium — VPN для комфортной работы, скачивания больших файлов и просмотра видео в высоком разрешении. Работает для любых сайтов без ограничений. @@ -232,18 +120,13 @@ %1 MBit/s - %1 Мбит/с + %1 days %1 дней - - - - - VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, <a href="%1/free" style="color: #FBB26A;">more details on the website.</a> @@ -293,6 +176,12 @@ ConnectionController + + VPN Protocols is not installed. + Please install VPN container at first + VPN-протоколы не установлены. + Пожалуйста, установите протокол + Connecting... @@ -318,6 +207,14 @@ Settings updated successfully Настройки успешно обновлены + + The selected protocol is not supported on the current platform + Выбранный протокол не поддерживается на данном устройстве + + + unable to create configuration + не удалось создать конфигурацию + Reconnecting... @@ -358,7 +255,7 @@ ContextMenuType - + C&ut Вырезать @@ -368,23 +265,34 @@ Копировать - + &Paste Вставить - + &SelectAll Выбрать всё + + ExportController + + Access error! + Ошибка доступа! + + HomeContainersListView - + Unable change protocol while there is an active connection Невозможно изменить протокол во время активного соединения + + The selected protocol is not supported on the current platform + Выбранный протокол не поддерживается на данном устройстве + HomeSplitTunnelingDrawer @@ -435,6 +343,14 @@ Can't be disabled for current server ImportController + + Unable to open file + Невозможно открыть файл + + + Invalid configuration file + Неверный файл конфигурации + Scanned %1 of %2. @@ -450,6 +366,10 @@ Can't be disabled for current server <br>In the imported configuration, potentially dangerous lines were found: <br>В импортированной конфигурации обнаружены потенциально опасные строки: + + In the imported configuration, potentially dangerous lines were found: + В импортированной конфигурации были обнаружены потенциально опасные строки: + InstallController @@ -478,50 +398,62 @@ Already installed containers were found on the server. All installed containers На сервере обнаружены установленные протоколы и сервисы. Все они были добавлены в приложение - + Settings updated successfully Настройки успешно обновлены - + Server '%1' was rebooted Сервер '%1' был перезагружен - + Server '%1' was removed Сервер '%1' был удален - + All containers from server '%1' have been removed Все протоколы и сервисы были удалены с сервера '%1' - + %1 has been removed from the server '%2' %1 был удален с сервера '%2' - + Api config removed Конфигурация API удалена - + %1 cached profile cleared %1 закэшированный профиль очищен - + Please login as the user Пожалуйста, войдите в систему от имени пользователя - + Server added successfully Сервер успешно добавлен + + %1 installed successfully. + %1 успешно установлен. + + + API config reloaded + Конфигурация API перезагружена + + + Successfully changed the country of connection to %1 + Изменение страны подключения на %1 + InstalledAppsDrawer @@ -588,24 +520,6 @@ Already installed containers were found on the server. All installed containers Обнаружена незащищенная сеть: - - OtpCodeDrawer - - - OTP code was sent to your email - Одноразовый код был отправлен на ваш email - - - - OTP Code - Одноразовый код - - - - Continue - Продолжить - - PageDeinstalling @@ -635,47 +549,34 @@ Already installed containers were found on the server. All installed containers PageHome - - You've successfully switched to the new Amnezia Premium subscription! - Вы успешно перешли на новый тип подписки Amnezia Premium! - - - - Old keys will no longer work. Please use your new subscription key to connect. -Thank you for staying with us! - Старые ключи перестанут работать. Пожалуйста, используйте новый ключ для подключения. -Спасибо, что остаетесь с нами! - - - - Continue - Продолжить - - - + Logging enabled Логирование включено - + Split tunneling enabled Раздельное туннелирование включено - + Split tunneling disabled Раздельное туннелирование выключено - + VPN protocol VPN-протокол - + Servers Серверы + + Unable change server while there is an active connection + Невозможно изменить сервер во время активного соединения + PageProtocolAwgClientSettings @@ -742,6 +643,18 @@ Thank you for staying with us! Port Порт + + MTU + MTU + + + Remove AmneziaWG + Удалить AmneziaWG + + + Remove AmneziaWG from server? + Удалить AmneziaWG с сервера? + All users with whom you shared a connection with will no longer be able to connect to it. @@ -1037,6 +950,26 @@ Thank you for staying with us! Unable change settings while there is an active connection Невозможно изменить настройки во время активного соединения + + Remove OpenVPN + Удалить OpenVPN + + + Remove OpenVPN from server? + Удалить OpenVPN с сервера? + + + All users with whom you shared a connection with will no longer be able to connect to it. + Все пользователи, с которыми вы поделились конфигурацией вашего VPN, больше не смогут к нему подключаться. + + + Continue + Продолжить + + + Cancel + Отменить + Save @@ -1199,11 +1132,27 @@ Thank you for staying with us! All users with whom you shared a connection with will no longer be able to connect to it. Все пользователи, с которыми вы поделились конфигурацией вашего VPN, больше не смогут к нему подключаться. + + MTU + MTU + Unable change settings while there is an active connection Невозможно изменить настройки во время активного соединения + + Remove WG + Удалить WG + + + Remove WG from server? + Удалить WG с сервера? + + + All users with whom you shared a connection will no longer be able to connect to it. + Все пользователи, с которыми вы поделились конфигурацией вашего VPN, больше не смогут к нему подключаться. + Continue @@ -1233,17 +1182,12 @@ Thank you for staying with us! Замаскировать трафик под - - Port - Порт - - - + Save Сохранить - + Unable change settings while there is an active connection Невозможно изменить настройки во время активного соединения @@ -1362,6 +1306,22 @@ Thank you for staying with us! Detailed instructions Подробные инструкции + + Remove SFTP and all data stored there + Удалить SFTP-хранилище со всеми данными + + + Remove SFTP and all data stored there? + Удалить SFTP-хранилище и все хранящиеся там данные? + + + Continue + Продолжить + + + Cancel + Отменить + PageServiceSocksProxySettings @@ -1470,6 +1430,22 @@ Thank you for staying with us! When configuring WordPress set the this onion address as domain. При настройке WordPress укажите этот onion-адрес в качестве домена. + + Remove website + Удалить сайт + + + The site with all data will be removed from the tor network. + Сайт со всеми данными будет удален из сети Tor. + + + Continue + Продолжить + + + Cancel + Отменить + PageSettings @@ -1555,11 +1531,19 @@ Thank you for staying with us! support@amnezia.org support@amnezia.org + + Mail + Почта + For reviews and bug reports Для отзывов и сообщений об ошибках + + Copied + Скопировано + mailto:support@amnezia.org @@ -1590,6 +1574,10 @@ Thank you for staying with us! Visit official website Посетить официальный сайт + + https://amnezia.org + https://amnezia.org + Software version: %1 @@ -1621,6 +1609,10 @@ Thank you for staying with us! PageSettingsApiDevices + + Active devices + Активные устройства + Active Devices @@ -1663,8 +1655,8 @@ Thank you for staying with us! - This will unlink the device from your subscription. You can reconnect it anytime by pressing "Reload API config" in subscription settings on device. - Это отключит устройство от вашей подписки. Вы можете повторно подключить его в любое время, нажав "Перезагрузить конфигурацию API" в настройках подписки на устройстве. + This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect. + Это устройство будет отвязано от вашей подписки. Вы можете подключить его снова в любой момент, нажав кнопку "Подключиться". @@ -1760,6 +1752,13 @@ Thank you for staying with us! Инструкции по настройке + + PageSettingsApiLanguageList + + Unable change server location while there is an active connection + Невозможно изменить локацию во время активного соединения + + PageSettingsApiNativeConfigs @@ -1767,6 +1766,10 @@ Thank you for staying with us! Save AmneziaVPN config Сохранить конфигурацию AmneziaVPN + + Configuration files + Файл конфигурации + Configuration Files @@ -1845,26 +1848,74 @@ Thank you for staying with us! PageSettingsApiServerInfo + + For the region + Для региона + + + Price + Цена + + + Work period + Период работы + + + Valid until + Действует до + + + Speed + Скорость + + + Copied + Скопировано + + + Subscription status + Статус подписки + + + Active connections + Активные соединения + Configurations have been updated for some countries. Download and install the updated configuration files Сетевые адреса одного или нескольких серверов были обновлены. Пожалуйста, удалите старые конфигурацию и загрузите новые файлы + + Subscription key + Ключ для подключения + Amnezia Premium subscription key Ключ подписки Amnezia Premium + + Save VPN key to file + Сохранить VPN-ключ в файле + Copy VPN key Скопировать VPN ключ + + Configuration files + Файл конфигурации + Manage configuration files Управление файлами конфигурации + + Active devices + Активные устройства + Subscription Status @@ -1956,8 +2007,8 @@ Thank you for staying with us! - This will unlink the device from your subscription. You can reconnect it anytime by pressing "Reload API config" in subscription settings on device. - Это отключит устройство от вашей подписки. Вы можете повторно подключить его в любое время, нажав "Перезагрузить конфигурацию API" в настройках подписки на устройстве. + This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect. + Это устройство будет отвязано от вашей подписки. Вы можете подключить его снова в любой момент, нажав кнопку Подключиться. @@ -1987,21 +2038,40 @@ Thank you for staying with us! Telegram + + Email Support + Email + Email Email + + + support@amnezia.org + + Email Billing & Orders По вопросам оплаты + + + help@vpnpay.io + + Website Сайт + + + amnezia.org + amnezia.org + Support @@ -2013,12 +2083,12 @@ Thank you for staying with us! Наши специалисты технической поддержки всегда готовы помочь вам. - + Support tag - + Copied Скопировано @@ -2046,37 +2116,37 @@ Thank you for staying with us! Раздельное туннелирование приложений - + Mode Режим - + Remove Удалить - + Continue Продолжить - + Cancel Отменить - + application name название приложения - + Open executable file Открыть исполняемый файл - + Executable files (*.*) Исполняемые файлы (*.*) @@ -2186,6 +2256,10 @@ Thank you for staying with us! PageSettingsBackup + + Backup + Резервное копирование + Settings restored from backup file @@ -2196,6 +2270,10 @@ Thank you for staying with us! Back up your configuration Создать резервную копию конфигурации + + Configuration backup + Резервная копия конфигурации + You can save your settings to a backup file to restore them the next time you install the application. @@ -2270,6 +2348,14 @@ Thank you for staying with us! Connection Соединение + + Auto connect + Автоподключение + + + Connect to VPN on app start + Подключение к VPN при запуске приложения + Use AmneziaDNS @@ -2291,19 +2377,28 @@ Thank you for staying with us! Когда AmneziaDNS не используется или не установлен - + Allows you to use the VPN only for certain Apps Позволяет использовать VPN только для определенных приложений - + KillSwitch KillSwitch - - Blocks network connections without VPN - Блокирует интернет-соединение без VPN + + Disables your internet if your encrypted VPN connection drops out for any reason. + Отключает ваше интернет-соединение, если ваше зашифрованное VPN-соединение по какой-либо причине прерывается. + + + + Cannot change KillSwitch settings during active connection + Невозможно изменить настройки KillSwitch во время активного подключения + + + Cannot change killSwitch settings during active connection + Невозможно изменить настройки аварийного выключателя во время активного соединения @@ -2316,10 +2411,14 @@ Thank you for staying with us! Позволяет выбирать, к каким сайтам подключаться через VPN - + App-based split tunneling Раздельное туннелирование приложений + + Allows you to use the VPN only for certain applications + Позволяет использовать VPN только для определённых приложений + PageSettingsDns @@ -2333,6 +2432,10 @@ Thank you for staying with us! DNS servers DNS-серверы + + When AmneziaDNS is not used or installed + Когда AmneziaDNS не используется или не установлен + If AmneziaDNS is not used or installed @@ -2371,7 +2474,7 @@ Thank you for staying with us! Settings have been reset - Настройки были сброшены + Настройки сброшены @@ -2384,165 +2487,12 @@ Thank you for staying with us! Настройки сохранены - - PageSettingsKillSwitch - - - KillSwitch - KillSwitch - - - - Enable to ensure network traffic goes through a secure VPN tunnel, preventing accidental exposure of your IP and DNS queries if the connection drops - Включите, чтобы весь сетевой трафик проходил только через безопасный VPN-туннель. Это предотвратит случайное раскрытие вашего IP-адреса и DNS-запросов при разрыве соединения - - - - KillSwitch settings cannot be changed during an active connection - Настройки KillSwitch нельзя изменить во время активного подключения - - - - Soft KillSwitch - Soft KillSwitch - - - - Internet access is blocked if the VPN disconnects unexpectedly - Доступ в интернет блокируется при разрыве VPN-соединения - - - - Strict KillSwitch - Strict KillSwitch - - - - Internet connection is blocked even when VPN is turned off manually or hasn't started - Доступ в интернет блокируется, даже если VPN отключен вручную или не был запущен - - - - Just a little heads-up - Небольшое предупреждение - - - - If the VPN disconnects or drops while Strict KillSwitch is enabled, internet access will be blocked. To restore access, reconnect VPN or disable/change the KillSwitch. - Если VPN отключится или соединение прервётся при включённом Strict KillSwitch, доступ в интернет будет заблокирован. Чтобы восстановить доступ, снова подключитесь к VPN или отключите (измените) режим KillSwitch. - - - - Continue - Продолжить - - - - Cancel - Отменить - - - - DNS Exceptions - Исключения для DNS - - - - DNS servers listed here will remain accessible when KillSwitch is active. - DNS-серверы из этого списка останутся доступными при активном KillSwitch. - - - - PageSettingsKillSwitchExceptions - - - DNS Exceptions - Исключения для DNS - - - DNS servers listed here will be excluded from KillSwitch restrictions and remain accessible when KillSwitch is active. - Перечисленные DNS-серверы будут исключены из ограничений KillSwitch и останутся доступными при активном KillSwitch. - - - DNS servers from the list will remain accessible when KillSwitch is triggered - DNS-серверы из этого списка будут исключены из ограничений KillSwitch и останутся доступными при активном KillSwitch. - - - - DNS servers listed here will remain accessible when KillSwitch is active - DNS-серверы из этого списка останутся доступными при активном KillSwitch - - - - Delete - Удалить - - - - Continue - Продолжить - - - - Cancel - Отменить - - - - IPv4 address - IPv4 адрес - - - - Import / Export addresses - Импорт / Экспорт адресов - - - - Import - Импорт - - - - Save address list - Сохранить список адресов - - - - Save addresses - Сохранить адреса - - - - - - Address files (*.json) - Файлы адресов (*.json) - - - - Import address list - Импорт списка адресов - - - - Replace address list - Заменить список адресов - - - - - Open address file - Открыть файл адресов - - - - Add imported addresses to existing ones - Добавить импортированные адреса к существующим - - PageSettingsLogging + + Logging is enabled. Note that logs will be automatically disabled after 14 days, and all log files will be deleted. + Логирование включено. Обратите внимание, что логирование будет автоматически отключено через 14 дней, и все логи будут удалены. + Logging @@ -2553,6 +2503,14 @@ Thank you for staying with us! Enabling this function will save application's logs automatically. By default, logging functionality is disabled. Enable log saving in case of application malfunction. Включение этой функции позволяет сохранять логи на вашем устройстве. По умолчанию она отключена. Включите сохранение логов в случае сбоев в работе приложения. + + Save logs + Сохранять логи + + + Open folder with logs + Открыть папку с логами + @@ -2571,6 +2529,10 @@ Thank you for staying with us! Logs file saved Файл с логами сохранен + + Save logs to file + Сохранить логи в файл + Enable logs @@ -2639,11 +2601,28 @@ Thank you for staying with us! All installed containers have been added to the application Все установленные протоколы и сервисы были добавлены в приложение + + Clear Amnezia cache + Очистить кэш Amnezia + + + May be needed when changing other settings + Может понадобиться при изменении других настроек + + + Clear cached profiles? + Удалить кэш Amnezia? + No new installed containers found Новые установленные протоколы и сервисы не обнаружены + + + + + @@ -2730,16 +2709,15 @@ Thank you for staying with us! Cannot reset API config during active connection Невозможно сбросить конфигурацию API во время активного соединения - - - Switch to the new Amnezia Premium subscription - Перейти на новый тип подписки Amnezia Premium - Remove server from application Удалить сервер из приложения + + Remove server? + Удалить сервер? + All installed AmneziaVPN services will still remain on the server. @@ -2750,9 +2728,29 @@ Thank you for staying with us! Clear server from Amnezia software Очистить сервер от протоколов и сервисов Amnezia + + Clear server from Amnezia software? + Удалить все сервисы и протоколы Amnezia с сервера? + + + All containers will be deleted on the server. This means that configuration files, keys and certificates will be deleted. + На сервере будут удалены все данные, связанные с Amnezia: протоколы, сервисы, конфигурационные файлы, ключи и сертификаты. + PageSettingsServerInfo + + Subscription is valid until + Подписка заканчивается через + + + Server name + Имя сервера + + + Save + Сохранить + Protocols @@ -2780,11 +2778,20 @@ Thank you for staying with us! settings настройки + + Clear %1 profile + Очистить профиль %1 + Clear %1 profile? Очистить профиль %1? + + + + + connection settings @@ -2835,6 +2842,10 @@ Thank you for staying with us! Cannot remove active container Невозможно удалить активный контейнер + + All users who you shared a connection with will no longer be able to connect to it. + Все пользователи, с которыми вы поделились VPN, больше не смогут к нему подключаться. + @@ -2873,32 +2884,36 @@ Thank you for staying with us! Адреса из списка не должны открываться через VPN - + Split tunneling Раздельное туннелирование сайтов - + Mode Режим - + Remove Удалить - + Continue Продолжить - + Cancel Отменить - + Website or IP + Сайт или IP + + + Import / Export Sites Импорт/экспорт сайтов @@ -2913,50 +2928,50 @@ Thank you for staying with us! Невозможно изменить настройки раздельного туннелирования во время активного соединения - + website or IP веб-сайт или IP - + Import Импорт - + Save site list Сохранить список сайтов - + Save sites Сохранить сайты - - - + + + Sites files (*.json) Файлы сайтов (*.json) - + Import a list of sites Импортировать список с сайтами - + Replace site list Заменить список с сайтами - - + + Open sites file Открыть список с сайтами - + Add imported sites to existing ones Добавить импортированные сайты к существующим @@ -3010,161 +3025,187 @@ Thank you for staying with us! PageSetupWizardConfigSource - + Server connection + Подключение к серверу + + + Do not use connection code from public sources. It may have been created to intercept your data. + +It's okay as long as it's from someone you trust. + Не используйте код подключения из публичных источников. Его могли создать, чтобы перехватить ваши данные. + +Всё в порядке, если кодом поделился пользователь, которому вы доверяете. + + + Do not use connection codes from untrusted sources, as they may be created to intercept your data. + Не используйте коды подключения из ненадежных источников, так как они могут быть созданы для перехвата ваших данных. + + + What do you have? + Что у вас есть? + + + File with connection settings Файл с настройками подключения - + File with connection settings or backup + Файл с настройками подключения или резервной копией + + + Connection Соединение - + Settings Настройки - + Enable logs Включить запись логов - Export client logs - Экспорт логов клиента - - - - Save - Сохранить - - - - Logs files (*.log) - Файлы логов (*.log) - - - - Logs file saved - Файл с логами сохранен - - - Support tag Support tag - + Copied Скопировано - + Insert the key, add a configuration file or scan the QR-code Вставьте ключ, добавьте файл конфигурации или отсканируйте QR-код - + Insert key Вставьте ключ - + Insert Вставить - + Continue Продолжить - + Other connection options Другие варианты подключения - + Site Amnezia Сайт Amnezia - + VPN by Amnezia VPN от Amnezia - + Connect to classic paid and free VPN services from Amnezia Подключайтесь к классическим платным и бесплатным VPN-сервисам от Amnezia - + Self-hosted VPN Self-hosted VPN - + Configure Amnezia VPN on your own server Настроить VPN на собственном сервере - + Restore from backup Восстановить из резервной копии - - - - + - + Open backup file Открыть резервную копию - + Backup files (*.backup) Файлы резервных копий (*.backup) - + Open config file Открыть файл с конфигурацией - + QR code QR-код - + I have nothing У меня ничего нет + + Key as text + Ключ в виде текста + PageSetupWizardCredentials + + Server connection + Подключение к серверу + Server IP address [:port] IP-адрес[:порт] сервера + + 255.255.255.255:88 + 255.255.255.255:88 + + + Password / SSH private key + Password / SSH private key + Continue Продолжить + + All data you enter will remain strictly confidential +and will not be shared or disclosed to the Amnezia or any third parties + Все данные, которые вы вводите, останутся строго конфиденциальными и не будут переданы или раскрыты Amnezia или каким-либо третьим лицам + Enter the address in the format 255.255.255.255:88 Введите адрес в формате 255.255.255.255:88 + + Login to connect via SSH + Login to connect via SSH + Configure your server @@ -3218,6 +3259,10 @@ Thank you for staying with us! PageSetupWizardEasy + + What is the level of internet control in your region? + Какой уровень контроля над интернетом в вашем регионе? + Choose Installation Type @@ -3238,11 +3283,23 @@ Thank you for staying with us! Skip setup Пропустить настройку + + Set up a VPN yourself + Настроить VPN самостоятельно + + + I want to choose a VPN protocol + Выбрать VPN-протокол + Continue Продолжить + + Set up later + Настроить позднее + PageSetupWizardInstalling @@ -3344,6 +3401,30 @@ Thank you for staying with us! PageSetupWizardStart + + Settings restored from backup file + Настройки восстановлены из резервной копии + + + Free service for creating a personal VPN on your server. + Простое и бесплатное приложение для запуска собственного VPN на своем сервере. + + + Helps you access blocked content without revealing your privacy, even to VPN providers. + Помогает получить доступ к заблокированному контенту, не раскрывая вашу конфиденциальность даже провайдерам VPN. + + + I have the data to connect + У меня есть данные для подключения + + + I have nothing + У меня ничего нет + + + https://amnezia.org/instructions/0_starter-guide + https://amnezia.org/ru/starter-guide + Let's get started @@ -3385,6 +3466,10 @@ Thank you for staying with us! New connection Новое соединение + + Do not use connection code from public sources. It could be created to intercept your data. + Не используйте код подключения из публичных источников. Его могли создать, чтобы перехватить ваши данные. + Collapse content @@ -3423,17 +3508,33 @@ Thank you for staying with us! WireGuard native format Оригинальный формат WireGuard + + VPN Access + VPN-Доступ + Connection Соединение + + VPN access without the ability to manage the server + Доступ к VPN без возможности управления сервером + + + Access to server management. The user with whom you share full access to the connection will be able to add and remove your protocols and services to the server, as well as change settings. + Доступ к управлению сервером. Пользователь, с которым вы делитесь полным доступом к соединению, сможет добавлять и удалять ваши протоколы и службы на сервере, а также изменять настройки. + Server Сервер + + Accessing + Доступ + Config revoked @@ -3560,6 +3661,10 @@ Thank you for staying with us! Allowed IPs: %1 Разрешенные подсети: %1 + + Creation date: + Дата создания: + Rename @@ -3600,6 +3705,10 @@ Thank you for staying with us! Cancel Отменить + + Full access + Полный доступ + Share VPN access without the ability to manage the server @@ -3948,6 +4057,10 @@ Thank you for staying with us! No error Нет ошибки + + Unknown Error + Неизвестная ошибка + Function not implemented @@ -4019,219 +4132,251 @@ Thank you for staying with us! Требуется пароль пользователя - - Docker error: runc doesn't work on cgroups v2 - Docker error: runc не работает на cgroups v2 - - - - Server error: cgroup mountpoint does not exist - Server error: cgroup mountpoint не существует - - - + SSH request was denied SSH-запрос был отклонён - + SSH request was interrupted SSH-запрос был прерван - + SSH internal error Внутренняя ошибка SSH - + Invalid private key or invalid passphrase entered Введен неверный закрытый ключ или неверная парольная фраза - + The selected private key format is not supported, use openssh ED25519 key types or PEM key types Выбранный формат закрытого ключа не поддерживается, используйте типы ключей openssh ED25519 или PEM - + Timeout connecting to server Тайм-аут подключения к серверу - + SCP error: Generic failure Ошибка SCP: общий сбой - + Sftp error: End-of-file encountered + Sftp error: End-of-file encountered + + + Sftp error: File does not exist + Sftp error: File does not exist + + + Sftp error: Permission denied + Sftp error: Permission denied + + + Sftp error: Generic failure + Sftp error: Generic failure + + + Sftp error: Garbage received from server + Sftp error: Garbage received from server + + + Sftp error: No connection has been set up + Sftp error: No connection has been set up + + + Sftp error: There was a connection, but we lost it + Sftp error: There was a connection, but we lost it + + + Sftp error: Operation not supported by libssh yet + Sftp error: Operation not supported by libssh yet + + + Sftp error: Invalid file handle + Sftp error: Invalid file handle + + + Sftp error: No such file or directory path exists + Sftp error: No such file or directory path exists + + + Sftp error: An attempt to create an already existing file or directory has been made + Sftp error: An attempt to create an already existing file or directory has been made + + + Sftp error: Write-protected filesystem + Sftp error: Write-protected filesystem + + + Sftp error: No media was in remote drive + Sftp error: No media was in remote drive + + + The config does not contain any containers and credentials for connecting to the server Конфигурация не содержит каких-либо контейнеров и учетных данных для подключения к серверу - - + + Error when retrieving configuration from API Ошибка при получении конфигурации из API - + This config has already been added to the application Данная конфигурация уже была добавлена в приложение - - A migration error has occurred. Please contact our technical support - Произошла ошибка миграции. Обратитесь в нашу техническую поддержку - - - - Please update the application to use this feature - Пожалуйста, обновите приложение, чтобы использовать эту функцию - - - + ErrorCode: %1. Код ошибки: %1. - + Failed to save config to disk + Failed to save config to disk + + + OpenVPN config missing Отсутствует конфигурация OpenVPN - + OpenVPN management server error Серверная ошибка управлением OpenVPN - + OpenVPN executable missing Отсутствует исполняемый файл OpenVPN - + Shadowsocks (ss-local) executable missing Отсутствует исполняемый файл Shadowsocks (ss-local) - + Cloak (ck-client) executable missing Отсутствует исполняемый файл Cloak (ck-client) - + Amnezia helper service error Ошибка вспомогательной службы Amnezia - + OpenSSL failed Ошибка OpenSSL - + Can't connect: another VPN connection is active Невозможно подключиться: активно другое VPN-соединение - + Can't setup OpenVPN TAP network adapter Невозможно настроить сетевой адаптер OpenVPN TAP - + VPN pool error: no available addresses Ошибка пула VPN: нет доступных адресов - + Unable to open config file Не удалось открыть файл конфигурации - + VPN Protocols is not installed. Please install VPN container at first VPN-протоколы не установлены. Пожалуйста, установите протокол - + VPN connection error Ошибка VPN-соединения - + In the response from the server, an empty config was received В ответе от сервера была получена пустая конфигурация - + SSL error occurred Произошла ошибка SSL - + Server response timeout on api request Тайм-аут ответа сервера на запрос API - + Missing AGW public key Отсутствует публичный ключ AGW - + Failed to decrypt response payload - + Missing list of available services Отсутствует список доступных сервисов - + The limit of allowed configurations per subscription has been exceeded Превышен лимит разрешенных конфигураций для одной подписки - A migration has error occurred. Please contact our technical support - Произошла ошибка миграции. Обратитесь в нашу техническую поддержку - - - + QFile error: The file could not be opened Ошибка QFile: не удалось открыть файл - + QFile error: An error occurred when reading from the file Ошибка QFile: произошла ошибка при чтении из файла - + QFile error: The file could not be accessed Ошибка QFile: не удалось получить доступ к файлу - + QFile error: An unspecified error occurred Ошибка QFile: произошла неизвестная ошибка - + QFile error: A fatal error occurred Ошибка QFile: произошла фатальная ошибка - + QFile error: The operation was aborted Ошибка QFile: операция была прервана - + Internal error Внутренняя ошибка @@ -4240,6 +4385,14 @@ Thank you for staying with us! IPsec IPsec + + Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions. + Shadowsocks маскирует VPN-трафик под обычный веб-трафик, но распознается системами анализа в некоторых регионах с высоким уровнем цензуры. + + + OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against active-probing detection. Ideal for bypassing blocking in regions with the highest levels of censorship. + OpenVPN over Cloak — это OpenVPN с маскировкой VPN-трафика под обычный веб-трафик и защитой от обнаружения активным зондированием. Подходит для регионов с самым высоким уровнем цензуры. + IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss. It has native support on the latest versions of Android and iOS. @@ -4251,56 +4404,95 @@ Thank you for staying with us! Создайте на сервере файловое хранилище для безопасного хранения и передачи файлов. - - AmneziaWG is a modern VPN protocol based on WireGuard, combining simplified architecture with high performance across all devices. It addresses WireGuard's main vulnerability (easy detection by DPI systems) through advanced obfuscation techniques, making VPN traffic indistinguishable from regular internet traffic. + This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for protecting against blocking. -AmneziaWG is an excellent choice for those seeking a fast, stealthy VPN connection. +OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client and the server. -Features: -* Available on all AmneziaVPN platforms -* Low battery consumption on mobile devices -* Minimal settings required -* Undetectable by traffic analysis systems (DPI) -* Operates over UDP protocol - AmneziaWG — современный VPN-протокол на основе WireGuard, сочетающий простую архитектуру и высокую производительность на всех устройствах. Он устраняет основной недостаток WireGuard (лёгкое обнаружение трафика системами DPI) за счёт эффективного маскирования VPN-трафика под обычный интернет-трафик. +Cloak protects OpenVPN from detection and blocking. -Таким образом, AmneziaWG идеально подойдёт тем, кто ищет быстрое и незаметное VPN-соединение. +Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, and also protects the VPN from detection by Active Probing. This makes it very resistant to being detected -Особенности: -* Доступен во всех версиях AmneziaVPN +Immediately after receiving the first data packet, Cloak authenticates the incoming connection. If authentication fails, the plugin masks the server as a fake website and your VPN becomes invisible to analysis systems. + +If there is a extreme level of Internet censorship in your region, we advise you to use only OpenVPN over Cloak from the first connection + +* Available in the AmneziaVPN across all platforms +* High power consumption on mobile devices +* Flexible settings +* Not recognised by DPI analysis systems +* Works over TCP network protocol, 443 port. + + Это связка протокола OpenVPN и плагина Cloak, разработанная специально для защиты от блокировки. + +OpenVPN обеспечивает безопасное VPN-соединение, шифруя весь интернет-трафик между клиентом и сервером. + +Cloak защищает OpenVPN от обнаружения и блокировки. + +Cloak изменяет метаданные пакетов таким образом, что полностью маскирует VPN-трафик под обычный веб-трафик, а также защищает VPN от обнаружения с помощью активного зондирования. Это делает его очень защищенным от обнаружения. + +Сразу после получения первого пакета данных Cloak устанавливает подлинность входящего соединения. Если аутентификация не проходит, плагин маскирует сервер под фальшивый веб-сайт, и ваш VPN становится невидимым для систем анализа трафика. + +Если в вашем регионе наблюдается жесткая интернет-цензура, мы советуем вам уже при первом подключении использовать только OpenVPN over Cloak. + +* Доступен в AmneziaVPN на всех платформах +* Высокое энергопотребление на мобильных устройствах +* Гибкие настройки +* Не распознается системами DPI-анализа +* Работает по сетевому протоколу TCP, использует порт 443 + + + A relatively new popular VPN protocol with a simplified architecture. +WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. +WireGuard is very susceptible to blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. + +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Easily recognised by DPI analysis systems, susceptible to blocking +* Works over UDP network protocol. + Относительно новый и популярный VPN-протокол с простой архитектурой. +WireGuard обеспечивает стабильное VPN-соединение и высокую производительность на всех устройствах. Он использует строго заданные настройки шифрования. WireGuard по сравнению с OpenVPN имеет меньшую задержку и лучшую пропускную способность при передаче данных. +WireGuard очень уязвим для блокировки из-за характерных сигнатур пакетов. В отличие от некоторых других VPN-протоколов, использующих методы обфускации, последовательные сигнатуры пакетов WireGuard легче идентифицируются и, следовательно, могут блокироваться современными Deep Packet Inspection (DPI) системами и другими инструментами для сетевого мониторинга. + +* Доступен в AmneziaVPN на всех платформах * Низкое энергопотребление на мобильных устройствах -* Минимум настроек -* Незаметен для систем анализа трафика (DPI) -* Работает по протоколу UDP - +* Минимальная конфигурация +* Легко распознается системами DPI-анализа, поддается блокировке +* Работает по сетевому протоколу UDP - - REALITY is an innovative protocol developed by the creators of XRay, designed specifically to combat high levels of internet censorship. REALITY identifies censorship systems during the TLS handshake, redirecting suspicious traffic seamlessly to legitimate websites like google.com while providing genuine TLS certificates. This allows VPN traffic to blend indistinguishably with regular web traffic without special configuration. -Unlike older protocols such as VMess, VLESS, and XTLS-Vision, REALITY incorporates an advanced built-in "friend-or-foe" detection mechanism, effectively protecting against DPI and other traffic analysis methods. - -Features: -* Resistant to active probing and DPI detection -* No special configuration required to disguise traffic -* Highly effective in heavily censored regions -* Minimal battery consumption on devices -* Operates over TCP protocol - REALITY — это инновационный протокол от разработчиков XRay, специально созданный для эффективного противодействия жесткой интернет-цензуре. - -REALITY распознаёт системы блокировки во время TLS-рукопожатия и незаметно перенаправляет подозрительные запросы на реальные сайты, такие как google.com, предъявляя подлинные TLS-сертификаты. Это позволяет маскировать VPN-трафик под обычный веб-трафик без дополнительных настроек. - -В отличие от протоколов старого поколения (VMess, VLESS и XTLS-Vision), REALITY использует встроенную технологию распознавания «свой-чужой», надёжно защищая от DPI и других методов сетевого анализа. - -Особенности: -* Устойчив к активному зондированию и DPI-системам -* Не требует специальной настройки для маскировки трафика -* Эффективен в регионах с жесткой цензурой -* Минимальное энергопотребление на устройствах -* Работает по протоколу TCP - + The REALITY protocol, a pioneering development by the creators of XRay, is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion. +It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, thus presenting an authentic TLS certificate and data. +This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. +Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship. + Протокол REALITY, новаторская разработка создателей XRay, специально спроектирован для противодействия самой строгой цензуре с помощью нового способа обхода блокировок. +Он уникальным образом идентифицирует цензоров на этапе TLS-рукопожатия, беспрепятственно работая в качестве прокси для реальных клиентов и перенаправляя цензоров на реальные сайты, такие как google.com, тем самым предъявляя подлинный TLS-сертификат и данные. +REALITY отличается от аналогичных технологий благодаря способности без специальной настройки маскировать веб-трафик так, как будто он поступает со случайных легитимных сайтов. +В отличие от более старых протоколов, таких как VMess, VLESS и транспорт XTLS-Vision, технология распознавания "друг или враг" на этапе TLS-рукопожатия повышает безопасность и обходит обнаружение сложными системами DPI-анализа, которые используют методы активного зондирования. Это делает REALITY эффективным решением для поддержания свободы интернета в регионах с жесткой цензурой. - + + IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol. +One of its distinguishing features is its ability to swiftly switch between networks and devices, making it particularly adaptive in dynamic network environments. +While it offers a blend of security, stability, and speed, it's essential to note that IKEv2 can be easily detected and is susceptible to blocking. + +* Available in the AmneziaVPN only on Windows +* Low power consumption, on mobile devices +* Minimal configuration +* Recognised by DPI analysis systems +* Works over UDP network protocol, ports 500 and 4500. + IKEv2 в сочетании с уровнем шифрования IPSec представляет собой современный и стабильный VPN-протокол. +Он может быстро переключаться между сетями и устройствами, что делает его особенно адаптивным в динамичных сетевых средах. +Несмотря на сочетание безопасности, стабильности и скорости, необходимо отметить, что IKEv2 легко обнаруживается и подвержен блокировке. + +* Доступен в AmneziaVPN только для Windows +* Низкое энергопотребление на мобильных устройствах +* Минимальная конфигурация +* Распознается системами DPI-анализа +* Работает по сетевому протоколу UDP, использует порты 500 и 4500 + + + DNS Service Сервис DNS @@ -4311,7 +4503,7 @@ REALITY распознаёт системы блокировки во время - + Website in Tor network Веб-сайт в сети Tor @@ -4350,141 +4542,126 @@ REALITY распознаёт системы блокировки во время XRay with REALITY masks VPN traffic as web traffic and protects against active probing. It is highly resistant to detection and offers high speed. XRay с REALITY маскирует VPN-трафик под веб-трафик. Обладает высокой устойчивостью к обнаружению и обеспечивает высокую скорость соединения. - - - - - - OpenVPN is one of the most popular and reliable VPN protocols. It uses SSL/TLS encryption, supports a wide variety of devices and operating systems, and is continuously improved by the community due to its open-source nature. It provides a good balance between speed and security but is easily recognized by DPI systems, making it susceptible to blocking. + OpenVPN stands as one of the most popular and time-tested VPN protocols available. +It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, catering to a wide range of devices and operating systems. Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, which continually reinforces its security. With a strong balance of performance, security, and compatibility, OpenVPN remains a top choice for privacy-conscious individuals and businesses alike. -Features: -* Available on all AmneziaVPN platforms -* Normal battery consumption on mobile devices -* Flexible customization for various devices and OS -* Operates over both TCP and UDP protocols - OpenVPN — один из самых популярных и надежных VPN-протоколов. Он использует шифрование SSL/TLS, совместим со множеством устройств и ОС, а благодаря открытому коду постоянно совершенствуется сообществом. Имеет хороший баланс скорости и безопасности, но легко распознаётся системами DPI, что делает его уязвимым к блокировкам. - -Особенности: -* Доступен во всех приложениях AmneziaVPN -* Нормальное энергопотребление на мобильных устройствах -* Гибкие настройки под разные устройства и ОС -* Работает по TCP и UDP +* Available in the AmneziaVPN across all platforms +* Normal power consumption on mobile devices +* Flexible customisation to suit user needs to work with different operating systems and devices +* Recognised by DPI systems and therefore susceptible to blocking +* Can operate over both TCP and UDP network protocols. + OpenVPN является одним из самых популярных и проверенных временем VPN-протоколов. Он использует собственный протокол безопасности, и криптографические протоколы SSL/TLS для шифрования и обмена ключами. Более того, поддержка множества методов аутентификации делает OpenVPN универсальным, адаптируемым и подходящим для широкого спектра устройств и операционных систем. Благодаря своему открытому коду, OpenVPN подвергается тщательной проверке со стороны мирового сообщества, что постоянно укрепляет его безопасность. Имея отличный баланс между производительностью, безопасностью и совместимостью OpenVPN остается лучшим выбором для людей и компаний, заботящихся о конфиденциальности, однако OpenVPN легко распознается современными системами анализа трафика. +Доступен в AmneziaVPN на всех платформах +Нормальное энергопотребление на мобильных устройствах +Гибкая настройка полезная при работе с различными операционными системами и устройствами +Распознается системами DPI и, следовательно, уязвим к блокировкам +Может работать как по TCP, так и по UDP протоколу. - - Shadowsocks is based on the SOCKS5 protocol and encrypts connections using AEAD cipher. Although designed to be discreet, it doesn't mimic a standard HTTPS connection and can be detected by some DPI systems. Due to limited support in Amnezia, we recommend using the AmneziaWG protocol. + + This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for protecting against detection. -Features: -* Available in AmneziaVPN only on desktop platforms -* Customizable encryption protocol -* Detectable by some DPI systems -* Operates over TCP protocol - - Shadowsocks основан на протоколе SOCKS5 и шифрует соединение алгоритмом AEAD. Он разработан так, чтобы быть малозаметным, однако не идентичен HTTPS, поэтому может распознаваться некоторыми системами DPI. В связи с ограниченной поддержкой в Amnezia, рекомендуем использовать протокол AmneziaWG. +OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client and the server. -Особенности: -* Доступен только на ПК в AmneziaVPN -* Настраиваемое шифрование -* Может обнаруживаться некоторыми DPI-системами -* Работает по протоколу TCP - - - - This combination includes the OpenVPN protocol and the Cloak plugin, specifically designed to protect against blocking. +Cloak protects OpenVPN from detection. -OpenVPN securely encrypts all internet traffic between your device and the server. +Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, and also protects the VPN from detection by Active Probing. This makes it very resistant to being detected -The Cloak plugin further protects the connection from DPI detection. It modifies traffic metadata to disguise VPN traffic as regular web traffic and prevents detection through active probing. If an incoming connection fails authentication, Cloak serves a fake website, making your VPN invisible to traffic analysis systems. +Immediately after receiving the first data packet, Cloak authenticates the incoming connection. If authentication fails, the plugin masks the server as a fake website and your VPN becomes invisible to analysis systems. -In regions with heavy internet censorship, we strongly recommend using OpenVPN with Cloak from your first connection. - -Features: -* Available on all AmneziaVPN platforms +* Available in the AmneziaVPN across all platforms * High power consumption on mobile devices -* Flexible configuration options -* Undetectable by DPI systems -* Operates over TCP protocol on port 443 - Эта комбинация состоит из протокола OpenVPN и плагина Cloak, специально разработанных для защиты от блокировок. +* Flexible settings +* Not recognised by detection systems +* Works over TCP network protocol, 443 port. + + Это связка протокола OpenVPN и плагина Cloak, созданная специально для защиты от обнаружения. -OpenVPN надёжно шифрует весь интернет-трафик между вами и сервером. +OpenVPN обеспечивает безопасное VPN-соединение, шифруя весь интернет-трафик между клиентом и сервером. -Плагин Cloak дополнительно защищает соединение от распознавания системами DPI. Он изменяет метаданные трафика, маскируя VPN-подключение под обычный веб-трафик, и предотвращает обнаружение с помощью активного зондирования. Если попытка подключения не прошла аутентификацию, Cloak выдаёт поддельный веб-сайт, делая VPN невидимым для анализирующих систем. +Плагин Cloak защищает OpenVPN от обнаружения. -Если в вашем регионе сильная интернет-цензура, мы рекомендуем сразу использовать OpenVPN с плагином Cloak. +Cloak может изменять метаданные пакета, чтобы полностью замаскировать VPN-трафик под обычный веб-трафик, а также защищает VPN от обнаружения с помощью метода Active Probing. Это делает его очень устойчивым к обнаружению. -Особенности: -* Доступен на всех платформах AmneziaVPN +Сразу после получения первого пакета данных Cloak аутентифицирует входящее соединение, если аутентификация не удалась, плагин маскирует сервер под настоящий веб-сайт, и ваш VPN становится невидимым для систем анализа. Имеет низкую скорость работы в сравнении с другими похожими протоколами. + +* Доступно в AmneziaVPN на всех платформах. * Высокое энергопотребление на мобильных устройствах * Гибкие настройки -* Незаметен для систем DPI-анализа -* Использует протокол TCP на порту 443 +* Не распознается системами обнаружения. +* Работает по сетевому протоколу TCP, порт 443. + - - WireGuard is a modern, streamlined VPN protocol offering stable connectivity and excellent performance across all devices. It uses fixed encryption settings, delivering lower latency and higher data transfer speeds compared to OpenVPN. However, WireGuard is easily identifiable by DPI systems due to its distinctive packet signatures, making it susceptible to blocking. + + A relatively new popular VPN protocol with a simplified architecture. +WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. +WireGuard is very susceptible to detection and blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. -Features: -* Available on all AmneziaVPN platforms -* Low power consumption on mobile devices -* Minimal configuration required -* Easily detected by DPI systems (susceptible to blocking) -* Operates over UDP protocol - WireGuard — современный и простой VPN-протокол, который обеспечивает стабильное соединение и высокую скорость передачи данных на любых устройствах. Он использует фиксированные настройки шифрования, имеет меньшую задержку и выше пропускную способность по сравнению с OpenVPN. +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Easily recognised by DPI analysis systems, susceptible to blocking +* Works over UDP network protocol. + Популярный VPN-протокол с упрощенной архитектурой. +WireGuard обеспечивает стабильное VPN-соединение и высокую производительность на всех устройствах. Он использует закодированные настройки шифрования. WireGuard по сравнению с OpenVPN имеет меньшую задержку и лучшую пропускную способность передачи данных. +WireGuard очень чувствителен к обнаружению и блокировке из-за различных сигнатур пакетов. В отличие от некоторых других VPN протоколов, использующих методы запутывания, последовательные шаблоны сигнатур пакетов WireGuard легко идентифицируются системами анализа трафика. -Однако WireGuard легко распознаётся системами DPI из-за характерных сигнатур трафика, что делает его уязвимым к блокировкам. - -Особенности: -* Доступен на всех платформах AmneziaVPN -* Низкое энергопотребление на мобильных устройствах -* Минимум настроек -* Легко определяется DPI-системами (подвержен блокировкам) -* Работает по протоколу UDP +* Доступно в AmneziaVPN на всех платформах. +* Низкое энергопотребление +* Минимальное количество настроек +* Легко распознается системами анализа DPI, подвержен блокировке. +* Работает по сетевому протоколу UDP. - - IKEv2, combined with IPSec encryption, is a modern and reliable VPN protocol. It reconnects quickly when switching networks or devices, making it ideal for dynamic network environments. While it provides good security and speed, it's easily recognized by DPI systems and susceptible to blocking. + + A modern iteration of the popular VPN protocol, AmneziaWG builds upon the foundation set by WireGuard, retaining its simplified architecture and high-performance capabilities across devices. +While WireGuard is known for its efficiency, it had issues with being easily detected due to its distinct packet signatures. AmneziaWG solves this problem by using better obfuscation methods, making its traffic blend in with regular internet traffic. +This means that AmneziaWG keeps the fast performance of the original while adding an extra layer of stealth, making it a great choice for those wanting a fast and discreet VPN connection. -Features: -* Available in AmneziaVPN only on Windows -* Low battery consumption on mobile devices -* Minimal configuration required -* Detectable by DPI analysis systems(easily blocked) -* Operates over UDP protocol(ports 500 and 4500) - IKEv2 — современный и стабильный VPN-протокол, работающий совместно с шифрованием IPSec. Он обеспечивает быстрое переподключение при смене сети или устройства, отлично подходит для динамичных сетевых условий. Несмотря на хорошую скорость и безопасность, легко распознаётся системами DPI и подвержен блокировкам. +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Not recognised by traffic analysis systems +* Works over UDP network protocol. + AmneziaWG — это современная версия популярного VPN протокола, основанная на базе WireGuard, сохранившая упрощенную архитектуру и высокопроизводительные возможности на всех устройствах. +Хотя WireGuard известен своей эффективностью, обнаружить его довольно легко из-за различных сигнатур пакетов. AmneziaWG решает эту проблему, используя более совершенные методы работы, смешивая свой трафик с обычным интернет-трафиком. +Это означает, что AmneziaWG сохраняет высокую производительность оригинала, добавляя при этом дополнительный уровень скрытности, что делает его отличным выбором для тех, кому нужно быстрое и незаметное VPN-соединение. -Особенности: -* Доступен в AmneziaVPN только на Windows -* Низкое энергопотребление на мобильных устройствах -* Минимум настроек -* Распознаётся DPI-системами (легко блокируется) -* Работает по UDP (порты 500 и 4500) +* Доступно в AmneziaVPN на всех платформах. +* Низкое энергопотребление +* Минимальное количество настроек +* Не распознается системами анализа трафика. +* Работает по сетевому протоколу UDP. - - AmneziaWG is a modern VPN protocol based on WireGuard, combining simplified architecture with high performance across all devices. It addresses WireGuard’s main vulnerability (easy detection by DPI systems) through advanced obfuscation techniques, making VPN traffic indistinguishable from regular internet traffic. - - AmneziaWG is an excellent choice for those seeking a fast, stealthy VPN connection. - - Features: - - * Available on all AmneziaVPN platforms - * Low battery consumption on mobile devices - * Minimal settings required - * Undetectable by traffic analysis systems (DPI) - * Operates over UDP protocol - - AmneziaWG — современный VPN-протокол на основе WireGuard, сочетающий простую архитектуру и высокую производительность на всех устройствах. Он устраняет основной недостаток WireGuard (лёгкое обнаружение трафика системами DPI) за счёт эффективного маскирования VPN-трафика под обычный интернет-трафик. - -Таким образом, AmneziaWG идеально подойдёт тем, кто ищет быстрое и незаметное VPN-соединение. - -Особенности: -* Доступен во всех версиях AmneziaVPN -* Низкое энергопотребление на мобильных устройствах -* Минимум настроек -* Незаметен для систем анализа трафика (DPI) -* Работает по протоколу UDP + + The REALITY protocol, a pioneering development by the creators of XRay, is designed to provide the highest level of protection against detection through its innovative approach to security and privacy. +It uniquely identifies attackers during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting attackers to genuine websites, thus presenting an authentic TLS certificate and data. +This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. +Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security. This makes REALITY a robust solution for maintaining internet freedom. + Протокол REALITY, современная разработка от создателей XRay. Призван обеспечить высочайший уровень защиты от обнаружения благодаря инновационному подходу к безопасности и конфиденциальности. +Он безошибочно идентифицирует злоумышленников на этапе установления связи TLS, беспрепятственно работая в качестве прокси-сервера для оригинального клиента и перенаправляя злоумышленников на подлинные веб-сайты, предоставляя тем самым подлинный сертификат TLS и данные. +Эта расширенная возможность отличает REALITY от аналогичных технологий тем, что способна маскироваться под случайный веб-трафик без использования специальных настроек. +В отличие от старых протоколов, таких как VMess, VLESS и транспорт XTLS-Vision, REALITY имеет инновационную технологию распознавания «свой-чужой».Это делает REALITY надежным решением для обеспечения доступа к свободному интернету. + + + WireGuard - New popular VPN protocol with high performance, high speed and low power consumption. Recommended for regions with low levels of censorship. + WireGuard — новый популярный VPN-протокол с высокой производительностью, высокой скоростью и низким энергопотреблением. Рекомендуется для регионов с низким уровнем цензуры. + + + AmneziaWG - Special protocol from Amnezia, based on WireGuard. It's fast like WireGuard, but very resistant to blockages. Recommended for regions with high levels of censorship. + AmneziaWG — специальный протокол от Amnezia, основанный на протоколе WireGuard. Он такой же быстрый, как WireGuard, но очень устойчив к блокировкам. Рекомендуется для регионов с высоким уровнем цензуры. + + + XRay with REALITY - Suitable for countries with the highest level of internet censorship. Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods. + XRay with REALITY подойдет для стран с самым высоким уровнем цензуры. Маскировка трафика под веб-трафик на уровне TLS и защита от обнаружения методами активного зондирования. + + + IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss. + IKEv2/IPsec — современный стабильный протокол, немного быстрее других, восстанавливает соединение после потери сигнала. @@ -4497,7 +4674,50 @@ Features: Замените текущий DNS-сервер на свой собственный. Это повысит уровень вашей конфиденциальности. - + OpenVPN stands as one of the most popular and time-tested VPN protocols available. +It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, catering to a wide range of devices and operating systems. Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, which continually reinforces its security. With a strong balance of performance, security, and compatibility, OpenVPN remains a top choice for privacy-conscious individuals and businesses alike. + +* Available in the AmneziaVPN across all platforms +* Normal power consumption on mobile devices +* Flexible customisation to suit user needs to work with different operating systems and devices +* Recognised by DPI analysis systems and therefore susceptible to blocking +* Can operate over both TCP and UDP network protocols. + OpenVPN — один из самых популярных и проверенных временем VPN-протоколов. +В нем используется уникальный протокол безопасности, опирающийся на SSL/TLS для шифрования и обмена ключами. Кроме того, OpenVPN поддерживает множество методов аутентификации, что делает его универсальным и адаптируемым к широкому спектру устройств и операционных систем. Благодаря открытому исходному коду OpenVPN подвергается тщательному анализу со стороны мирового сообщества, что постоянно повышает его безопасность. Оптимальное соотношение производительности, безопасности и совместимости делает OpenVPN лучшим выбором как для частных лиц, так и для компаний, заботящихся о конфиденциальности. + +* Доступен в AmneziaVPN на всех платформах +* Нормальное энергопотребление на мобильных устройствах +* Гибкая настройка под нужды пользователя для работы с различными операционными системами и устройствами +* Распознается системами DPI-анализа и поэтому подвержен блокировке +* Может работать по сетевым протоколам TCP и UDP + + + + Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection.However, certain traffic analysis systems might still detect a Shadowsocks connection. Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol. + +* Available in the AmneziaVPN only on desktop platforms +* Configurable encryption protocol +* Detectable by some DPI systems +* Works over TCP network protocol. + Shadowsocks создан на основе протокола SOCKS5, защищает соединение с помощью шифра AEAD. Несмотря на то, что протокол Shadowsocks разработан таким образом, чтобы быть незаметным и сложным для идентификации, он не идентичен стандартному HTTPS-соединению, поэтому некоторые системы анализа трафика всё же могут обнаружить соединение Shadowsocks. В связи с ограниченной поддержкой в Amnezia рекомендуется использовать протокол AmneziaWG. + +* Доступен в AmneziaVPN только для ПК и ноутбуков +* Настраиваемый протокол шифрования +* Распознается некоторыми системами DPI-анализа +* Работает по сетевому протоколу TCP. + + + The REALITY protocol, a pioneering development by the creators of XRay, is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion. + It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, thus presenting an authentic TLS certificate and data. + This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. + Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship. + Протокол REALITY, новаторская разработка создателей XRay, специально спроектирован для противодействия самой строгой цензуре с помощью нового способа обхода блокировок. + Он уникальным образом идентифицирует цензоров на этапе TLS-рукопожатия, беспрепятственно работая в качестве прокси для реальных клиентов и перенаправляя цензоров на реальные сайты, такие как google.com, тем самым предъявляя подлинный TLS-сертификат и данные. + REALITY отличается от аналогичных технологий благодаря способности без специальной настройки маскировать веб-трафик так, как будто он поступает со случайных легитимных сайтов. + В отличие от более старых протоколов, таких как VMess, VLESS и XTLS-Vision, технология распознавания "друг или враг" на этапе TLS-рукопожатия повышает безопасность и обходит обнаружение сложными системами DPI-анализа, которые используют методы активного зондирования. Это делает REALITY эффективным решением для поддержания свободы интернета в регионах с жесткой цензурой. + + + After installation, Amnezia will create a file storage on your server. You will be able to access it using @@ -4515,6 +4735,96 @@ For more detailed information, you can Более подробную информацию вы можете найти в разделе поддержки "Создание файлового хранилища SFTP." + + This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for blocking protection. + +OpenVPN provides a secure VPN connection by encrypting all Internet traffic between the client and the server. + +Cloak protects OpenVPN from detection and blocking. + +Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, and also protects the VPN from detection by Active Probing. This makes it very resistant to being detected + +Immediately after receiving the first data packet, Cloak authenticates the incoming connection. If authentication fails, the plugin masks the server as a fake website and your VPN becomes invisible to analysis systems. + +If there is a extreme level of Internet censorship in your region, we advise you to use only OpenVPN over Cloak from the first connection + +* Available in the AmneziaVPN across all platforms +* High power consumption on mobile devices +* Flexible settings +* Not recognised by DPI analysis systems +* Works over TCP network protocol, 443 port. + + OpenVPN over Cloak - это комбинация протокола OpenVPN и плагина Cloak, разработанного специально для защиты от обнаружения и блокировок. + +Протокол OpenVPN обеспечивает безопасное VPN-соединение за счет шифрования всего интернет-трафика между клиентом и сервером. + +Плагин Cloak защищает OpenVPN от обнаружения и блокировок. + +Cloak может изменять метаданные пакетов. Он полностью маскирует VPN-трафик под обычный веб-трафик, а также защищает VPN от обнаружения с помощью Active Probing. Это делает его очень устойчивым к обнаружению + +Сразу же после получения первого пакета данных Cloak проверяет подлинность входящего соединения. Если аутентификация не проходит, плагин маскирует сервер под поддельный сайт, и ваш VPN становится невидимым для аналитических систем. + +Если в вашем регионе экстремальный уровень цензуры в Интернете, мы советуем вам с первого подключения использовать только OpenVPN over Cloak + +* Доступен в AmneziaVPN для всех платформ +* Высокое энергопотребление на мобильных устройствах +* Гибкие настройки +* Не распознается системами DPI-анализа +* Работает по сетевому протоколу TCP, 443 порт. + + + + A relatively new popular VPN protocol with a simplified architecture. +Provides stable VPN connection, high performance on all devices. Uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. +WireGuard is very susceptible to blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. + +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Easily recognised by DPI analysis systems, susceptible to blocking +* Works over UDP network protocol. + WireGuard - относительно новый популярный VPN-протокол с упрощенной архитектурой. +Обеспечивает стабильное VPN-соединение, высокую производительность на всех устройствах. Использует жестко заданные настройки шифрования. WireGuard по сравнению с OpenVPN имеет меньшую задержку и лучшую пропускную способность при передаче данных. +WireGuard очень восприимчив к блокированию из-за особенностей сигнатур пакетов. В отличие от некоторых других VPN-протоколов, использующих методы обфускации, последовательные сигнатуры пакетов WireGuard легче выявляются и, соответственно, блокируются современными системами глубокой проверки пакетов (DPI) и другими средствами сетевого мониторинга. + +* Доступен в AmneziaVPN для всех платформ +* Низкое энергопотребление +* Минимальное количество настроек +* Легко распознается системами DPI-анализа, подвержен блокировке +* Работает по сетевому протоколу UDP. + + + A modern iteration of the popular VPN protocol, AmneziaWG builds upon the foundation set by WireGuard, retaining its simplified architecture and high-performance capabilities across devices. +While WireGuard is known for its efficiency, it had issues with being easily detected due to its distinct packet signatures. AmneziaWG solves this problem by using better obfuscation methods, making its traffic blend in with regular internet traffic. +This means that AmneziaWG keeps the fast performance of the original while adding an extra layer of stealth, making it a great choice for those wanting a fast and discreet VPN connection. + +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Not recognised by DPI analysis systems, resistant to blocking +* Works over UDP network protocol. + AmneziaWG — усовершенствованная версия популярного VPN-протокола WireGuard. AmneziaWG опирается на фундамент, заложенный WireGuard, сохраняя упрощенную архитектуру и высокую производительность на различных устройствах. +Хотя WireGuard известен своей эффективностью, у него были проблемы с обнаружением из-за характерных сигнатур пакетов. AmneziaWG решает эту проблему за счет использования более совершенных методов обфускации, благодаря чему его трафик сливается с обычным интернет-трафиком. +Таким образом, AmneziaWG сохраняет высокую производительность оригинального протокола, добавляя при этом дополнительный уровень скрытности, что делает его отличным выбором для тех, кому нужно быстрое и незаметное VPN-соединение. + +* Доступен в AmneziaVPN на всех платформах +* Низкое энергопотребление на мобильных устройствах +* Минимальное количество настроек +* Не распознается системами DPI-анализа, устойчив к блокировке +* Работает по сетевому протоколу UDP + + + AmneziaWG container + AmneziaWG протокол + + + Sftp file sharing service - is secure FTP service + Файловое хранилище для безопасного хранения данных + + + Sftp service + SFTP-сервис + Entry not found @@ -4584,7 +4894,7 @@ For more detailed information, you can - + SOCKS5 proxy server Прокси-сервер SOCKS5 @@ -4770,6 +5080,10 @@ For more detailed information, you can All settings have been reset to default values Все настройки сброшены до значений по умолчанию + + Cached profiles cleared + Закэшированные профили очищены + Backup file is corrupted @@ -4903,7 +5217,7 @@ For more detailed information, you can VpnConnection - + Mbps Мбит/с @@ -4954,12 +5268,52 @@ For more detailed information, you can amnezia::ContainerProps - + Low + Низкий + + + High + Высокий + + + Extreme + Экстремальный + + + I just want to increase the level of my privacy. + Я просто хочу повысить уровень своей приватности. + + + I want to bypass censorship. This option recommended in most cases. + Я хочу обойти блокировки. Этот вариант рекомендуется в большинстве случаев. + + + Most VPN protocols are blocked. Recommended if other options are not working. + Большинство VPN-протоколов заблокированы. Рекомендуется, если другие варианты не работают. + + + Medium + Средний + + + Many foreign websites and VPN providers are blocked + Многие иностранные сайты и VPN-провайдеры заблокированы + + + Some foreign sites are blocked, but VPN providers are not blocked + Некоторые иностранные сайты заблокированы, но VPN-провайдеры не блокируются + + + I just want to increase the level of privacy + Хочу просто повысить уровень приватности + + + Automatic Автоматическая - + AmneziaWG protocol will be installed. It provides high connection speed and ensures stable operation even in the most challenging network conditions. Будет установлен протокол AmneziaWG. Он обеспечивает высокую скорость соединения и гарантирует стабильную работу даже в самых сложных условиях. diff --git a/client/ui/Controls2 b/client/ui/Controls2 new file mode 100644 index 00000000..13f01bb7 --- /dev/null +++ b/client/ui/Controls2 @@ -0,0 +1,34 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +TextArea { + id: root + + width: parent.width + + topPadding: 16 + leftPadding: 16 + + color: "#D7D8DB" + selectionColor: "#412102" + selectedTextColor: "#D7D8DB" + placeholderTextColor: "#878B91" + + font.pixelSize: 16 + font.weight: Font.Medium + font.family: "PT Root UI VF" + + wrapMode: Text.Wrap + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.RightButton + onClicked: contextMenu.open() + } + + ContextMenuType { + id: contextMenu + textObj: textField + } +} diff --git a/client/ui/controllers/api/apiConfigsController.cpp b/client/ui/controllers/api/apiConfigsController.cpp index 0f42beb7..74e22a85 100644 --- a/client/ui/controllers/api/apiConfigsController.cpp +++ b/client/ui/controllers/api/apiConfigsController.cpp @@ -18,7 +18,6 @@ namespace { constexpr char cloak[] = "cloak"; constexpr char awg[] = "awg"; - constexpr char vless[] = "vless"; constexpr char apiEndpoint[] = "api_endpoint"; constexpr char accessToken[] = "api_key"; @@ -36,6 +35,10 @@ namespace constexpr char serviceInfo[] = "service_info"; constexpr char serviceProtocol[] = "service_protocol"; + constexpr char aesKey[] = "aes_key"; + constexpr char aesIv[] = "aes_iv"; + constexpr char aesSalt[] = "aes_salt"; + constexpr char apiPayload[] = "api_payload"; constexpr char keyPayload[] = "key_payload"; @@ -44,185 +47,6 @@ namespace constexpr char config[] = "config"; } - - struct ProtocolData - { - OpenVpnConfigurator::ConnectionData certRequest; - - QString wireGuardClientPrivKey; - QString wireGuardClientPubKey; - - QString xrayUuid; - }; - - struct GatewayRequestData - { - QString osVersion; - QString appVersion; - - QString installationUuid; - - QString userCountryCode; - QString serverCountryCode; - QString serviceType; - QString serviceProtocol; - - QJsonObject authData; - - QJsonObject toJsonObject() const - { - QJsonObject obj; - if (!osVersion.isEmpty()) { - obj[configKey::osVersion] = osVersion; - } - if (!appVersion.isEmpty()) { - obj[configKey::appVersion] = appVersion; - } - if (!installationUuid.isEmpty()) { - obj[configKey::uuid] = installationUuid; - } - if (!userCountryCode.isEmpty()) { - obj[configKey::userCountryCode] = userCountryCode; - } - if (!serverCountryCode.isEmpty()) { - obj[configKey::serverCountryCode] = serverCountryCode; - } - if (!serviceType.isEmpty()) { - obj[configKey::serviceType] = serviceType; - } - if (!serviceProtocol.isEmpty()) { - obj[configKey::serviceProtocol] = serviceProtocol; - } - if (!authData.isEmpty()) { - obj[configKey::authData] = authData; - } - return obj; - } - }; - - ProtocolData generateProtocolData(const QString &protocol) - { - ProtocolData protocolData; - if (protocol == configKey::cloak) { - protocolData.certRequest = OpenVpnConfigurator::createCertRequest(); - } else if (protocol == configKey::awg) { - auto connData = WireguardConfigurator::genClientKeys(); - protocolData.wireGuardClientPubKey = connData.clientPubKey; - protocolData.wireGuardClientPrivKey = connData.clientPrivKey; - } else if (protocol == configKey::vless) { - protocolData.xrayUuid = QUuid::createUuid().toString(QUuid::WithoutBraces); - } - - return protocolData; - } - - void appendProtocolDataToApiPayload(const QString &protocol, const ProtocolData &protocolData, QJsonObject &apiPayload) - { - if (protocol == configKey::cloak) { - apiPayload[configKey::certificate] = protocolData.certRequest.request; - } else if (protocol == configKey::awg) { - apiPayload[configKey::publicKey] = protocolData.wireGuardClientPubKey; - } else if (protocol == configKey::vless) { - apiPayload[configKey::publicKey] = protocolData.xrayUuid; - } - } - - ErrorCode fillServerConfig(const QString &protocol, const ProtocolData &apiPayloadData, const QByteArray &apiResponseBody, - QJsonObject &serverConfig) - { - QString data = QJsonDocument::fromJson(apiResponseBody).object().value(config_key::config).toString(); - - data.replace("vpn://", ""); - QByteArray ba = QByteArray::fromBase64(data.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); - - if (ba.isEmpty()) { - qDebug() << "empty vpn key"; - return ErrorCode::ApiConfigEmptyError; - } - - QByteArray ba_uncompressed = qUncompress(ba); - if (!ba_uncompressed.isEmpty()) { - ba = ba_uncompressed; - } - - QString configStr = ba; - if (protocol == configKey::cloak) { - configStr.replace("", "\n"); - configStr.replace("$OPENVPN_PRIV_KEY", apiPayloadData.certRequest.privKey); - } else if (protocol == configKey::awg) { - configStr.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey); - auto newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object(); - auto containers = newServerConfig.value(config_key::containers).toArray(); - if (containers.isEmpty()) { - qDebug() << "missing containers field"; - return ErrorCode::ApiConfigEmptyError; - } - auto container = containers.at(0).toObject(); - QString containerName = ContainerProps::containerTypeToString(DockerContainer::Awg); - auto serverProtocolConfig = container.value(containerName).toObject(); - auto clientProtocolConfig = - QJsonDocument::fromJson(serverProtocolConfig.value(config_key::last_config).toString().toUtf8()).object(); - - //TODO looks like this block can be removed after v1 configs EOL - - serverProtocolConfig[config_key::junkPacketCount] = clientProtocolConfig.value(config_key::junkPacketCount); - serverProtocolConfig[config_key::junkPacketMinSize] = clientProtocolConfig.value(config_key::junkPacketMinSize); - serverProtocolConfig[config_key::junkPacketMaxSize] = clientProtocolConfig.value(config_key::junkPacketMaxSize); - serverProtocolConfig[config_key::initPacketJunkSize] = clientProtocolConfig.value(config_key::initPacketJunkSize); - serverProtocolConfig[config_key::responsePacketJunkSize] = clientProtocolConfig.value(config_key::responsePacketJunkSize); - serverProtocolConfig[config_key::initPacketMagicHeader] = clientProtocolConfig.value(config_key::initPacketMagicHeader); - serverProtocolConfig[config_key::responsePacketMagicHeader] = clientProtocolConfig.value(config_key::responsePacketMagicHeader); - serverProtocolConfig[config_key::underloadPacketMagicHeader] = clientProtocolConfig.value(config_key::underloadPacketMagicHeader); - serverProtocolConfig[config_key::transportPacketMagicHeader] = clientProtocolConfig.value(config_key::transportPacketMagicHeader); - - serverProtocolConfig[config_key::cookieReplyPacketJunkSize] = clientProtocolConfig.value(config_key::cookieReplyPacketJunkSize); - serverProtocolConfig[config_key::transportPacketJunkSize] = clientProtocolConfig.value(config_key::transportPacketJunkSize); - serverProtocolConfig[config_key::specialJunk1] = clientProtocolConfig.value(config_key::specialJunk1); - serverProtocolConfig[config_key::specialJunk2] = clientProtocolConfig.value(config_key::specialJunk2); - serverProtocolConfig[config_key::specialJunk3] = clientProtocolConfig.value(config_key::specialJunk3); - serverProtocolConfig[config_key::specialJunk4] = clientProtocolConfig.value(config_key::specialJunk4); - serverProtocolConfig[config_key::specialJunk5] = clientProtocolConfig.value(config_key::specialJunk5); - serverProtocolConfig[config_key::controlledJunk1] = clientProtocolConfig.value(config_key::controlledJunk1); - serverProtocolConfig[config_key::controlledJunk2] = clientProtocolConfig.value(config_key::controlledJunk2); - serverProtocolConfig[config_key::controlledJunk3] = clientProtocolConfig.value(config_key::controlledJunk3); - serverProtocolConfig[config_key::specialHandshakeTimeout] = clientProtocolConfig.value(config_key::specialHandshakeTimeout); - - // - - container[containerName] = serverProtocolConfig; - containers.replace(0, container); - newServerConfig[config_key::containers] = containers; - configStr = QString(QJsonDocument(newServerConfig).toJson()); - } - - QJsonObject newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object(); - serverConfig[config_key::dns1] = newServerConfig.value(config_key::dns1); - serverConfig[config_key::dns2] = newServerConfig.value(config_key::dns2); - serverConfig[config_key::containers] = newServerConfig.value(config_key::containers); - serverConfig[config_key::hostName] = newServerConfig.value(config_key::hostName); - - if (newServerConfig.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) { - serverConfig[config_key::configVersion] = newServerConfig.value(config_key::configVersion); - serverConfig[config_key::description] = newServerConfig.value(config_key::description); - serverConfig[config_key::name] = newServerConfig.value(config_key::name); - } - - auto defaultContainer = newServerConfig.value(config_key::defaultContainer).toString(); - serverConfig[config_key::defaultContainer] = defaultContainer; - - QVariantMap map = serverConfig.value(configKey::apiConfig).toObject().toVariantMap(); - map.insert(newServerConfig.value(configKey::apiConfig).toObject().toVariantMap()); - auto apiConfig = QJsonObject::fromVariantMap(map); - - if (newServerConfig.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) { - apiConfig.insert(apiDefs::key::supportedProtocols, - QJsonDocument::fromJson(apiResponseBody).object().value(apiDefs::key::supportedProtocols).toArray()); - } - - serverConfig[configKey::apiConfig] = apiConfig; - - return ErrorCode::NoError; - } } ApiConfigsController::ApiConfigsController(const QSharedPointer &serversModel, @@ -239,26 +63,22 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode, return false; } + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex()); auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject(); - GatewayRequestData gatewayRequestData { QSysInfo::productType(), - QString(APP_VERSION), - m_settings->getInstallationUuid(true), - apiConfigObject.value(configKey::userCountryCode).toString(), - serverCountryCode, - apiConfigObject.value(configKey::serviceType).toString(), - m_apiServicesModel->getSelectedServiceProtocol(), - serverConfigObject.value(configKey::authData).toObject() }; - QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString(); - ProtocolData protocolData = generateProtocolData(protocol); + ApiPayloadData apiPayloadData = generateApiPayloadData(protocol); - QJsonObject apiPayload = gatewayRequestData.toJsonObject(); - appendProtocolDataToApiPayload(gatewayRequestData.serviceProtocol, protocolData, apiPayload); + QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData); + apiPayload[configKey::userCountryCode] = apiConfigObject.value(configKey::userCountryCode); + apiPayload[configKey::serverCountryCode] = serverCountryCode; + apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType); + apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData); QByteArray responseBody; - ErrorCode errorCode = executeRequest(QString("%1v1/native_config"), apiPayload, responseBody); + ErrorCode errorCode = gatewayController.post(QString("%1v1/native_config"), apiPayload, responseBody); if (errorCode != ErrorCode::NoError) { emit errorOccurred(errorCode); return false; @@ -266,7 +86,7 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode, QJsonObject jsonConfig = QJsonDocument::fromJson(responseBody).object(); QString nativeConfig = jsonConfig.value(configKey::config).toString(); - nativeConfig.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", protocolData.wireGuardClientPrivKey); + nativeConfig.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey); SystemController::saveFile(fileName, nativeConfig); return true; @@ -274,22 +94,22 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode, bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode) { + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex()); auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject(); - GatewayRequestData gatewayRequestData { QSysInfo::productType(), - QString(APP_VERSION), - m_settings->getInstallationUuid(true), - apiConfigObject.value(configKey::userCountryCode).toString(), - serverCountryCode, - apiConfigObject.value(configKey::serviceType).toString(), - m_apiServicesModel->getSelectedServiceProtocol(), - serverConfigObject.value(configKey::authData).toObject() }; + QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString(); + ApiPayloadData apiPayloadData = generateApiPayloadData(protocol); - QJsonObject apiPayload = gatewayRequestData.toJsonObject(); + QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData); + apiPayload[configKey::userCountryCode] = apiConfigObject.value(configKey::userCountryCode); + apiPayload[configKey::serverCountryCode] = serverCountryCode; + apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType); + apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData); QByteArray responseBody; - ErrorCode errorCode = executeRequest(QString("%1v1/revoke_native_config"), apiPayload, responseBody); + ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_native_config"), apiPayload, responseBody); if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) { emit errorOccurred(errorCode); return false; @@ -320,11 +140,13 @@ void ApiConfigsController::copyVpnKeyToClipboard() bool ApiConfigsController::fillAvailableServices() { + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + QJsonObject apiPayload; apiPayload[configKey::osVersion] = QSysInfo::productType(); QByteArray responseBody; - ErrorCode errorCode = executeRequest(QString("%1v1/services"), apiPayload, responseBody); + ErrorCode errorCode = gatewayController.post(QString("%1v1/services"), apiPayload, responseBody); if (errorCode == ErrorCode::NoError) { if (!responseBody.contains("services")) { errorCode = ErrorCode::ApiServicesMissingError; @@ -343,36 +165,32 @@ bool ApiConfigsController::fillAvailableServices() bool ApiConfigsController::importServiceFromGateway() { - GatewayRequestData gatewayRequestData { QSysInfo::productType(), - QString(APP_VERSION), - m_settings->getInstallationUuid(true), - m_apiServicesModel->getCountryCode(), - "", - m_apiServicesModel->getSelectedServiceType(), - m_apiServicesModel->getSelectedServiceProtocol(), - QJsonObject() }; - - if (m_serversModel->isServerFromApiAlreadyExists(gatewayRequestData.userCountryCode, gatewayRequestData.serviceType, - gatewayRequestData.serviceProtocol)) { + if (m_serversModel->isServerFromApiAlreadyExists(m_apiServicesModel->getCountryCode(), m_apiServicesModel->getSelectedServiceType(), + m_apiServicesModel->getSelectedServiceProtocol())) { emit errorOccurred(ErrorCode::ApiConfigAlreadyAdded); return false; } - ProtocolData protocolData = generateProtocolData(gatewayRequestData.serviceProtocol); + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); - QJsonObject apiPayload = gatewayRequestData.toJsonObject(); - appendProtocolDataToApiPayload(gatewayRequestData.serviceProtocol, protocolData, apiPayload); + auto installationUuid = m_settings->getInstallationUuid(true); + auto userCountryCode = m_apiServicesModel->getCountryCode(); + auto serviceType = m_apiServicesModel->getSelectedServiceType(); + auto serviceProtocol = m_apiServicesModel->getSelectedServiceProtocol(); + + ApiPayloadData apiPayloadData = generateApiPayloadData(serviceProtocol); + + QJsonObject apiPayload = fillApiPayload(serviceProtocol, apiPayloadData); + apiPayload[configKey::userCountryCode] = userCountryCode; + apiPayload[configKey::serviceType] = serviceType; + apiPayload[configKey::uuid] = installationUuid; QByteArray responseBody; - ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody); + ErrorCode errorCode = gatewayController.post(QString("%1v1/config"), apiPayload, responseBody); QJsonObject serverConfig; if (errorCode == ErrorCode::NoError) { - errorCode = fillServerConfig(gatewayRequestData.serviceProtocol, protocolData, responseBody, serverConfig); - if (errorCode != ErrorCode::NoError) { - emit errorOccurred(errorCode); - return false; - } + fillServerConfig(serviceProtocol, apiPayloadData, responseBody, serverConfig); QJsonObject apiConfig = serverConfig.value(configKey::apiConfig).toObject(); apiConfig.insert(configKey::userCountryCode, m_apiServicesModel->getCountryCode()); @@ -393,33 +211,37 @@ bool ApiConfigsController::importServiceFromGateway() bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName, bool reloadServiceConfig) { + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + auto serverConfig = m_serversModel->getServerConfig(serverIndex); auto apiConfig = serverConfig.value(configKey::apiConfig).toObject(); + auto authData = serverConfig.value(configKey::authData).toObject(); - GatewayRequestData gatewayRequestData { QSysInfo::productType(), - QString(APP_VERSION), - m_settings->getInstallationUuid(true), - apiConfig.value(configKey::userCountryCode).toString(), - newCountryCode, - apiConfig.value(configKey::serviceType).toString(), - apiConfig.value(configKey::serviceProtocol).toString(), - serverConfig.value(configKey::authData).toObject() }; + auto installationUuid = m_settings->getInstallationUuid(true); + auto userCountryCode = apiConfig.value(configKey::userCountryCode).toString(); + auto serviceType = apiConfig.value(configKey::serviceType).toString(); + auto serviceProtocol = apiConfig.value(configKey::serviceProtocol).toString(); - ProtocolData protocolData = generateProtocolData(gatewayRequestData.serviceProtocol); + ApiPayloadData apiPayloadData = generateApiPayloadData(serviceProtocol); - QJsonObject apiPayload = gatewayRequestData.toJsonObject(); - appendProtocolDataToApiPayload(gatewayRequestData.serviceProtocol, protocolData, apiPayload); + QJsonObject apiPayload = fillApiPayload(serviceProtocol, apiPayloadData); + apiPayload[configKey::userCountryCode] = userCountryCode; + apiPayload[configKey::serviceType] = serviceType; + apiPayload[configKey::uuid] = installationUuid; + + if (!newCountryCode.isEmpty()) { + apiPayload[configKey::serverCountryCode] = newCountryCode; + } + if (!authData.isEmpty()) { + apiPayload[configKey::authData] = authData; + } QByteArray responseBody; - ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody); + ErrorCode errorCode = gatewayController.post(QString("%1v1/config"), apiPayload, responseBody); QJsonObject newServerConfig; if (errorCode == ErrorCode::NoError) { - errorCode = fillServerConfig(gatewayRequestData.serviceProtocol, protocolData, responseBody, newServerConfig); - if (errorCode != ErrorCode::NoError) { - emit errorOccurred(errorCode); - return false; - } + fillServerConfig(serviceProtocol, apiPayloadData, responseBody, newServerConfig); QJsonObject newApiConfig = newServerConfig.value(configKey::apiConfig).toObject(); newApiConfig.insert(configKey::userCountryCode, apiConfig.value(configKey::userCountryCode)); @@ -428,12 +250,8 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const newApiConfig.insert(apiDefs::key::vpnKey, apiConfig.value(apiDefs::key::vpnKey)); newServerConfig.insert(configKey::apiConfig, newApiConfig); - newServerConfig.insert(configKey::authData, gatewayRequestData.authData); + newServerConfig.insert(configKey::authData, authData); - if (serverConfig.value(config_key::nameOverriddenByUser).toBool()) { - newServerConfig.insert(config_key::name, serverConfig.value(config_key::name)); - newServerConfig.insert(config_key::nameOverriddenByUser, true); - } m_serversModel->editServer(newServerConfig, serverIndex); if (reloadServiceConfig) { emit reloadServerFromApiFinished(tr("API config reloaded")); @@ -456,20 +274,16 @@ bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex) QThread::msleep(10); #endif - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, - m_settings->isStrictKillSwitchEnabled()); + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); auto serverConfig = m_serversModel->getServerConfig(serverIndex); auto installationUuid = m_settings->getInstallationUuid(true); QString serviceProtocol = serverConfig.value(configKey::protocol).toString(); - ProtocolData protocolData = generateProtocolData(serviceProtocol); + ApiPayloadData apiPayloadData = generateApiPayloadData(serviceProtocol); - QJsonObject apiPayload; - appendProtocolDataToApiPayload(serviceProtocol, protocolData, apiPayload); + QJsonObject apiPayload = fillApiPayload(serviceProtocol, apiPayloadData); apiPayload[configKey::uuid] = installationUuid; - apiPayload[configKey::osVersion] = QSysInfo::productType(); - apiPayload[configKey::appVersion] = QString(APP_VERSION); apiPayload[configKey::accessToken] = serverConfig.value(configKey::accessToken).toString(); apiPayload[configKey::apiEndpoint] = serverConfig.value(configKey::apiEndpoint).toString(); @@ -477,11 +291,7 @@ bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex) ErrorCode errorCode = gatewayController.post(QString("%1v1/proxy_config"), apiPayload, responseBody); if (errorCode == ErrorCode::NoError) { - errorCode = fillServerConfig(serviceProtocol, protocolData, responseBody, serverConfig); - if (errorCode != ErrorCode::NoError) { - emit errorOccurred(errorCode); - return false; - } + fillServerConfig(serviceProtocol, apiPayloadData, responseBody, serverConfig); m_serversModel->editServer(serverConfig, serverIndex); emit updateServerFromApiFinished(); @@ -494,6 +304,8 @@ bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex) bool ApiConfigsController::deactivateDevice() { + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + auto serverIndex = m_serversModel->getProcessedServerIndex(); auto serverConfigObject = m_serversModel->getServerConfig(serverIndex); auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject(); @@ -502,19 +314,18 @@ bool ApiConfigsController::deactivateDevice() return true; } - GatewayRequestData gatewayRequestData { QSysInfo::productType(), - QString(APP_VERSION), - m_settings->getInstallationUuid(true), - apiConfigObject.value(configKey::userCountryCode).toString(), - apiConfigObject.value(configKey::serverCountryCode).toString(), - apiConfigObject.value(configKey::serviceType).toString(), - "", - serverConfigObject.value(configKey::authData).toObject() }; + QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString(); + ApiPayloadData apiPayloadData = generateApiPayloadData(protocol); - QJsonObject apiPayload = gatewayRequestData.toJsonObject(); + QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData); + apiPayload[configKey::userCountryCode] = apiConfigObject.value(configKey::userCountryCode); + apiPayload[configKey::serverCountryCode] = apiConfigObject.value(configKey::serverCountryCode); + apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType); + apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData); + apiPayload[configKey::uuid] = m_settings->getInstallationUuid(true); QByteArray responseBody; - ErrorCode errorCode = executeRequest(QString("%1v1/revoke_config"), apiPayload, responseBody); + ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody); if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) { emit errorOccurred(errorCode); return false; @@ -528,6 +339,8 @@ bool ApiConfigsController::deactivateDevice() bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode) { + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + auto serverIndex = m_serversModel->getProcessedServerIndex(); auto serverConfigObject = m_serversModel->getServerConfig(serverIndex); auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject(); @@ -536,19 +349,18 @@ bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const Q return true; } - GatewayRequestData gatewayRequestData { QSysInfo::productType(), - QString(APP_VERSION), - uuid, - apiConfigObject.value(configKey::userCountryCode).toString(), - serverCountryCode, - apiConfigObject.value(configKey::serviceType).toString(), - "", - serverConfigObject.value(configKey::authData).toObject() }; + QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString(); + ApiPayloadData apiPayloadData = generateApiPayloadData(protocol); - QJsonObject apiPayload = gatewayRequestData.toJsonObject(); + QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData); + apiPayload[configKey::userCountryCode] = apiConfigObject.value(configKey::userCountryCode); + apiPayload[configKey::serverCountryCode] = serverCountryCode; + apiPayload[configKey::serviceType] = apiConfigObject.value(configKey::serviceType); + apiPayload[configKey::authData] = serverConfigObject.value(configKey::authData); + apiPayload[configKey::uuid] = uuid; QByteArray responseBody; - ErrorCode errorCode = executeRequest(QString("%1v1/revoke_config"), apiPayload, responseBody); + ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody); if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) { emit errorOccurred(errorCode); return false; @@ -587,29 +399,108 @@ bool ApiConfigsController::isConfigValid() return true; } -void ApiConfigsController::setCurrentProtocol(const QString &protocolName) +ApiConfigsController::ApiPayloadData ApiConfigsController::generateApiPayloadData(const QString &protocol) { - auto serverIndex = m_serversModel->getProcessedServerIndex(); - auto serverConfigObject = m_serversModel->getServerConfig(serverIndex); - auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject(); - - apiConfigObject[configKey::serviceProtocol] = protocolName; - - serverConfigObject.insert(configKey::apiConfig, apiConfigObject); - - m_serversModel->editServer(serverConfigObject, serverIndex); + ApiConfigsController::ApiPayloadData apiPayload; + if (protocol == configKey::cloak) { + apiPayload.certRequest = OpenVpnConfigurator::createCertRequest(); + } else if (protocol == configKey::awg) { + auto connData = WireguardConfigurator::genClientKeys(); + apiPayload.wireGuardClientPubKey = connData.clientPubKey; + apiPayload.wireGuardClientPrivKey = connData.clientPrivKey; + } + return apiPayload; } -bool ApiConfigsController::isVlessProtocol() +QJsonObject ApiConfigsController::fillApiPayload(const QString &protocol, const ApiPayloadData &apiPayloadData) { - auto serverIndex = m_serversModel->getProcessedServerIndex(); - auto serverConfigObject = m_serversModel->getServerConfig(serverIndex); - auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject(); - - if (apiConfigObject[configKey::serviceProtocol].toString() == "vless") { - return true; + QJsonObject obj; + if (protocol == configKey::cloak) { + obj[configKey::certificate] = apiPayloadData.certRequest.request; + } else if (protocol == configKey::awg) { + obj[configKey::publicKey] = apiPayloadData.wireGuardClientPubKey; } - return false; + + obj[configKey::osVersion] = QSysInfo::productType(); + obj[configKey::appVersion] = QString(APP_VERSION); + + return obj; +} + +void ApiConfigsController::fillServerConfig(const QString &protocol, const ApiPayloadData &apiPayloadData, + const QByteArray &apiResponseBody, QJsonObject &serverConfig) +{ + QString data = QJsonDocument::fromJson(apiResponseBody).object().value(config_key::config).toString(); + + data.replace("vpn://", ""); + QByteArray ba = QByteArray::fromBase64(data.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); + + if (ba.isEmpty()) { + emit errorOccurred(ErrorCode::ApiConfigEmptyError); + return; + } + + QByteArray ba_uncompressed = qUncompress(ba); + if (!ba_uncompressed.isEmpty()) { + ba = ba_uncompressed; + } + + QString configStr = ba; + if (protocol == configKey::cloak) { + configStr.replace("", "\n"); + configStr.replace("$OPENVPN_PRIV_KEY", apiPayloadData.certRequest.privKey); + } else if (protocol == configKey::awg) { + configStr.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey); + auto newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object(); + auto containers = newServerConfig.value(config_key::containers).toArray(); + if (containers.isEmpty()) { + return; // todo process error + } + auto container = containers.at(0).toObject(); + QString containerName = ContainerProps::containerTypeToString(DockerContainer::Awg); + auto containerConfig = container.value(containerName).toObject(); + auto protocolConfig = QJsonDocument::fromJson(containerConfig.value(config_key::last_config).toString().toUtf8()).object(); + containerConfig[config_key::junkPacketCount] = protocolConfig.value(config_key::junkPacketCount); + containerConfig[config_key::junkPacketMinSize] = protocolConfig.value(config_key::junkPacketMinSize); + containerConfig[config_key::junkPacketMaxSize] = protocolConfig.value(config_key::junkPacketMaxSize); + containerConfig[config_key::initPacketJunkSize] = protocolConfig.value(config_key::initPacketJunkSize); + containerConfig[config_key::responsePacketJunkSize] = protocolConfig.value(config_key::responsePacketJunkSize); + containerConfig[config_key::initPacketMagicHeader] = protocolConfig.value(config_key::initPacketMagicHeader); + containerConfig[config_key::responsePacketMagicHeader] = protocolConfig.value(config_key::responsePacketMagicHeader); + containerConfig[config_key::underloadPacketMagicHeader] = protocolConfig.value(config_key::underloadPacketMagicHeader); + containerConfig[config_key::transportPacketMagicHeader] = protocolConfig.value(config_key::transportPacketMagicHeader); + container[containerName] = containerConfig; + containers.replace(0, container); + newServerConfig[config_key::containers] = containers; + configStr = QString(QJsonDocument(newServerConfig).toJson()); + } + + QJsonObject newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object(); + serverConfig[config_key::dns1] = newServerConfig.value(config_key::dns1); + serverConfig[config_key::dns2] = newServerConfig.value(config_key::dns2); + serverConfig[config_key::containers] = newServerConfig.value(config_key::containers); + serverConfig[config_key::hostName] = newServerConfig.value(config_key::hostName); + + if (newServerConfig.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) { + serverConfig[config_key::configVersion] = newServerConfig.value(config_key::configVersion); + serverConfig[config_key::description] = newServerConfig.value(config_key::description); + serverConfig[config_key::name] = newServerConfig.value(config_key::name); + } + + auto defaultContainer = newServerConfig.value(config_key::defaultContainer).toString(); + serverConfig[config_key::defaultContainer] = defaultContainer; + + QVariantMap map = serverConfig.value(configKey::apiConfig).toObject().toVariantMap(); + map.insert(newServerConfig.value(configKey::apiConfig).toObject().toVariantMap()); + auto apiConfig = QJsonObject::fromVariantMap(map); + + if (newServerConfig.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) { + apiConfig.insert(configKey::serviceInfo, QJsonDocument::fromJson(apiResponseBody).object().value(configKey::serviceInfo).toObject()); + } + + serverConfig[configKey::apiConfig] = apiConfig; + + return; } QList ApiConfigsController::getQrCodes() @@ -626,10 +517,3 @@ QString ApiConfigsController::getVpnKey() { return m_vpnKey; } - -ErrorCode ApiConfigsController::executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody) -{ - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, - m_settings->isStrictKillSwitchEnabled()); - return gatewayController.post(endpoint, apiPayload, responseBody); -} diff --git a/client/ui/controllers/api/apiConfigsController.h b/client/ui/controllers/api/apiConfigsController.h index a04a142c..2fe981e4 100644 --- a/client/ui/controllers/api/apiConfigsController.h +++ b/client/ui/controllers/api/apiConfigsController.h @@ -35,9 +35,6 @@ public slots: bool isConfigValid(); - void setCurrentProtocol(const QString &protocolName); - bool isVlessProtocol(); - signals: void errorOccurred(ErrorCode errorCode); @@ -49,12 +46,23 @@ signals: void vpnKeyExportReady(); private: + struct ApiPayloadData + { + OpenVpnConfigurator::ConnectionData certRequest; + + QString wireGuardClientPrivKey; + QString wireGuardClientPubKey; + }; + + ApiPayloadData generateApiPayloadData(const QString &protocol); + QJsonObject fillApiPayload(const QString &protocol, const ApiPayloadData &apiPayloadData); + void fillServerConfig(const QString &protocol, const ApiPayloadData &apiPayloadData, const QByteArray &apiResponseBody, + QJsonObject &serverConfig); + QList getQrCodes(); int getQrCodesCount(); QString getVpnKey(); - ErrorCode executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody); - QList m_qrCodes; QString m_vpnKey; diff --git a/client/ui/controllers/api/apiPremV1MigrationController.cpp b/client/ui/controllers/api/apiPremV1MigrationController.cpp deleted file mode 100644 index 77352003..00000000 --- a/client/ui/controllers/api/apiPremV1MigrationController.cpp +++ /dev/null @@ -1,133 +0,0 @@ -#include "apiPremV1MigrationController.h" - -#include -#include - -#include "core/api/apiDefs.h" -#include "core/api/apiUtils.h" -#include "core/controllers/gatewayController.h" - -ApiPremV1MigrationController::ApiPremV1MigrationController(const QSharedPointer &serversModel, - const std::shared_ptr &settings, QObject *parent) - : QObject(parent), m_serversModel(serversModel), m_settings(settings) -{ -} - -bool ApiPremV1MigrationController::hasConfigsToMigration() -{ - QJsonArray vpnKeys; - - auto serversCount = m_serversModel->getServersCount(); - for (size_t i = 0; i < serversCount; i++) { - auto serverConfigObject = m_serversModel->getServerConfig(i); - - if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV1) { - continue; - } - - QString vpnKey = apiUtils::getPremiumV1VpnKey(serverConfigObject); - vpnKeys.append(vpnKey); - } - - if (!vpnKeys.isEmpty()) { - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, - m_settings->isStrictKillSwitchEnabled()); - QJsonObject apiPayload; - - apiPayload["configs"] = vpnKeys; - QByteArray responseBody; - ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/is-active-subscription"), apiPayload, responseBody); - - auto migrationsStatus = QJsonDocument::fromJson(responseBody).object(); - for (const auto &migrationStatus : migrationsStatus) { - if (migrationStatus == "not_found") { - return true; - } - } - } - - return false; -} - -void ApiPremV1MigrationController::getSubscriptionList(const QString &email) -{ - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, - m_settings->isStrictKillSwitchEnabled()); - QJsonObject apiPayload; - - apiPayload[apiDefs::key::email] = email; - QByteArray responseBody; - ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/subscription-list"), apiPayload, responseBody); - - if (errorCode == ErrorCode::NoError) { - m_email = email; - m_subscriptionsModel = QJsonDocument::fromJson(responseBody).array(); - if (m_subscriptionsModel.isEmpty()) { - emit noSubscriptionToMigrate(); - return; - } - - emit subscriptionsModelChanged(); - } else { - emit errorOccurred(ErrorCode::ApiMigrationError); - } -} - -QJsonArray ApiPremV1MigrationController::getSubscriptionModel() -{ - return m_subscriptionsModel; -} - -void ApiPremV1MigrationController::sendMigrationCode(const int subscriptionIndex) -{ - QEventLoop wait; - QTimer::singleShot(1000, &wait, &QEventLoop::quit); - wait.exec(); - - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, - m_settings->isStrictKillSwitchEnabled()); - QJsonObject apiPayload; - - apiPayload[apiDefs::key::email] = m_email; - QByteArray responseBody; - ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/migration-code"), apiPayload, responseBody); - - if (errorCode == ErrorCode::NoError) { - m_subscriptionIndex = subscriptionIndex; - emit otpSuccessfullySent(); - } else { - emit errorOccurred(ErrorCode::ApiMigrationError); - } -} - -void ApiPremV1MigrationController::migrate(const QString &migrationCode) -{ - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, - m_settings->isStrictKillSwitchEnabled()); - QJsonObject apiPayload; - - apiPayload[apiDefs::key::email] = m_email; - apiPayload[apiDefs::key::orderId] = m_subscriptionsModel.at(m_subscriptionIndex).toObject().value(apiDefs::key::id).toString(); - apiPayload[apiDefs::key::migrationCode] = migrationCode; - QByteArray responseBody; - ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/migrate"), apiPayload, responseBody); - - if (errorCode == ErrorCode::NoError) { - auto responseObject = QJsonDocument::fromJson(responseBody).object(); - QString premiumV2VpnKey = responseObject.value(apiDefs::key::config).toString(); - - emit importPremiumV2VpnKey(premiumV2VpnKey); - } else { - emit errorOccurred(ErrorCode::ApiMigrationError); - } -} - -bool ApiPremV1MigrationController::isPremV1MigrationReminderActive() -{ - return m_settings->isPremV1MigrationReminderActive(); -} - -void ApiPremV1MigrationController::disablePremV1MigrationReminder() -{ - m_settings->disablePremV1MigrationReminder(); -} diff --git a/client/ui/controllers/api/apiPremV1MigrationController.h b/client/ui/controllers/api/apiPremV1MigrationController.h deleted file mode 100644 index d7c10460..00000000 --- a/client/ui/controllers/api/apiPremV1MigrationController.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef APIPREMV1MIGRATIONCONTROLLER_H -#define APIPREMV1MIGRATIONCONTROLLER_H - -#include - -#include "ui/models/servers_model.h" - -class ApiPremV1MigrationController : public QObject -{ - Q_OBJECT -public: - ApiPremV1MigrationController(const QSharedPointer &serversModel, const std::shared_ptr &settings, - QObject *parent = nullptr); - - Q_PROPERTY(QJsonArray subscriptionsModel READ getSubscriptionModel NOTIFY subscriptionsModelChanged) - -public slots: - bool hasConfigsToMigration(); - void getSubscriptionList(const QString &email); - QJsonArray getSubscriptionModel(); - void sendMigrationCode(const int subscriptionIndex); - void migrate(const QString &migrationCode); - - bool isPremV1MigrationReminderActive(); - void disablePremV1MigrationReminder(); - -signals: - void subscriptionsModelChanged(); - - void otpSuccessfullySent(); - - void importPremiumV2VpnKey(const QString &vpnKey); - - void errorOccurred(ErrorCode errorCode); - - void showMigrationDrawer(); - void migrationFinished(); - - void noSubscriptionToMigrate(); - -private: - QSharedPointer m_serversModel; - std::shared_ptr m_settings; - - QJsonArray m_subscriptionsModel; - int m_subscriptionIndex; - QString m_email; -}; - -#endif // APIPREMV1MIGRATIONCONTROLLER_H diff --git a/client/ui/controllers/api/apiSettingsController.cpp b/client/ui/controllers/api/apiSettingsController.cpp index c4a75a5b..8927312d 100644 --- a/client/ui/controllers/api/apiSettingsController.cpp +++ b/client/ui/controllers/api/apiSettingsController.cpp @@ -5,7 +5,6 @@ #include "core/api/apiUtils.h" #include "core/controllers/gatewayController.h" -#include "version.h" namespace { @@ -49,8 +48,7 @@ bool ApiSettingsController::getAccountInfo(bool reload) wait.exec(); } - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), requestTimeoutMsecs, - m_settings->isStrictKillSwitchEnabled()); + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), requestTimeoutMsecs); auto processedIndex = m_serversModel->getProcessedServerIndex(); auto serverConfig = m_serversModel->getServerConfig(processedIndex); @@ -61,14 +59,15 @@ bool ApiSettingsController::getAccountInfo(bool reload) apiPayload[configKey::userCountryCode] = apiConfig.value(configKey::userCountryCode).toString(); apiPayload[configKey::serviceType] = apiConfig.value(configKey::serviceType).toString(); apiPayload[configKey::authData] = authData; - apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION); QByteArray responseBody; - ErrorCode errorCode = gatewayController.post(QString("%1v1/account_info"), apiPayload, responseBody); - if (errorCode != ErrorCode::NoError) { - emit errorOccurred(errorCode); - return false; + if (apiUtils::isPremiumServer(serverConfig)) { + ErrorCode errorCode = gatewayController.post(QString("%1v1/account_info"), apiPayload, responseBody); + if (errorCode != ErrorCode::NoError) { + emit errorOccurred(errorCode); + return false; + } } QJsonObject accountInfo = QJsonDocument::fromJson(responseBody).object(); diff --git a/client/ui/controllers/exportController.cpp b/client/ui/controllers/exportController.cpp index c487e3b2..b47111ae 100644 --- a/client/ui/controllers/exportController.cpp +++ b/client/ui/controllers/exportController.cpp @@ -145,7 +145,7 @@ void ExportController::generateOpenVpnConfig(const QString &clientName) } QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n"); - for (const QString &line : std::as_const(lines)) { + for (const QString &line : lines) { m_config.append(line + "\n"); } @@ -163,7 +163,7 @@ void ExportController::generateWireGuardConfig(const QString &clientName) } QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n"); - for (const QString &line : std::as_const(lines)) { + for (const QString &line : lines) { m_config.append(line + "\n"); } @@ -183,7 +183,7 @@ void ExportController::generateAwgConfig(const QString &clientName) } QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n"); - for (const QString &line : std::as_const(lines)) { + for (const QString &line : lines) { m_config.append(line + "\n"); } @@ -211,7 +211,7 @@ void ExportController::generateShadowSocksConfig() } QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n"); - for (const QString &line : std::as_const(lines)) { + for (const QString &line : lines) { m_config.append(line + "\n"); } @@ -240,7 +240,7 @@ void ExportController::generateCloakConfig() nativeConfig.insert("ProxyMethod", "shadowsocks"); QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n"); - for (const QString &line : std::as_const(lines)) { + for (const QString &line : lines) { m_config.append(line + "\n"); } @@ -257,7 +257,7 @@ void ExportController::generateXrayConfig(const QString &clientName) } QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n"); - for (const QString &line : std::as_const(lines)) { + for (const QString &line : lines) { m_config.append(line + "\n"); } diff --git a/client/ui/controllers/importController.cpp b/client/ui/controllers/importController.cpp index ea1d5d8e..65b54f56 100644 --- a/client/ui/controllers/importController.cpp +++ b/client/ui/controllers/importController.cpp @@ -12,7 +12,6 @@ #include "core/errorstrings.h" #include "core/qrCodeUtils.h" #include "core/serialization/serialization.h" -#include "protocols/protocols_defs.h" #include "systemController.h" #include "utilities.h" @@ -287,19 +286,6 @@ void ImportController::processNativeWireGuardConfig() clientProtocolConfig[config_key::underloadPacketMagicHeader] = "3"; clientProtocolConfig[config_key::transportPacketMagicHeader] = "4"; - // clientProtocolConfig[config_key::cookieReplyPacketJunkSize] = "0"; - // clientProtocolConfig[config_key::transportPacketJunkSize] = "0"; - - // clientProtocolConfig[config_key::specialJunk1] = ""; - // clientProtocolConfig[config_key::specialJunk2] = ""; - // clientProtocolConfig[config_key::specialJunk3] = ""; - // clientProtocolConfig[config_key::specialJunk4] = ""; - // clientProtocolConfig[config_key::specialJunk5] = ""; - // clientProtocolConfig[config_key::controlledJunk1] = ""; - // clientProtocolConfig[config_key::controlledJunk2] = ""; - // clientProtocolConfig[config_key::controlledJunk3] = ""; - // clientProtocolConfig[config_key::specialHandshakeTimeout] = "0"; - clientProtocolConfig[config_key::isObfuscationEnabled] = true; serverProtocolConfig[config_key::last_config] = QString(QJsonDocument(clientProtocolConfig).toJson()); @@ -452,33 +438,21 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data) lastConfig[config_key::allowed_ips] = allowedIpsJsonArray; QString protocolName = "wireguard"; - - const QStringList requiredJunkFields = { config_key::junkPacketCount, config_key::junkPacketMinSize, - config_key::junkPacketMaxSize, config_key::initPacketJunkSize, - config_key::responsePacketJunkSize, config_key::initPacketMagicHeader, - config_key::responsePacketMagicHeader, config_key::underloadPacketMagicHeader, - config_key::transportPacketMagicHeader }; - - const QStringList optionalJunkFields = { // config_key::cookieReplyPacketJunkSize, - // config_key::transportPacketJunkSize, - config_key::specialJunk1, config_key::specialJunk2, config_key::specialJunk3, - config_key::specialJunk4, config_key::specialJunk5, config_key::controlledJunk1, - config_key::controlledJunk2, config_key::controlledJunk3, config_key::specialHandshakeTimeout - }; - - bool hasAllRequiredFields = std::all_of(requiredJunkFields.begin(), requiredJunkFields.end(), - [&configMap](const QString &field) { return !configMap.value(field).isEmpty(); }); - if (hasAllRequiredFields) { - for (const QString &field : requiredJunkFields) { - lastConfig[field] = configMap.value(field); - } - - for (const QString &field : optionalJunkFields) { - if (!configMap.value(field).isEmpty()) { - lastConfig[field] = configMap.value(field); - } - } - + if (!configMap.value(config_key::junkPacketCount).isEmpty() && !configMap.value(config_key::junkPacketMinSize).isEmpty() + && !configMap.value(config_key::junkPacketMaxSize).isEmpty() && !configMap.value(config_key::initPacketJunkSize).isEmpty() + && !configMap.value(config_key::responsePacketJunkSize).isEmpty() && !configMap.value(config_key::initPacketMagicHeader).isEmpty() + && !configMap.value(config_key::responsePacketMagicHeader).isEmpty() + && !configMap.value(config_key::underloadPacketMagicHeader).isEmpty() + && !configMap.value(config_key::transportPacketMagicHeader).isEmpty()) { + lastConfig[config_key::junkPacketCount] = configMap.value(config_key::junkPacketCount); + lastConfig[config_key::junkPacketMinSize] = configMap.value(config_key::junkPacketMinSize); + lastConfig[config_key::junkPacketMaxSize] = configMap.value(config_key::junkPacketMaxSize); + lastConfig[config_key::initPacketJunkSize] = configMap.value(config_key::initPacketJunkSize); + lastConfig[config_key::responsePacketJunkSize] = configMap.value(config_key::responsePacketJunkSize); + lastConfig[config_key::initPacketMagicHeader] = configMap.value(config_key::initPacketMagicHeader); + lastConfig[config_key::responsePacketMagicHeader] = configMap.value(config_key::responsePacketMagicHeader); + lastConfig[config_key::underloadPacketMagicHeader] = configMap.value(config_key::underloadPacketMagicHeader); + lastConfig[config_key::transportPacketMagicHeader] = configMap.value(config_key::transportPacketMagicHeader); protocolName = "awg"; m_configType = ConfigTypes::Awg; } @@ -682,39 +656,47 @@ void ImportController::checkForMaliciousStrings(const QJsonObject &serverConfig) const QJsonArray &containers = serverConfig[config_key::containers].toArray(); for (const QJsonValue &container : containers) { auto containerConfig = container.toObject(); - auto containerName = containerConfig[config_key::container].toString(); - if ((containerName == ContainerProps::containerToString(DockerContainer::OpenVpn)) - || (containerName == ContainerProps::containerToString(DockerContainer::Cloak)) - || (containerName == ContainerProps::containerToString(DockerContainer::ShadowSocks))) { + QString containerName = containerConfig[config_key::container].toString(); - QString protocolConfig = - containerConfig[ProtocolProps::protoToString(Proto::OpenVpn)].toObject()[config_key::last_config].toString(); - QString protocolConfigJson = QJsonDocument::fromJson(protocolConfig.toUtf8()).object()[config_key::config].toString(); + if (containerName == ContainerProps::containerToString(DockerContainer::OpenVpn) + || containerName == ContainerProps::containerToString(DockerContainer::Cloak) + || containerName == ContainerProps::containerToString(DockerContainer::ShadowSocks)) + { + QString protoCfgB64 = containerConfig[ProtocolProps::protoToString(Proto::OpenVpn)] + .toObject()[config_key::last_config].toString(); + QString cfgJson = QJsonDocument::fromJson(protoCfgB64.toUtf8()) + .object()[config_key::config].toString(); + + QStringList lines = cfgJson.replace("\r", "").split('\n'); + + const size_t dangerousTagsMaxCount = 1; // https://github.com/OpenVPN/openvpn/blob/master/doc/man-sections/script-options.rst - QStringList dangerousTags { - "up", "tls-verify", "ipchange", "client-connect", "route-up", "route-pre-down", "client-disconnect", "down", "learn-address", "auth-user-pass-verify" + const QStringList dangerousTags { + "up", "tls-verify", "ipchange", "client-connect", + "route-up", "route-pre-down", "client-disconnect", + "down", "learn-address", "auth-user-pass-verify" }; + const QStringList allowedTags { "up", "down" }; - QStringList maliciousStrings; - QStringList lines = protocolConfigJson.split('\n', Qt::SkipEmptyParts); - - for (const QString &rawLine : lines) { - QString line = rawLine.trimmed(); - - QString command = line.section(' ', 0, 0, QString::SectionSkipEmpty); - if (dangerousTags.contains(command, Qt::CaseInsensitive)) { - maliciousStrings << rawLine; + QStringList found; + for (QString line : lines) { + line = line.trimmed(); + if (line.isEmpty() || line.startsWith('#') || line.startsWith(';')) + continue; + QString tag = line.section(QRegularExpression("\\s+"), 0, 0); + if (dangerousTags.contains(tag) && !allowedTags.contains(tag)) { + found << line; } } m_maliciousWarningText = tr("This configuration contains an OpenVPN setup. OpenVPN configurations can include malicious " "scripts, so only add it if you fully trust the provider of this config. "); - if (!maliciousStrings.isEmpty()) { - m_maliciousWarningText.push_back(tr("
In the imported configuration, potentially dangerous lines were found:")); - for (const auto &string : maliciousStrings) { - m_maliciousWarningText.push_back(QString("
%1").arg(string)); + if (found.size() >= dangerousTagsMaxCount) { + m_maliciousWarningText += tr("
Potentially dangerous directives found:"); + for (auto &l : found) { + m_maliciousWarningText += QString("
%1").arg(l); } } } diff --git a/client/ui/controllers/installController.cpp b/client/ui/controllers/installController.cpp index d7f9dfbc..7a6d8d40 100755 --- a/client/ui/controllers/installController.cpp +++ b/client/ui/controllers/installController.cpp @@ -8,7 +8,6 @@ #include #include -#include "core/api/apiUtils.h" #include "core/controllers/serverController.h" #include "core/controllers/vpnConfigurationController.h" #include "core/networkUtilities.h" @@ -16,6 +15,7 @@ #include "ui/models/protocols/awgConfigModel.h" #include "ui/models/protocols/wireguardConfigModel.h" #include "utilities.h" +#include "core/api/apiUtils.h" namespace { @@ -79,36 +79,12 @@ void InstallController::install(DockerContainer container, int port, TransportPr int s1 = QRandomGenerator::global()->bounded(15, 150); int s2 = QRandomGenerator::global()->bounded(15, 150); - // int s3 = QRandomGenerator::global()->bounded(15, 150); - // int s4 = QRandomGenerator::global()->bounded(15, 150); - - // Ensure all values are unique and don't create equal packet sizes - QSet usedValues; - usedValues.insert(s1); - - while (usedValues.contains(s2) || s1 + AwgConstant::messageInitiationSize == s2 + AwgConstant::messageResponseSize) { + while (s1 + AwgConstant::messageInitiationSize == s2 + AwgConstant::messageResponseSize) { s2 = QRandomGenerator::global()->bounded(15, 150); } - usedValues.insert(s2); - - // while (usedValues.contains(s3) - // || s1 + AwgConstant::messageInitiationSize == s3 + AwgConstant::messageCookieReplySize - // || s2 + AwgConstant::messageResponseSize == s3 + AwgConstant::messageCookieReplySize) { - // s3 = QRandomGenerator::global()->bounded(15, 150); - // } - // usedValues.insert(s3); - - // while (usedValues.contains(s4) - // || s1 + AwgConstant::messageInitiationSize == s4 + AwgConstant::messageTransportSize - // || s2 + AwgConstant::messageResponseSize == s4 + AwgConstant::messageTransportSize - // || s3 + AwgConstant::messageCookieReplySize == s4 + AwgConstant::messageTransportSize) { - // s4 = QRandomGenerator::global()->bounded(15, 150); - // } QString initPacketJunkSize = QString::number(s1); QString responsePacketJunkSize = QString::number(s2); - // QString cookieReplyPacketJunkSize = QString::number(s3); - // QString transportPacketJunkSize = QString::number(s4); QSet headersValue; while (headersValue.size() != 4) { @@ -132,21 +108,6 @@ void InstallController::install(DockerContainer container, int port, TransportPr containerConfig[config_key::responsePacketMagicHeader] = responsePacketMagicHeader; containerConfig[config_key::underloadPacketMagicHeader] = underloadPacketMagicHeader; containerConfig[config_key::transportPacketMagicHeader] = transportPacketMagicHeader; - - // TODO: - // containerConfig[config_key::cookieReplyPacketJunkSize] = cookieReplyPacketJunkSize; - // containerConfig[config_key::transportPacketJunkSize] = transportPacketJunkSize; - - // containerConfig[config_key::specialJunk1] = specialJunk1; - // containerConfig[config_key::specialJunk2] = specialJunk2; - // containerConfig[config_key::specialJunk3] = specialJunk3; - // containerConfig[config_key::specialJunk4] = specialJunk4; - // containerConfig[config_key::specialJunk5] = specialJunk5; - // containerConfig[config_key::controlledJunk1] = controlledJunk1; - // containerConfig[config_key::controlledJunk2] = controlledJunk2; - // containerConfig[config_key::controlledJunk3] = controlledJunk3; - // containerConfig[config_key::specialHandshakeTimeout] = specialHandshakeTimeout; - } else if (container == DockerContainer::Sftp) { containerConfig.insert(config_key::userName, protocols::sftp::defaultUserName); containerConfig.insert(config_key::password, Utils::getRandomString(16)); @@ -402,8 +363,7 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia QJsonObject config; Proto mainProto = ContainerProps::defaultProtocol(container); - const auto &protocols = ContainerProps::protocolsForContainer(container); - for (const auto &protocol : protocols) { + for (auto protocol : ContainerProps::protocolsForContainer(container)) { QJsonObject containerConfig; if (protocol == mainProto) { containerConfig.insert(config_key::port, port); @@ -427,7 +387,6 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia } } - containerConfig[config_key::subnet_address] = serverConfigMap.value("Address").remove("/24"); containerConfig[config_key::junkPacketCount] = serverConfigMap.value(config_key::junkPacketCount); containerConfig[config_key::junkPacketMinSize] = serverConfigMap.value(config_key::junkPacketMinSize); containerConfig[config_key::junkPacketMaxSize] = serverConfigMap.value(config_key::junkPacketMaxSize); @@ -439,38 +398,6 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia serverConfigMap.value(config_key::underloadPacketMagicHeader); containerConfig[config_key::transportPacketMagicHeader] = serverConfigMap.value(config_key::transportPacketMagicHeader); - - // containerConfig[config_key::cookieReplyPacketJunkSize] = serverConfigMap.value(config_key::cookieReplyPacketJunkSize); - // containerConfig[config_key::transportPacketJunkSize] = serverConfigMap.value(config_key::transportPacketJunkSize); - - // containerConfig[config_key::specialJunk1] = serverConfigMap.value(config_key::specialJunk1); - // containerConfig[config_key::specialJunk2] = serverConfigMap.value(config_key::specialJunk2); - // containerConfig[config_key::specialJunk3] = serverConfigMap.value(config_key::specialJunk3); - // containerConfig[config_key::specialJunk4] = serverConfigMap.value(config_key::specialJunk4); - // containerConfig[config_key::specialJunk5] = serverConfigMap.value(config_key::specialJunk5); - // containerConfig[config_key::controlledJunk1] = serverConfigMap.value(config_key::controlledJunk1); - // containerConfig[config_key::controlledJunk2] = serverConfigMap.value(config_key::controlledJunk2); - // containerConfig[config_key::controlledJunk3] = serverConfigMap.value(config_key::controlledJunk3); - // containerConfig[config_key::specialHandshakeTimeout] = serverConfigMap.value(config_key::specialHandshakeTimeout); - - } else if (protocol == Proto::WireGuard) { - QString serverConfig = serverController->getTextFileFromContainer(container, credentials, - protocols::wireguard::serverConfigPath, errorCode); - - QMap serverConfigMap; - auto serverConfigLines = serverConfig.split("\n"); - for (auto &line : serverConfigLines) { - auto trimmedLine = line.trimmed(); - if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) { - continue; - } else { - QStringList parts = trimmedLine.split(" = "); - if (parts.count() == 2) { - serverConfigMap.insert(parts[0].trimmed(), parts[1].trimmed()); - } - } - } - containerConfig[config_key::subnet_address] = serverConfigMap.value("Address").remove("/24"); } else if (protocol == Proto::Sftp) { stdOut.clear(); script = QString("sudo docker inspect --format '{{.Config.Cmd}}' %1").arg(name); @@ -505,51 +432,6 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia containerConfig.insert(config_key::userName, userName); containerConfig.insert(config_key::password, password); } - } else if (protocol == Proto::Xray) { - QString currentConfig = serverController->getTextFileFromContainer( - container, credentials, amnezia::protocols::xray::serverConfigPath, errorCode); - - QJsonDocument doc = QJsonDocument::fromJson(currentConfig.toUtf8()); - qDebug() << doc; - if (doc.isNull() || !doc.isObject()) { - logger.error() << "Failed to parse server config JSON"; - errorCode = ErrorCode::InternalError; - return errorCode; - } - QJsonObject serverConfig = doc.object(); - - if (!serverConfig.contains("inbounds")) { - logger.error() << "Server config missing 'inbounds' field"; - errorCode = ErrorCode::InternalError; - return errorCode; - } - - QJsonArray inbounds = serverConfig["inbounds"].toArray(); - if (inbounds.isEmpty()) { - logger.error() << "Server config has empty 'inbounds' array"; - errorCode = ErrorCode::InternalError; - return errorCode; - } - - QJsonObject inbound = inbounds[0].toObject(); - if (!inbound.contains("streamSettings")) { - logger.error() << "Inbound missing 'streamSettings' field"; - errorCode = ErrorCode::InternalError; - return errorCode; - } - - QJsonObject streamSettings = inbound["streamSettings"].toObject(); - QJsonObject realitySettings = streamSettings["realitySettings"].toObject(); - if (!realitySettings.contains("serverNames")) { - logger.error() << "Settings missing 'clients' field"; - errorCode = ErrorCode::InternalError; - return errorCode; - } - - QString siteName = realitySettings["serverNames"][0].toString(); - qDebug() << siteName; - - containerConfig.insert(config_key::site, siteName); } config.insert(config_key::container, ContainerProps::containerToString(container)); diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index f8e97a1f..c5c569db 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -126,13 +126,7 @@ void SettingsController::clearLogs() void SettingsController::backupAppConfig(const QString &fileName) { - QByteArray data = m_settings->backupAppConfig(); - QJsonDocument doc = QJsonDocument::fromJson(data); - QJsonObject config = doc.object(); - - config["Conf/autoStart"] = Autostart::isAutostart(); - - SystemController::saveFile(fileName, QJsonDocument(config).toJson()); + SystemController::saveFile(fileName, m_settings->backupAppConfig()); } void SettingsController::restoreAppConfig(const QString &fileName) @@ -146,30 +140,9 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data) { bool ok = m_settings->restoreAppConfig(data); if (ok) { - QJsonObject newConfigData = QJsonDocument::fromJson(data).object(); - -#if defined(Q_OS_WINDOWS) || defined(Q_OS_LINUX) || defined(Q_OS_MACX) - bool autoStart = false; - if (newConfigData.contains("Conf/autoStart")) { - autoStart = newConfigData["Conf/autoStart"].toBool(); - } - toggleAutoStart(autoStart); -#endif m_serversModel->resetModel(); m_languageModel->changeLanguage( static_cast(m_languageModel->getCurrentLanguageIndex())); - -#if defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID) - int appSplitTunnelingRouteMode = newConfigData.value("Conf/appsRouteMode").toInt(); - bool appSplittunnelingEnabled = newConfigData.value("Conf/appsSplitTunnelingEnabled").toBool(); - m_appSplitTunnelingModel->setRouteMode(appSplitTunnelingRouteMode); - m_appSplitTunnelingModel->toggleSplitTunneling(appSplittunnelingEnabled); -#endif - int siteSplitTunnelingRouteMode = newConfigData.value("Conf/routeMode").toInt(); - bool siteSplittunnelingEnabled = newConfigData.value("Conf/sitesSplitTunnelingEnabled").toBool(); - m_sitesModel->setRouteMode(siteSplitTunnelingRouteMode); - m_sitesModel->toggleSplitTunneling(siteSplittunnelingEnabled); - emit restoreBackupFinished(); } else { emit changeSettingsErrorOccurred(tr("Backup file is corrupted")); @@ -194,8 +167,6 @@ void SettingsController::clearSettings() m_appSplitTunnelingModel->setRouteMode(Settings::AppsRouteMode::VpnAllExceptApps); m_appSplitTunnelingModel->toggleSplitTunneling(false); - toggleAutoStart(false); - emit changeSettingsFinished(tr("All settings have been reset to default values")); #ifdef Q_OS_IOS diff --git a/client/ui/models/api/apiAccountInfoModel.cpp b/client/ui/models/api/apiAccountInfoModel.cpp index bd3027a4..fdd4e2ca 100644 --- a/client/ui/models/api/apiAccountInfoModel.cpp +++ b/client/ui/models/api/apiAccountInfoModel.cpp @@ -75,12 +75,6 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const } return false; } - case IsProtocolSelectionSupportedRole: { - if (m_accountInfoData.supportedProtocols.size() > 1) { - return true; - } - return false; - } } return QVariant(); @@ -101,10 +95,6 @@ void ApiAccountInfoModel::updateModel(const QJsonObject &accountInfoObject, cons accountInfoData.configType = apiUtils::getConfigType(serverConfig); - for (const auto &protocol : accountInfoObject.value(apiDefs::key::supportedProtocols).toArray()) { - accountInfoData.supportedProtocols.push_back(protocol.toString()); - } - m_accountInfoData = accountInfoData; m_supportInfo = accountInfoObject.value(apiDefs::key::supportInfo).toObject(); @@ -169,7 +159,6 @@ QHash ApiAccountInfoModel::roleNames() const roles[ServiceDescriptionRole] = "serviceDescription"; roles[IsComponentVisibleRole] = "isComponentVisible"; roles[HasExpiredWorkerRole] = "hasExpiredWorker"; - roles[IsProtocolSelectionSupportedRole] = "isProtocolSelectionSupported"; return roles; } diff --git a/client/ui/models/api/apiAccountInfoModel.h b/client/ui/models/api/apiAccountInfoModel.h index f0203967..ead92488 100644 --- a/client/ui/models/api/apiAccountInfoModel.h +++ b/client/ui/models/api/apiAccountInfoModel.h @@ -18,8 +18,7 @@ public: ServiceDescriptionRole, EndDateRole, IsComponentVisibleRole, - HasExpiredWorkerRole, - IsProtocolSelectionSupportedRole + HasExpiredWorkerRole }; explicit ApiAccountInfoModel(QObject *parent = nullptr); @@ -52,8 +51,6 @@ private: int maxDeviceCount; apiDefs::ConfigType configType; - - QStringList supportedProtocols; }; AccountInfoData m_accountInfoData; diff --git a/client/ui/models/api/apiServicesModel.cpp b/client/ui/models/api/apiServicesModel.cpp index b893096a..65f17758 100644 --- a/client/ui/models/api/apiServicesModel.cpp +++ b/client/ui/models/api/apiServicesModel.cpp @@ -69,7 +69,7 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const "Access all websites and online resources. Speeds up to %1 Mbps.") .arg(speed); } else if (serviceType == serviceType::amneziaFree) { - QString description = tr("Amnezia Free provides unlimited, free access to a basic set of websites and apps, including Facebook, Instagram, Twitter (X), Discord, Telegram, and more. YouTube is not included in the free plan."); + QString description = tr("AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan."); if (!isServiceAvailable) { description += tr("

Not available in your region. If you have VPN enabled, disable it, " "return to the previous screen, and try again."); @@ -82,7 +82,7 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const return tr("Amnezia Premium is classic VPN for for seamless work, downloading large files, and watching videos. " "Access all websites and online resources."); } else { - return tr("Amnezia Free provides unlimited, free access to a basic set of websites and apps, including Facebook, Instagram, Twitter (X), Discord, Telegram, and more. YouTube is not included in the free plan."); + return tr("AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan."); } } case IsServiceAvailableRole: { diff --git a/client/ui/models/protocols/awgConfigModel.cpp b/client/ui/models/protocols/awgConfigModel.cpp index e14a3152..860c8395 100644 --- a/client/ui/models/protocols/awgConfigModel.cpp +++ b/client/ui/models/protocols/awgConfigModel.cpp @@ -28,17 +28,7 @@ bool AwgConfigModel::setData(const QModelIndex &index, const QVariant &value, in case Roles::ClientJunkPacketCountRole: m_clientProtocolConfig.insert(config_key::junkPacketCount, value.toString()); break; case Roles::ClientJunkPacketMinSizeRole: m_clientProtocolConfig.insert(config_key::junkPacketMinSize, value.toString()); break; case Roles::ClientJunkPacketMaxSizeRole: m_clientProtocolConfig.insert(config_key::junkPacketMaxSize, value.toString()); break; - case Roles::ClientSpecialJunk1Role: m_clientProtocolConfig.insert(config_key::specialJunk1, value.toString()); break; - case Roles::ClientSpecialJunk2Role: m_clientProtocolConfig.insert(config_key::specialJunk2, value.toString()); break; - case Roles::ClientSpecialJunk3Role: m_clientProtocolConfig.insert(config_key::specialJunk3, value.toString()); break; - case Roles::ClientSpecialJunk4Role: m_clientProtocolConfig.insert(config_key::specialJunk4, value.toString()); break; - case Roles::ClientSpecialJunk5Role: m_clientProtocolConfig.insert(config_key::specialJunk5, value.toString()); break; - case Roles::ClientControlledJunk1Role: m_clientProtocolConfig.insert(config_key::controlledJunk1, value.toString()); break; - case Roles::ClientControlledJunk2Role: m_clientProtocolConfig.insert(config_key::controlledJunk2, value.toString()); break; - case Roles::ClientControlledJunk3Role: m_clientProtocolConfig.insert(config_key::controlledJunk3, value.toString()); break; - case Roles::ClientSpecialHandshakeTimeoutRole: - m_clientProtocolConfig.insert(config_key::specialHandshakeTimeout, value.toString()); - break; + case Roles::ServerJunkPacketCountRole: m_serverProtocolConfig.insert(config_key::junkPacketCount, value.toString()); break; case Roles::ServerJunkPacketMinSizeRole: m_serverProtocolConfig.insert(config_key::junkPacketMinSize, value.toString()); break; case Roles::ServerJunkPacketMaxSizeRole: m_serverProtocolConfig.insert(config_key::junkPacketMaxSize, value.toString()); break; @@ -46,12 +36,6 @@ bool AwgConfigModel::setData(const QModelIndex &index, const QVariant &value, in case Roles::ServerResponsePacketJunkSizeRole: m_serverProtocolConfig.insert(config_key::responsePacketJunkSize, value.toString()); break; - // case Roles::ServerCookieReplyPacketJunkSizeRole: - // m_serverProtocolConfig.insert(config_key::cookieReplyPacketJunkSize, value.toString()); - // break; - // case Roles::ServerTransportPacketJunkSizeRole: - // m_serverProtocolConfig.insert(config_key::transportPacketJunkSize, value.toString()); - // break; case Roles::ServerInitPacketMagicHeaderRole: m_serverProtocolConfig.insert(config_key::initPacketMagicHeader, value.toString()); break; case Roles::ServerResponsePacketMagicHeaderRole: m_serverProtocolConfig.insert(config_key::responsePacketMagicHeader, value.toString()); @@ -82,23 +66,12 @@ QVariant AwgConfigModel::data(const QModelIndex &index, int role) const case Roles::ClientJunkPacketCountRole: return m_clientProtocolConfig.value(config_key::junkPacketCount); case Roles::ClientJunkPacketMinSizeRole: return m_clientProtocolConfig.value(config_key::junkPacketMinSize); case Roles::ClientJunkPacketMaxSizeRole: return m_clientProtocolConfig.value(config_key::junkPacketMaxSize); - case Roles::ClientSpecialJunk1Role: return m_clientProtocolConfig.value(config_key::specialJunk1); - case Roles::ClientSpecialJunk2Role: return m_clientProtocolConfig.value(config_key::specialJunk2); - case Roles::ClientSpecialJunk3Role: return m_clientProtocolConfig.value(config_key::specialJunk3); - case Roles::ClientSpecialJunk4Role: return m_clientProtocolConfig.value(config_key::specialJunk4); - case Roles::ClientSpecialJunk5Role: return m_clientProtocolConfig.value(config_key::specialJunk5); - case Roles::ClientControlledJunk1Role: return m_clientProtocolConfig.value(config_key::controlledJunk1); - case Roles::ClientControlledJunk2Role: return m_clientProtocolConfig.value(config_key::controlledJunk2); - case Roles::ClientControlledJunk3Role: return m_clientProtocolConfig.value(config_key::controlledJunk3); - case Roles::ClientSpecialHandshakeTimeoutRole: return m_clientProtocolConfig.value(config_key::specialHandshakeTimeout); case Roles::ServerJunkPacketCountRole: return m_serverProtocolConfig.value(config_key::junkPacketCount); case Roles::ServerJunkPacketMinSizeRole: return m_serverProtocolConfig.value(config_key::junkPacketMinSize); case Roles::ServerJunkPacketMaxSizeRole: return m_serverProtocolConfig.value(config_key::junkPacketMaxSize); case Roles::ServerInitPacketJunkSizeRole: return m_serverProtocolConfig.value(config_key::initPacketJunkSize); case Roles::ServerResponsePacketJunkSizeRole: return m_serverProtocolConfig.value(config_key::responsePacketJunkSize); - // case Roles::ServerCookieReplyPacketJunkSizeRole: return m_serverProtocolConfig.value(config_key::cookieReplyPacketJunkSize); - // case Roles::ServerTransportPacketJunkSizeRole: return m_serverProtocolConfig.value(config_key::transportPacketJunkSize); case Roles::ServerInitPacketMagicHeaderRole: return m_serverProtocolConfig.value(config_key::initPacketMagicHeader); case Roles::ServerResponsePacketMagicHeaderRole: return m_serverProtocolConfig.value(config_key::responsePacketMagicHeader); case Roles::ServerUnderloadPacketMagicHeaderRole: return m_serverProtocolConfig.value(config_key::underloadPacketMagicHeader); @@ -121,8 +94,7 @@ void AwgConfigModel::updateModel(const QJsonObject &config) m_serverProtocolConfig.insert(config_key::transport_proto, serverProtocolConfig.value(config_key::transport_proto).toString(defaultTransportProto)); m_serverProtocolConfig[config_key::last_config] = serverProtocolConfig.value(config_key::last_config); - m_serverProtocolConfig[config_key::subnet_address] = - serverProtocolConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress); + m_serverProtocolConfig[config_key::subnet_address] = serverProtocolConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress); m_serverProtocolConfig[config_key::port] = serverProtocolConfig.value(config_key::port).toString(protocols::awg::defaultPort); m_serverProtocolConfig[config_key::junkPacketCount] = serverProtocolConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount); @@ -134,10 +106,6 @@ void AwgConfigModel::updateModel(const QJsonObject &config) serverProtocolConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize); m_serverProtocolConfig[config_key::responsePacketJunkSize] = serverProtocolConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize); - // m_serverProtocolConfig[config_key::cookieReplyPacketJunkSize] = - // serverProtocolConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize); - // m_serverProtocolConfig[config_key::transportPacketJunkSize] = - // serverProtocolConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize); m_serverProtocolConfig[config_key::initPacketMagicHeader] = serverProtocolConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader); m_serverProtocolConfig[config_key::responsePacketMagicHeader] = @@ -156,24 +124,6 @@ void AwgConfigModel::updateModel(const QJsonObject &config) clientProtocolConfig.value(config_key::junkPacketMinSize).toString(m_serverProtocolConfig[config_key::junkPacketMinSize].toString()); m_clientProtocolConfig[config_key::junkPacketMaxSize] = clientProtocolConfig.value(config_key::junkPacketMaxSize).toString(m_serverProtocolConfig[config_key::junkPacketMaxSize].toString()); - m_clientProtocolConfig[config_key::specialJunk1] = - clientProtocolConfig.value(config_key::specialJunk1).toString(protocols::awg::defaultSpecialJunk1); - m_clientProtocolConfig[config_key::specialJunk2] = - clientProtocolConfig.value(config_key::specialJunk2).toString(protocols::awg::defaultSpecialJunk2); - m_clientProtocolConfig[config_key::specialJunk3] = - clientProtocolConfig.value(config_key::specialJunk3).toString(protocols::awg::defaultSpecialJunk3); - m_clientProtocolConfig[config_key::specialJunk4] = - clientProtocolConfig.value(config_key::specialJunk4).toString(protocols::awg::defaultSpecialJunk4); - m_clientProtocolConfig[config_key::specialJunk5] = - clientProtocolConfig.value(config_key::specialJunk5).toString(protocols::awg::defaultSpecialJunk5); - m_clientProtocolConfig[config_key::controlledJunk1] = - clientProtocolConfig.value(config_key::controlledJunk1).toString(protocols::awg::defaultControlledJunk1); - m_clientProtocolConfig[config_key::controlledJunk2] = - clientProtocolConfig.value(config_key::controlledJunk2).toString(protocols::awg::defaultControlledJunk2); - m_clientProtocolConfig[config_key::controlledJunk3] = - clientProtocolConfig.value(config_key::controlledJunk3).toString(protocols::awg::defaultControlledJunk3); - m_clientProtocolConfig[config_key::specialHandshakeTimeout] = - clientProtocolConfig.value(config_key::specialHandshakeTimeout).toString(protocols::awg::defaultSpecialHandshakeTimeout); endResetModel(); } @@ -191,15 +141,6 @@ QJsonObject AwgConfigModel::getConfig() jsonConfig[config_key::junkPacketCount] = m_clientProtocolConfig[config_key::junkPacketCount]; jsonConfig[config_key::junkPacketMinSize] = m_clientProtocolConfig[config_key::junkPacketMinSize]; jsonConfig[config_key::junkPacketMaxSize] = m_clientProtocolConfig[config_key::junkPacketMaxSize]; - jsonConfig[config_key::specialJunk1] = m_clientProtocolConfig[config_key::specialJunk1]; - jsonConfig[config_key::specialJunk2] = m_clientProtocolConfig[config_key::specialJunk2]; - jsonConfig[config_key::specialJunk3] = m_clientProtocolConfig[config_key::specialJunk3]; - jsonConfig[config_key::specialJunk4] = m_clientProtocolConfig[config_key::specialJunk4]; - jsonConfig[config_key::specialJunk5] = m_clientProtocolConfig[config_key::specialJunk5]; - jsonConfig[config_key::controlledJunk1] = m_clientProtocolConfig[config_key::controlledJunk1]; - jsonConfig[config_key::controlledJunk2] = m_clientProtocolConfig[config_key::controlledJunk2]; - jsonConfig[config_key::controlledJunk3] = m_clientProtocolConfig[config_key::controlledJunk3]; - jsonConfig[config_key::specialHandshakeTimeout] = m_clientProtocolConfig[config_key::specialHandshakeTimeout]; m_serverProtocolConfig[config_key::last_config] = QString(QJsonDocument(jsonConfig).toJson()); } @@ -218,17 +159,6 @@ bool AwgConfigModel::isPacketSizeEqual(const int s1, const int s2) return (AwgConstant::messageInitiationSize + s1 == AwgConstant::messageResponseSize + s2); } -// bool AwgConfigModel::isPacketSizeEqual(const int s1, const int s2, const int s3, const int s4) -// { -// int initSize = AwgConstant::messageInitiationSize + s1; -// int responseSize = AwgConstant::messageResponseSize + s2; -// int cookieSize = AwgConstant::messageCookieReplySize + s3; -// int transportSize = AwgConstant::messageTransportSize + s4; - -// return (initSize == responseSize || initSize == cookieSize || initSize == transportSize || responseSize == cookieSize -// || responseSize == transportSize || cookieSize == transportSize); -// } - bool AwgConfigModel::isServerSettingsEqual() { const AwgConfig oldConfig(m_fullConfig.value(config_key::awg).toObject()); @@ -248,24 +178,12 @@ QHash AwgConfigModel::roleNames() const roles[ClientJunkPacketCountRole] = "clientJunkPacketCount"; roles[ClientJunkPacketMinSizeRole] = "clientJunkPacketMinSize"; roles[ClientJunkPacketMaxSizeRole] = "clientJunkPacketMaxSize"; - roles[ClientSpecialJunk1Role] = "clientSpecialJunk1"; - roles[ClientSpecialJunk2Role] = "clientSpecialJunk2"; - roles[ClientSpecialJunk3Role] = "clientSpecialJunk3"; - roles[ClientSpecialJunk4Role] = "clientSpecialJunk4"; - roles[ClientSpecialJunk5Role] = "clientSpecialJunk5"; - roles[ClientControlledJunk1Role] = "clientControlledJunk1"; - roles[ClientControlledJunk2Role] = "clientControlledJunk2"; - roles[ClientControlledJunk3Role] = "clientControlledJunk3"; - roles[ClientSpecialHandshakeTimeoutRole] = "clientSpecialHandshakeTimeout"; roles[ServerJunkPacketCountRole] = "serverJunkPacketCount"; roles[ServerJunkPacketMinSizeRole] = "serverJunkPacketMinSize"; roles[ServerJunkPacketMaxSizeRole] = "serverJunkPacketMaxSize"; roles[ServerInitPacketJunkSizeRole] = "serverInitPacketJunkSize"; roles[ServerResponsePacketJunkSizeRole] = "serverResponsePacketJunkSize"; - roles[ServerCookieReplyPacketJunkSizeRole] = "serverCookieReplyPacketJunkSize"; - roles[ServerTransportPacketJunkSizeRole] = "serverTransportPacketJunkSize"; - roles[ServerInitPacketMagicHeaderRole] = "serverInitPacketMagicHeader"; roles[ServerResponsePacketMagicHeaderRole] = "serverResponsePacketMagicHeader"; roles[ServerUnderloadPacketMagicHeaderRole] = "serverUnderloadPacketMagicHeader"; @@ -282,16 +200,6 @@ AwgConfig::AwgConfig(const QJsonObject &serverProtocolConfig) clientJunkPacketCount = clientProtocolConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount); clientJunkPacketMinSize = clientProtocolConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize); clientJunkPacketMaxSize = clientProtocolConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize); - clientSpecialJunk1 = clientProtocolConfig.value(config_key::specialJunk1).toString(protocols::awg::defaultSpecialJunk1); - clientSpecialJunk2 = clientProtocolConfig.value(config_key::specialJunk2).toString(protocols::awg::defaultSpecialJunk2); - clientSpecialJunk3 = clientProtocolConfig.value(config_key::specialJunk3).toString(protocols::awg::defaultSpecialJunk3); - clientSpecialJunk4 = clientProtocolConfig.value(config_key::specialJunk4).toString(protocols::awg::defaultSpecialJunk4); - clientSpecialJunk5 = clientProtocolConfig.value(config_key::specialJunk5).toString(protocols::awg::defaultSpecialJunk5); - clientControlledJunk1 = clientProtocolConfig.value(config_key::controlledJunk1).toString(protocols::awg::defaultControlledJunk1); - clientControlledJunk2 = clientProtocolConfig.value(config_key::controlledJunk2).toString(protocols::awg::defaultControlledJunk2); - clientControlledJunk3 = clientProtocolConfig.value(config_key::controlledJunk3).toString(protocols::awg::defaultControlledJunk3); - clientSpecialHandshakeTimeout = - clientProtocolConfig.value(config_key::specialHandshakeTimeout).toString(protocols::awg::defaultSpecialHandshakeTimeout); subnetAddress = serverProtocolConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress); port = serverProtocolConfig.value(config_key::port).toString(protocols::awg::defaultPort); @@ -301,10 +209,6 @@ AwgConfig::AwgConfig(const QJsonObject &serverProtocolConfig) serverInitPacketJunkSize = serverProtocolConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize); serverResponsePacketJunkSize = serverProtocolConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize); - // serverCookieReplyPacketJunkSize = - // serverProtocolConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize); - // serverTransportPacketJunkSize = - // serverProtocolConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize); serverInitPacketMagicHeader = serverProtocolConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader); serverResponsePacketMagicHeader = @@ -320,8 +224,6 @@ bool AwgConfig::hasEqualServerSettings(const AwgConfig &other) const if (subnetAddress != other.subnetAddress || port != other.port || serverJunkPacketCount != other.serverJunkPacketCount || serverJunkPacketMinSize != other.serverJunkPacketMinSize || serverJunkPacketMaxSize != other.serverJunkPacketMaxSize || serverInitPacketJunkSize != other.serverInitPacketJunkSize || serverResponsePacketJunkSize != other.serverResponsePacketJunkSize - // || serverCookieReplyPacketJunkSize != other.serverCookieReplyPacketJunkSize - // || serverTransportPacketJunkSize != other.serverTransportPacketJunkSize || serverInitPacketMagicHeader != other.serverInitPacketMagicHeader || serverResponsePacketMagicHeader != other.serverResponsePacketMagicHeader || serverUnderloadPacketMagicHeader != other.serverUnderloadPacketMagicHeader @@ -334,12 +236,7 @@ bool AwgConfig::hasEqualServerSettings(const AwgConfig &other) const bool AwgConfig::hasEqualClientSettings(const AwgConfig &other) const { if (clientMtu != other.clientMtu || clientJunkPacketCount != other.clientJunkPacketCount - || clientJunkPacketMinSize != other.clientJunkPacketMinSize || clientJunkPacketMaxSize != other.clientJunkPacketMaxSize - || clientSpecialJunk1 != other.clientSpecialJunk1 || clientSpecialJunk2 != other.clientSpecialJunk2 - || clientSpecialJunk3 != other.clientSpecialJunk3 || clientSpecialJunk4 != other.clientSpecialJunk4 - || clientSpecialJunk5 != other.clientSpecialJunk5 || clientControlledJunk1 != other.clientControlledJunk1 - || clientControlledJunk2 != other.clientControlledJunk2 || clientControlledJunk3 != other.clientControlledJunk3 - || clientSpecialHandshakeTimeout != other.clientSpecialHandshakeTimeout) { + || clientJunkPacketMinSize != other.clientJunkPacketMinSize || clientJunkPacketMaxSize != other.clientJunkPacketMaxSize) { return false; } return true; diff --git a/client/ui/models/protocols/awgConfigModel.h b/client/ui/models/protocols/awgConfigModel.h index 0c2374fc..c1f8bb27 100644 --- a/client/ui/models/protocols/awgConfigModel.h +++ b/client/ui/models/protocols/awgConfigModel.h @@ -6,12 +6,9 @@ #include "containers/containers_defs.h" -namespace AwgConstant -{ +namespace AwgConstant { const int messageInitiationSize = 148; const int messageResponseSize = 92; - const int messageCookieReplySize = 64; - const int messageTransportSize = 32; } struct AwgConfig @@ -25,23 +22,12 @@ struct AwgConfig QString clientJunkPacketCount; QString clientJunkPacketMinSize; QString clientJunkPacketMaxSize; - QString clientSpecialJunk1; - QString clientSpecialJunk2; - QString clientSpecialJunk3; - QString clientSpecialJunk4; - QString clientSpecialJunk5; - QString clientControlledJunk1; - QString clientControlledJunk2; - QString clientControlledJunk3; - QString clientSpecialHandshakeTimeout; QString serverJunkPacketCount; QString serverJunkPacketMinSize; QString serverJunkPacketMaxSize; QString serverInitPacketJunkSize; QString serverResponsePacketJunkSize; - QString serverCookieReplyPacketJunkSize; - QString serverTransportPacketJunkSize; QString serverInitPacketMagicHeader; QString serverResponsePacketMagicHeader; QString serverUnderloadPacketMagicHeader; @@ -49,6 +35,7 @@ struct AwgConfig bool hasEqualServerSettings(const AwgConfig &other) const; bool hasEqualClientSettings(const AwgConfig &other) const; + }; class AwgConfigModel : public QAbstractListModel @@ -64,28 +51,16 @@ public: ClientJunkPacketCountRole, ClientJunkPacketMinSizeRole, ClientJunkPacketMaxSizeRole, - ClientSpecialJunk1Role, - ClientSpecialJunk2Role, - ClientSpecialJunk3Role, - ClientSpecialJunk4Role, - ClientSpecialJunk5Role, - ClientControlledJunk1Role, - ClientControlledJunk2Role, - ClientControlledJunk3Role, - ClientSpecialHandshakeTimeoutRole, ServerJunkPacketCountRole, ServerJunkPacketMinSizeRole, ServerJunkPacketMaxSizeRole, ServerInitPacketJunkSizeRole, ServerResponsePacketJunkSizeRole, - ServerCookieReplyPacketJunkSizeRole, - ServerTransportPacketJunkSizeRole, - ServerInitPacketMagicHeaderRole, ServerResponsePacketMagicHeaderRole, ServerUnderloadPacketMagicHeaderRole, - ServerTransportPacketMagicHeaderRole, + ServerTransportPacketMagicHeaderRole }; explicit AwgConfigModel(QObject *parent = nullptr); @@ -100,7 +75,7 @@ public slots: QJsonObject getConfig(); bool isHeadersEqual(const QString &h1, const QString &h2, const QString &h3, const QString &h4); - bool isPacketSizeEqual(const int s1, const int s2/*, const int s3, const int s4*/); + bool isPacketSizeEqual(const int s1, const int s2); bool isServerSettingsEqual(); diff --git a/client/ui/models/protocols/xrayConfigModel.cpp b/client/ui/models/protocols/xrayConfigModel.cpp index 3917b544..84bbb2f7 100644 --- a/client/ui/models/protocols/xrayConfigModel.cpp +++ b/client/ui/models/protocols/xrayConfigModel.cpp @@ -20,7 +20,6 @@ bool XrayConfigModel::setData(const QModelIndex &index, const QVariant &value, i switch (role) { case Roles::SiteRole: m_protocolConfig.insert(config_key::site, value.toString()); break; - case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break; } emit dataChanged(index, index, QList { role }); @@ -35,7 +34,6 @@ QVariant XrayConfigModel::data(const QModelIndex &index, int role) const switch (role) { case Roles::SiteRole: return m_protocolConfig.value(config_key::site).toString(protocols::xray::defaultSite); - case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(protocols::xray::defaultPort); } return QVariant(); @@ -69,7 +67,6 @@ QHash XrayConfigModel::roleNames() const QHash roles; roles[SiteRole] = "site"; - roles[PortRole] = "port"; return roles; } diff --git a/client/ui/models/protocols/xrayConfigModel.h b/client/ui/models/protocols/xrayConfigModel.h index 41aac589..cb57f858 100644 --- a/client/ui/models/protocols/xrayConfigModel.h +++ b/client/ui/models/protocols/xrayConfigModel.h @@ -12,8 +12,7 @@ class XrayConfigModel : public QAbstractListModel public: enum Roles { - SiteRole, - PortRole + SiteRole }; explicit XrayConfigModel(QObject *parent = nullptr); diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index 22813312..7cde28b4 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -8,8 +8,6 @@ #include #endif -#include "core/api/apiUtils.h" - namespace { namespace configKey @@ -68,7 +66,6 @@ bool ServersModel::setData(const QModelIndex &index, const QVariant &value, int } else { server.insert(config_key::description, value.toString()); } - server.insert(config_key::nameOverriddenByUser, true); m_settings->editServer(index.row(), server); m_servers.replace(index.row(), server); if (index.row() == m_defaultServerIndex) { @@ -351,25 +348,6 @@ void ServersModel::removeServer() endResetModel(); } -void ServersModel::removeServer(const int serverIndex) -{ - beginResetModel(); - m_settings->removeServer(serverIndex); - m_servers = m_settings->serversArray(); - - if (m_settings->defaultServerIndex() == serverIndex) { - setDefaultServerIndex(0); - } else if (m_settings->defaultServerIndex() > serverIndex) { - setDefaultServerIndex(m_settings->defaultServerIndex() - 1); - } - - if (m_settings->serversCount() == 0) { - setDefaultServerIndex(-1); - } - setProcessedServerIndex(m_defaultServerIndex); - endResetModel(); -} - QHash ServersModel::roleNames() const { QHash roles; @@ -429,7 +407,7 @@ void ServersModel::updateDefaultServerContainersModel() emit defaultServerContainersUpdated(containers); } -QJsonObject ServersModel::getServerConfig(const int serverIndex) const +QJsonObject ServersModel::getServerConfig(const int serverIndex) { return m_servers.at(serverIndex).toObject(); } @@ -816,8 +794,3 @@ const QString ServersModel::getDefaultServerImagePathCollapsed() } return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(countryCode.toUpper()); } - -bool ServersModel::processedServerIsPremium() const -{ - return apiUtils::isPremiumServer(getServerConfig(m_processedServerIndex)); -} diff --git a/client/ui/models/servers_model.h b/client/ui/models/servers_model.h index c36b6534..4b790c7a 100644 --- a/client/ui/models/servers_model.h +++ b/client/ui/models/servers_model.h @@ -63,9 +63,6 @@ public: Q_PROPERTY(bool isDefaultServerFromApi READ isDefaultServerFromApi NOTIFY defaultServerIndexChanged) Q_PROPERTY(int processedIndex READ getProcessedServerIndex WRITE setProcessedServerIndex NOTIFY processedServerIndexChanged) - Q_PROPERTY(bool processedServerIsPremium READ processedServerIsPremium NOTIFY processedServerChanged) - - bool processedServerIsPremium() const; public slots: void setDefaultServerIndex(const int index); @@ -93,9 +90,8 @@ public slots: void addServer(const QJsonObject &server); void editServer(const QJsonObject &server, const int serverIndex); void removeServer(); - void removeServer(const int serverIndex); - QJsonObject getServerConfig(const int serverIndex) const; + QJsonObject getServerConfig(const int serverIndex); void reloadDefaultServerContainerConfig(); void updateContainerConfig(const int containerIndex, const QJsonObject config); diff --git a/client/ui/qml/Components/ApiPremV1MigrationDrawer.qml b/client/ui/qml/Components/ApiPremV1MigrationDrawer.qml deleted file mode 100644 index 21aa78a0..00000000 --- a/client/ui/qml/Components/ApiPremV1MigrationDrawer.qml +++ /dev/null @@ -1,194 +0,0 @@ -pragma ComponentBehavior: Bound - -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts - -import QtCore - -import PageEnum 1.0 -import Style 1.0 - -import "./" -import "../Controls2" -import "../Controls2/TextTypes" -import "../Config" -import "../Components" - -DrawerType2 { - id: root - - expandedHeight: parent.height * 0.9 - - Connections { - target: ApiPremV1MigrationController - - function onErrorOccurred(error, goToPageHome) { - PageController.showErrorMessage(error) - root.closeTriggered() - } - } - - expandedStateContent: Item { - implicitHeight: root.expandedHeight - - ListViewType { - id: listView - - anchors.fill: parent - - model: 1 // fake model to force the ListView to be created without a model - snapMode: ListView.NoSnap - - header: ColumnLayout { - width: listView.width - - Header2Type { - id: header - Layout.fillWidth: true - Layout.topMargin: 20 - Layout.leftMargin: 16 - Layout.rightMargin: 16 - - headerText: qsTr("Switch to the new Amnezia Premium subscription") - } - } - - delegate: ColumnLayout { - width: listView.width - - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: 16 - anchors.rightMargin: 16 - - ParagraphTextType { - Layout.fillWidth: true - Layout.topMargin: 24 - Layout.bottomMargin: 24 - - horizontalAlignment: Text.AlignLeft - textFormat: Text.RichText - text: { - var str = qsTr("We'll preserve all remaining days of your current subscription and give you an extra month as a thank you. ") - str += qsTr("This new subscription type will be actively developed with more locations and features added regularly. Currently available:") - str += "

    " - str += qsTr("
  • 13 locations (with more coming soon)
  • ") - str += qsTr("
  • Easier switching between countries in the app
  • ") - str += qsTr("
  • Personal dashboard to manage your subscription
  • ") - str += "
" - str += qsTr("Old keys will be deactivated after switching.") - } - } - - TextFieldWithHeaderType { - id: emailLabel - Layout.fillWidth: true - - borderColor: AmneziaStyle.color.mutedGray - headerTextColor: AmneziaStyle.color.paleGray - - headerText: qsTr("Email") - textField.placeholderText: qsTr("mail@example.com") - - - textField.onFocusChanged: { - textField.text = textField.text.replace(/^\s+|\s+$/g, '') - } - - Connections { - target: ApiPremV1MigrationController - - function onNoSubscriptionToMigrate() { - emailLabel.errorText = qsTr("No old format subscriptions for a given email") - } - } - } - - CaptionTextType { - Layout.fillWidth: true - Layout.topMargin: 16 - - color: AmneziaStyle.color.mutedGray - - text: qsTr("Enter the email you used for your current subscription") - } - - ApiPremV1SubListDrawer { - id: apiPremV1SubListDrawer - parent: root - - anchors.fill: parent - } - - OtpCodeDrawer { - id: otpCodeDrawer - parent: root - - anchors.fill: parent - } - - BasicButtonType { - id: yesButton - Layout.fillWidth: true - Layout.topMargin: 32 - - text: qsTr("Continue") - - clickedFunc: function() { - PageController.showBusyIndicator(true) - ApiPremV1MigrationController.getSubscriptionList(emailLabel.textField.text) - PageController.showBusyIndicator(false) - } - } - - BasicButtonType { - id: noButton - Layout.fillWidth: true - - defaultColor: AmneziaStyle.color.transparent - hoveredColor: AmneziaStyle.color.translucentWhite - pressedColor: AmneziaStyle.color.sheerWhite - disabledColor: AmneziaStyle.color.mutedGray - textColor: AmneziaStyle.color.paleGray - borderWidth: 1 - - text: qsTr("Remind me later") - - clickedFunc: function() { - root.closeTriggered() - } - } - - BasicButtonType { - Layout.alignment: Qt.AlignHCenter - Layout.topMargin: 32 - Layout.bottomMargin: 32 - implicitHeight: 32 - - defaultColor: "transparent" - hoveredColor: AmneziaStyle.color.translucentWhite - pressedColor: AmneziaStyle.color.sheerWhite - textColor: AmneziaStyle.color.vibrantRed - - text: qsTr("Don't remind me again") - - clickedFunc: function() { - var headerText = qsTr("No more reminders? You can always switch to the new format in the server settings") - var yesButtonText = qsTr("Continue") - var noButtonText = qsTr("Cancel") - - var yesButtonFunction = function() { - ApiPremV1MigrationController.disablePremV1MigrationReminder() - root.closeTriggered() - } - var noButtonFunction = function() { - } - - showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) - } - } - } - } - } -} diff --git a/client/ui/qml/Components/ApiPremV1SubListDrawer.qml b/client/ui/qml/Components/ApiPremV1SubListDrawer.qml deleted file mode 100644 index 221b7a89..00000000 --- a/client/ui/qml/Components/ApiPremV1SubListDrawer.qml +++ /dev/null @@ -1,89 +0,0 @@ -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts - -import Style 1.0 - -import "../Controls2" -import "../Controls2/TextTypes" -import "../Config" - -DrawerType2 { - id: root - - Connections { - target: ApiPremV1MigrationController - - function onSubscriptionsModelChanged() { - if (ApiPremV1MigrationController.subscriptionsModel.length > 1) { - root.openTriggered() - } else { - sendMigrationCode(0) - } - } - } - - function sendMigrationCode(index) { - PageController.showBusyIndicator(true) - ApiPremV1MigrationController.sendMigrationCode(index) - root.closeTriggered() - PageController.showBusyIndicator(false) - } - - expandedHeight: parent.height * 0.9 - - expandedStateContent: Item { - implicitHeight: root.expandedHeight - - ListViewType { - id: listView - - anchors.fill: parent - - model: ApiPremV1MigrationController.subscriptionsModel - - header: ColumnLayout { - width: listView.width - - Header2Type { - id: header - Layout.fillWidth: true - Layout.topMargin: 20 - Layout.leftMargin: 16 - Layout.rightMargin: 16 - - headerText: qsTr("Choose Subscription") - } - } - - delegate: Item { - implicitWidth: listView.width - implicitHeight: delegateContent.implicitHeight - - ColumnLayout { - id: delegateContent - - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - - LabelWithButtonType { - id: server - Layout.fillWidth: true - - text: qsTr("Order ID: ") + modelData.id - - descriptionText: qsTr("Purchase Date: ") + Qt.formatDateTime(new Date(modelData.created_at), "dd.MM.yyyy hh:mm") - rightImageSource: "qrc:/images/controls/chevron-right.svg" - - clickedFunction: function() { - sendMigrationCode(index) - } - } - - DividerType {} - } - } - } - } -} diff --git a/client/ui/qml/Components/AwgTextField.qml b/client/ui/qml/Components/AwgTextField.qml deleted file mode 100644 index 87b023d9..00000000 --- a/client/ui/qml/Components/AwgTextField.qml +++ /dev/null @@ -1,15 +0,0 @@ -pragma ComponentBehavior: Bound - -import QtQuick -import QtQuick.Layouts - -import "../Controls2" - -TextFieldWithHeaderType { - Layout.fillWidth: true - Layout.topMargin: 16 - - textField.validator: IntValidator { bottom: 0 } - - checkEmptyText: true -} diff --git a/client/ui/qml/Components/OtpCodeDrawer.qml b/client/ui/qml/Components/OtpCodeDrawer.qml deleted file mode 100644 index e26982b9..00000000 --- a/client/ui/qml/Components/OtpCodeDrawer.qml +++ /dev/null @@ -1,77 +0,0 @@ -pragma ComponentBehavior: Bound - -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts - -import Style 1.0 - -import "../Controls2" -import "../Controls2/TextTypes" - -import "../Config" - -DrawerType2 { - id: root - - Connections { - target: ApiPremV1MigrationController - - function onOtpSuccessfullySent() { - root.openTriggered() - } - } - - expandedHeight: parent.height * 0.6 - - expandedStateContent: Item { - implicitHeight: root.expandedHeight - - ColumnLayout { - - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: 16 - anchors.rightMargin: 16 - spacing: 0 - - Header2Type { - id: header - Layout.fillWidth: true - Layout.topMargin: 20 - - headerText: qsTr("OTP code was sent to your email") - } - - TextFieldWithHeaderType { - id: otpFiled - - borderColor: AmneziaStyle.color.mutedGray - headerTextColor: AmneziaStyle.color.paleGray - - Layout.fillWidth: true - Layout.topMargin: 16 - headerText: qsTr("OTP Code") - textField.maximumLength: 30 - checkEmptyText: true - } - - BasicButtonType { - id: saveButton - - Layout.fillWidth: true - Layout.topMargin: 16 - - text: qsTr("Continue") - - clickedFunc: function() { - PageController.showBusyIndicator(true) - ApiPremV1MigrationController.migrate(otpFiled.textField.text) - PageController.showBusyIndicator(false) - root.closeTriggered() - } - } - } - } -} diff --git a/client/ui/qml/Components/QuestionDrawer.qml b/client/ui/qml/Components/QuestionDrawer.qml index ddf5cda4..0c14e52d 100644 --- a/client/ui/qml/Components/QuestionDrawer.qml +++ b/client/ui/qml/Components/QuestionDrawer.qml @@ -1,5 +1,3 @@ -pragma ComponentBehavior: Bound - import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -41,7 +39,7 @@ DrawerType2 { Layout.rightMargin: 16 Layout.leftMargin: 16 - text: root.headerText + text: headerText } ParagraphTextType { @@ -50,7 +48,7 @@ DrawerType2 { Layout.rightMargin: 16 Layout.leftMargin: 16 - text: root.descriptionText + text: descriptionText } BasicButtonType { @@ -60,11 +58,11 @@ DrawerType2 { Layout.rightMargin: 16 Layout.leftMargin: 16 - text: root.yesButtonText + text: yesButtonText clickedFunc: function() { - if (root.yesButtonFunction && typeof root.yesButtonFunction === "function") { - root.yesButtonFunction() + if (yesButtonFunction && typeof yesButtonFunction === "function") { + yesButtonFunction() } } } @@ -82,13 +80,11 @@ DrawerType2 { textColor: AmneziaStyle.color.paleGray borderWidth: 1 - visible: root.noButtonText !== "" - - text: root.noButtonText + text: noButtonText clickedFunc: function() { - if (root.noButtonFunction && typeof root.noButtonFunction === "function") { - root.noButtonFunction() + if (noButtonFunction && typeof noButtonFunction === "function") { + noButtonFunction() } } } diff --git a/client/ui/qml/Pages2/PageHome.qml b/client/ui/qml/Pages2/PageHome.qml index 7934e5fb..f7233a89 100644 --- a/client/ui/qml/Pages2/PageHome.qml +++ b/client/ui/qml/Pages2/PageHome.qml @@ -33,31 +33,6 @@ PageType { } } - Connections { - - target: ApiPremV1MigrationController - - function onMigrationFinished() { - apiPremV1MigrationDrawer.closeTriggered() - - var headerText = qsTr("You've successfully switched to the new Amnezia Premium subscription!") - var descriptionText = qsTr("Old keys will no longer work. Please use your new subscription key to connect. \nThank you for staying with us!") - var yesButtonText = qsTr("Continue") - var noButtonText = "" - - var yesButtonFunction = function() { - } - var noButtonFunction = function() { - } - - showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) - } - - function onShowMigrationDrawer() { - apiPremV1MigrationDrawer.openTriggered() - } - } - Item { objectName: "homeColumnItem" @@ -454,9 +429,4 @@ PageType { } } } - - ApiPremV1MigrationDrawer { - id: apiPremV1MigrationDrawer - anchors.fill: parent - } } diff --git a/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml b/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml index d97d09e8..b8cf5f93 100644 --- a/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml @@ -115,10 +115,14 @@ PageType { KeyNavigation.tab: junkPacketCountTextField.textField } - AwgTextField { + TextFieldWithHeaderType { id: junkPacketCountTextField + Layout.fillWidth: true + Layout.topMargin: 16 + headerText: "Jc - Junk packet count" textField.text: clientJunkPacketCount + textField.validator: IntValidator { bottom: 0 } textField.onEditingFinished: { if (textField.text !== clientJunkPacketCount) { @@ -126,13 +130,19 @@ PageType { } } + checkEmptyText: true + KeyNavigation.tab: junkPacketMinSizeTextField.textField } - AwgTextField { + TextFieldWithHeaderType { id: junkPacketMinSizeTextField + Layout.fillWidth: true + Layout.topMargin: 16 + headerText: "Jmin - Junk packet minimum size" textField.text: clientJunkPacketMinSize + textField.validator: IntValidator { bottom: 0 } textField.onEditingFinished: { if (textField.text !== clientJunkPacketMinSize) { @@ -140,144 +150,28 @@ PageType { } } + checkEmptyText: true + KeyNavigation.tab: junkPacketMaxSizeTextField.textField } - AwgTextField { + TextFieldWithHeaderType { id: junkPacketMaxSizeTextField + Layout.fillWidth: true + Layout.topMargin: 16 + headerText: "Jmax - Junk packet maximum size" textField.text: clientJunkPacketMaxSize + textField.validator: IntValidator { bottom: 0 } textField.onEditingFinished: { if (textField.text !== clientJunkPacketMaxSize) { clientJunkPacketMaxSize = textField.text } } - } - AwgTextField { - id: specialJunk1TextField - headerText: qsTr("I1 - First special junk packet") - textField.text: clientSpecialJunk1 - textField.validator: null - checkEmptyText: false + checkEmptyText: true - textField.onEditingFinished: { - if (textField.text !== clientSpecialJunk1) { - clientSpecialJunk1 = textField.text - } - } - } - - AwgTextField { - id: specialJunk2TextField - headerText: qsTr("I2 - Second special junk packet") - textField.text: clientSpecialJunk2 - textField.validator: null - checkEmptyText: false - - textField.onEditingFinished: { - if (textField.text !== clientSpecialJunk2) { - clientSpecialJunk2 = textField.text - } - } - } - - AwgTextField { - id: specialJunk3TextField - headerText: qsTr("I3 - Third special junk packet") - textField.text: clientSpecialJunk3 - textField.validator: null - checkEmptyText: false - - textField.onEditingFinished: { - if (textField.text !== clientSpecialJunk3) { - clientSpecialJunk3 = textField.text - } - } - } - - AwgTextField { - id: specialJunk4TextField - headerText: qsTr("I4 - Fourth special junk packet") - textField.text: clientSpecialJunk4 - textField.validator: null - checkEmptyText: false - - textField.onEditingFinished: { - if (textField.text !== clientSpecialJunk4) { - clientSpecialJunk4 = textField.text - } - } - } - - AwgTextField { - id: specialJunk5TextField - headerText: qsTr("I5 - Fifth special junk packet") - textField.text: clientSpecialJunk5 - textField.validator: null - checkEmptyText: false - - textField.onEditingFinished: { - if (textField.text !== clientSpecialJunk5 ) { - clientSpecialJunk5 = textField.text - } - } - } - - AwgTextField { - id: controlledJunk1TextField - headerText: qsTr("J1 - First controlled junk packet") - textField.text: clientControlledJunk1 - textField.validator: null - checkEmptyText: false - - textField.onEditingFinished: { - if (textField.text !== clientControlledJunk1) { - clientControlledJunk1 = textField.text - } - } - } - - AwgTextField { - id: controlledJunk2TextField - headerText: qsTr("J2 - Second controlled junk packet") - textField.text: clientControlledJunk2 - textField.validator: null - checkEmptyText: false - - textField.onEditingFinished: { - if (textField.text !== clientControlledJunk2) { - clientControlledJunk2 = textField.text - } - } - } - - AwgTextField { - id: controlledJunk3TextField - headerText: qsTr("J3 - Third controlled junk packet") - textField.text: clientControlledJunk3 - textField.validator: null - checkEmptyText: false - - textField.onEditingFinished: { - if (textField.text !== clientControlledJunk3) { - clientControlledJunk3 = textField.text - } - } - } - - AwgTextField { - id: iTimeTextField - headerText: qsTr("Itime - Special handshake timeout") - textField.text: clientSpecialHandshakeTimeout - checkEmptyText: false - - textField.onEditingFinished: { - if (textField.text !== clientSpecialHandshakeTimeout) { - clientSpecialHandshakeTimeout = textField.text - } - } } Header2TextType { @@ -287,78 +181,82 @@ PageType { text: qsTr("Server settings") } - AwgTextField { + TextFieldWithHeaderType { id: portTextField + Layout.fillWidth: true + Layout.topMargin: 8 + enabled: false headerText: qsTr("Port") textField.text: port } - AwgTextField { + TextFieldWithHeaderType { id: initPacketJunkSizeTextField + Layout.fillWidth: true + Layout.topMargin: 16 + enabled: false headerText: "S1 - Init packet junk size" textField.text: serverInitPacketJunkSize } - AwgTextField { + TextFieldWithHeaderType { id: responsePacketJunkSizeTextField + Layout.fillWidth: true + Layout.topMargin: 16 + enabled: false headerText: "S2 - Response packet junk size" textField.text: serverResponsePacketJunkSize } - // AwgTextField { - // id: cookieReplyPacketJunkSizeTextField - // enabled: false - - // headerText: "S3 - Cookie Reply packet junk size" - // textField.text: serverCookieReplyPacketJunkSize - // } - - // AwgTextField { - // id: transportPacketJunkSizeTextField - // enabled: false - - // headerText: "S4 - Transport packet junk size" - // textField.text: serverTransportPacketJunkSize - // } - - AwgTextField { + TextFieldWithHeaderType { id: initPacketMagicHeaderTextField + Layout.fillWidth: true + Layout.topMargin: 16 + enabled: false headerText: "H1 - Init packet magic header" textField.text: serverInitPacketMagicHeader } - AwgTextField { + TextFieldWithHeaderType { id: responsePacketMagicHeaderTextField + Layout.fillWidth: true + Layout.topMargin: 16 + enabled: false headerText: "H2 - Response packet magic header" textField.text: serverResponsePacketMagicHeader } - AwgTextField { + TextFieldWithHeaderType { id: underloadPacketMagicHeaderTextField + Layout.fillWidth: true + Layout.topMargin: 16 + enabled: false headerText: "H3 - Underload packet magic header" textField.text: serverUnderloadPacketMagicHeader } - AwgTextField { + TextFieldWithHeaderType { id: transportPacketMagicHeaderTextField + Layout.fillWidth: true + Layout.topMargin: 16 + enabled: false headerText: "H4 - Transport packet magic header" textField.text: serverTransportPacketMagicHeader } - } } } diff --git a/client/ui/qml/Pages2/PageProtocolAwgSettings.qml b/client/ui/qml/Pages2/PageProtocolAwgSettings.qml index 699ae724..e8fd2b94 100644 --- a/client/ui/qml/Pages2/PageProtocolAwgSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolAwgSettings.qml @@ -138,138 +138,183 @@ PageType { checkEmptyText: true } - AwgTextField { + TextFieldWithHeaderType { id: junkPacketCountTextField + Layout.fillWidth: true + Layout.topMargin: 16 + headerText: qsTr("Jc - Junk packet count") textField.text: serverJunkPacketCount + textField.validator: IntValidator { bottom: 0 } textField.onEditingFinished: { + if (textField.text === "") { + textField.text = "0" + } + if (textField.text !== serverJunkPacketCount) { serverJunkPacketCount = textField.text } } + + checkEmptyText: true } - AwgTextField { + TextFieldWithHeaderType { id: junkPacketMinSizeTextField + Layout.fillWidth: true + Layout.topMargin: 16 + headerText: qsTr("Jmin - Junk packet minimum size") textField.text: serverJunkPacketMinSize + textField.validator: IntValidator { bottom: 0 } textField.onEditingFinished: { if (textField.text !== serverJunkPacketMinSize) { serverJunkPacketMinSize = textField.text } } + + checkEmptyText: true } - AwgTextField { + TextFieldWithHeaderType { id: junkPacketMaxSizeTextField + Layout.fillWidth: true + Layout.topMargin: 16 + headerText: qsTr("Jmax - Junk packet maximum size") textField.text: serverJunkPacketMaxSize + textField.validator: IntValidator { bottom: 0 } textField.onEditingFinished: { if (textField.text !== serverJunkPacketMaxSize) { serverJunkPacketMaxSize = textField.text } } + + checkEmptyText: true } - AwgTextField { + TextFieldWithHeaderType { id: initPacketJunkSizeTextField + Layout.fillWidth: true + Layout.topMargin: 16 + headerText: qsTr("S1 - Init packet junk size") textField.text: serverInitPacketJunkSize + textField.validator: IntValidator { bottom: 0 } textField.onEditingFinished: { if (textField.text !== serverInitPacketJunkSize) { serverInitPacketJunkSize = textField.text } } + + checkEmptyText: true + + onActiveFocusChanged: { + if(activeFocus) { + listview.positionViewAtEnd() + } + } } - AwgTextField { + TextFieldWithHeaderType { id: responsePacketJunkSizeTextField + Layout.fillWidth: true + Layout.topMargin: 16 + headerText: qsTr("S2 - Response packet junk size") textField.text: serverResponsePacketJunkSize + textField.validator: IntValidator { bottom: 0 } textField.onEditingFinished: { if (textField.text !== serverResponsePacketJunkSize) { serverResponsePacketJunkSize = textField.text } } + + checkEmptyText: true + + onActiveFocusChanged: { + if(activeFocus) { + listview.positionViewAtEnd() + } + } } - // AwgTextField { - // id: cookieReplyPacketJunkSizeTextField - // headerText: qsTr("S3 - Cookie reply packet junk size") - // textField.text: serverCookieReplyPacketJunkSize - - // textField.onEditingFinished: { - // if (textField.text !== serverCookieReplyPacketJunkSize) { - // serverCookieReplyPacketJunkSize = textField.text - // } - // } - // } - - // AwgTextField { - // id: transportPacketJunkSizeTextField - // headerText: qsTr("S4 - Transport packet junk size") - // textField.text: serverTransportPacketJunkSize - - // textField.onEditingFinished: { - // if (textField.text !== serverTransportPacketJunkSize) { - // serverTransportPacketJunkSize = textField.text - // } - // } - // } - - AwgTextField { + TextFieldWithHeaderType { id: initPacketMagicHeaderTextField + Layout.fillWidth: true + Layout.topMargin: 16 + headerText: qsTr("H1 - Init packet magic header") textField.text: serverInitPacketMagicHeader + textField.validator: IntValidator { bottom: 0 } textField.onEditingFinished: { if (textField.text !== serverInitPacketMagicHeader) { serverInitPacketMagicHeader = textField.text } } + + checkEmptyText: true } - AwgTextField { + TextFieldWithHeaderType { id: responsePacketMagicHeaderTextField + Layout.fillWidth: true + Layout.topMargin: 16 + headerText: qsTr("H2 - Response packet magic header") textField.text: serverResponsePacketMagicHeader + textField.validator: IntValidator { bottom: 0 } textField.onEditingFinished: { if (textField.text !== serverResponsePacketMagicHeader) { serverResponsePacketMagicHeader = textField.text } } + + checkEmptyText: true } - AwgTextField { - id: underloadPacketMagicHeaderTextField - headerText: qsTr("H3 - Underload packet magic header") - textField.text: serverUnderloadPacketMagicHeader - - textField.onEditingFinished: { - if (textField.text !== serverUnderloadPacketMagicHeader) { - serverUnderloadPacketMagicHeader = textField.text - } - } - } - - AwgTextField { + TextFieldWithHeaderType { id: transportPacketMagicHeaderTextField + Layout.fillWidth: true + Layout.topMargin: 16 + headerText: qsTr("H4 - Transport packet magic header") textField.text: serverTransportPacketMagicHeader + textField.validator: IntValidator { bottom: 0 } textField.onEditingFinished: { if (textField.text !== serverTransportPacketMagicHeader) { serverTransportPacketMagicHeader = textField.text } } + + checkEmptyText: true } + TextFieldWithHeaderType { + id: underloadPacketMagicHeaderTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + headerText: qsTr("H3 - Underload packet magic header") + textField.text: serverUnderloadPacketMagicHeader + textField.validator: IntValidator { bottom: 0 } + + textField.onEditingFinished: { + if (textField.text !== serverUnderloadPacketMagicHeader) { + serverUnderloadPacketMagicHeader = textField.text + } + } + + checkEmptyText: true + } BasicButtonType { id: saveRestartButton @@ -283,8 +328,6 @@ PageType { responsePacketMagicHeaderTextField.errorText === "" && initPacketMagicHeaderTextField.errorText === "" && responsePacketJunkSizeTextField.errorText === "" && - // cookieReplyHeaderJunkTextField.errorText === "" && - // transportHeaderJunkTextField.errorText === "" && initPacketJunkSizeTextField.errorText === "" && junkPacketMaxSizeTextField.errorText === "" && junkPacketMinSizeTextField.errorText === "" && @@ -317,13 +360,6 @@ PageType { PageController.showErrorMessage(qsTr("The value of the field S1 + message initiation size (148) must not equal S2 + message response size (92)")) return } - // if (AwgConfigModel.isPacketSizeEqual(parseInt(initPacketJunkSizeTextField.textField.text), - // parseInt(responsePacketJunkSizeTextField.textField.text), - // parseInt(cookieReplyPacketJunkSizeTextField.textField.text), - // parseInt(transportPacketJunkSizeTextField.textField.text))) { - // PageController.showErrorMessage(qsTr("The value of the field S1 + message initiation size (148) must not equal S2 + message response size (92) + S3 + cookie reply size (64) + S4 + transport packet size (32)")) - // return - // } } var headerText = qsTr("Save settings?") diff --git a/client/ui/qml/Pages2/PageProtocolCloakSettings.qml b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml index 8e5129b0..7a0fafbd 100644 --- a/client/ui/qml/Pages2/PageProtocolCloakSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml @@ -59,14 +59,11 @@ PageType { model: CloakConfigModel delegate: Item { - id: delegateItem - - property alias trafficFromField: trafficFromField - property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess() - implicitWidth: listview.width implicitHeight: col.implicitHeight + property alias trafficFromField: trafficFromField + ColumnLayout { id: col @@ -81,6 +78,7 @@ PageType { BaseHeaderType { Layout.fillWidth: true + headerText: qsTr("Cloak settings") } @@ -90,8 +88,6 @@ PageType { Layout.fillWidth: true Layout.topMargin: 32 - enabled: delegateItem.isEnabled - headerText: qsTr("Disguised as traffic from") textField.text: site @@ -108,8 +104,6 @@ PageType { } } } - - checkEmptyText: true } TextFieldWithHeaderType { @@ -118,8 +112,6 @@ PageType { Layout.fillWidth: true Layout.topMargin: 16 - enabled: delegateItem.isEnabled - headerText: qsTr("Port") textField.text: port textField.maximumLength: 5 @@ -130,8 +122,6 @@ PageType { port = textField.text } } - - checkEmptyText: true } DropDownType { @@ -139,8 +129,6 @@ PageType { Layout.fillWidth: true Layout.topMargin: 16 - enabled: delegateItem.isEnabled - descriptionText: qsTr("Cipher") headerText: qsTr("Cipher") @@ -178,46 +166,25 @@ PageType { } BasicButtonType { - id: saveButton + id: saveRestartButton Layout.fillWidth: true Layout.topMargin: 24 Layout.bottomMargin: 24 - enabled: trafficFromField.errorText === "" && - portTextField.errorText === "" - text: qsTr("Save") clickedFunc: function() { forceActiveFocus() - var headerText = qsTr("Save settings?") - var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.") - var yesButtonText = qsTr("Continue") - var noButtonText = qsTr("Cancel") - - var yesButtonFunction = function() { - if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { - PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection")) - return - } - - PageController.goToPage(PageEnum.PageSetupWizardInstalling) - InstallController.updateContainer(CloakConfigModel.getConfig()) + if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { + PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection")) + return } - var noButtonFunction = function() { - if (!GC.isMobile()) { - saveButton.forceActiveFocus() - } - } - - showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) + PageController.goToPage(PageEnum.PageSetupWizardInstalling); + InstallController.updateContainer(CloakConfigModel.getConfig()) } - - Keys.onEnterPressed: saveButton.clicked() - Keys.onReturnPressed: saveButton.clicked() } } } diff --git a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml index 62cbd1f6..2e00d54a 100644 --- a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml @@ -58,14 +58,11 @@ PageType { model: OpenVpnConfigModel delegate: Item { - id: delegateItem - - property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField - property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess() - implicitWidth: listview.width implicitHeight: col.implicitHeight + property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField + ColumnLayout { id: col @@ -80,6 +77,7 @@ PageType { BaseHeaderType { Layout.fillWidth: true + headerText: qsTr("OpenVPN settings") } @@ -89,8 +87,6 @@ PageType { Layout.fillWidth: true Layout.topMargin: 32 - enabled: delegateItem.isEnabled - headerText: qsTr("VPN address subnet") textField.text: subnetAddress @@ -101,8 +97,6 @@ PageType { subnetAddress = textField.text } } - - checkEmptyText: true } ParagraphTextType { @@ -140,7 +134,7 @@ PageType { Layout.topMargin: 40 parentFlickable: fl - enabled: delegateItem.isEnabled + enabled: isPortEditable headerText: qsTr("Port") textField.text: port @@ -152,8 +146,6 @@ PageType { port = textField.text } } - - checkEmptyText: true } SwitcherType { @@ -396,45 +388,26 @@ PageType { } BasicButtonType { - id: saveButton + id: saveRestartButton Layout.fillWidth: true Layout.topMargin: 24 Layout.bottomMargin: 24 - enabled: vpnAddressSubnetTextField.errorText === "" && - portTextField.errorText === "" - text: qsTr("Save") parentFlickable: fl - onClicked: function() { + clickedFunc: function() { forceActiveFocus() - var headerText = qsTr("Save settings?") - var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.") - var yesButtonText = qsTr("Continue") - var noButtonText = qsTr("Cancel") - - var yesButtonFunction = function() { - if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { - PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection")) - return - } - - PageController.goToPage(PageEnum.PageSetupWizardInstalling); - InstallController.updateContainer(OpenVpnConfigModel.getConfig()) + if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { + PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection")) + return } - var noButtonFunction = function() { - if (!GC.isMobile()) { - saveButton.forceActiveFocus() - } - } - showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) + + PageController.goToPage(PageEnum.PageSetupWizardInstalling); + InstallController.updateContainer(OpenVpnConfigModel.getConfig()) } - - Keys.onEnterPressed: saveButton.clicked() - Keys.onReturnPressed: saveButton.clicked() } } } diff --git a/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml index 92df3ec7..63e60dcb 100644 --- a/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml @@ -57,13 +57,15 @@ PageType { model: ShadowSocksConfigModel delegate: Item { - id: delegateItem - - property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess() - implicitWidth: listview.width implicitHeight: col.implicitHeight + property var focusItemId: portTextField.enabled ? + portTextField : + cipherDropDown.enabled ? + cipherDropDown : + saveRestartButton + ColumnLayout { id: col @@ -78,6 +80,7 @@ PageType { BaseHeaderType { Layout.fillWidth: true + headerText: qsTr("Shadowsocks settings") } @@ -87,7 +90,7 @@ PageType { Layout.fillWidth: true Layout.topMargin: 40 - enabled: delegateItem.isEnabled + enabled: isPortEditable headerText: qsTr("Port") textField.text: port @@ -99,8 +102,6 @@ PageType { port = textField.text } } - - checkEmptyText: true } DropDownType { @@ -108,7 +109,7 @@ PageType { Layout.fillWidth: true Layout.topMargin: 20 - enabled: delegateItem.isEnabled + enabled: isCipherEditable descriptionText: qsTr("Cipher") headerText: qsTr("Cipher") @@ -148,43 +149,27 @@ PageType { } BasicButtonType { - id: saveButton + id: saveRestartButton Layout.fillWidth: true Layout.topMargin: 24 Layout.bottomMargin: 24 - enabled: portTextField.errorText === "" + enabled: isPortEditable | isCipherEditable text: qsTr("Save") clickedFunc: function() { forceActiveFocus() - var headerText = qsTr("Save settings?") - var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.") - var yesButtonText = qsTr("Continue") - var noButtonText = qsTr("Cancel") - - var yesButtonFunction = function() { - if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { - PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection")) - return - } - - PageController.goToPage(PageEnum.PageSetupWizardInstalling); - InstallController.updateContainer(ShadowSocksConfigModel.getConfig()) + if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { + PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection")) + return } - var noButtonFunction = function() { - if (!GC.isMobile()) { - saveButton.forceActiveFocus() - } - } - showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) + + PageController.goToPage(PageEnum.PageSetupWizardInstalling); + InstallController.updateContainer(ShadowSocksConfigModel.getConfig()) } - - Keys.onEnterPressed: saveButton.clicked() - Keys.onReturnPressed: saveButton.clicked() } } } diff --git a/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml b/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml index 21b35bc1..7b5180f3 100644 --- a/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml @@ -152,7 +152,7 @@ PageType { } var noButtonFunction = function() { if (!GC.isMobile()) { - saveButton.forceActiveFocus() + saveRestartButton.forceActiveFocus() } } showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) diff --git a/client/ui/qml/Pages2/PageProtocolXraySettings.qml b/client/ui/qml/Pages2/PageProtocolXraySettings.qml index 0bcd14de..ca30e0a9 100644 --- a/client/ui/qml/Pages2/PageProtocolXraySettings.qml +++ b/client/ui/qml/Pages2/PageProtocolXraySettings.qml @@ -58,10 +58,7 @@ PageType { model: XrayConfigModel delegate: Item { - id: delegateItem - property alias focusItemId: textFieldWithHeaderType.textField - property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess() implicitWidth: listview.width implicitHeight: col.implicitHeight @@ -88,8 +85,6 @@ PageType { Layout.fillWidth: true Layout.topMargin: 32 - enabled: delegateItem.isEnabled - headerText: qsTr("Disguised as traffic from") textField.text: site @@ -98,77 +93,39 @@ PageType { var tmpText = textField.text tmpText = tmpText.toLocaleLowerCase() - if (tmpText.startsWith("https://")) { + var indexHttps = tmpText.indexOf("https://") + if (indexHttps === 0) { tmpText = textField.text.substring(8) - site = tmpText } else { site = textField.text } } } - - checkEmptyText: true - } - - TextFieldWithHeaderType { - id: portTextField - Layout.fillWidth: true - Layout.topMargin: 16 - - enabled: delegateItem.isEnabled - - headerText: qsTr("Port") - textField.text: port - textField.maximumLength: 5 - textField.validator: IntValidator { bottom: 1; top: 65535 } - - textField.onEditingFinished: { - if (textField.text !== port) { - port = textField.text - } - } - - checkEmptyText: true } BasicButtonType { - id: saveButton + id: basicButton Layout.fillWidth: true Layout.topMargin: 24 Layout.bottomMargin: 24 - enabled: portTextField.errorText === "" - text: qsTr("Save") - onClicked: function() { + onClicked: { forceActiveFocus() - var headerText = qsTr("Save settings?") - var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.") - var yesButtonText = qsTr("Continue") - var noButtonText = qsTr("Cancel") - - var yesButtonFunction = function() { - if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { - PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection")) - return - } - - PageController.goToPage(PageEnum.PageSetupWizardInstalling); - InstallController.updateContainer(XrayConfigModel.getConfig()) - //focusItem.forceActiveFocus() + if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { + PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection")) + return } - var noButtonFunction = function() { - if (!GC.isMobile()) { - saveButton.forceActiveFocus() - } - } - showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) + + PageController.goToPage(PageEnum.PageSetupWizardInstalling); + InstallController.updateContainer(XrayConfigModel.getConfig()) + focusItem.forceActiveFocus() } - Keys.onEnterPressed: saveButton.clicked() - Keys.onReturnPressed: saveButton.clicked() + Keys.onEnterPressed: basicButton.clicked() + Keys.onReturnPressed: basicButton.clicked() } } } diff --git a/client/ui/qml/Pages2/PageSettingsApiDevices.qml b/client/ui/qml/Pages2/PageSettingsApiDevices.qml index 231b58a0..d8e24d42 100644 --- a/client/ui/qml/Pages2/PageSettingsApiDevices.qml +++ b/client/ui/qml/Pages2/PageSettingsApiDevices.qml @@ -77,7 +77,7 @@ PageType { } var headerText = qsTr("Are you sure you want to unlink this device?") - var descriptionText = qsTr("This will unlink the device from your subscription. You can reconnect it anytime by pressing \"Reload API config\" in subscription settings on device.") + var descriptionText = qsTr("This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect.") var yesButtonText = qsTr("Continue") var noButtonText = qsTr("Cancel") diff --git a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml index 75832fa6..48080c3a 100644 --- a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml @@ -158,32 +158,6 @@ PageType { readonly property bool isVisibleForAmneziaFree: ApiAccountInfoModel.data("isComponentVisible") - SwitcherType { - id: switcher - - readonly property bool isVlessProtocol: ApiConfigsController.isVlessProtocol() - - Layout.fillWidth: true - Layout.topMargin: 24 - Layout.rightMargin: 16 - Layout.leftMargin: 16 - - visible: ApiAccountInfoModel.data("isProtocolSelectionSupported") - - text: qsTr("Use VLESS protocol") - checked: switcher.isVlessProtocol - onToggled: function() { - if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) { - PageController.showNotificationMessage(qsTr("Cannot change protocol during active connection")) - } else { - PageController.showBusyIndicator(true) - ApiConfigsController.setCurrentProtocol(switcher.isVlessProtocol ? "awg" : "vless") - ApiConfigsController.updateServiceFromGateway(ServersModel.processedIndex, "", "", true) - PageController.showBusyIndicator(false) - } - } - } - WarningType { id: warning @@ -359,7 +333,7 @@ PageType { clickedFunc: function() { var headerText = qsTr("Are you sure you want to unlink this device?") - var descriptionText = qsTr("This will unlink the device from your subscription. You can reconnect it anytime by pressing \"Reload API config\" in subscription settings on device.") + var descriptionText = qsTr("This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect.") var yesButtonText = qsTr("Continue") var noButtonText = qsTr("Cancel") diff --git a/client/ui/qml/Pages2/PageSettingsApiSupport.qml b/client/ui/qml/Pages2/PageSettingsApiSupport.qml index e10eb325..af629ebe 100644 --- a/client/ui/qml/Pages2/PageSettingsApiSupport.qml +++ b/client/ui/qml/Pages2/PageSettingsApiSupport.qml @@ -88,7 +88,6 @@ PageType { LabelWithButtonType { Layout.fillWidth: true - visible: link !== "" text: title descriptionText: description rightImageSource: "qrc:/images/controls/external-link.svg" diff --git a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml index d6d73b20..f6e0f5d7 100644 --- a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml +++ b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml @@ -49,7 +49,7 @@ PageType { if (!ConnectionController.isConnected) { SettingsController.isKillSwitchEnabled = checked } else { - PageController.showNotificationMessage(qsTr("KillSwitch settings cannot be changed during an active connection")) + PageController.showNotificationMessage(qsTr("Cannot change killSwitch settings during active connection")) switcher.checked = SettingsController.isKillSwitchEnabled } } @@ -66,7 +66,7 @@ PageType { checked: !SettingsController.strictKillSwitchEnabled text: qsTr("Soft KillSwitch") - descriptionText: qsTr("Internet access is blocked if the VPN disconnects unexpectedly") + descriptionText: qsTr("Internet connection is blocked if VPN connection drops accidentally") onClicked: function() { SettingsController.strictKillSwitchEnabled = false @@ -81,17 +81,15 @@ PageType { Layout.leftMargin: 16 Layout.rightMargin: 16 - visible: false - enabled: false - // enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected + enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected checked: SettingsController.strictKillSwitchEnabled text: qsTr("Strict KillSwitch") - descriptionText: qsTr("Internet connection is blocked even when VPN is turned off manually or hasn't started") + descriptionText: qsTr("Internet connection is blocked even if VPN was turned off manually or not started") onClicked: function() { var headerText = qsTr("Just a little heads-up") - var descriptionText = qsTr("If the VPN disconnects or drops while Strict KillSwitch is enabled, internet access will be blocked. To restore access, reconnect VPN or disable/change the KillSwitch.") + var descriptionText = qsTr("If you disconnect from VPN or the VPN connection drops while the Strict Kill Switch is turned on, your internet access will be disabled. To restore it, connect to VPN, change the Kill Switch mode or turn the Kill Switch off.") var yesButtonText = qsTr("Continue") var noButtonText = qsTr("Cancel") @@ -105,9 +103,7 @@ PageType { } } - DividerType { - visible: false - } + DividerType {} LabelWithButtonType { Layout.topMargin: 32 @@ -115,7 +111,7 @@ PageType { enabled: true text: qsTr("DNS Exceptions") - descriptionText: qsTr("DNS servers listed here will remain accessible when KillSwitch is active.") + descriptionText: qsTr("DNS servers from the list will remain accessible when Kill Switch is triggered") rightImageSource: "qrc:/images/controls/chevron-right.svg" clickedFunction: function() { diff --git a/client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml b/client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml index 31d1721b..d442b60c 100644 --- a/client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml +++ b/client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml @@ -43,7 +43,7 @@ PageType { Layout.leftMargin: 16 headerText: qsTr("DNS Exceptions") - descriptionText: qsTr("DNS servers listed here will remain accessible when KillSwitch is active") + descriptionText: qsTr("DNS servers from the list will remain accessible when Kill Switch is triggered") } } diff --git a/client/ui/qml/Pages2/PageSettingsServerData.qml b/client/ui/qml/Pages2/PageSettingsServerData.qml index 82552958..977e669e 100644 --- a/client/ui/qml/Pages2/PageSettingsServerData.qml +++ b/client/ui/qml/Pages2/PageSettingsServerData.qml @@ -257,24 +257,6 @@ PageType { DividerType { visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") } - - LabelWithButtonType { - id: labelWithButton6 - visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") && ServersModel.processedServerIsPremium - Layout.fillWidth: true - - text: qsTr("Switch to the new Amnezia Premium subscription") - textColor: AmneziaStyle.color.vibrantRed - - clickedFunction: function() { - PageController.goToPageHome() - ApiPremV1MigrationController.showMigrationDrawer() - } - } - - DividerType { - visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") && ServersModel.processedServerIsPremium - } } } } diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index 0f0976bc..48f74acf 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -429,11 +429,6 @@ PageType { fillConnectionTypeModel() - if (exportTypeSelector.currentIndex >= root.connectionTypesModel.length) { - exportTypeSelector.currentIndex = 0 - exportTypeSelector.text = root.connectionTypesModel[0].name - } - if (accessTypeSelector.currentIndex === 1) { PageController.showBusyIndicator(true) ExportController.updateClientManagementModel(ContainersModel.getProcessedContainerIndex(), diff --git a/deploy/DeveloperIDG2CA.cer b/deploy/DeveloperIDG2CA.cer deleted file mode 100644 index 8cbcf6f4..00000000 Binary files a/deploy/DeveloperIDG2CA.cer and /dev/null differ diff --git a/deploy/build_macos.sh b/deploy/build_macos.sh old mode 100644 new mode 100755 index 03f286fc..5f6e9786 --- a/deploy/build_macos.sh +++ b/deploy/build_macos.sh @@ -1,15 +1,4 @@ #!/bin/bash -# ----------------------------------------------------------------------------- -# Usage: -# Export the required signing credentials before running this script, e.g.: -# export MAC_APP_CERT_PW='pw-for-DeveloperID-Application' -# export MAC_INSTALL_CERT_PW='pw-for-DeveloperID-Installer' -# export MAC_SIGNER_ID='Developer ID Application: Some Company Name (XXXXXXXXXX)' -# export MAC_INSTALLER_SIGNER_ID='Developer ID Installer: Some Company Name (XXXXXXXXXX)' -# export APPLE_DEV_EMAIL='your@email.com' -# export APPLE_DEV_PASSWORD='' -# bash deploy/build_macos.sh [-n] -# ----------------------------------------------------------------------------- echo "Build script started ..." set -o errexit -o nounset @@ -25,10 +14,10 @@ done PROJECT_DIR=$(pwd) DEPLOY_DIR=$PROJECT_DIR/deploy -mkdir -p "$DEPLOY_DIR/build" -BUILD_DIR="$DEPLOY_DIR/build" +mkdir -p $DEPLOY_DIR/build +BUILD_DIR=$DEPLOY_DIR/build -echo "Project dir: ${PROJECT_DIR}" +echo "Project dir: ${PROJECT_DIR}" echo "Build dir: ${BUILD_DIR}" APP_NAME=AmneziaVPN @@ -39,45 +28,39 @@ PLIST_NAME=$APP_NAME.plist OUT_APP_DIR=$BUILD_DIR/client BUNDLE_DIR=$OUT_APP_DIR/$APP_FILENAME -# Prebuilt deployment assets are available via the symlink under deploy/data PREBUILT_DEPLOY_DATA_DIR=$PROJECT_DIR/deploy/data/deploy-prebuilt/macos DEPLOY_DATA_DIR=$PROJECT_DIR/deploy/data/macos +INSTALLER_DATA_DIR=$BUILD_DIR/installer/packages/$APP_DOMAIN/data +INSTALLER_BUNDLE_DIR=$BUILD_DIR/installer/$APP_FILENAME +DMG_FILENAME=$PROJECT_DIR/${APP_NAME}.dmg # Search Qt if [ -z "${QT_VERSION+x}" ]; then -QT_VERSION=6.8.3; +QT_VERSION=6.4.3; +QIF_VERSION=4.6 QT_BIN_DIR=$HOME/Qt/$QT_VERSION/macos/bin +QIF_BIN_DIR=$QT_BIN_DIR/../../../Tools/QtInstallerFramework/$QIF_VERSION/bin fi echo "Using Qt in $QT_BIN_DIR" +echo "Using QIF in $QIF_BIN_DIR" # Checking env -"$QT_BIN_DIR/qt-cmake" --version +$QT_BIN_DIR/qt-cmake --version cmake --version clang -v # Build App echo "Building App..." -cd "$BUILD_DIR" +cd $BUILD_DIR -"$QT_BIN_DIR/qt-cmake" -S "$PROJECT_DIR" -B "$BUILD_DIR" +$QT_BIN_DIR/qt-cmake -S $PROJECT_DIR -B $BUILD_DIR cmake --build . --config release --target all # Build and run tests here -# Create a temporary keychain and import certificates -KEYCHAIN_PATH="$PROJECT_DIR/mac_sign.keychain" -trap 'echo "Cleaning up mac_sign.keychain..."; security delete-keychain "$KEYCHAIN_PATH" 2>/dev/null || true; rm -f "$KEYCHAIN_PATH" 2>/dev/null || true' EXIT -KEYCHAIN=$(security default-keychain -d user | tr -d '"[:space:]"') -security list-keychains -d user -s "$KEYCHAIN_PATH" "$KEYCHAIN" "$(security list-keychains -d user | tr '\n' ' ')" -security create-keychain -p "" "$KEYCHAIN_PATH" -security import "$DEPLOY_DIR/DeveloperIdApplicationCertificate.p12" -k "$KEYCHAIN_PATH" -P "$MAC_APP_CERT_PW" -T /usr/bin/codesign -security import "$DEPLOY_DIR/DeveloperIdInstallerCertificate.p12" -k "$KEYCHAIN_PATH" -P "$MAC_INSTALL_CERT_PW" -T /usr/bin/codesign -security import "$DEPLOY_DIR/DeveloperIDG2CA.cer" -k "$KEYCHAIN_PATH" -T /usr/bin/codesign -security list-keychains -d user -s "$KEYCHAIN_PATH" - echo "____________________________________" echo "............Deploy.................." echo "____________________________________" @@ -86,159 +69,102 @@ echo "____________________________________" echo "Packaging ..." -cp -Rv "$PREBUILT_DEPLOY_DATA_DIR"/* "$BUNDLE_DIR/Contents/macOS" -"$QT_BIN_DIR/macdeployqt" "$OUT_APP_DIR/$APP_FILENAME" -always-overwrite -qmldir="$PROJECT_DIR" -cp -av "$BUILD_DIR/service/server/$APP_NAME-service" "$BUNDLE_DIR/Contents/macOS" -rsync -av --exclude="$PLIST_NAME" --exclude=post_install.sh --exclude=post_uninstall.sh "$DEPLOY_DATA_DIR/" "$BUNDLE_DIR/Contents/macOS/" +cp -Rv $PREBUILT_DEPLOY_DATA_DIR/* $BUNDLE_DIR/Contents/macOS +$QT_BIN_DIR/macdeployqt $OUT_APP_DIR/$APP_FILENAME -always-overwrite -qmldir=$PROJECT_DIR +cp -av $BUILD_DIR/service/server/$APP_NAME-service $BUNDLE_DIR/Contents/macOS +cp -Rv $PROJECT_DIR/deploy/data/macos/* $BUNDLE_DIR/Contents/macOS +rm -f $BUNDLE_DIR/Contents/macOS/post_install.sh $BUNDLE_DIR/Contents/macOS/post_uninstall.sh -if [ "${MAC_APP_CERT_PW+x}" ]; then +if [ "${MAC_CERT_PW+x}" ]; then - # Path to the p12 that contains the Developer ID *Application* certificate - CERTIFICATE_P12=$DEPLOY_DIR/DeveloperIdApplicationCertificate.p12 + CERTIFICATE_P12=$DEPLOY_DIR/PrivacyTechAppleCertDeveloperId.p12 + WWDRCA=$DEPLOY_DIR/WWDRCA.cer + KEYCHAIN=amnezia.build.macos.keychain + TEMP_PASS=tmp_pass - # Ensure launchd plist is bundled, but place it inside Resources so that - # the bundle keeps a valid structure (nothing but `Contents` at the root). - mkdir -p "$BUNDLE_DIR/Contents/Resources" - cp "$DEPLOY_DATA_DIR/$PLIST_NAME" "$BUNDLE_DIR/Contents/Resources/$PLIST_NAME" + security create-keychain -p $TEMP_PASS $KEYCHAIN || true + security default-keychain -s $KEYCHAIN + security unlock-keychain -p $TEMP_PASS $KEYCHAIN - # Show available signing identities (useful for debugging) - security find-identity -p codesigning || true + security default-keychain + security list-keychains + + security import $WWDRCA -k $KEYCHAIN -T /usr/bin/codesign || true + security import $CERTIFICATE_P12 -k $KEYCHAIN -P $MAC_CERT_PW -T /usr/bin/codesign || true + + security set-key-partition-list -S apple-tool:,apple: -k $TEMP_PASS $KEYCHAIN + security find-identity -p codesigning echo "Signing App bundle..." - /usr/bin/codesign --deep --force --verbose --timestamp -o runtime --keychain "$KEYCHAIN_PATH" --sign "$MAC_SIGNER_ID" "$BUNDLE_DIR" - /usr/bin/codesign --verify -vvvv "$BUNDLE_DIR" || true - spctl -a -vvvv "$BUNDLE_DIR" || true + /usr/bin/codesign --deep --force --verbose --timestamp -o runtime --sign "$MAC_SIGNER_ID" $BUNDLE_DIR + /usr/bin/codesign --verify -vvvv $BUNDLE_DIR || true + spctl -a -vvvv $BUNDLE_DIR || true + if [ "${NOTARIZE_APP+x}" ]; then + echo "Notarizing App bundle..." + /usr/bin/ditto -c -k --keepParent $BUNDLE_DIR $PROJECT_DIR/Bundle_to_notarize.zip + xcrun notarytool submit $PROJECT_DIR/Bundle_to_notarize.zip --apple-id $APPLE_DEV_EMAIL --team-id $MAC_TEAM_ID --password $APPLE_DEV_PASSWORD + rm $PROJECT_DIR/Bundle_to_notarize.zip + sleep 300 + xcrun stapler staple $BUNDLE_DIR + xcrun stapler validate $BUNDLE_DIR + spctl -a -vvvv $BUNDLE_DIR || true + fi fi echo "Packaging installer..." -PKG_DIR=$BUILD_DIR/pkg -# Remove any stale packaging data from previous runs -rm -rf "$PKG_DIR" -PKG_ROOT=$PKG_DIR/root -SCRIPTS_DIR=$PKG_DIR/scripts -RESOURCES_DIR=$PKG_DIR/resources -INSTALL_PKG=$PKG_DIR/${APP_NAME}_install.pkg -UNINSTALL_PKG=$PKG_DIR/${APP_NAME}_uninstall.pkg -FINAL_PKG=$PKG_DIR/${APP_NAME}.pkg -UNINSTALL_SCRIPTS_DIR=$PKG_DIR/uninstall_scripts +mkdir -p $INSTALLER_DATA_DIR +cp -av $PROJECT_DIR/deploy/installer $BUILD_DIR +cp -av $DEPLOY_DATA_DIR/post_install.sh $INSTALLER_DATA_DIR/post_install.sh +cp -av $DEPLOY_DATA_DIR/post_uninstall.sh $INSTALLER_DATA_DIR/post_uninstall.sh +cp -av $DEPLOY_DATA_DIR/$PLIST_NAME $INSTALLER_DATA_DIR/$PLIST_NAME -mkdir -p "$PKG_ROOT/Applications" "$SCRIPTS_DIR" "$RESOURCES_DIR" "$UNINSTALL_SCRIPTS_DIR" +chmod a+x $INSTALLER_DATA_DIR/post_install.sh $INSTALLER_DATA_DIR/post_uninstall.sh -cp -R "$BUNDLE_DIR" "$PKG_ROOT/Applications" -# launchd plist is already inside the bundle; no need to add it again after signing -/usr/bin/codesign --deep --force --verbose --timestamp -o runtime --keychain "$KEYCHAIN_PATH" --sign "$MAC_SIGNER_ID" "$PKG_ROOT/Applications/$APP_FILENAME" -/usr/bin/codesign --verify --deep --strict --verbose=4 "$PKG_ROOT/Applications/$APP_FILENAME" || true -cp "$DEPLOY_DATA_DIR/post_install.sh" "$SCRIPTS_DIR/post_install.sh" -cp "$DEPLOY_DATA_DIR/post_uninstall.sh" "$UNINSTALL_SCRIPTS_DIR/postinstall" -mkdir -p "$RESOURCES_DIR/scripts" -cp "$DEPLOY_DATA_DIR/check_install.sh" "$RESOURCES_DIR/scripts/check_install.sh" -cp "$DEPLOY_DATA_DIR/check_uninstall.sh" "$RESOURCES_DIR/scripts/check_uninstall.sh" +cd $BUNDLE_DIR +tar czf $INSTALLER_DATA_DIR/$APP_NAME.tar.gz ./ -cat > "$SCRIPTS_DIR/postinstall" <<'EOS' -#!/bin/bash -SCRIPT_DIR="$(dirname "$0")" -bash "$SCRIPT_DIR/post_install.sh" -exit 0 -EOS +echo "Building installer..." +$QIF_BIN_DIR/binarycreator --offline-only -v -c $BUILD_DIR/installer/config/macos.xml -p $BUILD_DIR/installer/packages -f $INSTALLER_BUNDLE_DIR -chmod +x "$SCRIPTS_DIR"/* -chmod +x "$UNINSTALL_SCRIPTS_DIR"/* -chmod +x "$RESOURCES_DIR/scripts"/* -cp "$PROJECT_DIR/LICENSE" "$RESOURCES_DIR/LICENSE" +if [ "${MAC_CERT_PW+x}" ]; then + echo "Signing installer bundle..." + security unlock-keychain -p $TEMP_PASS $KEYCHAIN + /usr/bin/codesign --deep --force --verbose --timestamp -o runtime --sign "$MAC_SIGNER_ID" $INSTALLER_BUNDLE_DIR + /usr/bin/codesign --verify -vvvv $INSTALLER_BUNDLE_DIR || true -APP_VERSION=$(grep -m1 -E 'project\(' "$PROJECT_DIR/CMakeLists.txt" | sed -E 's/.*VERSION ([0-9.]+).*/\1/') -echo "Building component package $INSTALL_PKG ..." - -# Disable bundle relocation so the app always ends up in /Applications even if -# another copy is lying around somewhere. We do this by letting pkgbuild -# analyse the contents, flipping the BundleIsRelocatable flag to false for every -# bundle it discovers and then feeding that plist back to pkgbuild. - -COMPONENT_PLIST="$PKG_DIR/component.plist" -# Create the component description plist first -pkgbuild --analyze --root "$PKG_ROOT" "$COMPONENT_PLIST" - -# Turn all `BundleIsRelocatable` keys to false (PlistBuddy is available on all -# macOS systems). We first convert to xml1 to ensure predictable formatting. - -# Turn relocation off for every bundle entry in the plist. PlistBuddy cannot -# address keys that contain slashes without quoting, so we iterate through the -# top-level keys it prints. -plutil -convert xml1 "$COMPONENT_PLIST" -for bundle_key in $(/usr/libexec/PlistBuddy -c "Print" "$COMPONENT_PLIST" | awk '/^[ \t]*[A-Za-z0-9].*\.app/ {print $1}'); do - /usr/libexec/PlistBuddy -c "Set :'${bundle_key}':BundleIsRelocatable false" "$COMPONENT_PLIST" || true -done - -# Now build the real payload package with the edited plist so that the final -# PackageInfo contains relocatable="false". -pkgbuild --root "$PKG_ROOT" \ - --identifier "$APP_DOMAIN" \ - --version "$APP_VERSION" \ - --install-location "/" \ - --scripts "$SCRIPTS_DIR" \ - --component-plist "$COMPONENT_PLIST" \ - --sign "$MAC_INSTALLER_SIGNER_ID" \ - "$INSTALL_PKG" - -# Build uninstaller component package -UNINSTALL_COMPONENT_PKG=$PKG_DIR/${APP_NAME}_uninstall_component.pkg -echo "Building uninstaller component package $UNINSTALL_COMPONENT_PKG ..." -pkgbuild --nopayload \ - --identifier "$APP_DOMAIN.uninstall" \ - --version "$APP_VERSION" \ - --scripts "$UNINSTALL_SCRIPTS_DIR" \ - --sign "$MAC_INSTALLER_SIGNER_ID" \ - "$UNINSTALL_COMPONENT_PKG" - -# Wrap uninstaller component in a distribution package for clearer UI -echo "Building uninstaller distribution package $UNINSTALL_PKG ..." -UNINSTALL_RESOURCES=$PKG_DIR/uninstall_resources -rm -rf "$UNINSTALL_RESOURCES" -mkdir -p "$UNINSTALL_RESOURCES" -cp "$DEPLOY_DATA_DIR/uninstall_welcome.html" "$UNINSTALL_RESOURCES" -cp "$DEPLOY_DATA_DIR/uninstall_conclusion.html" "$UNINSTALL_RESOURCES" -productbuild \ - --distribution "$DEPLOY_DATA_DIR/distribution_uninstall.xml" \ - --package-path "$PKG_DIR" \ - --resources "$UNINSTALL_RESOURCES" \ - --sign "$MAC_INSTALLER_SIGNER_ID" \ - "$UNINSTALL_PKG" - -cp "$PROJECT_DIR/deploy/data/macos/distribution.xml" "$PKG_DIR/distribution.xml" - -echo "Creating final installer $FINAL_PKG ..." -productbuild --distribution "$PKG_DIR/distribution.xml" \ - --package-path "$PKG_DIR" \ - --resources "$RESOURCES_DIR" \ - --sign "$MAC_INSTALLER_SIGNER_ID" \ - "$FINAL_PKG" - -if [ "${MAC_INSTALL_CERT_PW+x}" ] && [ "${NOTARIZE_APP+x}" ]; then - echo "Notarizing installer package..." - xcrun notarytool submit "$FINAL_PKG" \ - --apple-id "$APPLE_DEV_EMAIL" \ - --team-id "$MAC_TEAM_ID" \ - --password "$APPLE_DEV_PASSWORD" \ - --wait - - echo "Stapling ticket..." - xcrun stapler staple "$FINAL_PKG" - xcrun stapler validate "$FINAL_PKG" + if [ "${NOTARIZE_APP+x}" ]; then + echo "Notarizing installer bundle..." + /usr/bin/ditto -c -k --keepParent $INSTALLER_BUNDLE_DIR $PROJECT_DIR/Installer_bundle_to_notarize.zip + xcrun notarytool submit $PROJECT_DIR/Installer_bundle_to_notarize.zip --apple-id $APPLE_DEV_EMAIL --team-id $MAC_TEAM_ID --password $APPLE_DEV_PASSWORD + rm $PROJECT_DIR/Installer_bundle_to_notarize.zip + sleep 300 + xcrun stapler staple $INSTALLER_BUNDLE_DIR + xcrun stapler validate $INSTALLER_BUNDLE_DIR + spctl -a -vvvv $INSTALLER_BUNDLE_DIR || true + fi fi -if [ "${MAC_INSTALL_CERT_PW+x}" ]; then - /usr/bin/codesign --verify -vvvv "$FINAL_PKG" || true - spctl -a -vvvv "$FINAL_PKG" || true +echo "Building DMG installer..." +# Allow Terminal to make changes in Privacy & Security > App Management +hdiutil create -size 256mb -volname AmneziaVPN -srcfolder $BUILD_DIR/installer/$APP_NAME.app -ov -format UDZO $DMG_FILENAME + +if [ "${MAC_CERT_PW+x}" ]; then + echo "Signing DMG installer..." + security unlock-keychain -p $TEMP_PASS $KEYCHAIN + /usr/bin/codesign --deep --force --verbose --timestamp -o runtime --sign "$MAC_SIGNER_ID" $DMG_FILENAME + /usr/bin/codesign --verify -vvvv $DMG_FILENAME || true + + if [ "${NOTARIZE_APP+x}" ]; then + echo "Notarizing DMG installer..." + xcrun notarytool submit $DMG_FILENAME --apple-id $APPLE_DEV_EMAIL --team-id $MAC_TEAM_ID --password $APPLE_DEV_PASSWORD + sleep 300 + xcrun stapler staple $DMG_FILENAME + xcrun stapler validate $DMG_FILENAME + fi fi -# Sign app bundle -/usr/bin/codesign --deep --force --verbose --timestamp -o runtime --keychain "$KEYCHAIN_PATH" --sign "$MAC_SIGNER_ID" "$BUNDLE_DIR" -spctl -a -vvvv "$BUNDLE_DIR" || true +echo "Finished, artifact is $DMG_FILENAME" -# Restore login keychain as the only user keychain and delete the temporary keychain -KEYCHAIN="$HOME/Library/Keychains/login.keychain-db" -security list-keychains -d user -s "$KEYCHAIN" -security delete-keychain "$KEYCHAIN_PATH" - -echo "Finished, artifact is $FINAL_PKG" +# restore keychain +security default-keychain -s login.keychain diff --git a/deploy/data/macos/check_install.sh b/deploy/data/macos/check_install.sh deleted file mode 100755 index adf63550..00000000 --- a/deploy/data/macos/check_install.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -if [ -d "/Applications/AmneziaVPN.app" ] || pgrep -x "AmneziaVPN-service" >/dev/null; then - exit 1 -fi -exit 0 diff --git a/deploy/data/macos/check_uninstall.sh b/deploy/data/macos/check_uninstall.sh deleted file mode 100755 index e7a6f7e0..00000000 --- a/deploy/data/macos/check_uninstall.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -if [ -d "/Applications/AmneziaVPN.app" ] || pgrep -x "AmneziaVPN-service" >/dev/null; then - exit 0 -fi -exit 1 diff --git a/deploy/data/macos/distribution.xml b/deploy/data/macos/distribution.xml deleted file mode 100644 index c0a1dc68..00000000 --- a/deploy/data/macos/distribution.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - AmneziaVPN Installer - - - - - - - - - - - - AmneziaVPN_install.pkg - AmneziaVPN_uninstall_component.pkg - diff --git a/deploy/data/macos/distribution_uninstall.xml b/deploy/data/macos/distribution_uninstall.xml deleted file mode 100644 index cf8932b9..00000000 --- a/deploy/data/macos/distribution_uninstall.xml +++ /dev/null @@ -1,13 +0,0 @@ - - Uninstall AmneziaVPN - - - - - - - - - - AmneziaVPN_uninstall_component.pkg - diff --git a/deploy/data/macos/post_install.sh b/deploy/data/macos/post_install.sh index 053c8e13..acd3f93f 100755 --- a/deploy/data/macos/post_install.sh +++ b/deploy/data/macos/post_install.sh @@ -7,42 +7,29 @@ LOG_FOLDER=/var/log/$APP_NAME LOG_FILE="$LOG_FOLDER/post-install.log" APP_PATH=/Applications/$APP_NAME.app -# Handle new installations unpacked into localized folder -if [ -d "/Applications/${APP_NAME}.localized" ]; then - echo "`date` Detected ${APP_NAME}.localized, migrating to standard path" >> $LOG_FILE - sudo rm -rf "$APP_PATH" - sudo mv "/Applications/${APP_NAME}.localized/${APP_NAME}.app" "$APP_PATH" - sudo rm -rf "/Applications/${APP_NAME}.localized" -fi - if launchctl list "$APP_NAME-service" &> /dev/null; then - launchctl unload "$LAUNCH_DAEMONS_PLIST_NAME" - rm -f "$LAUNCH_DAEMONS_PLIST_NAME" + launchctl unload $LAUNCH_DAEMONS_PLIST_NAME + rm -f $LAUNCH_DAEMONS_PLIST_NAME fi -sudo chmod -R a-w "$APP_PATH/" -sudo chown -R root "$APP_PATH/" -sudo chgrp -R wheel "$APP_PATH/" +tar xzf $APP_PATH/$APP_NAME.tar.gz -C $APP_PATH +rm -f $APP_PATH/$APP_NAME.tar.gz +sudo chmod -R a-w $APP_PATH/ +sudo chown -R root $APP_PATH/ +sudo chgrp -R wheel $APP_PATH/ rm -rf $LOG_FOLDER mkdir -p $LOG_FOLDER echo "`date` Script started" > $LOG_FILE -echo "Requesting ${APP_NAME} to quit gracefully" >> "$LOG_FILE" -osascript -e 'tell application "AmneziaVPN" to quit' +killall -9 $APP_NAME-service 2>> $LOG_FILE -PLIST_SOURCE="$APP_PATH/Contents/Resources/$PLIST_NAME" -if [ -f "$PLIST_SOURCE" ]; then - mv -f "$PLIST_SOURCE" "$LAUNCH_DAEMONS_PLIST_NAME" 2>> $LOG_FILE -else - echo "`date` ERROR: service plist not found at $PLIST_SOURCE" >> $LOG_FILE -fi - -chown root:wheel "$LAUNCH_DAEMONS_PLIST_NAME" -launchctl load "$LAUNCH_DAEMONS_PLIST_NAME" -echo "`date` Launching ${APP_NAME} application" >> $LOG_FILE -open -a "$APP_PATH" 2>> $LOG_FILE || true +mv -f $APP_PATH/$PLIST_NAME $LAUNCH_DAEMONS_PLIST_NAME 2>> $LOG_FILE +chown root:wheel $LAUNCH_DAEMONS_PLIST_NAME +launchctl load $LAUNCH_DAEMONS_PLIST_NAME echo "`date` Service status: $?" >> $LOG_FILE echo "`date` Script finished" >> $LOG_FILE + +#rm -- "$0" diff --git a/deploy/data/macos/post_uninstall.sh b/deploy/data/macos/post_uninstall.sh index d6c5cdbd..de7846db 100755 --- a/deploy/data/macos/post_uninstall.sh +++ b/deploy/data/macos/post_uninstall.sh @@ -9,19 +9,6 @@ SYSTEM_APP_SUPPORT="/Library/Application Support/$APP_NAME" LOG_FOLDER="/var/log/$APP_NAME" CACHES_FOLDER="$HOME/Library/Caches/$APP_NAME" -# Attempt to quit the GUI application if it's currently running -if pgrep -x "$APP_NAME" > /dev/null; then - echo "Quitting $APP_NAME..." - osascript -e 'tell application "'"$APP_NAME"'" to quit' || true - # Wait up to 10 seconds for the app to terminate gracefully - for i in {1..10}; do - if ! pgrep -x "$APP_NAME" > /dev/null; then - break - fi - sleep 1 - done -fi - # Stop the running service if it exists if pgrep -x "${APP_NAME}-service" > /dev/null; then sudo killall -9 "${APP_NAME}-service" @@ -45,40 +32,3 @@ sudo rm -rf "$LOG_FOLDER" # Remove any caches left behind rm -rf "$CACHES_FOLDER" - -# Remove PF data directory created by firewall helper, if present -sudo rm -rf "/Library/Application Support/${APP_NAME}/pf" - -# ---------------- PF firewall cleanup ---------------------- -# Rules are loaded under the anchor "amn" (see macosfirewall.cpp) -# Flush only that anchor to avoid destroying user/system rules. - -PF_ANCHOR="amn" - -### Flush all PF rules, NATs, and tables under our anchor and sub-anchors ### -anchors=$(sudo pfctl -s Anchors 2>/dev/null | awk '/^'"${PF_ANCHOR}"'/ {sub(/\*$/, "", $1); print $1}') -for anc in $anchors; do - echo "Flushing PF anchor $anc" - sudo pfctl -a "$anc" -F all 2>/dev/null || true - # flush tables under this anchor - tables=$(sudo pfctl -s Tables 2>/dev/null | awk '/^'"$anc"'/ {print}') - for tbl in $tables; do - echo "Killing PF table $tbl" - sudo pfctl -t "$tbl" -T kill 2>/dev/null || true - done -done - -### Reload default PF config to restore system rules ### -if [ -f /etc/pf.conf ]; then - echo "Restoring system PF config" - sudo pfctl -f /etc/pf.conf 2>/dev/null || true -fi - -### Disable PF if no rules remain ### -if sudo pfctl -s info 2>/dev/null | grep -q '^Status: Enabled' && \ - ! sudo pfctl -sr 2>/dev/null | grep -q .; then - echo "Disabling PF" - sudo pfctl -d 2>/dev/null || true -fi - -# ----------------------------------------------------------- diff --git a/deploy/data/macos/uninstall_conclusion.html b/deploy/data/macos/uninstall_conclusion.html deleted file mode 100644 index f5b8bb63..00000000 --- a/deploy/data/macos/uninstall_conclusion.html +++ /dev/null @@ -1,7 +0,0 @@ - -Uninstall Complete - -

AmneziaVPN has been uninstalled

-

Thank you for using AmneziaVPN. The application and its components have been removed.

- - \ No newline at end of file diff --git a/deploy/data/macos/uninstall_welcome.html b/deploy/data/macos/uninstall_welcome.html deleted file mode 100644 index 9f3d97cb..00000000 --- a/deploy/data/macos/uninstall_welcome.html +++ /dev/null @@ -1,7 +0,0 @@ - -Uninstall AmneziaVPN - -

Uninstall AmneziaVPN

-

This process will remove AmneziaVPN from your system. Click Continue to proceed.

- - \ No newline at end of file diff --git a/deploy/deploy_s3.sh b/deploy/deploy_s3.sh index a139a5a5..c109a286 100755 --- a/deploy/deploy_s3.sh +++ b/deploy/deploy_s3.sh @@ -28,10 +28,10 @@ wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSIO wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_armeabi-v7a.apk wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_x86.apk wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_x86_64.apk -wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_linux_x64.tar.zip +wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_linux.tar.zip wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_macos.dmg wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_macos_old.dmg -wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_windows_x64.exe +wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_x64.exe cd ../ diff --git a/deploy/installer/config.cmake b/deploy/installer/config.cmake index 3c33a33c..13f09986 100644 --- a/deploy/installer/config.cmake +++ b/deploy/installer/config.cmake @@ -4,6 +4,11 @@ if(WIN32) ${CMAKE_CURRENT_LIST_DIR}/config/windows.xml.in ${CMAKE_BINARY_DIR}/installer/config/windows.xml ) +elseif(APPLE AND NOT IOS) + configure_file( + ${CMAKE_CURRENT_LIST_DIR}/config/macos.xml.in + ${CMAKE_BINARY_DIR}/installer/config/macos.xml + ) elseif(LINUX) set(ApplicationsDir "@ApplicationsDir@") configure_file( diff --git a/deploy/installer/config/AmneziaVPN.desktop.in b/deploy/installer/config/AmneziaVPN.desktop.in index 03ab570c..2a53074e 100755 --- a/deploy/installer/config/AmneziaVPN.desktop.in +++ b/deploy/installer/config/AmneziaVPN.desktop.in @@ -2,7 +2,7 @@ [Desktop Entry] Type=Application Name=AmneziaVPN -Version=1.0 +Version=@CMAKE_PROJECT_VERSION@ Comment=Client of your self-hosted VPN Exec=AmneziaVPN Icon=/usr/share/pixmaps/AmneziaVPN.png diff --git a/deploy/installer/config/macos.xml.in b/deploy/installer/config/macos.xml.in new file mode 100644 index 00000000..3888d08d --- /dev/null +++ b/deploy/installer/config/macos.xml.in @@ -0,0 +1,27 @@ + + + AmneziaVPN + @CMAKE_PROJECT_VERSION@ + AmneziaVPN + AmneziaVPN + AmneziaVPN + /Applications/AmneziaVPN.app + 600 + 380 + Mac + true + true + false + controlscript.js + false + true + false + true + + + https://amneziavpn.org/updates/macos + true + AmneziaVPN - repository for macOS + + + diff --git a/service/server/killswitch.cpp b/service/server/killswitch.cpp index d0cba03a..c44bd6a2 100644 --- a/service/server/killswitch.cpp +++ b/service/server/killswitch.cpp @@ -192,14 +192,7 @@ bool KillSwitch::addAllowedRange(const QStringList &ranges) { bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) { #ifdef Q_OS_WIN InterfaceConfig config; - - config.m_primaryDnsServer = configStr.value(amnezia::config_key::dns1).toString(); - - // We don't use secondary DNS if primary DNS is AmneziaDNS - if (!config.m_primaryDnsServer.contains(amnezia::protocols::dns::amneziaDnsIp)) { - config.m_secondaryDnsServer = configStr.value(amnezia::config_key::dns2).toString(); - } - + config.m_dnsServer = configStr.value(amnezia::config_key::dns1).toString(); config.m_serverPublicKey = "openvpn"; config.m_serverIpv4Gateway = configStr.value("vpnGateway").toString(); config.m_serverIpv4AddrIn = configStr.value("vpnServer").toString(); @@ -262,9 +255,6 @@ bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) { bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex) { #ifdef Q_OS_WIN - if (configStr.value("splitTunnelType").toInt() != 0) { - WindowsFirewall::create(this)->allowAllTraffic(); - } return WindowsFirewall::create(this)->enableInterface(vpnAdapterIndex); #endif @@ -314,14 +304,8 @@ bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIn LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), true); LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), true); QStringList dnsServers; - dnsServers.append(configStr.value(amnezia::config_key::dns1).toString()); - - // We don't use secondary DNS if primary DNS is AmneziaDNS - if (!configStr.value(amnezia::config_key::dns1).toString().contains(amnezia::protocols::dns::amneziaDnsIp)) { - dnsServers.append(configStr.value(amnezia::config_key::dns2).toString()); - } - + dnsServers.append(configStr.value(amnezia::config_key::dns2).toString()); dnsServers.append("127.0.0.1"); dnsServers.append("127.0.0.53"); @@ -358,11 +342,7 @@ bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIn QStringList dnsServers; dnsServers.append(configStr.value(amnezia::config_key::dns1).toString()); - - // We don't use secondary DNS if primary DNS is AmneziaDNS - if (!configStr.value(amnezia::config_key::dns1).toString().contains(amnezia::protocols::dns::amneziaDnsIp)) { - dnsServers.append(configStr.value(amnezia::config_key::dns2).toString()); - } + dnsServers.append(configStr.value(amnezia::config_key::dns2).toString()); for (auto dns : configStr.value(amnezia::config_key::allowedDnsServers).toArray()) { if (!dns.isString()) {