diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 86779f33..0c9dfb32 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -255,7 +255,6 @@ jobs: 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 }} @@ -283,11 +282,6 @@ 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 @@ -301,14 +295,13 @@ 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_old_installer - path: AmneziaVPN.dmg + path: deploy/build/pkg/AmneziaVPN.pkg retention-days: 7 - name: 'Upload unpacked artifact' @@ -325,7 +318,6 @@ jobs: env: QT_VERSION: 6.8.0 - QIF_VERSION: 4.8.1 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 }} @@ -353,11 +345,6 @@ 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 @@ -371,14 +358,13 @@ 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: AmneziaVPN.dmg + path: deploy/build/pkg/AmneziaVPN.pkg retention-days: 7 - name: 'Upload unpacked artifact' diff --git a/.gitmodules b/.gitmodules index decab9b7..90edb582 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,6 +7,7 @@ [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 a759bd80..fec613de 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.7.2 +project(${PROJECT} VERSION 4.8.8.1 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 2086) +set(APP_ANDROID_VERSION_CODE 2087) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(MZ_PLATFORM_NAME "linux") @@ -28,7 +28,7 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten") endif() set(QT_BUILD_TOOLS_WHEN_CROSS_COMPILING ON) -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) if(APPLE AND NOT IOS) diff --git a/client/3rd-prebuilt b/client/3rd-prebuilt index 0f3748ef..840b7b07 160000 --- a/client/3rd-prebuilt +++ b/client/3rd-prebuilt @@ -1 +1 @@ -Subproject commit 0f3748efd7cc04e0c914304b68931f925bed1259 +Subproject commit 840b7b070e6ac8b90dda2fac6e98859b23727c0c diff --git a/client/3rd/amneziawg-apple b/client/3rd/amneziawg-apple index 76e7db55..811af0a8 160000 --- a/client/3rd/amneziawg-apple +++ b/client/3rd/amneziawg-apple @@ -1 +1 @@ -Subproject commit 76e7db556a6d7e2582f9481df91db188a46c009c +Subproject commit 811af0a83b3faeade89a9093a588595666d32066 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 80cab96d..42a27de4 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,10 +120,21 @@ 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 7ae3d43b..2dfbbae8 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,10 +20,21 @@ 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? + 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? ) : ProtocolConfig(protocolConfigBuilder) { protected constructor(builder: Builder) : this( @@ -39,10 +50,21 @@ open class WireguardConfig protected constructor( builder.jmax, builder.s1, builder.s2, + builder.s3, + builder.s4, builder.h1, builder.h2, builder.h3, - builder.h4 + builder.h4, + builder.i1, + builder.i2, + builder.i3, + builder.i4, + builder.i5, + builder.j1, + builder.j2, + builder.j3, + builder.itime ) fun toWgUserspaceString(): String = with(StringBuilder()) { @@ -61,10 +83,21 @@ 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") } } } @@ -117,10 +150,21 @@ 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 } @@ -139,10 +183,21 @@ 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/sources.cmake b/client/cmake/sources.cmake index fe8e37e3..c3af531a 100644 --- a/client/cmake/sources.cmake +++ b/client/cmake/sources.cmake @@ -116,7 +116,6 @@ file(GLOB UI_MODELS_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/ui/models/services/*.h ${CLIENT_ROOT_DIR}/ui/models/api/*.h ) - file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/ui/models/*.cpp ${CLIENT_ROOT_DIR}/ui/models/protocols/*.cpp @@ -128,33 +127,17 @@ file(GLOB UI_CONTROLLERS_H CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/ui/controllers/*.h ${CLIENT_ROOT_DIR}/ui/controllers/api/*.h ) - file(GLOB UI_CONTROLLERS_CPP CONFIGURE_DEPENDS ${CLIENT_ROOT_DIR}/ui/controllers/*.cpp ${CLIENT_ROOT_DIR}/ui/controllers/api/*.cpp ) -file(GLOB CORE_MODELS_H CONFIGURE_DEPENDS - ${CLIENT_ROOT_DIR}/core/models/*.h - ${CLIENT_ROOT_DIR}/core/models/containers/*.h - ${CLIENT_ROOT_DIR}/core/models/protocols/*.h - ${CLIENT_ROOT_DIR}/core/models/servers/*.h -) - -file(GLOB CORE_MODELS_CPP CONFIGURE_DEPENDS - ${CLIENT_ROOT_DIR}/core/models/*.cpp - ${CLIENT_ROOT_DIR}/core/models/containers/*.cpp - ${CLIENT_ROOT_DIR}/core/models/protocols/*.cpp - ${CLIENT_ROOT_DIR}/core/models/servers/*.cpp -) - set(HEADERS ${HEADERS} ${COMMON_FILES_H} ${PAGE_LOGIC_H} ${CONFIGURATORS_H} ${UI_MODELS_H} ${UI_CONTROLLERS_H} - ${CORE_MODELS_H} ) set(SOURCES ${SOURCES} ${COMMON_FILES_CPP} @@ -162,7 +145,6 @@ set(SOURCES ${SOURCES} ${CONFIGURATORS_CPP} ${UI_MODELS_CPP} ${UI_CONTROLLERS_CPP} - ${CORE_MODELS_CPP} ) if(WIN32) diff --git a/client/configurators/awg_configurator.cpp b/client/configurators/awg_configurator.cpp index 21b61ba4..f83acb19 100644 --- a/client/configurators/awg_configurator.cpp +++ b/client/configurators/awg_configurator.cpp @@ -1,4 +1,5 @@ #include "awg_configurator.h" +#include "protocols/protocols_defs.h" #include #include @@ -39,6 +40,20 @@ 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 6d6603da..f6996320 100644 --- a/client/configurators/openvpn_configurator.cpp +++ b/client/configurators/openvpn_configurator.cpp @@ -118,6 +118,12 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPairisSitesSplitTunnelingEnabled()) { config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n"); config.append("block-ipv6\n"); @@ -161,6 +167,12 @@ QString OpenVpnConfigurator::processConfigWithExportSettings(const QPair(serverConfigObject.value(apiDefs::key::configVersion).toInt()); + return static_cast(serverConfigObject.value(apiDefs::key::configVersion).toInt()); } amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList &sslErrors, QNetworkReply *reply) diff --git a/client/core/api/apiUtils.h b/client/core/api/apiUtils.h index 9e6fdbbe..45eaf2de 100644 --- a/client/core/api/apiUtils.h +++ b/client/core/api/apiUtils.h @@ -16,7 +16,7 @@ namespace apiUtils bool isPremiumServer(const QJsonObject &serverConfigObject); apiDefs::ConfigType getConfigType(const QJsonObject &serverConfigObject); - amnezia::ServerConfigType getConfigSource(const QJsonObject &serverConfigObject); + apiDefs::ConfigSource getConfigSource(const QJsonObject &serverConfigObject); amnezia::ErrorCode checkNetworkReplyErrors(const QList &sslErrors, QNetworkReply *reply); diff --git a/client/core/controllers/coreController.cpp b/client/core/controllers/coreController.cpp index 2a116a29..0e72ef1a 100644 --- a/client/core/controllers/coreController.cpp +++ b/client/core/controllers/coreController.cpp @@ -54,43 +54,38 @@ void CoreController::initModels() m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this)); m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get()); - m_openVpnConfigModel = QSharedPointer::create(this); + m_protocolsModel.reset(new ProtocolsModel(m_settings, this)); + m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get()); + + m_openVpnConfigModel.reset(new OpenVpnConfigModel(this)); m_engine->rootContext()->setContextProperty("OpenVpnConfigModel", m_openVpnConfigModel.get()); - m_shadowSocksConfigModel = QSharedPointer::create(this); + m_shadowSocksConfigModel.reset(new ShadowSocksConfigModel(this)); m_engine->rootContext()->setContextProperty("ShadowSocksConfigModel", m_shadowSocksConfigModel.get()); - m_cloakConfigModel = QSharedPointer::create(this); + m_cloakConfigModel.reset(new CloakConfigModel(this)); m_engine->rootContext()->setContextProperty("CloakConfigModel", m_cloakConfigModel.get()); - m_wireGuardConfigModel = QSharedPointer::create(this); + m_wireGuardConfigModel.reset(new WireGuardConfigModel(this)); m_engine->rootContext()->setContextProperty("WireGuardConfigModel", m_wireGuardConfigModel.get()); - m_awgConfigModel = QSharedPointer::create(this); + m_awgConfigModel.reset(new AwgConfigModel(this)); m_engine->rootContext()->setContextProperty("AwgConfigModel", m_awgConfigModel.get()); - m_xrayConfigModel = QSharedPointer::create(this); + m_xrayConfigModel.reset(new XrayConfigModel(this)); m_engine->rootContext()->setContextProperty("XrayConfigModel", m_xrayConfigModel.get()); #ifdef Q_OS_WINDOWS - m_ikev2ConfigModel = QSharedPointer::create(this); + m_ikev2ConfigModel.reset(new Ikev2ConfigModel(this)); m_engine->rootContext()->setContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel.get()); #endif - m_sftpConfigModel = QSharedPointer::create(this); + m_sftpConfigModel.reset(new SftpConfigModel(this)); m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get()); - m_socks5ConfigModel = QSharedPointer::create(this); + m_socks5ConfigModel.reset(new Socks5ProxyConfigModel(this)); m_engine->rootContext()->setContextProperty("Socks5ProxyConfigModel", m_socks5ConfigModel.get()); - m_protocolsModel.reset(new ProtocolsModel(m_openVpnConfigModel, m_shadowSocksConfigModel, m_cloakConfigModel, m_wireGuardConfigModel, - m_awgConfigModel, m_xrayConfigModel, -#ifdef Q_OS_WINDOWS - m_ikev2ConfigModel, -#endif - m_sftpConfigModel, m_socks5ConfigModel, this)); - m_engine->rootContext()->setContextProperty("ProtocolsModel", m_protocolsModel.get()); - m_clientManagementModel.reset(new ClientManagementModel(m_settings, this)); m_engine->rootContext()->setContextProperty("ClientManagementModel", m_clientManagementModel.get()); @@ -304,10 +299,13 @@ void CoreController::setQmlRoot() void CoreController::initApiCountryModelUpdateHandler() { + // TODO connect(m_serversModel.get(), &ServersModel::updateApiCountryModel, this, [this]() { m_apiCountryModel->updateModel(m_serversModel->getProcessedServerData("apiAvailableCountries").toJsonArray(), m_serversModel->getProcessedServerData("apiServerCountryCode").toString()); }); + connect(m_serversModel.get(), &ServersModel::updateApiServicesModel, this, + [this]() { m_apiServicesModel->updateModel(m_serversModel->getProcessedServerData("apiConfig").toJsonObject()); }); } void CoreController::initContainerModelUpdateHandler() diff --git a/client/core/controllers/coreController.h b/client/core/controllers/coreController.h index a3b6616d..9ae53562 100644 --- a/client/core/controllers/coreController.h +++ b/client/core/controllers/coreController.h @@ -129,17 +129,17 @@ private: QSharedPointer m_apiAccountInfoModel; QSharedPointer m_apiDevicesModel; - QSharedPointer m_openVpnConfigModel; - QSharedPointer m_shadowSocksConfigModel; - QSharedPointer m_cloakConfigModel; - QSharedPointer m_xrayConfigModel; - QSharedPointer m_wireGuardConfigModel; - QSharedPointer m_awgConfigModel; + QScopedPointer m_openVpnConfigModel; + QScopedPointer m_shadowSocksConfigModel; + QScopedPointer m_cloakConfigModel; + QScopedPointer m_xrayConfigModel; + QScopedPointer m_wireGuardConfigModel; + QScopedPointer m_awgConfigModel; #ifdef Q_OS_WINDOWS - QSharedPointer m_ikev2ConfigModel; + QScopedPointer m_ikev2ConfigModel; #endif - QSharedPointer m_sftpConfigModel; - QSharedPointer m_socks5ConfigModel; + QScopedPointer m_sftpConfigModel; + QScopedPointer m_socks5ConfigModel; }; #endif // CORECONTROLLER_H diff --git a/client/core/controllers/serverController.cpp b/client/core/controllers/serverController.cpp index 8ff6b6c8..3c24edea 100644 --- a/client/core/controllers/serverController.cpp +++ b/client/core/controllers/serverController.cpp @@ -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,8 +366,13 @@ 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::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)) + return true; } @@ -375,7 +380,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; } @@ -455,11 +460,13 @@ 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; } @@ -639,6 +646,9 @@ 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(); @@ -825,7 +835,7 @@ ErrorCode ServerController::isServerDpkgBusy(const ServerCredentials &credential if (stdOut.contains("Packet manager not found")) return ErrorCode::ServerPacketManagerError; - if (stdOut.contains("fuser not installed")) + if (stdOut.contains("fuser not installed") || stdOut.contains("cat not installed")) return ErrorCode::NoError; if (stdOut.isEmpty()) { diff --git a/client/core/defs.h b/client/core/defs.h index 3d64932a..64f52ce6 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -19,13 +19,6 @@ namespace amnezia } }; - enum ServerConfigType - { - SelfHosted, - ApiV1, - ApiV2 - }; - struct InstalledAppInfo { QString appName; QString packageName; @@ -67,6 +60,7 @@ namespace amnezia ServerUserPasswordRequired = 210, ServerDockerOnCgroupsV2 = 211, ServerCgroupMountpoint = 212, + DockerPullRateLimit = 213, // Ssh connection errors SshRequestDeniedError = 300, diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp index 7cc46220..bd5ccaba 100644 --- a/client/core/errorstrings.cpp +++ b/client/core/errorstrings.cpp @@ -28,6 +28,7 @@ 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; diff --git a/client/core/models/containers/containerConfig.cpp b/client/core/models/containers/containerConfig.cpp deleted file mode 100644 index 119ff2b6..00000000 --- a/client/core/models/containers/containerConfig.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "containerConfig.h" - -ContainerConfig::ContainerConfig() -{ -} diff --git a/client/core/models/containers/containerConfig.h b/client/core/models/containers/containerConfig.h deleted file mode 100644 index 7207d028..00000000 --- a/client/core/models/containers/containerConfig.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef CONTAINERCONFIG_H -#define CONTAINERCONFIG_H - -#include -#include -#include - -#include "core/models/protocols/protocolConfig.h" - -class ContainerConfig -{ -public: - ContainerConfig(); - - QString containerName; - QMap> protocolConfigs; -}; - -#endif // CONTAINERCONFIG_H diff --git a/client/core/models/protocols/awgProtocolConfig.cpp b/client/core/models/protocols/awgProtocolConfig.cpp deleted file mode 100644 index 9e82cede..00000000 --- a/client/core/models/protocols/awgProtocolConfig.cpp +++ /dev/null @@ -1,242 +0,0 @@ -#include "awgProtocolConfig.h" - -#include -#include - -#include "protocols/protocols_defs.h" - -using namespace amnezia; - -AwgProtocolConfig::AwgProtocolConfig(const QString &protocolName) : ProtocolConfig(protocolName) -{ -} - -AwgProtocolConfig::AwgProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName) : ProtocolConfig(protocolName) -{ - serverProtocolConfig.port = protocolConfigObject.value(config_key::port).toString(); - serverProtocolConfig.transportProto = protocolConfigObject.value(config_key::transport_proto).toString(); - serverProtocolConfig.subnetAddress = protocolConfigObject.value(config_key::subnet_address).toString(); - - serverProtocolConfig.awgData.junkPacketCount = protocolConfigObject.value(config_key::junkPacketCount).toString(); - serverProtocolConfig.awgData.junkPacketMinSize = protocolConfigObject.value(config_key::junkPacketMinSize).toString(); - serverProtocolConfig.awgData.junkPacketMaxSize = protocolConfigObject.value(config_key::junkPacketMaxSize).toString(); - serverProtocolConfig.awgData.initPacketJunkSize = protocolConfigObject.value(config_key::initPacketJunkSize).toString(); - serverProtocolConfig.awgData.responsePacketJunkSize = protocolConfigObject.value(config_key::responsePacketJunkSize).toString(); - serverProtocolConfig.awgData.initPacketMagicHeader = protocolConfigObject.value(config_key::initPacketMagicHeader).toString(); - serverProtocolConfig.awgData.responsePacketMagicHeader = protocolConfigObject.value(config_key::responsePacketMagicHeader).toString(); - serverProtocolConfig.awgData.underloadPacketMagicHeader = protocolConfigObject.value(config_key::underloadPacketMagicHeader).toString(); - serverProtocolConfig.awgData.transportPacketMagicHeader = protocolConfigObject.value(config_key::transportPacketMagicHeader).toString(); - - auto clientProtocolString = protocolConfigObject.value(config_key::last_config).toString(); - if (!clientProtocolString.isEmpty()) { - clientProtocolConfig.isEmpty = false; - - QJsonObject clientProtocolConfigObject = QJsonDocument::fromJson(clientProtocolString.toUtf8()).object(); - - clientProtocolConfig.awgData.junkPacketCount = clientProtocolConfigObject.value(config_key::junkPacketCount).toString(); - clientProtocolConfig.awgData.junkPacketMinSize = clientProtocolConfigObject.value(config_key::junkPacketMinSize).toString(); - clientProtocolConfig.awgData.junkPacketMaxSize = clientProtocolConfigObject.value(config_key::junkPacketMaxSize).toString(); - clientProtocolConfig.awgData.initPacketJunkSize = clientProtocolConfigObject.value(config_key::initPacketJunkSize).toString(); - clientProtocolConfig.awgData.responsePacketJunkSize = clientProtocolConfigObject.value(config_key::responsePacketJunkSize).toString(); - clientProtocolConfig.awgData.initPacketMagicHeader = clientProtocolConfigObject.value(config_key::initPacketMagicHeader).toString(); - clientProtocolConfig.awgData.responsePacketMagicHeader = - clientProtocolConfigObject.value(config_key::responsePacketMagicHeader).toString(); - clientProtocolConfig.awgData.underloadPacketMagicHeader = - clientProtocolConfigObject.value(config_key::underloadPacketMagicHeader).toString(); - clientProtocolConfig.awgData.transportPacketMagicHeader = - clientProtocolConfigObject.value(config_key::transportPacketMagicHeader).toString(); - - clientProtocolConfig.clientId = clientProtocolConfigObject.value(config_key::clientId).toString(); - - clientProtocolConfig.wireGuardData.clientIp = clientProtocolConfigObject.value(config_key::client_ip).toString(); - clientProtocolConfig.wireGuardData.clientPrivateKey = clientProtocolConfigObject.value(config_key::client_priv_key).toString(); - clientProtocolConfig.wireGuardData.clientPublicKey = clientProtocolConfigObject.value(config_key::client_pub_key).toString(); - clientProtocolConfig.wireGuardData.persistentKeepAlive = - clientProtocolConfigObject.value(config_key::persistent_keep_alive).toString(); - clientProtocolConfig.wireGuardData.pskKey = clientProtocolConfigObject.value(config_key::psk_key).toString(); - clientProtocolConfig.wireGuardData.serverPubKey = clientProtocolConfigObject.value(config_key::server_pub_key).toString(); - clientProtocolConfig.wireGuardData.mtu = clientProtocolConfigObject.value(config_key::mtu).toString(); - - clientProtocolConfig.hostname = clientProtocolConfigObject.value(config_key::hostName).toString(); - clientProtocolConfig.port = clientProtocolConfigObject.value(config_key::port).toInt(0); - - clientProtocolConfig.nativeConfig = clientProtocolConfigObject.value(config_key::config).toString(); - - if (clientProtocolConfigObject.contains(config_key::allowed_ips) - && clientProtocolConfigObject.value(config_key::allowed_ips).isArray()) { - auto allowedIpsArray = clientProtocolConfigObject.value(config_key::allowed_ips).toArray(); - for (const auto &ip : allowedIpsArray) { - clientProtocolConfig.wireGuardData.allowedIps.append(ip.toString()); - } - } - } -} - -AwgProtocolConfig::AwgProtocolConfig(const AwgProtocolConfig &other) : ProtocolConfig(other.protocolName) -{ - serverProtocolConfig = other.serverProtocolConfig; - clientProtocolConfig = other.clientProtocolConfig; -} - -QJsonObject AwgProtocolConfig::toJson() const -{ - QJsonObject json; - - if (!serverProtocolConfig.port.isEmpty()) { - json[config_key::port] = serverProtocolConfig.port; - } - if (!serverProtocolConfig.transportProto.isEmpty()) { - json[config_key::transport_proto] = serverProtocolConfig.transportProto; - } - if (!serverProtocolConfig.subnetAddress.isEmpty()) { - json[config_key::subnet_address] = serverProtocolConfig.subnetAddress; - } - - if (!serverProtocolConfig.awgData.junkPacketCount.isEmpty()) { - json[config_key::junkPacketCount] = serverProtocolConfig.awgData.junkPacketCount; - } - if (!serverProtocolConfig.awgData.junkPacketMinSize.isEmpty()) { - json[config_key::junkPacketMinSize] = serverProtocolConfig.awgData.junkPacketMinSize; - } - if (!serverProtocolConfig.awgData.junkPacketMaxSize.isEmpty()) { - json[config_key::junkPacketMaxSize] = serverProtocolConfig.awgData.junkPacketMaxSize; - } - if (!serverProtocolConfig.awgData.initPacketJunkSize.isEmpty()) { - json[config_key::initPacketJunkSize] = serverProtocolConfig.awgData.initPacketJunkSize; - } - if (!serverProtocolConfig.awgData.responsePacketJunkSize.isEmpty()) { - json[config_key::responsePacketJunkSize] = serverProtocolConfig.awgData.responsePacketJunkSize; - } - if (!serverProtocolConfig.awgData.initPacketMagicHeader.isEmpty()) { - json[config_key::initPacketMagicHeader] = serverProtocolConfig.awgData.initPacketMagicHeader; - } - if (!serverProtocolConfig.awgData.responsePacketMagicHeader.isEmpty()) { - json[config_key::responsePacketMagicHeader] = serverProtocolConfig.awgData.responsePacketMagicHeader; - } - if (!serverProtocolConfig.awgData.underloadPacketMagicHeader.isEmpty()) { - json[config_key::underloadPacketMagicHeader] = serverProtocolConfig.awgData.underloadPacketMagicHeader; - } - if (!serverProtocolConfig.awgData.transportPacketMagicHeader.isEmpty()) { - json[config_key::transportPacketMagicHeader] = serverProtocolConfig.awgData.transportPacketMagicHeader; - } - - if (!clientProtocolConfig.isEmpty) { - QJsonObject clientConfigJson; - - if (!clientProtocolConfig.clientId.isEmpty()) { - clientConfigJson[config_key::clientId] = clientProtocolConfig.clientId; - } - - if (!clientProtocolConfig.awgData.junkPacketCount.isEmpty()) { - clientConfigJson[config_key::junkPacketCount] = clientProtocolConfig.awgData.junkPacketCount; - } - if (!clientProtocolConfig.awgData.junkPacketMinSize.isEmpty()) { - clientConfigJson[config_key::junkPacketMinSize] = clientProtocolConfig.awgData.junkPacketMinSize; - } - if (!clientProtocolConfig.awgData.junkPacketMaxSize.isEmpty()) { - clientConfigJson[config_key::junkPacketMaxSize] = clientProtocolConfig.awgData.junkPacketMaxSize; - } - if (!clientProtocolConfig.awgData.initPacketJunkSize.isEmpty()) { - clientConfigJson[config_key::initPacketJunkSize] = clientProtocolConfig.awgData.initPacketJunkSize; - } - if (!clientProtocolConfig.awgData.responsePacketJunkSize.isEmpty()) { - clientConfigJson[config_key::responsePacketJunkSize] = clientProtocolConfig.awgData.responsePacketJunkSize; - } - if (!clientProtocolConfig.awgData.initPacketMagicHeader.isEmpty()) { - clientConfigJson[config_key::initPacketMagicHeader] = clientProtocolConfig.awgData.initPacketMagicHeader; - } - if (!clientProtocolConfig.awgData.responsePacketMagicHeader.isEmpty()) { - clientConfigJson[config_key::responsePacketMagicHeader] = clientProtocolConfig.awgData.responsePacketMagicHeader; - } - if (!clientProtocolConfig.awgData.underloadPacketMagicHeader.isEmpty()) { - clientConfigJson[config_key::underloadPacketMagicHeader] = clientProtocolConfig.awgData.underloadPacketMagicHeader; - } - if (!clientProtocolConfig.awgData.transportPacketMagicHeader.isEmpty()) { - clientConfigJson[config_key::transportPacketMagicHeader] = clientProtocolConfig.awgData.transportPacketMagicHeader; - } - - if (!clientProtocolConfig.wireGuardData.clientIp.isEmpty()) { - clientConfigJson[config_key::client_ip] = clientProtocolConfig.wireGuardData.clientIp; - } - if (!clientProtocolConfig.wireGuardData.clientPrivateKey.isEmpty()) { - clientConfigJson[config_key::client_priv_key] = clientProtocolConfig.wireGuardData.clientPrivateKey; - } - if (!clientProtocolConfig.wireGuardData.clientPublicKey.isEmpty()) { - clientConfigJson[config_key::client_pub_key] = clientProtocolConfig.wireGuardData.clientPublicKey; - } - if (!clientProtocolConfig.wireGuardData.persistentKeepAlive.isEmpty()) { - clientConfigJson[config_key::persistent_keep_alive] = clientProtocolConfig.wireGuardData.persistentKeepAlive; - } - if (!clientProtocolConfig.wireGuardData.pskKey.isEmpty()) { - clientConfigJson[config_key::psk_key] = clientProtocolConfig.wireGuardData.pskKey; - } - if (!clientProtocolConfig.wireGuardData.serverPubKey.isEmpty()) { - clientConfigJson[config_key::server_pub_key] = clientProtocolConfig.wireGuardData.serverPubKey; - } - if (!clientProtocolConfig.wireGuardData.mtu.isEmpty()) { - clientConfigJson[config_key::mtu] = clientProtocolConfig.wireGuardData.mtu; - } - - if (!clientProtocolConfig.wireGuardData.allowedIps.isEmpty()) { - QJsonArray allowedIpsArray; - for (const auto &ip : clientProtocolConfig.wireGuardData.allowedIps) { - if (!ip.isEmpty()) { - allowedIpsArray.append(ip); - } - } - if (!allowedIpsArray.isEmpty()) { - clientConfigJson[config_key::allowed_ips] = allowedIpsArray; - } - } - - if (!clientProtocolConfig.hostname.isEmpty()) { - clientConfigJson[config_key::hostName] = clientProtocolConfig.hostname; - } - if (clientProtocolConfig.port) { - clientConfigJson[config_key::port] = clientProtocolConfig.port; - } - if (!clientProtocolConfig.nativeConfig.isEmpty()) { - clientConfigJson[config_key::config] = clientProtocolConfig.nativeConfig; - } - - if (!clientConfigJson.isEmpty()) { - json[config_key::last_config] = QString(QJsonDocument(clientConfigJson).toJson(QJsonDocument::Compact)); - } - } - - return json; -} - -bool AwgProtocolConfig::hasEqualServerSettings(const AwgProtocolConfig &other) const -{ - if (serverProtocolConfig.subnetAddress != other.serverProtocolConfig.subnetAddress - || serverProtocolConfig.port != other.serverProtocolConfig.port - || serverProtocolConfig.awgData.junkPacketCount != other.serverProtocolConfig.awgData.junkPacketCount - || serverProtocolConfig.awgData.junkPacketMinSize != other.serverProtocolConfig.awgData.junkPacketMinSize - || serverProtocolConfig.awgData.junkPacketMaxSize != other.serverProtocolConfig.awgData.junkPacketMaxSize - || serverProtocolConfig.awgData.initPacketJunkSize != other.serverProtocolConfig.awgData.initPacketJunkSize - || serverProtocolConfig.awgData.responsePacketJunkSize != other.serverProtocolConfig.awgData.responsePacketJunkSize - || serverProtocolConfig.awgData.initPacketMagicHeader != other.serverProtocolConfig.awgData.initPacketMagicHeader - || serverProtocolConfig.awgData.responsePacketMagicHeader != other.serverProtocolConfig.awgData.responsePacketMagicHeader - || serverProtocolConfig.awgData.underloadPacketMagicHeader != other.serverProtocolConfig.awgData.underloadPacketMagicHeader - || serverProtocolConfig.awgData.transportPacketMagicHeader != other.serverProtocolConfig.awgData.transportPacketMagicHeader) { - return false; - } - return true; -} - -bool AwgProtocolConfig::hasEqualClientSettings(const AwgProtocolConfig &other) const -{ - if (clientProtocolConfig.wireGuardData.mtu != other.clientProtocolConfig.wireGuardData.mtu - || clientProtocolConfig.awgData.junkPacketCount != other.clientProtocolConfig.awgData.junkPacketCount - || clientProtocolConfig.awgData.junkPacketMinSize != other.clientProtocolConfig.awgData.junkPacketMinSize - || clientProtocolConfig.awgData.junkPacketMaxSize != other.clientProtocolConfig.awgData.junkPacketMaxSize) { - return false; - } - return true; -} - -void AwgProtocolConfig::clearClientSettings() -{ - clientProtocolConfig = awg::ClientProtocolConfig(); -} diff --git a/client/core/models/protocols/awgProtocolConfig.h b/client/core/models/protocols/awgProtocolConfig.h deleted file mode 100644 index 90ef9ba4..00000000 --- a/client/core/models/protocols/awgProtocolConfig.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef AWGPROTOCOLCONFIG_H -#define AWGPROTOCOLCONFIG_H - -#include -#include - -#include "protocolConfig.h" -#include "wireguardProtocolConfig.h" - -namespace awg -{ - struct AwgData - { - QString junkPacketCount; - QString junkPacketMinSize; - QString junkPacketMaxSize; - - QString initPacketJunkSize; - QString responsePacketJunkSize; - - QString initPacketMagicHeader; - QString responsePacketMagicHeader; - QString underloadPacketMagicHeader; - QString transportPacketMagicHeader; - }; - - struct ServerProtocolConfig - { - QString port; - QString transportProto; - - QString subnetAddress; - - AwgData awgData; - }; - - struct ClientProtocolConfig - { - bool isEmpty = true; - - QString clientId; - - wireguard::WireGuardData wireGuardData; - - AwgData awgData; - - QString hostname; - int port; - - QString nativeConfig; - }; - - const int messageInitiationSize = 148; - const int messageResponseSize = 92; -} - -class AwgProtocolConfig : public ProtocolConfig -{ -public: - AwgProtocolConfig(const QString &protocolName); - AwgProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName); - AwgProtocolConfig(const AwgProtocolConfig &other); - - QJsonObject toJson() const override; - - bool hasEqualServerSettings(const AwgProtocolConfig &other) const; - bool hasEqualClientSettings(const AwgProtocolConfig &other) const; - void clearClientSettings(); - - awg::ServerProtocolConfig serverProtocolConfig; - awg::ClientProtocolConfig clientProtocolConfig; -}; - -#endif // AWGPROTOCOLCONFIG_H diff --git a/client/core/models/protocols/cloakProtocolConfig.cpp b/client/core/models/protocols/cloakProtocolConfig.cpp deleted file mode 100644 index c5dd916a..00000000 --- a/client/core/models/protocols/cloakProtocolConfig.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "cloakProtocolConfig.h" - -#include -#include -#include "protocols/protocols_defs.h" - -using namespace amnezia; - -CloakProtocolConfig::CloakProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName) : ProtocolConfig(protocolName) -{ - serverProtocolConfig.port = protocolConfigObject.value(config_key::port).toString(); - serverProtocolConfig.cipher = protocolConfigObject.value(config_key::cipher).toString(); - serverProtocolConfig.site = protocolConfigObject.value(config_key::site).toString(); - - auto clientProtocolString = protocolConfigObject.value(config_key::last_config).toString(); - if (!clientProtocolString.isEmpty()) { - clientProtocolConfig.isEmpty = false; - - QJsonObject clientProtocolConfigObject = QJsonDocument::fromJson(clientProtocolString.toUtf8()).object(); - } -} - -QJsonObject CloakProtocolConfig::toJson() const -{ - QJsonObject json; - - if (!serverProtocolConfig.port.isEmpty()) { - json[config_key::port] = serverProtocolConfig.port; - } - if (!serverProtocolConfig.cipher.isEmpty()) { - json[config_key::cipher] = serverProtocolConfig.cipher; - } - if (!serverProtocolConfig.site.isEmpty()) { - json[config_key::site] = serverProtocolConfig.site; - } - - if (!clientProtocolConfig.isEmpty) { - QJsonObject clientConfigJson; - json[config_key::last_config] = QString(QJsonDocument(clientConfigJson).toJson()); - } - - return json; -} - -bool CloakProtocolConfig::hasEqualServerSettings(const CloakProtocolConfig &other) const -{ - if (serverProtocolConfig.port != other.serverProtocolConfig.port || - serverProtocolConfig.cipher != other.serverProtocolConfig.cipher || - serverProtocolConfig.site != other.serverProtocolConfig.site) { - return false; - } - return true; -} - -void CloakProtocolConfig::clearClientSettings() -{ - clientProtocolConfig = cloak::ClientProtocolConfig(); -} diff --git a/client/core/models/protocols/cloakProtocolConfig.h b/client/core/models/protocols/cloakProtocolConfig.h deleted file mode 100644 index f82dc34e..00000000 --- a/client/core/models/protocols/cloakProtocolConfig.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef CLOAKPROTOCOLCONFIG_H -#define CLOAKPROTOCOLCONFIG_H - -#include -#include - -#include "protocolConfig.h" - -namespace cloak -{ - struct ServerProtocolConfig - { - QString port; - QString cipher; - QString site; - }; - - struct ClientProtocolConfig - { - bool isEmpty = true; - }; -} - -class CloakProtocolConfig : public ProtocolConfig -{ -public: - CloakProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName); - - QJsonObject toJson() const override; - - bool hasEqualServerSettings(const CloakProtocolConfig &other) const; - void clearClientSettings(); - - cloak::ServerProtocolConfig serverProtocolConfig; - cloak::ClientProtocolConfig clientProtocolConfig; -}; - -#endif // CLOAKPROTOCOLCONFIG_H diff --git a/client/core/models/protocols/openvpnProtocolConfig.cpp b/client/core/models/protocols/openvpnProtocolConfig.cpp deleted file mode 100644 index 4b4c8e85..00000000 --- a/client/core/models/protocols/openvpnProtocolConfig.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "openvpnProtocolConfig.h" - -#include "protocols/protocols_defs.h" -#include - -using namespace amnezia; - -OpenVpnProtocolConfig::OpenVpnProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName) - : ProtocolConfig(protocolName) -{ - serverProtocolConfig.subnetAddress = protocolConfigObject.value(config_key::subnet_address).toString(); - serverProtocolConfig.transportProto = protocolConfigObject.value(config_key::transport_proto).toString(); - serverProtocolConfig.port = protocolConfigObject.value(config_key::port).toString(); - serverProtocolConfig.ncpDisable = protocolConfigObject.value(config_key::ncp_disable).toBool(protocols::openvpn::defaultNcpDisable); - serverProtocolConfig.hash = protocolConfigObject.value(config_key::hash).toString(); - serverProtocolConfig.cipher = protocolConfigObject.value(config_key::cipher).toString(); - serverProtocolConfig.tlsAuth = protocolConfigObject.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth); - serverProtocolConfig.blockOutsideDns = - protocolConfigObject.value(config_key::block_outside_dns).toBool(protocols::openvpn::defaultBlockOutsideDns); - serverProtocolConfig.additionalClientConfig = protocolConfigObject.value(config_key::additional_client_config).toString(); - serverProtocolConfig.additionalServerConfig = protocolConfigObject.value(config_key::additional_server_config).toString(); - - auto clientProtocolString = protocolConfigObject.value(config_key::last_config).toString(); - if (!clientProtocolString.isEmpty()) { - clientProtocolConfig.isEmpty = false; - - QJsonObject clientProtocolConfigObject = QJsonDocument::fromJson(clientProtocolString.toUtf8()).object(); - - clientProtocolConfig.clientId = clientProtocolConfigObject.value(config_key::clientId).toString(); - clientProtocolConfig.nativeConfig = clientProtocolConfigObject.value(config_key::config).toString(); - } -} - -QJsonObject OpenVpnProtocolConfig::toJson() const -{ - QJsonObject json; - - if (!serverProtocolConfig.subnetAddress.isEmpty()) { - json[config_key::subnet_address] = serverProtocolConfig.subnetAddress; - } - if (!serverProtocolConfig.transportProto.isEmpty()) { - json[config_key::transport_proto] = serverProtocolConfig.transportProto; - } - if (!serverProtocolConfig.port.isEmpty()) { - json[config_key::port] = serverProtocolConfig.port; - } - json[config_key::ncp_disable] = serverProtocolConfig.ncpDisable; - if (!serverProtocolConfig.hash.isEmpty()) { - json[config_key::hash] = serverProtocolConfig.hash; - } - if (!serverProtocolConfig.cipher.isEmpty()) { - json[config_key::cipher] = serverProtocolConfig.cipher; - } - json[config_key::tls_auth] = serverProtocolConfig.tlsAuth; - json[config_key::block_outside_dns] = serverProtocolConfig.blockOutsideDns; - if (!serverProtocolConfig.additionalClientConfig.isEmpty()) { - json[config_key::additional_client_config] = serverProtocolConfig.additionalClientConfig; - } - if (!serverProtocolConfig.additionalServerConfig.isEmpty()) { - json[config_key::additional_server_config] = serverProtocolConfig.additionalServerConfig; - } - - if (!clientProtocolConfig.isEmpty) { - QJsonObject clientConfigJson; - - if (!clientProtocolConfig.clientId.isEmpty()) { - clientConfigJson[config_key::clientId] = clientProtocolConfig.clientId; - } - if (!clientProtocolConfig.nativeConfig.isEmpty()) { - clientConfigJson[config_key::config] = clientProtocolConfig.nativeConfig; - } - - if (!clientConfigJson.isEmpty()) { - json[config_key::last_config] = QString(QJsonDocument(clientConfigJson).toJson()); - } - } - - return json; -} - -bool OpenVpnProtocolConfig::hasEqualServerSettings(const OpenVpnProtocolConfig &other) const -{ - if (serverProtocolConfig.subnetAddress != other.serverProtocolConfig.subnetAddress - || serverProtocolConfig.transportProto != other.serverProtocolConfig.transportProto - || serverProtocolConfig.port != other.serverProtocolConfig.port - || serverProtocolConfig.ncpDisable != other.serverProtocolConfig.ncpDisable - || serverProtocolConfig.hash != other.serverProtocolConfig.hash || serverProtocolConfig.cipher != other.serverProtocolConfig.cipher - || serverProtocolConfig.tlsAuth != other.serverProtocolConfig.tlsAuth - || serverProtocolConfig.blockOutsideDns != other.serverProtocolConfig.blockOutsideDns - || serverProtocolConfig.additionalClientConfig != other.serverProtocolConfig.additionalClientConfig - || serverProtocolConfig.additionalServerConfig != other.serverProtocolConfig.additionalServerConfig) { - return false; - } - return true; -} - -void OpenVpnProtocolConfig::clearClientSettings() -{ - clientProtocolConfig = openvpn::ClientProtocolConfig(); -} diff --git a/client/core/models/protocols/openvpnProtocolConfig.h b/client/core/models/protocols/openvpnProtocolConfig.h deleted file mode 100644 index 89d44754..00000000 --- a/client/core/models/protocols/openvpnProtocolConfig.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef OPENVPNPROTOCOLCONFIG_H -#define OPENVPNPROTOCOLCONFIG_H - -#include -#include - -#include "protocolConfig.h" - -namespace openvpn -{ - struct ServerProtocolConfig - { - QString subnetAddress; - QString transportProto; - QString port; - bool ncpDisable; - QString hash; - QString cipher; - bool tlsAuth; - bool blockOutsideDns; - QString additionalClientConfig; - QString additionalServerConfig; - }; - - struct ClientProtocolConfig - { - bool isEmpty = true; - - QString clientId; - - QString nativeConfig; - }; -} - -class OpenVpnProtocolConfig : public ProtocolConfig -{ -public: - OpenVpnProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName); - - QJsonObject toJson() const override; - - bool hasEqualServerSettings(const OpenVpnProtocolConfig &other) const; - void clearClientSettings(); - - openvpn::ServerProtocolConfig serverProtocolConfig; - openvpn::ClientProtocolConfig clientProtocolConfig; -}; - -#endif // OPENVPNPROTOCOLCONFIG_H diff --git a/client/core/models/protocols/protocolConfig.cpp b/client/core/models/protocols/protocolConfig.cpp deleted file mode 100644 index eb96c936..00000000 --- a/client/core/models/protocols/protocolConfig.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "protocolConfig.h" - -#include "core/models/protocols/awgProtocolConfig.h" -#include "core/models/protocols/cloakProtocolConfig.h" -#include "core/models/protocols/openvpnProtocolConfig.h" -#include "core/models/protocols/protocolConfig.h" -#include "core/models/protocols/shadowsocksProtocolConfig.h" -#include "core/models/protocols/wireguardProtocolConfig.h" -#include "core/models/protocols/xrayProtocolConfig.h" -#include "protocols/protocols_defs.h" - -using namespace amnezia; - -ProtocolConfig::ProtocolConfig(const QString &protocolName) : protocolName(protocolName) -{ -} - -ProtocolConfigVariant ProtocolConfig::getProtocolConfigVariant(const QSharedPointer &protocolConfig) -{ - ProtocolConfigVariant variant; - auto proto = ProtocolProps::protoFromString(protocolConfig->protocolName); - switch (proto) { - case Proto::OpenVpn: variant = qSharedPointerCast(protocolConfig); break; - case Proto::WireGuard: variant = qSharedPointerCast(protocolConfig); break; - case Proto::ShadowSocks: variant = qSharedPointerCast(protocolConfig); break; - case Proto::Cloak: variant = qSharedPointerCast(protocolConfig); break; - case Proto::Xray: variant = qSharedPointerCast(protocolConfig); break; - case Proto::Awg: variant = qSharedPointerCast(protocolConfig); break; - default: break; - } - return variant; -} - -bool ProtocolConfig::isServerSettingsEqual(const QSharedPointer &other) -{ - auto proto = ProtocolProps::protoFromString(protocolName); - - switch (proto) { - case Proto::OpenVpn: { - auto thisConfig = qSharedPointerCast(QSharedPointer(this)); - auto otherConfig = qSharedPointerCast(other); - return thisConfig->hasEqualServerSettings(*otherConfig.data()); - } - case Proto::WireGuard: { - auto thisConfig = qSharedPointerCast(QSharedPointer(this)); - auto otherConfig = qSharedPointerCast(other); - return thisConfig->hasEqualServerSettings(*otherConfig.data()); - } - case Proto::ShadowSocks: { - auto thisConfig = qSharedPointerCast(QSharedPointer(this)); - auto otherConfig = qSharedPointerCast(other); - return thisConfig->hasEqualServerSettings(*otherConfig.data()); - } - case Proto::Cloak: { - auto thisConfig = qSharedPointerCast(QSharedPointer(this)); - auto otherConfig = qSharedPointerCast(other); - return thisConfig->hasEqualServerSettings(*otherConfig.data()); - } - case Proto::Xray: { - auto thisConfig = qSharedPointerCast(QSharedPointer(this)); - auto otherConfig = qSharedPointerCast(other); - return thisConfig->hasEqualServerSettings(*otherConfig.data()); - } - case Proto::Awg: { - auto thisConfig = qSharedPointerCast(QSharedPointer(this)); - auto otherConfig = qSharedPointerCast(other); - return thisConfig->hasEqualServerSettings(*otherConfig.data()); - } - default: return false; - } -} - -QJsonObject ProtocolConfig::toJson() const -{ - return QJsonObject(); -} diff --git a/client/core/models/protocols/protocolConfig.h b/client/core/models/protocols/protocolConfig.h deleted file mode 100644 index 899e6ec0..00000000 --- a/client/core/models/protocols/protocolConfig.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef PROTOCOLCONFIG_H -#define PROTOCOLCONFIG_H - -#include -#include -#include - -class OpenVpnProtocolConfig; -class WireGuardProtocolConfig; -class ShadowsocksProtocolConfig; -class CloakProtocolConfig; -class XrayProtocolConfig; -class AwgProtocolConfig; - -using ProtocolConfigVariant = - std::variant, QSharedPointer, QSharedPointer, - QSharedPointer, QSharedPointer, QSharedPointer >; - -class ProtocolConfig -{ -public: - ProtocolConfig(const QString &protocolName); - virtual QJsonObject toJson() const; - - static ProtocolConfigVariant getProtocolConfigVariant(const QSharedPointer &protocolConfig); - bool isServerSettingsEqual(const QSharedPointer &protocolConfig); - - QString protocolName; -}; - -#endif // PROTOCOLCONFIG_H diff --git a/client/core/models/protocols/shadowsocksProtocolConfig.cpp b/client/core/models/protocols/shadowsocksProtocolConfig.cpp deleted file mode 100644 index 30539a9b..00000000 --- a/client/core/models/protocols/shadowsocksProtocolConfig.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "shadowsocksProtocolConfig.h" - -#include -#include -#include "protocols/protocols_defs.h" - -using namespace amnezia; - -ShadowsocksProtocolConfig::ShadowsocksProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName) : ProtocolConfig(protocolName) -{ - serverProtocolConfig.port = protocolConfigObject.value(config_key::port).toString(); - serverProtocolConfig.cipher = protocolConfigObject.value(config_key::cipher).toString(); - - auto clientProtocolString = protocolConfigObject.value(config_key::last_config).toString(); - if (!clientProtocolString.isEmpty()) { - clientProtocolConfig.isEmpty = false; - - QJsonObject clientProtocolConfigObject = QJsonDocument::fromJson(clientProtocolString.toUtf8()).object(); - } -} - -QJsonObject ShadowsocksProtocolConfig::toJson() const -{ - QJsonObject json; - - if (!serverProtocolConfig.port.isEmpty()) { - json[config_key::port] = serverProtocolConfig.port; - } - if (!serverProtocolConfig.cipher.isEmpty()) { - json[config_key::cipher] = serverProtocolConfig.cipher; - } - - if (!clientProtocolConfig.isEmpty) { - QJsonObject clientConfigJson; - json[config_key::last_config] = QString(QJsonDocument(clientConfigJson).toJson()); - } - - return json; -} - -bool ShadowsocksProtocolConfig::hasEqualServerSettings(const ShadowsocksProtocolConfig &other) const -{ - if (serverProtocolConfig.port != other.serverProtocolConfig.port || - serverProtocolConfig.cipher != other.serverProtocolConfig.cipher) { - return false; - } - return true; -} - -void ShadowsocksProtocolConfig::clearClientSettings() -{ - clientProtocolConfig = shadowsocks::ClientProtocolConfig(); -} diff --git a/client/core/models/protocols/shadowsocksProtocolConfig.h b/client/core/models/protocols/shadowsocksProtocolConfig.h deleted file mode 100644 index 1b7480f8..00000000 --- a/client/core/models/protocols/shadowsocksProtocolConfig.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef SHADOWSOCKSPROTOCOLCONFIG_H -#define SHADOWSOCKSPROTOCOLCONFIG_H - -#include -#include - -#include "protocolConfig.h" - -namespace shadowsocks -{ - struct ServerProtocolConfig - { - QString port; - QString cipher; - }; - - struct ClientProtocolConfig - { - bool isEmpty = true; - }; -} - -class ShadowsocksProtocolConfig : public ProtocolConfig -{ -public: - ShadowsocksProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName); - - QJsonObject toJson() const override; - - bool hasEqualServerSettings(const ShadowsocksProtocolConfig &other) const; - void clearClientSettings(); - - shadowsocks::ServerProtocolConfig serverProtocolConfig; - shadowsocks::ClientProtocolConfig clientProtocolConfig; -}; - -#endif // SHADOWSOCKSPROTOCOLCONFIG_H diff --git a/client/core/models/protocols/wireguardProtocolConfig.cpp b/client/core/models/protocols/wireguardProtocolConfig.cpp deleted file mode 100644 index c44173d2..00000000 --- a/client/core/models/protocols/wireguardProtocolConfig.cpp +++ /dev/null @@ -1,134 +0,0 @@ -#include "wireguardProtocolConfig.h" - -#include -#include - -#include "protocols/protocols_defs.h" - -using namespace amnezia; - -WireGuardProtocolConfig::WireGuardProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName) - : ProtocolConfig(protocolName) -{ - serverProtocolConfig.port = protocolConfigObject.value(config_key::port).toString(); - serverProtocolConfig.transportProto = protocolConfigObject.value(config_key::transport_proto).toString(); - serverProtocolConfig.subnetAddress = protocolConfigObject.value(config_key::subnet_address).toString(); - - auto clientProtocolString = protocolConfigObject.value(config_key::last_config).toString(); - if (!clientProtocolString.isEmpty()) { - clientProtocolConfig.isEmpty = false; - - QJsonObject clientProtocolConfigObject = QJsonDocument::fromJson(clientProtocolString.toUtf8()).object(); - - clientProtocolConfig.clientId = clientProtocolConfigObject.value(config_key::clientId).toString(); - - clientProtocolConfig.wireGuardData.clientIp = clientProtocolConfigObject.value(config_key::client_ip).toString(); - clientProtocolConfig.wireGuardData.clientPrivateKey = clientProtocolConfigObject.value(config_key::client_priv_key).toString(); - clientProtocolConfig.wireGuardData.clientPublicKey = clientProtocolConfigObject.value(config_key::client_pub_key).toString(); - clientProtocolConfig.wireGuardData.persistentKeepAlive = - clientProtocolConfigObject.value(config_key::persistent_keep_alive).toString(); - clientProtocolConfig.wireGuardData.pskKey = clientProtocolConfigObject.value(config_key::psk_key).toString(); - clientProtocolConfig.wireGuardData.serverPubKey = clientProtocolConfigObject.value(config_key::server_pub_key).toString(); - clientProtocolConfig.wireGuardData.mtu = clientProtocolConfigObject.value(config_key::mtu).toString(); - - clientProtocolConfig.hostname = clientProtocolConfigObject.value(config_key::hostName).toString(); - clientProtocolConfig.port = clientProtocolConfigObject.value(config_key::port).toInt(0); - - clientProtocolConfig.nativeConfig = clientProtocolConfigObject.value(config_key::config).toString(); - - if (clientProtocolConfigObject.contains(config_key::allowed_ips) - && clientProtocolConfigObject.value(config_key::allowed_ips).isArray()) { - auto allowedIpsArray = clientProtocolConfigObject.value(config_key::allowed_ips).toArray(); - for (const auto &ip : allowedIpsArray) { - clientProtocolConfig.wireGuardData.allowedIps.append(ip.toString()); - } - } - } -} - -QJsonObject WireGuardProtocolConfig::toJson() const -{ - QJsonObject json; - - if (!serverProtocolConfig.port.isEmpty()) { - json[config_key::port] = serverProtocolConfig.port; - } - if (!serverProtocolConfig.transportProto.isEmpty()) { - json[config_key::transport_proto] = serverProtocolConfig.transportProto; - } - if (!serverProtocolConfig.subnetAddress.isEmpty()) { - json[config_key::subnet_address] = serverProtocolConfig.subnetAddress; - } - - if (!clientProtocolConfig.isEmpty) { - QJsonObject clientConfigJson; - - if (!clientProtocolConfig.clientId.isEmpty()) { - clientConfigJson[config_key::clientId] = clientProtocolConfig.clientId; - } - - if (!clientProtocolConfig.wireGuardData.clientIp.isEmpty()) { - clientConfigJson[config_key::client_ip] = clientProtocolConfig.wireGuardData.clientIp; - } - if (!clientProtocolConfig.wireGuardData.clientPrivateKey.isEmpty()) { - clientConfigJson[config_key::client_priv_key] = clientProtocolConfig.wireGuardData.clientPrivateKey; - } - if (!clientProtocolConfig.wireGuardData.clientPublicKey.isEmpty()) { - clientConfigJson[config_key::client_pub_key] = clientProtocolConfig.wireGuardData.clientPublicKey; - } - if (!clientProtocolConfig.wireGuardData.persistentKeepAlive.isEmpty()) { - clientConfigJson[config_key::persistent_keep_alive] = clientProtocolConfig.wireGuardData.persistentKeepAlive; - } - if (!clientProtocolConfig.wireGuardData.pskKey.isEmpty()) { - clientConfigJson[config_key::psk_key] = clientProtocolConfig.wireGuardData.pskKey; - } - if (!clientProtocolConfig.wireGuardData.serverPubKey.isEmpty()) { - clientConfigJson[config_key::server_pub_key] = clientProtocolConfig.wireGuardData.serverPubKey; - } - if (!clientProtocolConfig.wireGuardData.mtu.isEmpty()) { - clientConfigJson[config_key::mtu] = clientProtocolConfig.wireGuardData.mtu; - } - - if (!clientProtocolConfig.wireGuardData.allowedIps.isEmpty()) { - QJsonArray allowedIpsArray; - for (const auto &ip : clientProtocolConfig.wireGuardData.allowedIps) { - if (!ip.isEmpty()) { - allowedIpsArray.append(ip); - } - } - if (!allowedIpsArray.isEmpty()) { - clientConfigJson[config_key::allowed_ips] = allowedIpsArray; - } - } - - if (!clientProtocolConfig.hostname.isEmpty()) { - clientConfigJson[config_key::hostName] = clientProtocolConfig.hostname; - } - if (clientProtocolConfig.port) { - clientConfigJson[config_key::port] = clientProtocolConfig.port; - } - if (!clientProtocolConfig.nativeConfig.isEmpty()) { - clientConfigJson[config_key::config] = clientProtocolConfig.nativeConfig; - } - - if (!clientConfigJson.isEmpty()) { - json[config_key::last_config] = QString(QJsonDocument(clientConfigJson).toJson()); - } - } - - return json; -} - -bool WireGuardProtocolConfig::hasEqualServerSettings(const WireGuardProtocolConfig &other) const -{ - if (serverProtocolConfig.subnetAddress != other.serverProtocolConfig.subnetAddress || - serverProtocolConfig.port != other.serverProtocolConfig.port) { - return false; - } - return true; -} - -void WireGuardProtocolConfig::clearClientSettings() -{ - clientProtocolConfig = wireguard::ClientProtocolConfig(); -} diff --git a/client/core/models/protocols/wireguardProtocolConfig.h b/client/core/models/protocols/wireguardProtocolConfig.h deleted file mode 100644 index 7d3f6620..00000000 --- a/client/core/models/protocols/wireguardProtocolConfig.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef WIREGUARDPROTOCOLCONFIG_H -#define WIREGUARDPROTOCOLCONFIG_H - -#include -#include - -#include "protocolConfig.h" - -namespace wireguard -{ - struct WireGuardData - { - QStringList allowedIps; - - QString clientIp; - QString clientPrivateKey; - QString clientPublicKey; - QString mtu; - QString persistentKeepAlive; - QString pskKey; - QString serverPubKey; - }; - - struct ServerProtocolConfig - { - QString port; - QString transportProto; - - QString subnetAddress; - }; - - struct ClientProtocolConfig - { - bool isEmpty = true; - - QString clientId; - - WireGuardData wireGuardData; - - QString hostname; - int port; - - QString nativeConfig; - }; -} - -class WireGuardProtocolConfig : public ProtocolConfig -{ -public: - WireGuardProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName); - - QJsonObject toJson() const override; - - bool hasEqualServerSettings(const WireGuardProtocolConfig &other) const; - void clearClientSettings(); - - wireguard::ServerProtocolConfig serverProtocolConfig; - wireguard::ClientProtocolConfig clientProtocolConfig; -}; - -#endif // WIREGUARDPROTOCOLCONFIG_H diff --git a/client/core/models/protocols/xrayProtocolConfig.cpp b/client/core/models/protocols/xrayProtocolConfig.cpp deleted file mode 100644 index 00127031..00000000 --- a/client/core/models/protocols/xrayProtocolConfig.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "xrayProtocolConfig.h" - -#include -#include -#include "protocols/protocols_defs.h" - -using namespace amnezia; - -XrayProtocolConfig::XrayProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName) : ProtocolConfig(protocolName) -{ - serverProtocolConfig.site = protocolConfigObject.value(config_key::site).toString(); - serverProtocolConfig.port = protocolConfigObject.value(config_key::port).toString(); - serverProtocolConfig.transportProto = protocolConfigObject.value(config_key::transport_proto).toString(); - - auto clientProtocolString = protocolConfigObject.value(config_key::last_config).toString(); - if (!clientProtocolString.isEmpty()) { - clientProtocolConfig.isEmpty = false; - - QJsonObject clientProtocolConfigObject = QJsonDocument::fromJson(clientProtocolString.toUtf8()).object(); - } -} - -QJsonObject XrayProtocolConfig::toJson() const -{ - QJsonObject json; - - if (!serverProtocolConfig.site.isEmpty()) { - json[config_key::site] = serverProtocolConfig.site; - } - if (!serverProtocolConfig.port.isEmpty()) { - json[config_key::port] = serverProtocolConfig.port; - } - if (!serverProtocolConfig.transportProto.isEmpty()) { - json[config_key::transport_proto] = serverProtocolConfig.transportProto; - } - - if (!clientProtocolConfig.isEmpty) { - QJsonObject clientConfigJson; - json[config_key::last_config] = QString(QJsonDocument(clientConfigJson).toJson()); - } - - return json; -} - -bool XrayProtocolConfig::hasEqualServerSettings(const XrayProtocolConfig &other) const -{ - if (serverProtocolConfig.site != other.serverProtocolConfig.site || - serverProtocolConfig.port != other.serverProtocolConfig.port || - serverProtocolConfig.transportProto != other.serverProtocolConfig.transportProto) { - return false; - } - return true; -} - -void XrayProtocolConfig::clearClientSettings() -{ - clientProtocolConfig = xray::ClientProtocolConfig(); -} diff --git a/client/core/models/protocols/xrayProtocolConfig.h b/client/core/models/protocols/xrayProtocolConfig.h deleted file mode 100644 index 941816fe..00000000 --- a/client/core/models/protocols/xrayProtocolConfig.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef XRAYPROTOCOLCONFIG_H -#define XRAYPROTOCOLCONFIG_H - -#include -#include - -#include "protocolConfig.h" - -namespace xray -{ - struct ServerProtocolConfig - { - QString site; - QString port; - QString transportProto; - }; - - struct ClientProtocolConfig - { - bool isEmpty = true; - }; -} - -class XrayProtocolConfig : public ProtocolConfig -{ -public: - XrayProtocolConfig(const QJsonObject &protocolConfigObject, const QString &protocolName); - - QJsonObject toJson() const override; - - bool hasEqualServerSettings(const XrayProtocolConfig &other) const; - void clearClientSettings(); - - xray::ServerProtocolConfig serverProtocolConfig; - xray::ClientProtocolConfig clientProtocolConfig; -}; - -#endif // XRAYPROTOCOLCONFIG_H diff --git a/client/core/models/servers/apiV1ServerConfig.cpp b/client/core/models/servers/apiV1ServerConfig.cpp deleted file mode 100644 index 71fc4b7a..00000000 --- a/client/core/models/servers/apiV1ServerConfig.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "apiV1ServerConfig.h" - -#include -#include - -#include "protocols/protocols_defs.h" - -using namespace amnezia; - -ApiV1ServerConfig::ApiV1ServerConfig(const QJsonObject &serverConfigObject) : ServerConfig(serverConfigObject) -{ - name = serverConfigObject.value(config_key::name).toString(); - description = serverConfigObject.value(config_key::description).toString(); -} - -QJsonObject ApiV1ServerConfig::toJson() const -{ - QJsonObject json = ServerConfig::toJson(); - - if (!name.isEmpty()) { - json[config_key::name] = name; - } - if (!description.isEmpty()) { - json[config_key::description] = description; - } - - return json; -} diff --git a/client/core/models/servers/apiV1ServerConfig.h b/client/core/models/servers/apiV1ServerConfig.h deleted file mode 100644 index c3a9e52b..00000000 --- a/client/core/models/servers/apiV1ServerConfig.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef APIV1SERVERCONFIG_H -#define APIV1SERVERCONFIG_H - -#include "serverConfig.h" - -class ApiV1ServerConfig : public ServerConfig -{ -public: - ApiV1ServerConfig(const QJsonObject &serverConfigObject); - - QJsonObject toJson() const override; - - QString name; - QString description; -}; - -#endif // APIV1SERVERCONFIG_H diff --git a/client/core/models/servers/apiV2ServerConfig.cpp b/client/core/models/servers/apiV2ServerConfig.cpp deleted file mode 100644 index 19eeb39d..00000000 --- a/client/core/models/servers/apiV2ServerConfig.cpp +++ /dev/null @@ -1,125 +0,0 @@ -#include "apiV2ServerConfig.h" - -#include -#include - -#include "protocols/protocols_defs.h" - -using namespace amnezia; - -ApiV2ServerConfig::ApiV2ServerConfig(const QJsonObject &serverConfigObject) : ServerConfig(serverConfigObject) -{ - name = serverConfigObject.value(config_key::name).toString(); - description = serverConfigObject.value(config_key::description).toString(); - - auto apiConfigObject = serverConfigObject.value("api_config").toObject(); - - auto availableCountriesArray = apiConfigObject.value("available_countries").toArray(); - for (const auto &countryValue : availableCountriesArray) { - auto countryObject = countryValue.toObject(); - apiv2::Country country; - country.code = countryObject.value("server_country_code").toString(); - country.name = countryObject.value("server_country_name").toString(); - apiConfig.availableCountries.append(country); - } - - auto subscriptionObject = apiConfigObject.value("subscription").toObject(); - apiConfig.subscription.end_date = subscriptionObject.value("end_date").toString(); - - auto publicKeyObject = apiConfigObject.value("public_key").toObject(); - apiConfig.publicKey.expiresAt = publicKeyObject.value("expires_at").toString(); - - apiConfig.serverCountryCode = apiConfigObject.value("server_country_code").toString(); - apiConfig.serverCountryName = apiConfigObject.value("server_country_name").toString(); - - apiConfig.serviceProtocol = apiConfigObject.value("service_protocol").toString(); - apiConfig.serviceType = apiConfigObject.value("service_type").toString(); - - apiConfig.userCountryCode = apiConfigObject.value("user_country_code").toString(); - - apiConfig.vpnKey = apiConfigObject.value("vpn_key").toString(); - - auto authDataObject = serverConfigObject.value("auth_data").toObject(); - apiConfig.authData.apiKey = authDataObject.value("api_key").toString(); -} - -QJsonObject ApiV2ServerConfig::toJson() const -{ - QJsonObject json = ServerConfig::toJson(); - - if (!name.isEmpty()) { - json[config_key::name] = name; - } - if (!description.isEmpty()) { - json[config_key::description] = description; - } - - QJsonObject apiConfigJson; - - if (!apiConfig.availableCountries.isEmpty()) { - QJsonArray availableCountriesArray; - for (const auto &country : apiConfig.availableCountries) { - QJsonObject countryObject; - if (!country.code.isEmpty()) { - countryObject["server_country_code"] = country.code; - } - if (!country.name.isEmpty()) { - countryObject["server_country_name"] = country.name; - } - if (!countryObject.isEmpty()) { - availableCountriesArray.append(countryObject); - } - } - if (!availableCountriesArray.isEmpty()) { - apiConfigJson["available_countries"] = availableCountriesArray; - } - } - - if (!apiConfig.subscription.end_date.isEmpty()) { - QJsonObject subscriptionObject; - subscriptionObject["end_date"] = apiConfig.subscription.end_date; - apiConfigJson["subscription"] = subscriptionObject; - } - - if (!apiConfig.publicKey.expiresAt.isEmpty()) { - QJsonObject publicKeyObject; - publicKeyObject["expires_at"] = apiConfig.publicKey.expiresAt; - apiConfigJson["public_key"] = publicKeyObject; - } - - if (!apiConfig.serverCountryCode.isEmpty()) { - apiConfigJson["server_country_code"] = apiConfig.serverCountryCode; - } - if (!apiConfig.serverCountryName.isEmpty()) { - apiConfigJson["server_country_name"] = apiConfig.serverCountryName; - } - - if (!apiConfig.serviceProtocol.isEmpty()) { - apiConfigJson["service_protocol"] = apiConfig.serviceProtocol; - } - if (!apiConfig.serviceType.isEmpty()) { - apiConfigJson["service_type"] = apiConfig.serviceType; - } - - if (!apiConfig.userCountryCode.isEmpty()) { - apiConfigJson["user_country_code"] = apiConfig.userCountryCode; - } - - if (!apiConfig.vpnKey.isEmpty()) { - apiConfigJson["vpn_key"] = apiConfig.vpnKey; - } - - QJsonObject authDataJson; - if (!apiConfig.authData.apiKey.isEmpty()) { - authDataJson["api_key"] = apiConfig.authData.apiKey; - } - if (!authDataJson.isEmpty()) { - apiConfigJson["auth_data"] = authDataJson; - } - - if (!apiConfigJson.isEmpty()) { - json["api_config"] = apiConfigJson; - } - - return json; -} diff --git a/client/core/models/servers/apiV2ServerConfig.h b/client/core/models/servers/apiV2ServerConfig.h deleted file mode 100644 index cb72c612..00000000 --- a/client/core/models/servers/apiV2ServerConfig.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef APIV2SERVERCONFIG_H -#define APIV2SERVERCONFIG_H - -#include "serverConfig.h" - -namespace apiv2 -{ - struct Country { - QString code; - QString name; - }; - - struct PublicKey - { - QString expiresAt; - }; - - struct Subscription - { - QString end_date; - }; - - struct AuthData - { - QString apiKey; - }; - - struct ApiConfig { - QVector availableCountries; - - Subscription subscription; - PublicKey publicKey; - - AuthData authData; - - QString serverCountryCode; - QString serverCountryName; - - QString serviceProtocol; - QString serviceType; - - QString userCountryCode; - - QString vpnKey; - }; -} - -class ApiV2ServerConfig : public ServerConfig -{ -public: - ApiV2ServerConfig(const QJsonObject &serverConfigObject); - - QJsonObject toJson() const override; - - QString name; - QString description; - - apiv2::ApiConfig apiConfig; -}; - -#endif // APIV2SERVERCONFIG_H diff --git a/client/core/models/servers/selfHostedServerConfig.cpp b/client/core/models/servers/selfHostedServerConfig.cpp deleted file mode 100644 index b2cf189a..00000000 --- a/client/core/models/servers/selfHostedServerConfig.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "selfHostedServerConfig.h" - -#include -#include - -#include "protocols/protocols_defs.h" - -using namespace amnezia; - -SelfHostedServerConfig::SelfHostedServerConfig(const QJsonObject &serverConfigObject) : ServerConfig(serverConfigObject) -{ - name = serverConfigObject.value(config_key::description).toString(); - if (name.isEmpty()) { - name = serverConfigObject.value(config_key::hostName).toString(); - } - - serverCredentials.hostName = serverConfigObject.value(config_key::hostName).toString(); - serverCredentials.userName = serverConfigObject.value(config_key::userName).toString(); - serverCredentials.secretData = serverConfigObject.value(config_key::password).toString(); - serverCredentials.port = serverConfigObject.value(config_key::port).toInt(22); -} - -QJsonObject SelfHostedServerConfig::toJson() const -{ - // Сначала вызываем родительскую функцию для сериализации базовых полей - QJsonObject json = ServerConfig::toJson(); - - // Добавляем имя только если оно не пустое - if (!name.isEmpty()) { - json[config_key::description] = name; // Используем description как в конструкторе - } - - // Добавляем credentials только если они не пустые - if (!serverCredentials.hostName.isEmpty()) { - json[config_key::hostName] = serverCredentials.hostName; - } - if (!serverCredentials.userName.isEmpty()) { - json[config_key::userName] = serverCredentials.userName; - } - if (!serverCredentials.secretData.isEmpty()) { - json[config_key::password] = serverCredentials.secretData; - } - if (serverCredentials.port != 22) { // Добавляем порт только если не дефолтный - json[config_key::port] = serverCredentials.port; - } - - return json; -} diff --git a/client/core/models/servers/selfHostedServerConfig.h b/client/core/models/servers/selfHostedServerConfig.h deleted file mode 100644 index c11b8aa4..00000000 --- a/client/core/models/servers/selfHostedServerConfig.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef SELFHOSTEDSERVERCONFIG_H -#define SELFHOSTEDSERVERCONFIG_H - -#include "core/defs.h" -#include "serverConfig.h" - -class SelfHostedServerConfig : public ServerConfig -{ -public: - SelfHostedServerConfig(const QJsonObject &serverConfigObject); - - QJsonObject toJson() const override; - - QString name; - - amnezia::ServerCredentials serverCredentials; -}; - -#endif // SELFHOSTEDSERVERCONFIG_H diff --git a/client/core/models/servers/serverConfig.cpp b/client/core/models/servers/serverConfig.cpp deleted file mode 100644 index bf6f184b..00000000 --- a/client/core/models/servers/serverConfig.cpp +++ /dev/null @@ -1,167 +0,0 @@ -#include "serverConfig.h" - -#include - -#include "apiV1ServerConfig.h" -#include "apiV2ServerConfig.h" -#include "containers/containers_defs.h" -#include "core/models/protocols/awgProtocolConfig.h" -#include "core/models/protocols/cloakProtocolConfig.h" -#include "core/models/protocols/openvpnProtocolConfig.h" -#include "core/models/protocols/protocolConfig.h" -#include "core/models/protocols/shadowsocksProtocolConfig.h" -#include "core/models/protocols/xrayProtocolConfig.h" -#include "protocols/protocols_defs.h" -#include "selfHostedServerConfig.h" - -using namespace amnezia; - -ServerConfig::ServerConfig(const QJsonObject &serverConfigObject) -{ - type = static_cast(serverConfigObject.value(config_key::configVersion).toInt(0)); - - hostName = serverConfigObject.value(config_key::hostName).toString(); - - dns1 = serverConfigObject.value(config_key::dns1).toString(); - dns2 = serverConfigObject.value(config_key::dns2).toString(); - - defaultContainer = serverConfigObject.value(config_key::defaultContainer).toString(); - - crc = serverConfigObject.value(config_key::crc).toInt(0); - nameOverriddenByUser = serverConfigObject.value(config_key::nameOverriddenByUser).toBool(false); - - auto containers = serverConfigObject.value(config_key::containers).toArray(); - for (const auto &container : std::as_const(containers)) { - auto containerObject = container.toObject(); - - auto containerName = containerObject.value(config_key::container).toString(); - - ContainerConfig containerConfig; - containerConfig.containerName = containerName; - - auto protocols = ContainerProps::protocolsForContainer(ContainerProps::containerFromString(containerName)); - for (const auto &protocol : protocols) { - auto protocolName = ProtocolProps::protoToString(protocol); - auto protocolConfigObject = containerObject.value(protocolName).toObject(); - - switch (protocol) { - case Proto::OpenVpn: { - containerConfig.protocolConfigs.insert(protocolName, - QSharedPointer::create(protocolConfigObject, protocolName)); - break; - } - case Proto::ShadowSocks: { - containerConfig.protocolConfigs.insert( - protocolName, QSharedPointer::create(protocolConfigObject, protocolName)); - break; - } - case Proto::Cloak: { - containerConfig.protocolConfigs.insert(protocolName, - QSharedPointer::create(protocolConfigObject, protocolName)); - break; - } - case Proto::WireGuard: { - containerConfig.protocolConfigs.insert(protocolName, - QSharedPointer::create(protocolConfigObject, protocolName)); - break; - } - case Proto::Awg: { - containerConfig.protocolConfigs.insert(protocolName, - QSharedPointer::create(protocolConfigObject, protocolName)); - break; - } - case Proto::Xray: { - containerConfig.protocolConfigs.insert(protocolName, - QSharedPointer::create(protocolConfigObject, protocolName)); - break; - } - case Proto::Ikev2: break; - case Proto::L2tp: break; - case Proto::SSXray: break; - case Proto::TorWebSite: break; - case Proto::Dns: break; - case Proto::Sftp: break; - case Proto::Socks5Proxy: break; - default: break; - } - } - - containerConfigs.insert(containerName, containerConfig); - } -} - -QSharedPointer ServerConfig::createServerConfig(const QJsonObject &serverConfigObject) -{ - auto type = static_cast(serverConfigObject.value(config_key::configVersion).toInt(0)); - - switch (type) { - case ServerConfigType::SelfHosted: return QSharedPointer::create(serverConfigObject); - case ServerConfigType::ApiV1: return QSharedPointer::create(serverConfigObject); - case ServerConfigType::ApiV2: return QSharedPointer::create(serverConfigObject); - } -} - -ServerConfigVariant ServerConfig::getServerConfigVariant(const QSharedPointer &serverConfig) -{ - ServerConfigVariant variant; - switch (serverConfig->type) { - case amnezia::ServerConfigType::SelfHosted: variant = qSharedPointerCast(serverConfig); break; - case amnezia::ServerConfigType::ApiV1: variant = qSharedPointerCast(serverConfig); break; - case amnezia::ServerConfigType::ApiV2: variant = qSharedPointerCast(serverConfig); break; - } - return variant; -} - -QJsonObject ServerConfig::toJson() const -{ - QJsonObject json; - - if (type != ServerConfigType::SelfHosted) { - json[config_key::configVersion] = static_cast(type); - } - - if (!hostName.isEmpty()) { - json[config_key::hostName] = hostName; - } - if (!dns1.isEmpty()) { - json[config_key::dns1] = dns1; - } - if (!dns2.isEmpty()) { - json[config_key::dns2] = dns2; - } - if (!defaultContainer.isEmpty()) { - json[config_key::defaultContainer] = defaultContainer; - } - - if (!containerConfigs.isEmpty()) { - QJsonArray containersArray; - for (const auto &containerConfig : containerConfigs) { - QJsonObject containerObject; - containerObject[config_key::container] = containerConfig.containerName; - - if (!containerConfig.protocolConfigs.isEmpty()) { - for (const auto &protocolConfig : containerConfig.protocolConfigs) { - QJsonObject protocolJson = protocolConfig->toJson(); - if (!protocolJson.isEmpty()) { - containerObject[protocolConfig->protocolName] = protocolJson; - } - } - } - - containersArray.append(containerObject); - } - if (!containersArray.isEmpty()) { - json[config_key::containers] = containersArray; - } - } - - return json; -} - -void ServerConfig::updateProtocolConfig(const QString &containerName, const QMap> &protocolConfigs) -{ - if (containerConfigs.contains(containerName)) { - ContainerConfig &containerConfig = containerConfigs[containerName]; - containerConfig.protocolConfigs = protocolConfigs; - } -} diff --git a/client/core/models/servers/serverConfig.h b/client/core/models/servers/serverConfig.h deleted file mode 100644 index 6b0f6ff6..00000000 --- a/client/core/models/servers/serverConfig.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef SERVERCONFIG_H -#define SERVERCONFIG_H - -#include -#include -#include - -#include "core/defs.h" -#include "core/models/containers/containerConfig.h" - -class SelfHostedServerConfig; -class ApiV1ServerConfig; -class ApiV2ServerConfig; - -using ServerConfigVariant = - std::variant, QSharedPointer, QSharedPointer >; - -class ServerConfig -{ -public: - ServerConfig(const QJsonObject &serverConfigObject); - - virtual QJsonObject toJson() const; - - static QSharedPointer createServerConfig(const QJsonObject &serverConfigObject); - static ServerConfigVariant getServerConfigVariant(const QSharedPointer &serverConfig); - - void updateProtocolConfig(const QString &containerName, const QMap> &protocolConfigs); - - amnezia::ServerConfigType type; - - QString hostName; - - QString dns1; - QString dns2; - - QString defaultContainer; - - bool nameOverriddenByUser; - int crc; // TODO it makes sense to add for all server types or move it to the api - - QMap containerConfigs; -}; - -#endif // SERVERCONFIG_H diff --git a/client/daemon/daemon.cpp b/client/daemon/daemon.cpp index 24e72629..2faff0ef 100644 --- a/client/daemon/daemon.cpp +++ b/client/daemon/daemon.cpp @@ -169,11 +169,14 @@ bool Daemon::maybeUpdateResolvers(const InterfaceConfig& config) { if ((config.m_hopType == InterfaceConfig::MultiHopExit) || (config.m_hopType == InterfaceConfig::SingleHop)) { QList resolvers; - resolvers.append(QHostAddress(config.m_dnsServer)); + resolvers.append(QHostAddress(config.m_primaryDnsServer)); + if (!config.m_secondaryDnsServer.isEmpty()) { + resolvers.append(QHostAddress(config.m_secondaryDnsServer)); + } // If the DNS is not the Gateway, it's a user defined DNS // thus, not add any other :) - if (config.m_dnsServer == config.m_serverIpv4Gateway) { + if (config.m_primaryDnsServer == config.m_serverIpv4Gateway) { resolvers.append(QHostAddress(config.m_serverIpv6Gateway)); } @@ -279,15 +282,26 @@ 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("dnsServer")) { - config.m_dnsServer = QString(); + if (!obj.contains("primaryDnsServer")) { + config.m_primaryDnsServer = QString(); } else { - QJsonValue value = obj.value("dnsServer"); + QJsonValue value = obj.value("primaryDnsServer"); if (!value.isString()) { logger.error() << "dnsServer is not a string"; return false; } - config.m_dnsServer = value.toString(); + 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(); } if (!obj.contains("hopType")) { @@ -391,6 +405,13 @@ 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(); } @@ -404,6 +425,34 @@ 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; } @@ -446,7 +495,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 f0adcc92..53da5d36 100644 --- a/client/daemon/interfaceconfig.cpp +++ b/client/daemon/interfaceconfig.cpp @@ -28,7 +28,8 @@ QJsonObject InterfaceConfig::toJson() const { (m_hopType == InterfaceConfig::SingleHop)) { json.insert("serverIpv4Gateway", QJsonValue(m_serverIpv4Gateway)); json.insert("serverIpv6Gateway", QJsonValue(m_serverIpv6Gateway)); - json.insert("dnsServer", QJsonValue(m_dnsServer)); + json.insert("primaryDnsServer", QJsonValue(m_primaryDnsServer)); + json.insert("secondaryDnsServer", QJsonValue(m_secondaryDnsServer)); } QJsonArray allowedIPAddesses; @@ -100,11 +101,15 @@ QString InterfaceConfig::toWgConf(const QMap& extra) const { out << "MTU = " << m_deviceMTU << "\n"; } - if (!m_dnsServer.isNull()) { - QStringList dnsServers(m_dnsServer); + if (!m_primaryDnsServer.isNull()) { + QStringList dnsServers; + dnsServers.append(m_primaryDnsServer); + if (!m_secondaryDnsServer.isNull()) { + dnsServers.append(m_secondaryDnsServer); + } // If the DNS is not the Gateway, it's a user defined DNS // thus, not add any other :) - if (m_dnsServer == m_serverIpv4Gateway) { + if (m_primaryDnsServer == m_serverIpv4Gateway) { dnsServers.append(m_serverIpv6Gateway); } out << "DNS = " << dnsServers.join(", ") << "\n"; @@ -125,6 +130,12 @@ 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"; } @@ -138,6 +149,16 @@ 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 ee43a253..06288e80 100644 --- a/client/daemon/interfaceconfig.h +++ b/client/daemon/interfaceconfig.h @@ -32,7 +32,8 @@ class InterfaceConfig { QString m_serverIpv4AddrIn; QString m_serverPskKey; QString m_serverIpv6AddrIn; - QString m_dnsServer; + QString m_primaryDnsServer; + QString m_secondaryDnsServer; int m_serverPort = 0; int m_deviceMTU = 1420; QList m_allowedIPAddressRanges; @@ -49,10 +50,15 @@ 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/mozilla/localsocketcontroller.cpp b/client/mozilla/localsocketcontroller.cpp index afa29c47..9abab81c 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,7 +149,14 @@ 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("dnsServer", rawConfig.value(amnezia::config_key::dns1)); + + 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)); + } QJsonArray jsAllowedIPAddesses; @@ -237,28 +244,61 @@ 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::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()) { 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 e3b67efe..537687f1 100644 --- a/client/platforms/ios/WGConfig.swift +++ b/client/platforms/ios/WGConfig.swift @@ -4,7 +4,10 @@ struct WGConfig: Decodable { let initPacketMagicHeader, responsePacketMagicHeader: String? let underloadPacketMagicHeader, transportPacketMagicHeader: String? let junkPacketCount, junkPacketMinSize, junkPacketMaxSize: String? - let initPacketJunkSize, responsePacketJunkSize: String? + let initPacketJunkSize, responsePacketJunkSize, cookieReplyPacketJunkSize, transportPacketJunkSize: String? + let specialJunk1, specialJunk2, specialJunk3, specialJunk4, specialJunk5: String? + let controlledJunk1, controlledJunk2, controlledJunk3: String? + let specialHandshakeTimeout: String? let dns1: String let dns2: String let mtu: String @@ -23,7 +26,10 @@ 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" + 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 dns1 case dns2 case mtu @@ -40,19 +46,59 @@ struct WGConfig: Decodable { } var settings: String { - junkPacketCount == nil ? "" : - """ - Jc = \(junkPacketCount!) - Jmin = \(junkPacketMinSize!) - Jmax = \(junkPacketMaxSize!) - S1 = \(initPacketJunkSize!) - S2 = \(responsePacketJunkSize!) - H1 = \(initPacketMagicHeader!) - H2 = \(responsePacketMagicHeader!) - H3 = \(underloadPacketMagicHeader!) - H4 = \(transportPacketMagicHeader!) + 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!)") - """ + // 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 85fb50b7..e64c6dce 100644 --- a/client/platforms/ios/ios_controller.mm +++ b/client/platforms/ios/ios_controller.mm @@ -507,6 +507,8 @@ 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]); @@ -605,11 +607,23 @@ 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)); @@ -794,9 +808,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(); @@ -808,11 +822,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; } @@ -826,7 +840,7 @@ QString IosController::openFile() { if (!qtController) return; [qtController presentViewController:documentPicker animated:YES completion:nil]; - + __block QString filePath; documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) { @@ -841,7 +855,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/wireguardutilslinux.cpp b/client/platforms/linux/daemon/wireguardutilslinux.cpp index 0fbb65a8..cfde73e2 100644 --- a/client/platforms/linux/daemon/wireguardutilslinux.cpp +++ b/client/platforms/linux/daemon/wireguardutilslinux.cpp @@ -121,6 +121,12 @@ 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"; } @@ -134,13 +140,26 @@ 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_dnsServer); + params.dnsServers.append(config.m_primaryDnsServer); + if (!config.m_secondaryDnsServer.isEmpty()) { + params.dnsServers.append(config.m_secondaryDnsServer); + } 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/macosfirewall.cpp b/client/platforms/macos/daemon/macosfirewall.cpp index 0fe51f23..5211c440 100644 --- a/client/platforms/macos/daemon/macosfirewall.cpp +++ b/client/platforms/macos/daemon/macosfirewall.cpp @@ -43,8 +43,16 @@ namespace { #include "macosfirewall.h" -#define ResourceDir qApp->applicationDirPath() + "/pf" -#define DaemonDataDir qApp->applicationDirPath() + "/pf" +#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") #include @@ -121,6 +129,8 @@ 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/wireguardutilsmacos.cpp b/client/platforms/macos/daemon/wireguardutilsmacos.cpp index 1d8aa6e0..cce4afab 100644 --- a/client/platforms/macos/daemon/wireguardutilsmacos.cpp +++ b/client/platforms/macos/daemon/wireguardutilsmacos.cpp @@ -119,6 +119,12 @@ 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"; } @@ -132,30 +138,43 @@ 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_dnsServer); + 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_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 1834452e..2556c417 100644 --- a/client/platforms/windows/daemon/windowsfirewall.cpp +++ b/client/platforms/windows/daemon/windowsfirewall.cpp @@ -291,15 +291,32 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) { "Block Internet", config.m_serverPublicKey)) { return false; } - if (!config.m_dnsServer.isEmpty()) { - if (!allowTrafficTo(QHostAddress(config.m_dnsServer), 53, HIGH_WEIGHT, + if (!config.m_primaryDnsServer.isEmpty()) { + if (!allowTrafficTo(QHostAddress(config.m_primaryDnsServer), 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_dnsServer == config.m_serverIpv4Gateway) { + 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 (!allowTrafficTo(QHostAddress(config.m_serverIpv6Gateway), 53, HIGH_WEIGHT, "Allow extra IPv6 DNS-Server", config.m_serverPublicKey)) { diff --git a/client/platforms/windows/daemon/wireguardutilswindows.cpp b/client/platforms/windows/daemon/wireguardutilswindows.cpp index d01ef54a..a5c9c84d 100644 --- a/client/platforms/windows/daemon/wireguardutilswindows.cpp +++ b/client/platforms/windows/daemon/wireguardutilswindows.cpp @@ -130,6 +130,7 @@ 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 429b85a6..0bbdbd07 100644 --- a/client/protocols/openvpnprotocol.cpp +++ b/client/protocols/openvpnprotocol.cpp @@ -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(QJsonObject(), netInterfaces.at(i).index()); + IpcClient::Interface()->enableKillSwitch(m_configData, 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 c2d51454..b4cbb6de 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -72,10 +72,21 @@ 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"; @@ -216,10 +227,22 @@ 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 faad8e94..84922634 100755 --- a/client/protocols/xrayprotocol.cpp +++ b/client/protocols/xrayprotocol.cpp @@ -98,8 +98,13 @@ 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())); - dnsAddr.push_back(QHostAddress(m_configData.value(config_key::dns2).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())); + } #ifdef Q_OS_WIN QThread::msleep(8000); #endif @@ -134,7 +139,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(QJsonObject(), netInterfaces.at(i).index()); + IpcClient::Interface()->enableKillSwitch(m_configData, 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 72eb15c7..54b5846c 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -239,6 +239,7 @@ 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 8c536fc7..a6118a84 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,7 +40,8 @@ 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 2000c965..e327f080 100644 --- a/client/server_scripts/awg/configure_container.sh +++ b/client/server_scripts/awg/configure_container.sh @@ -23,4 +23,5 @@ 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 4e6a2c26..feddfed3 100644 --- a/client/server_scripts/check_server_is_busy.sh +++ b/client/server_scripts/check_server_is_busy.sh @@ -1,6 +1,7 @@ -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";\ +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";\ else echo "Packet manager not found"; echo "Internal error"; exit 1; fi;\ -if command -v fuser > /dev/null 2>&1; then sudo fuser $LOCK_FILE 2>/dev/null; else echo "fuser not installed"; 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 diff --git a/client/server_scripts/check_user_in_sudo.sh b/client/server_scripts/check_user_in_sudo.sh index 685e6a18..f83f2fd7 100644 --- a/client/server_scripts/check_user_in_sudo.sh +++ b/client/server_scripts/check_user_in_sudo.sh @@ -1,6 +1,7 @@ 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 619b08d6..1e41bb5a 100644 --- a/client/server_scripts/install_docker.sh +++ b/client/server_scripts/install_docker.sh @@ -1,6 +1,7 @@ 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/ui/controllers/api/apiConfigsController.cpp b/client/ui/controllers/api/apiConfigsController.cpp index a805dae2..0f42beb7 100644 --- a/client/ui/controllers/api/apiConfigsController.cpp +++ b/client/ui/controllers/api/apiConfigsController.cpp @@ -18,6 +18,7 @@ namespace { constexpr char cloak[] = "cloak"; constexpr char awg[] = "awg"; + constexpr char vless[] = "vless"; constexpr char apiEndpoint[] = "api_endpoint"; constexpr char accessToken[] = "api_key"; @@ -35,10 +36,6 @@ 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"; @@ -47,6 +44,185 @@ 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, @@ -63,24 +239,26 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode, return false; } - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, - m_settings->isStrictKillSwitchEnabled()); - auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex()); auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject(); - QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString(); - ApiPayloadData apiPayloadData = generateApiPayloadData(protocol); + 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() }; - 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[apiDefs::key::cliVersion] = QString(APP_VERSION); + QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString(); + ProtocolData protocolData = generateProtocolData(protocol); + + QJsonObject apiPayload = gatewayRequestData.toJsonObject(); + appendProtocolDataToApiPayload(gatewayRequestData.serviceProtocol, protocolData, apiPayload); QByteArray responseBody; - ErrorCode errorCode = gatewayController.post(QString("%1v1/native_config"), apiPayload, responseBody); + ErrorCode errorCode = executeRequest(QString("%1v1/native_config"), apiPayload, responseBody); if (errorCode != ErrorCode::NoError) { emit errorOccurred(errorCode); return false; @@ -88,7 +266,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", apiPayloadData.wireGuardClientPrivKey); + nativeConfig.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", protocolData.wireGuardClientPrivKey); SystemController::saveFile(fileName, nativeConfig); return true; @@ -96,24 +274,22 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode, bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode) { - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, - m_settings->isStrictKillSwitchEnabled()); - auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex()); auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject(); - QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString(); - ApiPayloadData apiPayloadData = generateApiPayloadData(protocol); + 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() }; - 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[apiDefs::key::cliVersion] = QString(APP_VERSION); + QJsonObject apiPayload = gatewayRequestData.toJsonObject(); QByteArray responseBody; - ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_native_config"), apiPayload, responseBody); + ErrorCode errorCode = executeRequest(QString("%1v1/revoke_native_config"), apiPayload, responseBody); if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) { emit errorOccurred(errorCode); return false; @@ -144,14 +320,11 @@ void ApiConfigsController::copyVpnKeyToClipboard() bool ApiConfigsController::fillAvailableServices() { - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, - m_settings->isStrictKillSwitchEnabled()); - QJsonObject apiPayload; apiPayload[configKey::osVersion] = QSysInfo::productType(); QByteArray responseBody; - ErrorCode errorCode = gatewayController.post(QString("%1v1/services"), apiPayload, responseBody); + ErrorCode errorCode = executeRequest(QString("%1v1/services"), apiPayload, responseBody); if (errorCode == ErrorCode::NoError) { if (!responseBody.contains("services")) { errorCode = ErrorCode::ApiServicesMissingError; @@ -170,34 +343,36 @@ bool ApiConfigsController::fillAvailableServices() bool ApiConfigsController::importServiceFromGateway() { - if (m_serversModel->isServerFromApiAlreadyExists(m_apiServicesModel->getCountryCode(), m_apiServicesModel->getSelectedServiceType(), - m_apiServicesModel->getSelectedServiceProtocol())) { + 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)) { emit errorOccurred(ErrorCode::ApiConfigAlreadyAdded); return false; } - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, - m_settings->isStrictKillSwitchEnabled()); + ProtocolData protocolData = generateProtocolData(gatewayRequestData.serviceProtocol); - 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; - apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION); + QJsonObject apiPayload = gatewayRequestData.toJsonObject(); + appendProtocolDataToApiPayload(gatewayRequestData.serviceProtocol, protocolData, apiPayload); QByteArray responseBody; - ErrorCode errorCode = gatewayController.post(QString("%1v1/config"), apiPayload, responseBody); + ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody); QJsonObject serverConfig; if (errorCode == ErrorCode::NoError) { - fillServerConfig(serviceProtocol, apiPayloadData, responseBody, serverConfig); + errorCode = fillServerConfig(gatewayRequestData.serviceProtocol, protocolData, responseBody, serverConfig); + if (errorCode != ErrorCode::NoError) { + emit errorOccurred(errorCode); + return false; + } QJsonObject apiConfig = serverConfig.value(configKey::apiConfig).toObject(); apiConfig.insert(configKey::userCountryCode, m_apiServicesModel->getCountryCode()); @@ -218,39 +393,33 @@ 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, - m_settings->isStrictKillSwitchEnabled()); - auto serverConfig = m_serversModel->getServerConfig(serverIndex); auto apiConfig = serverConfig.value(configKey::apiConfig).toObject(); - auto authData = 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(); + 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() }; - ApiPayloadData apiPayloadData = generateApiPayloadData(serviceProtocol); + ProtocolData protocolData = generateProtocolData(gatewayRequestData.serviceProtocol); - QJsonObject apiPayload = fillApiPayload(serviceProtocol, apiPayloadData); - apiPayload[configKey::userCountryCode] = userCountryCode; - apiPayload[configKey::serviceType] = serviceType; - apiPayload[configKey::uuid] = installationUuid; - apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION); - - if (!newCountryCode.isEmpty()) { - apiPayload[configKey::serverCountryCode] = newCountryCode; - } - if (!authData.isEmpty()) { - apiPayload[configKey::authData] = authData; - } + QJsonObject apiPayload = gatewayRequestData.toJsonObject(); + appendProtocolDataToApiPayload(gatewayRequestData.serviceProtocol, protocolData, apiPayload); QByteArray responseBody; - ErrorCode errorCode = gatewayController.post(QString("%1v1/config"), apiPayload, responseBody); + ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody); QJsonObject newServerConfig; if (errorCode == ErrorCode::NoError) { - fillServerConfig(serviceProtocol, apiPayloadData, responseBody, newServerConfig); + errorCode = fillServerConfig(gatewayRequestData.serviceProtocol, protocolData, responseBody, newServerConfig); + if (errorCode != ErrorCode::NoError) { + emit errorOccurred(errorCode); + return false; + } QJsonObject newApiConfig = newServerConfig.value(configKey::apiConfig).toObject(); newApiConfig.insert(configKey::userCountryCode, apiConfig.value(configKey::userCountryCode)); @@ -259,7 +428,7 @@ 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, authData); + newServerConfig.insert(configKey::authData, gatewayRequestData.authData); if (serverConfig.value(config_key::nameOverriddenByUser).toBool()) { newServerConfig.insert(config_key::name, serverConfig.value(config_key::name)); @@ -294,10 +463,13 @@ bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex) auto installationUuid = m_settings->getInstallationUuid(true); QString serviceProtocol = serverConfig.value(configKey::protocol).toString(); - ApiPayloadData apiPayloadData = generateApiPayloadData(serviceProtocol); + ProtocolData protocolData = generateProtocolData(serviceProtocol); - QJsonObject apiPayload = fillApiPayload(serviceProtocol, apiPayloadData); + QJsonObject apiPayload; + appendProtocolDataToApiPayload(serviceProtocol, protocolData, apiPayload); 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(); @@ -305,7 +477,11 @@ bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex) ErrorCode errorCode = gatewayController.post(QString("%1v1/proxy_config"), apiPayload, responseBody); if (errorCode == ErrorCode::NoError) { - fillServerConfig(serviceProtocol, apiPayloadData, responseBody, serverConfig); + errorCode = fillServerConfig(serviceProtocol, protocolData, responseBody, serverConfig); + if (errorCode != ErrorCode::NoError) { + emit errorOccurred(errorCode); + return false; + } m_serversModel->editServer(serverConfig, serverIndex); emit updateServerFromApiFinished(); @@ -318,9 +494,6 @@ bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex) bool ApiConfigsController::deactivateDevice() { - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, - m_settings->isStrictKillSwitchEnabled()); - auto serverIndex = m_serversModel->getProcessedServerIndex(); auto serverConfigObject = m_serversModel->getServerConfig(serverIndex); auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject(); @@ -329,19 +502,19 @@ bool ApiConfigsController::deactivateDevice() return true; } - QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString(); - ApiPayloadData apiPayloadData = generateApiPayloadData(protocol); + 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() }; - 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); - apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION); + QJsonObject apiPayload = gatewayRequestData.toJsonObject(); QByteArray responseBody; - ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody); + ErrorCode errorCode = executeRequest(QString("%1v1/revoke_config"), apiPayload, responseBody); if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) { emit errorOccurred(errorCode); return false; @@ -355,9 +528,6 @@ bool ApiConfigsController::deactivateDevice() bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode) { - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, - m_settings->isStrictKillSwitchEnabled()); - auto serverIndex = m_serversModel->getProcessedServerIndex(); auto serverConfigObject = m_serversModel->getServerConfig(serverIndex); auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject(); @@ -366,19 +536,19 @@ bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const Q return true; } - QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString(); - ApiPayloadData apiPayloadData = generateApiPayloadData(protocol); + GatewayRequestData gatewayRequestData { QSysInfo::productType(), + QString(APP_VERSION), + uuid, + apiConfigObject.value(configKey::userCountryCode).toString(), + serverCountryCode, + apiConfigObject.value(configKey::serviceType).toString(), + "", + serverConfigObject.value(configKey::authData).toObject() }; - 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; - apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION); + QJsonObject apiPayload = gatewayRequestData.toJsonObject(); QByteArray responseBody; - ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody); + ErrorCode errorCode = executeRequest(QString("%1v1/revoke_config"), apiPayload, responseBody); if (errorCode != ErrorCode::NoError && errorCode != ErrorCode::ApiNotFoundError) { emit errorOccurred(errorCode); return false; @@ -398,16 +568,16 @@ bool ApiConfigsController::isConfigValid() QJsonObject serverConfigObject = m_serversModel->getServerConfig(serverIndex); auto configSource = apiUtils::getConfigSource(serverConfigObject); - if (configSource == amnezia::ServerConfigType::ApiV1 + if (configSource == apiDefs::ConfigSource::Telegram && !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) { m_serversModel->removeApiConfig(serverIndex); return updateServiceFromTelegram(serverIndex); - } else if (configSource == amnezia::ServerConfigType::ApiV2 + } else if (configSource == apiDefs::ConfigSource::AmneziaGateway && !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) { return updateServiceFromGateway(serverIndex, "", ""); } else if (configSource && m_serversModel->isApiKeyExpired(serverIndex)) { qDebug() << "attempt to update api config by expires_at event"; - if (configSource == amnezia::ServerConfigType::ApiV2) { + if (configSource == apiDefs::ConfigSource::AmneziaGateway) { return updateServiceFromGateway(serverIndex, "", ""); } else { m_serversModel->removeApiConfig(serverIndex); @@ -417,108 +587,29 @@ bool ApiConfigsController::isConfigValid() return true; } -ApiConfigsController::ApiPayloadData ApiConfigsController::generateApiPayloadData(const QString &protocol) +void ApiConfigsController::setCurrentProtocol(const QString &protocolName) { - 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; + 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); } -QJsonObject ApiConfigsController::fillApiPayload(const QString &protocol, const ApiPayloadData &apiPayloadData) +bool ApiConfigsController::isVlessProtocol() { - QJsonObject obj; - if (protocol == configKey::cloak) { - obj[configKey::certificate] = apiPayloadData.certRequest.request; - } else if (protocol == configKey::awg) { - obj[configKey::publicKey] = apiPayloadData.wireGuardClientPubKey; + 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; } - - 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() == amnezia::ServerConfigType::ApiV2) { - 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() == amnezia::ServerConfigType::ApiV2) { - apiConfig.insert(configKey::serviceInfo, QJsonDocument::fromJson(apiResponseBody).object().value(configKey::serviceInfo).toObject()); - } - - serverConfig[configKey::apiConfig] = apiConfig; - - return; + return false; } QList ApiConfigsController::getQrCodes() @@ -535,3 +626,10 @@ 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 2fe981e4..a04a142c 100644 --- a/client/ui/controllers/api/apiConfigsController.h +++ b/client/ui/controllers/api/apiConfigsController.h @@ -35,6 +35,9 @@ public slots: bool isConfigValid(); + void setCurrentProtocol(const QString &protocolName); + bool isVlessProtocol(); + signals: void errorOccurred(ErrorCode errorCode); @@ -46,23 +49,12 @@ 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/importController.cpp b/client/ui/controllers/importController.cpp index fdc06120..ea1d5d8e 100644 --- a/client/ui/controllers/importController.cpp +++ b/client/ui/controllers/importController.cpp @@ -12,6 +12,7 @@ #include "core/errorstrings.h" #include "core/qrCodeUtils.h" #include "core/serialization/serialization.h" +#include "protocols/protocols_defs.h" #include "systemController.h" #include "utilities.h" @@ -286,6 +287,19 @@ 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()); @@ -438,21 +452,33 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data) lastConfig[config_key::allowed_ips] = allowedIpsJsonArray; QString protocolName = "wireguard"; - 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); + + 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); + } + } + protocolName = "awg"; m_configType = ConfigTypes::Awg; } diff --git a/client/ui/controllers/installController.cpp b/client/ui/controllers/installController.cpp index c12beb2d..d7f9dfbc 100755 --- a/client/ui/controllers/installController.cpp +++ b/client/ui/controllers/installController.cpp @@ -11,7 +11,6 @@ #include "core/api/apiUtils.h" #include "core/controllers/serverController.h" #include "core/controllers/vpnConfigurationController.h" -#include "core/models/servers/selfHostedServerConfig.h" #include "core/networkUtilities.h" #include "logger.h" #include "ui/models/protocols/awgConfigModel.h" @@ -80,12 +79,36 @@ void InstallController::install(DockerContainer container, int port, TransportPr int s1 = QRandomGenerator::global()->bounded(15, 150); int s2 = QRandomGenerator::global()->bounded(15, 150); - while (s1 + awg::messageInitiationSize == s2 + awg::messageResponseSize) { + // 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) { 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) { @@ -109,6 +132,21 @@ 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,6 +440,19 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia 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); @@ -553,34 +604,28 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia return ErrorCode::NoError; } -void InstallController::updateContainer() +void InstallController::updateContainer(QJsonObject config) { int serverIndex = m_serversModel->getProcessedServerIndex(); - auto oldServerConfig = m_serversModel->getServerConfig(serverIndex); - auto newServerConfig = QSharedPointer::create(oldServerConfig); - - const DockerContainer container = static_cast(m_containersModel->getProcessedContainerIndex()); - const QString containerName = m_containersModel->getProcessedContainerName(); - auto protocolConfigs = m_protocolModel->getProtocolConfigs(); - newServerConfig->updateProtocolConfig(containerName, protocolConfigs); - - auto oldProtocolConfigs = oldServerConfig->containerConfigs[containerName].protocolConfigs; - auto newProtocolConfigs = newServerConfig->containerConfigs[containerName].protocolConfigs; + ServerCredentials serverCredentials = + qvariant_cast(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole)); + const DockerContainer container = ContainerProps::containerFromString(config.value(config_key::container).toString()); + QJsonObject oldContainerConfig = m_containersModel->getContainerConfig(container); ErrorCode errorCode = ErrorCode::NoError; - if (isUpdateDockerContainerRequired(container, oldProtocolConfigs, newProtocolConfigs)) { + if (isUpdateDockerContainerRequired(container, oldContainerConfig, config)) { QSharedPointer serverController(new ServerController(m_settings)); connect(serverController.get(), &ServerController::serverIsBusy, this, &InstallController::serverIsBusy); connect(this, &InstallController::cancelInstallation, serverController.get(), &ServerController::cancelInstallation); - errorCode = serverController->updateContainer(newServerConfig->serverCredentials, container, oldContainerConfig, config); + errorCode = serverController->updateContainer(serverCredentials, container, oldContainerConfig, config); clearCachedProfile(serverController); } if (errorCode == ErrorCode::NoError) { - m_serversModel->editServer(newServerConfig, serverIndex); - m_protocolModel->updateModel(protocolConfigs); + m_serversModel->updateContainerConfig(container, config); + m_protocolModel->updateModel(config); auto defaultContainer = qvariant_cast(m_serversModel->data(serverIndex, ServersModel::Roles::DefaultContainerRole)); if ((serverIndex == m_serversModel->getDefaultServerIndex()) && (container == defaultContainer)) { @@ -614,7 +659,7 @@ void InstallController::removeProcessedServer() int serverIndex = m_serversModel->getProcessedServerIndex(); QString serverName = m_serversModel->data(serverIndex, ServersModel::Roles::NameRole).toString(); - m_serversModel->removeProcessedServer(); + m_serversModel->removeServer(); emit removeProcessedServerFinished(tr("Server '%1' was removed").arg(serverName)); } @@ -920,26 +965,29 @@ bool InstallController::isConfigValid() return true; } -bool InstallController::isUpdateDockerContainerRequired(const DockerContainer container, - const QMap> &oldProtocolConfigs, - const QMap> &newProtocolConfigs) +bool InstallController::isUpdateDockerContainerRequired(const DockerContainer container, const QJsonObject &oldConfig, + const QJsonObject &newConfig) { Proto mainProto = ContainerProps::defaultProtocol(container); - const auto oldProtoConfig = oldProtocolConfigs.value(ProtocolProps::protoToString(mainProto)); - const auto newProtoConfig = newProtocolConfigs.value(ProtocolProps::protoToString(mainProto)); + const QJsonObject &oldProtoConfig = oldConfig.value(ProtocolProps::protoToString(mainProto)).toObject(); + const QJsonObject &newProtoConfig = newConfig.value(ProtocolProps::protoToString(mainProto)).toObject(); - switch (mainProto) { - case Proto::Awg: { - auto newConfig = qSharedPointerCast(oldProtoConfig); - auto oldConfig = qSharedPointerCast(newProtoConfig); - return !newConfig->hasEqualServerSettings(*oldConfig.data()); - } - case Proto::WireGuard: { - auto newConfig = qSharedPointerCast(oldProtoConfig); - auto oldConfig = qSharedPointerCast(newProtoConfig); - return !newConfig->hasEqualServerSettings(*oldConfig.data()); - } - default: return true; + if (container == DockerContainer::Awg) { + const AwgConfig oldConfig(oldProtoConfig); + const AwgConfig newConfig(newProtoConfig); + + if (oldConfig.hasEqualServerSettings(newConfig)) { + return false; + } + } else if (container == DockerContainer::WireGuard) { + const WgConfig oldConfig(oldProtoConfig); + const WgConfig newConfig(newProtoConfig); + + if (oldConfig.hasEqualServerSettings(newConfig)) { + return false; + } } + + return true; } diff --git a/client/ui/controllers/installController.h b/client/ui/controllers/installController.h index c01c0646..8e42b5b2 100644 --- a/client/ui/controllers/installController.h +++ b/client/ui/controllers/installController.h @@ -28,7 +28,7 @@ public slots: void scanServerForInstalledContainers(); - void updateContainer(); + void updateContainer(QJsonObject config); void removeProcessedServer(); void rebootProcessedServer(); @@ -94,9 +94,7 @@ private: ErrorCode getAlreadyInstalledContainers(const ServerCredentials &credentials, const QSharedPointer &serverController, QMap &installedContainers); - bool isUpdateDockerContainerRequired(const DockerContainer container, - const QMap> &oldProtocolConfigs, - const QMap> &newProtocolConfigs); + bool isUpdateDockerContainerRequired(const DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig); QSharedPointer m_serversModel; QSharedPointer m_containersModel; diff --git a/client/ui/models/api/apiAccountInfoModel.cpp b/client/ui/models/api/apiAccountInfoModel.cpp index fdd4e2ca..bd3027a4 100644 --- a/client/ui/models/api/apiAccountInfoModel.cpp +++ b/client/ui/models/api/apiAccountInfoModel.cpp @@ -75,6 +75,12 @@ 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(); @@ -95,6 +101,10 @@ 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(); @@ -159,6 +169,7 @@ 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 ead92488..f0203967 100644 --- a/client/ui/models/api/apiAccountInfoModel.h +++ b/client/ui/models/api/apiAccountInfoModel.h @@ -18,7 +18,8 @@ public: ServiceDescriptionRole, EndDateRole, IsComponentVisibleRole, - HasExpiredWorkerRole + HasExpiredWorkerRole, + IsProtocolSelectionSupportedRole }; explicit ApiAccountInfoModel(QObject *parent = nullptr); @@ -51,6 +52,8 @@ private: int maxDeviceCount; apiDefs::ConfigType configType; + + QStringList supportedProtocols; }; AccountInfoData m_accountInfoData; diff --git a/client/ui/models/containers_model.cpp b/client/ui/models/containers_model.cpp index fa92088d..41d26bc7 100644 --- a/client/ui/models/containers_model.cpp +++ b/client/ui/models/containers_model.cpp @@ -2,7 +2,8 @@ #include -ContainersModel::ContainersModel(QObject *parent) : QAbstractListModel(parent) +ContainersModel::ContainersModel(QObject *parent) + : QAbstractListModel(parent) { } @@ -24,13 +25,19 @@ QVariant ContainersModel::data(const QModelIndex &index, int role) const case NameRole: return ContainerProps::containerHumanNames().value(container); case DescriptionRole: return ContainerProps::containerDescriptions().value(container); case DetailedDescriptionRole: return ContainerProps::containerDetailedDescriptions().value(container); + case ConfigRole: { + if (container == DockerContainer::None) { + return QJsonObject(); + } + return m_containers.value(container); + } case ServiceTypeRole: return ContainerProps::containerService(container); case DockerContainerRole: return container; case IsEasySetupContainerRole: return ContainerProps::isEasySetupContainer(container); case EasySetupHeaderRole: return ContainerProps::easySetupHeader(container); case EasySetupDescriptionRole: return ContainerProps::easySetupDescription(container); case EasySetupOrderRole: return ContainerProps::easySetupOrder(container); - case IsInstalledRole: return m_containerConfigs.contains(ContainerProps::containerToString(container)); + case IsInstalledRole: return m_containers.contains(container); case IsCurrentlyProcessedRole: return container == static_cast(m_processedContainerIndex); case IsSupportedRole: return ContainerProps::isSupportedByCurrentPlatform(container); case IsShareableRole: return ContainerProps::isShareable(container); @@ -46,10 +53,14 @@ QVariant ContainersModel::data(const int index, int role) const return data(modelIndex, role); } -void ContainersModel::updateModel(const QMap &containerConfigs) +void ContainersModel::updateModel(const QJsonArray &containers) { beginResetModel(); - m_containerConfigs = containerConfigs; + m_containers.clear(); + for (const QJsonValue &val : containers) { + m_containers.insert(ContainerProps::containerFromString(val.toObject().value(config_key::container).toString()), + val.toObject()); + } endResetModel(); } @@ -68,6 +79,11 @@ QString ContainersModel::getProcessedContainerName() return ContainerProps::containerHumanNames().value(static_cast(m_processedContainerIndex)); } +QJsonObject ContainersModel::getContainerConfig(const int containerIndex) +{ + return qvariant_cast(data(index(containerIndex), ConfigRole)); +} + bool ContainersModel::isSupportedByCurrentPlatform(const int containerIndex) { return qvariant_cast(data(index(containerIndex), IsSupportedRole)); @@ -80,8 +96,8 @@ bool ContainersModel::isServiceContainer(const int containerIndex) bool ContainersModel::hasInstalledServices() { - for (const auto &containerName : m_containerConfigs.keys()) { - if (ContainerProps::containerService(ContainerProps::containerFromString(containerName)) == ServiceType::Other) { + for (const auto &container : m_containers.keys()) { + if (ContainerProps::containerService(container) == ServiceType::Other) { return true; } } @@ -90,8 +106,8 @@ bool ContainersModel::hasInstalledServices() bool ContainersModel::hasInstalledProtocols() { - for (const auto &containerName : m_containerConfigs.keys()) { - if (ContainerProps::containerService(ContainerProps::containerFromString(containerName)) == ServiceType::Vpn) { + for (const auto &container : m_containers.keys()) { + if (ContainerProps::containerService(container) == ServiceType::Vpn) { return true; } } @@ -106,6 +122,7 @@ QHash ContainersModel::roleNames() const roles[DetailedDescriptionRole] = "detailedDescription"; roles[ServiceTypeRole] = "serviceType"; roles[DockerContainerRole] = "dockerContainer"; + roles[ConfigRole] = "config"; roles[IsEasySetupContainerRole] = "isEasySetupContainer"; roles[EasySetupHeaderRole] = "easySetupHeader"; diff --git a/client/ui/models/containers_model.h b/client/ui/models/containers_model.h index d471c58a..3bd0ddc1 100644 --- a/client/ui/models/containers_model.h +++ b/client/ui/models/containers_model.h @@ -3,9 +3,10 @@ #include #include +#include +#include #include "containers/containers_defs.h" -#include "core/models/containers/containerConfig.h" class ContainersModel : public QAbstractListModel { @@ -18,6 +19,7 @@ public: DescriptionRole, DetailedDescriptionRole, ServiceTypeRole, + ConfigRole, DockerContainerRole, IsEasySetupContainerRole, @@ -40,13 +42,15 @@ public: QVariant data(const int index, int role) const; public slots: - void updateModel(const QMap &containerConfigs); + void updateModel(const QJsonArray &containers); void setProcessedContainerIndex(int containerIndex); int getProcessedContainerIndex(); QString getProcessedContainerName(); + QJsonObject getContainerConfig(const int containerIndex); + bool isSupportedByCurrentPlatform(const int containerIndex); bool isServiceContainer(const int containerIndex); @@ -60,7 +64,7 @@ signals: void containersModelUpdated(); private: - QMap m_containerConfigs; + QMap m_containers; int m_processedContainerIndex; }; diff --git a/client/ui/models/protocols/awgConfigModel.cpp b/client/ui/models/protocols/awgConfigModel.cpp index 08c8338f..e14a3152 100644 --- a/client/ui/models/protocols/awgConfigModel.cpp +++ b/client/ui/models/protocols/awgConfigModel.cpp @@ -4,10 +4,7 @@ #include "protocols/protocols_defs.h" -AwgConfigModel::AwgConfigModel(QObject *parent) - : QAbstractListModel(parent), - m_newAwgProtocolConfig(ProtocolProps::protoToString(Proto::Awg)), - m_oldAwgProtocolConfig(ProtocolProps::protoToString(Proto::Awg)) +AwgConfigModel::AwgConfigModel(QObject *parent) : QAbstractListModel(parent) { } @@ -24,42 +21,46 @@ bool AwgConfigModel::setData(const QModelIndex &index, const QVariant &value, in } switch (role) { - case Roles::SubnetAddressRole: m_newAwgProtocolConfig.serverProtocolConfig.subnetAddress = value.toString(); break; - case Roles::PortRole: m_newAwgProtocolConfig.serverProtocolConfig.port = value.toString(); break; + case Roles::SubnetAddressRole: m_serverProtocolConfig.insert(config_key::subnet_address, value.toString()); break; + case Roles::PortRole: m_serverProtocolConfig.insert(config_key::port, value.toString()); break; - case Roles::ClientMtuRole: m_newAwgProtocolConfig.clientProtocolConfig.wireGuardData.mtu = value.toString(); break; - case Roles::ClientJunkPacketCountRole: m_newAwgProtocolConfig.clientProtocolConfig.awgData.junkPacketCount = value.toString(); break; - case Roles::ClientJunkPacketMinSizeRole: - m_newAwgProtocolConfig.clientProtocolConfig.awgData.junkPacketMinSize = value.toString(); - break; - case Roles::ClientJunkPacketMaxSizeRole: - m_newAwgProtocolConfig.clientProtocolConfig.awgData.junkPacketMaxSize = value.toString(); - break; - - case Roles::ServerJunkPacketCountRole: m_newAwgProtocolConfig.serverProtocolConfig.awgData.junkPacketCount = value.toString(); break; - case Roles::ServerJunkPacketMinSizeRole: - m_newAwgProtocolConfig.serverProtocolConfig.awgData.junkPacketMinSize = value.toString(); - break; - case Roles::ServerJunkPacketMaxSizeRole: - m_newAwgProtocolConfig.serverProtocolConfig.awgData.junkPacketMaxSize = value.toString(); - break; - case Roles::ServerInitPacketJunkSizeRole: - m_newAwgProtocolConfig.serverProtocolConfig.awgData.initPacketJunkSize = value.toString(); + case Roles::ClientMtuRole: m_clientProtocolConfig.insert(config_key::mtu, value.toString()); break; + 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; + case Roles::ServerInitPacketJunkSizeRole: m_serverProtocolConfig.insert(config_key::initPacketJunkSize, value.toString()); break; case Roles::ServerResponsePacketJunkSizeRole: - m_newAwgProtocolConfig.serverProtocolConfig.awgData.responsePacketJunkSize = value.toString(); - break; - case Roles::ServerInitPacketMagicHeaderRole: - m_newAwgProtocolConfig.serverProtocolConfig.awgData.initPacketMagicHeader = value.toString(); + 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_newAwgProtocolConfig.serverProtocolConfig.awgData.responsePacketMagicHeader = value.toString(); + m_serverProtocolConfig.insert(config_key::responsePacketMagicHeader, value.toString()); break; case Roles::ServerUnderloadPacketMagicHeaderRole: - m_newAwgProtocolConfig.serverProtocolConfig.awgData.underloadPacketMagicHeader = value.toString(); + m_serverProtocolConfig.insert(config_key::underloadPacketMagicHeader, value.toString()); break; case Roles::ServerTransportPacketMagicHeaderRole: - m_newAwgProtocolConfig.serverProtocolConfig.awgData.transportPacketMagicHeader = value.toString(); + m_serverProtocolConfig.insert(config_key::transportPacketMagicHeader, value.toString()); break; } @@ -74,42 +75,137 @@ QVariant AwgConfigModel::data(const QModelIndex &index, int role) const } switch (role) { - case Roles::SubnetAddressRole: return m_newAwgProtocolConfig.serverProtocolConfig.subnetAddress; - case Roles::PortRole: return m_newAwgProtocolConfig.serverProtocolConfig.port; + case Roles::SubnetAddressRole: return m_serverProtocolConfig.value(config_key::subnet_address).toString(); + case Roles::PortRole: return m_serverProtocolConfig.value(config_key::port).toString(); - case Roles::ClientMtuRole: return m_newAwgProtocolConfig.clientProtocolConfig.wireGuardData.mtu; - case Roles::ClientJunkPacketCountRole: return m_newAwgProtocolConfig.clientProtocolConfig.awgData.junkPacketCount; - case Roles::ClientJunkPacketMinSizeRole: return m_newAwgProtocolConfig.clientProtocolConfig.awgData.junkPacketMinSize; - case Roles::ClientJunkPacketMaxSizeRole: return m_newAwgProtocolConfig.clientProtocolConfig.awgData.junkPacketMaxSize; + case Roles::ClientMtuRole: return m_clientProtocolConfig.value(config_key::mtu); + 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_newAwgProtocolConfig.serverProtocolConfig.awgData.junkPacketCount; - case Roles::ServerJunkPacketMinSizeRole: return m_newAwgProtocolConfig.serverProtocolConfig.awgData.junkPacketMinSize; - case Roles::ServerJunkPacketMaxSizeRole: return m_newAwgProtocolConfig.serverProtocolConfig.awgData.junkPacketMaxSize; - case Roles::ServerInitPacketJunkSizeRole: return m_newAwgProtocolConfig.serverProtocolConfig.awgData.initPacketJunkSize; - case Roles::ServerResponsePacketJunkSizeRole: return m_newAwgProtocolConfig.serverProtocolConfig.awgData.responsePacketJunkSize; - case Roles::ServerInitPacketMagicHeaderRole: return m_newAwgProtocolConfig.serverProtocolConfig.awgData.initPacketMagicHeader; - case Roles::ServerResponsePacketMagicHeaderRole: return m_newAwgProtocolConfig.serverProtocolConfig.awgData.responsePacketMagicHeader; - case Roles::ServerUnderloadPacketMagicHeaderRole: return m_newAwgProtocolConfig.serverProtocolConfig.awgData.underloadPacketMagicHeader; - case Roles::ServerTransportPacketMagicHeaderRole: return m_newAwgProtocolConfig.serverProtocolConfig.awgData.transportPacketMagicHeader; + 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); + case Roles::ServerTransportPacketMagicHeaderRole: return m_serverProtocolConfig.value(config_key::transportPacketMagicHeader); } return QVariant(); } -void AwgConfigModel::updateModel(const AwgProtocolConfig awgProtocolConfig) +void AwgConfigModel::updateModel(const QJsonObject &config) { beginResetModel(); - m_newAwgProtocolConfig = awgProtocolConfig; - m_oldAwgProtocolConfig = awgProtocolConfig; + m_container = ContainerProps::containerFromString(config.value(config_key::container).toString()); + + m_fullConfig = config; + + QJsonObject serverProtocolConfig = config.value(config_key::awg).toObject(); + + auto defaultTransportProto = ProtocolProps::transportProtoToString(ProtocolProps::defaultTransportProto(Proto::Awg), Proto::Awg); + 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::port] = serverProtocolConfig.value(config_key::port).toString(protocols::awg::defaultPort); + m_serverProtocolConfig[config_key::junkPacketCount] = + serverProtocolConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount); + m_serverProtocolConfig[config_key::junkPacketMinSize] = + serverProtocolConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize); + m_serverProtocolConfig[config_key::junkPacketMaxSize] = + serverProtocolConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize); + m_serverProtocolConfig[config_key::initPacketJunkSize] = + 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] = + serverProtocolConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader); + m_serverProtocolConfig[config_key::underloadPacketMagicHeader] = + serverProtocolConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader); + m_serverProtocolConfig[config_key::transportPacketMagicHeader] = + serverProtocolConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader); + + auto lastConfig = m_serverProtocolConfig.value(config_key::last_config).toString(); + QJsonObject clientProtocolConfig = QJsonDocument::fromJson(lastConfig.toUtf8()).object(); + m_clientProtocolConfig[config_key::mtu] = clientProtocolConfig[config_key::mtu].toString(protocols::awg::defaultMtu); + m_clientProtocolConfig[config_key::junkPacketCount] = + clientProtocolConfig.value(config_key::junkPacketCount).toString(m_serverProtocolConfig[config_key::junkPacketCount].toString()); + m_clientProtocolConfig[config_key::junkPacketMinSize] = + 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(); } -QSharedPointer AwgConfigModel::getConfig() +QJsonObject AwgConfigModel::getConfig() { - if (m_oldAwgProtocolConfig.hasEqualServerSettings(m_newAwgProtocolConfig)) { - m_newAwgProtocolConfig.clearClientSettings(); + const AwgConfig oldConfig(m_fullConfig.value(config_key::awg).toObject()); + const AwgConfig newConfig(m_serverProtocolConfig); + + if (!oldConfig.hasEqualServerSettings(newConfig)) { + m_serverProtocolConfig.remove(config_key::last_config); + } else { + auto lastConfig = m_serverProtocolConfig.value(config_key::last_config).toString(); + QJsonObject jsonConfig = QJsonDocument::fromJson(lastConfig.toUtf8()).object(); + jsonConfig[config_key::mtu] = m_clientProtocolConfig[config_key::mtu]; + 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()); } - return QSharedPointer::create(m_newAwgProtocolConfig); + + m_fullConfig.insert(config_key::awg, m_serverProtocolConfig); + return m_fullConfig; } bool AwgConfigModel::isHeadersEqual(const QString &h1, const QString &h2, const QString &h3, const QString &h4) @@ -119,12 +215,26 @@ bool AwgConfigModel::isHeadersEqual(const QString &h1, const QString &h2, const bool AwgConfigModel::isPacketSizeEqual(const int s1, const int s2) { - return (awg::messageInitiationSize + s1 == awg::messageResponseSize + 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() { - return m_oldAwgProtocolConfig.hasEqualServerSettings(m_newAwgProtocolConfig); + const AwgConfig oldConfig(m_fullConfig.value(config_key::awg).toObject()); + const AwgConfig newConfig(m_serverProtocolConfig); + + return oldConfig.hasEqualServerSettings(newConfig); } QHash AwgConfigModel::roleNames() const @@ -138,12 +248,24 @@ 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"; @@ -151,3 +273,74 @@ QHash AwgConfigModel::roleNames() const return roles; } + +AwgConfig::AwgConfig(const QJsonObject &serverProtocolConfig) +{ + auto lastConfig = serverProtocolConfig.value(config_key::last_config).toString(); + QJsonObject clientProtocolConfig = QJsonDocument::fromJson(lastConfig.toUtf8()).object(); + clientMtu = clientProtocolConfig[config_key::mtu].toString(protocols::awg::defaultMtu); + 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); + serverJunkPacketCount = serverProtocolConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount); + serverJunkPacketMinSize = serverProtocolConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize); + serverJunkPacketMaxSize = serverProtocolConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize); + 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 = + serverProtocolConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader); + serverUnderloadPacketMagicHeader = + serverProtocolConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader); + serverTransportPacketMagicHeader = + serverProtocolConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader); +} + +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 + || serverTransportPacketMagicHeader != other.serverTransportPacketMagicHeader) { + return false; + } + return true; +} + +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) { + return false; + } + return true; +} diff --git a/client/ui/models/protocols/awgConfigModel.h b/client/ui/models/protocols/awgConfigModel.h index 26363c61..0c2374fc 100644 --- a/client/ui/models/protocols/awgConfigModel.h +++ b/client/ui/models/protocols/awgConfigModel.h @@ -5,7 +5,51 @@ #include #include "containers/containers_defs.h" -#include "core/models/protocols/awgProtocolConfig.h" + +namespace AwgConstant +{ + const int messageInitiationSize = 148; + const int messageResponseSize = 92; + const int messageCookieReplySize = 64; + const int messageTransportSize = 32; +} + +struct AwgConfig +{ + AwgConfig(const QJsonObject &jsonConfig); + + QString subnetAddress; + QString port; + + QString clientMtu; + 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; + QString serverTransportPacketMagicHeader; + + bool hasEqualServerSettings(const AwgConfig &other) const; + bool hasEqualClientSettings(const AwgConfig &other) const; +}; class AwgConfigModel : public QAbstractListModel { @@ -20,16 +64,28 @@ 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); @@ -40,11 +96,11 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; public slots: - void updateModel(const AwgProtocolConfig awgProtocolConfig); - QSharedPointer getConfig(); + void updateModel(const QJsonObject &config); + QJsonObject getConfig(); bool isHeadersEqual(const QString &h1, const QString &h2, const QString &h3, const QString &h4); - bool isPacketSizeEqual(const int s1, const int s2); + bool isPacketSizeEqual(const int s1, const int s2/*, const int s3, const int s4*/); bool isServerSettingsEqual(); @@ -52,8 +108,10 @@ protected: QHash roleNames() const override; private: - AwgProtocolConfig m_newAwgProtocolConfig; - AwgProtocolConfig m_oldAwgProtocolConfig; + DockerContainer m_container; + QJsonObject m_serverProtocolConfig; + QJsonObject m_clientProtocolConfig; + QJsonObject m_fullConfig; }; #endif // AWGCONFIGMODEL_H diff --git a/client/ui/models/protocols/cloakConfigModel.cpp b/client/ui/models/protocols/cloakConfigModel.cpp index 321ec9e6..a9f06f4d 100644 --- a/client/ui/models/protocols/cloakConfigModel.cpp +++ b/client/ui/models/protocols/cloakConfigModel.cpp @@ -2,10 +2,7 @@ #include "protocols/protocols_defs.h" -CloakConfigModel::CloakConfigModel(QObject *parent) - : QAbstractListModel(parent), - m_newCloakProtocolConfig(QJsonObject(), ProtocolProps::protoToString(Proto::Cloak)), - m_oldCloakProtocolConfig(QJsonObject(), ProtocolProps::protoToString(Proto::Cloak)) +CloakConfigModel::CloakConfigModel(QObject *parent) : QAbstractListModel(parent) { } @@ -22,9 +19,9 @@ bool CloakConfigModel::setData(const QModelIndex &index, const QVariant &value, } switch (role) { - case Roles::PortRole: m_newCloakProtocolConfig.serverProtocolConfig.port = value.toString(); break; - case Roles::CipherRole: m_newCloakProtocolConfig.serverProtocolConfig.cipher = value.toString(); break; - case Roles::SiteRole: m_newCloakProtocolConfig.serverProtocolConfig.site = value.toString(); break; + case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break; + case Roles::CipherRole: m_protocolConfig.insert(config_key::cipher, value.toString()); break; + case Roles::SiteRole: m_protocolConfig.insert(config_key::site, value.toString()); break; } emit dataChanged(index, index, QList { role }); @@ -38,33 +35,35 @@ QVariant CloakConfigModel::data(const QModelIndex &index, int role) const } switch (role) { - case Roles::PortRole: return m_newCloakProtocolConfig.serverProtocolConfig.port; - case Roles::CipherRole: return m_newCloakProtocolConfig.serverProtocolConfig.cipher; - case Roles::SiteRole: return m_newCloakProtocolConfig.serverProtocolConfig.site; + case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(protocols::cloak::defaultPort); + case Roles::CipherRole: return m_protocolConfig.value(config_key::cipher).toString(protocols::cloak::defaultCipher); + case Roles::SiteRole: return m_protocolConfig.value(config_key::site).toString(protocols::cloak::defaultRedirSite); } return QVariant(); } -void CloakConfigModel::updateModel(const CloakProtocolConfig cloakProtocolConfig) +void CloakConfigModel::updateModel(const QJsonObject &config) { beginResetModel(); - m_newCloakProtocolConfig = cloakProtocolConfig; - m_oldCloakProtocolConfig = cloakProtocolConfig; + m_container = ContainerProps::containerFromString(config.value(config_key::container).toString()); + + m_fullConfig = config; + QJsonObject protocolConfig = config.value(config_key::cloak).toObject(); + + auto defaultTransportProto = ProtocolProps::transportProtoToString(ProtocolProps::defaultTransportProto(Proto::Cloak), Proto::Cloak); + m_protocolConfig.insert(config_key::transport_proto, protocolConfig.value(config_key::transport_proto).toString(defaultTransportProto)); + m_protocolConfig.insert(config_key::cipher, protocolConfig.value(config_key::cipher).toString(protocols::cloak::defaultCipher)); + m_protocolConfig.insert(config_key::port, protocolConfig.value(config_key::port).toString(protocols::cloak::defaultPort)); + m_protocolConfig.insert(config_key::site, protocolConfig.value(config_key::site).toString(protocols::cloak::defaultRedirSite)); + endResetModel(); } -QSharedPointer CloakConfigModel::getConfig() +QJsonObject CloakConfigModel::getConfig() { - if (m_oldCloakProtocolConfig.hasEqualServerSettings(m_newCloakProtocolConfig)) { - m_newCloakProtocolConfig.clearClientSettings(); - } - return QSharedPointer::create(m_newCloakProtocolConfig); -} - -bool CloakConfigModel::isServerSettingsEqual() -{ - return m_oldCloakProtocolConfig.hasEqualServerSettings(m_newCloakProtocolConfig); + m_fullConfig.insert(config_key::cloak, m_protocolConfig); + return m_fullConfig; } QHash CloakConfigModel::roleNames() const @@ -77,4 +76,3 @@ QHash CloakConfigModel::roleNames() const return roles; } - diff --git a/client/ui/models/protocols/cloakConfigModel.h b/client/ui/models/protocols/cloakConfigModel.h index 8de8d305..31ff8c53 100644 --- a/client/ui/models/protocols/cloakConfigModel.h +++ b/client/ui/models/protocols/cloakConfigModel.h @@ -5,7 +5,6 @@ #include #include "containers/containers_defs.h" -#include "core/models/protocols/cloakProtocolConfig.h" class CloakConfigModel : public QAbstractListModel { @@ -26,17 +25,16 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; public slots: - void updateModel(const CloakProtocolConfig cloakProtocolConfig); - QSharedPointer getConfig(); - - bool isServerSettingsEqual(); + void updateModel(const QJsonObject &config); + QJsonObject getConfig(); protected: QHash roleNames() const override; private: - CloakProtocolConfig m_newCloakProtocolConfig; - CloakProtocolConfig m_oldCloakProtocolConfig; + DockerContainer m_container; + QJsonObject m_protocolConfig; + QJsonObject m_fullConfig; }; #endif // CLOAKCONFIGMODEL_H diff --git a/client/ui/models/protocols/openvpnConfigModel.cpp b/client/ui/models/protocols/openvpnConfigModel.cpp index 96f46ba0..a04c2b1a 100644 --- a/client/ui/models/protocols/openvpnConfigModel.cpp +++ b/client/ui/models/protocols/openvpnConfigModel.cpp @@ -2,10 +2,7 @@ #include "protocols/protocols_defs.h" -OpenVpnConfigModel::OpenVpnConfigModel(QObject *parent) - : QAbstractListModel(parent), - m_newOpenVpnProtocolConfig(QJsonObject(), ProtocolProps::protoToString(Proto::OpenVpn)), - m_oldOpenVpnProtocolConfig(QJsonObject(), ProtocolProps::protoToString(Proto::OpenVpn)) +OpenVpnConfigModel::OpenVpnConfigModel(QObject *parent) : QAbstractListModel(parent) { } @@ -22,16 +19,16 @@ bool OpenVpnConfigModel::setData(const QModelIndex &index, const QVariant &value } switch (role) { - case Roles::SubnetAddressRole: m_newOpenVpnProtocolConfig.serverProtocolConfig.subnetAddress = value.toString(); break; - case Roles::TransportProtoRole: m_newOpenVpnProtocolConfig.serverProtocolConfig.transportProto = value.toString(); break; - case Roles::PortRole: m_newOpenVpnProtocolConfig.serverProtocolConfig.port = value.toString(); break; - case Roles::AutoNegotiateEncryprionRole: m_newOpenVpnProtocolConfig.serverProtocolConfig.ncpDisable = !value.toBool(); break; - case Roles::HashRole: m_newOpenVpnProtocolConfig.serverProtocolConfig.hash = value.toString(); break; - case Roles::CipherRole: m_newOpenVpnProtocolConfig.serverProtocolConfig.cipher = value.toString(); break; - case Roles::TlsAuthRole: m_newOpenVpnProtocolConfig.serverProtocolConfig.tlsAuth = value.toBool(); break; - case Roles::BlockDnsRole: m_newOpenVpnProtocolConfig.serverProtocolConfig.blockOutsideDns = value.toBool(); break; - case Roles::AdditionalClientCommandsRole: m_newOpenVpnProtocolConfig.serverProtocolConfig.additionalClientConfig = value.toString(); break; - case Roles::AdditionalServerCommandsRole: m_newOpenVpnProtocolConfig.serverProtocolConfig.additionalServerConfig = value.toString(); break; + case Roles::SubnetAddressRole: m_protocolConfig.insert(amnezia::config_key::subnet_address, value.toString()); break; + case Roles::TransportProtoRole: m_protocolConfig.insert(config_key::transport_proto, value.toString()); break; + case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break; + case Roles::AutoNegotiateEncryprionRole: m_protocolConfig.insert(config_key::ncp_disable, !value.toBool()); break; + case Roles::HashRole: m_protocolConfig.insert(config_key::hash, value.toString()); break; + case Roles::CipherRole: m_protocolConfig.insert(config_key::cipher, value.toString()); break; + case Roles::TlsAuthRole: m_protocolConfig.insert(config_key::tls_auth, value.toBool()); break; + case Roles::BlockDnsRole: m_protocolConfig.insert(config_key::block_outside_dns, value.toBool()); break; + case Roles::AdditionalClientCommandsRole: m_protocolConfig.insert(config_key::additional_client_config, value.toString()); break; + case Roles::AdditionalServerCommandsRole: m_protocolConfig.insert(config_key::additional_server_config, value.toString()); break; } emit dataChanged(index, index, QList { role }); @@ -45,42 +42,72 @@ QVariant OpenVpnConfigModel::data(const QModelIndex &index, int role) const } switch (role) { - case Roles::SubnetAddressRole: return m_newOpenVpnProtocolConfig.serverProtocolConfig.subnetAddress; - case Roles::TransportProtoRole: return m_newOpenVpnProtocolConfig.serverProtocolConfig.transportProto; - case Roles::PortRole: return m_newOpenVpnProtocolConfig.serverProtocolConfig.port; - case Roles::AutoNegotiateEncryprionRole: return !m_newOpenVpnProtocolConfig.serverProtocolConfig.ncpDisable; - case Roles::HashRole: return m_newOpenVpnProtocolConfig.serverProtocolConfig.hash; - case Roles::CipherRole: return m_newOpenVpnProtocolConfig.serverProtocolConfig.cipher; - case Roles::TlsAuthRole: return m_newOpenVpnProtocolConfig.serverProtocolConfig.tlsAuth; - case Roles::BlockDnsRole: return m_newOpenVpnProtocolConfig.serverProtocolConfig.blockOutsideDns; - case Roles::AdditionalClientCommandsRole: return m_newOpenVpnProtocolConfig.serverProtocolConfig.additionalClientConfig; - case Roles::AdditionalServerCommandsRole: return m_newOpenVpnProtocolConfig.serverProtocolConfig.additionalServerConfig; - case Roles::IsPortEditable: return true; // TODO: implement container check if needed - case Roles::IsTransportProtoEditable: return true; // TODO: implement container check if needed - case Roles::HasRemoveButton: return true; // TODO: implement container check if needed + case Roles::SubnetAddressRole: + return m_protocolConfig.value(amnezia::config_key::subnet_address).toString(amnezia::protocols::openvpn::defaultSubnetAddress); + case Roles::TransportProtoRole: + return m_protocolConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto); + case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(protocols::openvpn::defaultPort); + case Roles::AutoNegotiateEncryprionRole: + return !m_protocolConfig.value(config_key::ncp_disable).toBool(protocols::openvpn::defaultNcpDisable); + case Roles::HashRole: return m_protocolConfig.value(config_key::hash).toString(protocols::openvpn::defaultHash); + case Roles::CipherRole: return m_protocolConfig.value(config_key::cipher).toString(protocols::openvpn::defaultCipher); + case Roles::TlsAuthRole: return m_protocolConfig.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth); + case Roles::BlockDnsRole: + return m_protocolConfig.value(config_key::block_outside_dns).toBool(protocols::openvpn::defaultBlockOutsideDns); + case Roles::AdditionalClientCommandsRole: + return m_protocolConfig.value(config_key::additional_client_config).toString(protocols::openvpn::defaultAdditionalClientConfig); + case Roles::AdditionalServerCommandsRole: + return m_protocolConfig.value(config_key::additional_server_config).toString(protocols::openvpn::defaultAdditionalServerConfig); + case Roles::IsPortEditable: return m_container == DockerContainer::OpenVpn ? true : false; + case Roles::IsTransportProtoEditable: return m_container == DockerContainer::OpenVpn ? true : false; + case Roles::HasRemoveButton: return m_container == DockerContainer::OpenVpn ? true : false; } return QVariant(); } -void OpenVpnConfigModel::updateModel(const OpenVpnProtocolConfig openVpnProtocolConfig) +void OpenVpnConfigModel::updateModel(const QJsonObject &config) { beginResetModel(); - m_newOpenVpnProtocolConfig = openVpnProtocolConfig; - m_oldOpenVpnProtocolConfig = openVpnProtocolConfig; + m_container = ContainerProps::containerFromString(config.value(config_key::container).toString()); + + m_fullConfig = config; + QJsonObject protocolConfig = config.value(config_key::openvpn).toObject(); + + m_protocolConfig.insert( + config_key::subnet_address, + protocolConfig.value(amnezia::config_key::subnet_address).toString(amnezia::protocols::openvpn::defaultSubnetAddress)); + + QString transportProto; + if (m_container == DockerContainer::OpenVpn) { + transportProto = protocolConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto); + } else { + transportProto = "tcp"; + } + + m_protocolConfig.insert(config_key::transport_proto, transportProto); + + m_protocolConfig.insert(config_key::ncp_disable, + protocolConfig.value(config_key::ncp_disable).toBool(protocols::openvpn::defaultNcpDisable)); + m_protocolConfig.insert(config_key::cipher, protocolConfig.value(config_key::cipher).toString(protocols::openvpn::defaultCipher)); + m_protocolConfig.insert(config_key::hash, protocolConfig.value(config_key::hash).toString(protocols::openvpn::defaultHash)); + m_protocolConfig.insert(config_key::block_outside_dns, + protocolConfig.value(config_key::block_outside_dns).toBool(protocols::openvpn::defaultBlockOutsideDns)); + m_protocolConfig.insert(config_key::port, protocolConfig.value(config_key::port).toString(protocols::openvpn::defaultPort)); + m_protocolConfig.insert(config_key::tls_auth, protocolConfig.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth)); + m_protocolConfig.insert( + config_key::additional_client_config, + protocolConfig.value(config_key::additional_client_config).toString(protocols::openvpn::defaultAdditionalClientConfig)); + m_protocolConfig.insert( + config_key::additional_server_config, + protocolConfig.value(config_key::additional_server_config).toString(protocols::openvpn::defaultAdditionalServerConfig)); + endResetModel(); } -QSharedPointer OpenVpnConfigModel::getConfig() +QJsonObject OpenVpnConfigModel::getConfig() { - if (m_oldOpenVpnProtocolConfig.hasEqualServerSettings(m_newOpenVpnProtocolConfig)) { - m_newOpenVpnProtocolConfig.clearClientSettings(); - } - return QSharedPointer::create(m_newOpenVpnProtocolConfig); -} - -bool OpenVpnConfigModel::isServerSettingsEqual() -{ - return m_oldOpenVpnProtocolConfig.hasEqualServerSettings(m_newOpenVpnProtocolConfig); + m_fullConfig.insert(config_key::openvpn, m_protocolConfig); + return m_fullConfig; } QHash OpenVpnConfigModel::roleNames() const diff --git a/client/ui/models/protocols/openvpnConfigModel.h b/client/ui/models/protocols/openvpnConfigModel.h index 0105c926..0357700c 100644 --- a/client/ui/models/protocols/openvpnConfigModel.h +++ b/client/ui/models/protocols/openvpnConfigModel.h @@ -5,7 +5,6 @@ #include #include "containers/containers_defs.h" -#include "core/models/protocols/openvpnProtocolConfig.h" class OpenVpnConfigModel : public QAbstractListModel { @@ -38,17 +37,16 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; public slots: - void updateModel(const OpenVpnProtocolConfig openVpnProtocolConfig); - QSharedPointer getConfig(); - - bool isServerSettingsEqual(); + void updateModel(const QJsonObject &config); + QJsonObject getConfig(); protected: QHash roleNames() const override; private: - OpenVpnProtocolConfig m_newOpenVpnProtocolConfig; - OpenVpnProtocolConfig m_oldOpenVpnProtocolConfig; + DockerContainer m_container; + QJsonObject m_protocolConfig; + QJsonObject m_fullConfig; }; #endif // OPENVPNCONFIGMODEL_H diff --git a/client/ui/models/protocols/shadowsocksConfigModel.cpp b/client/ui/models/protocols/shadowsocksConfigModel.cpp index 064bd070..769bef20 100644 --- a/client/ui/models/protocols/shadowsocksConfigModel.cpp +++ b/client/ui/models/protocols/shadowsocksConfigModel.cpp @@ -2,10 +2,7 @@ #include "protocols/protocols_defs.h" -ShadowSocksConfigModel::ShadowSocksConfigModel(QObject *parent) - : QAbstractListModel(parent), - m_newShadowsocksProtocolConfig(QJsonObject(), ProtocolProps::protoToString(Proto::ShadowSocks)), - m_oldShadowsocksProtocolConfig(QJsonObject(), ProtocolProps::protoToString(Proto::ShadowSocks)) +ShadowSocksConfigModel::ShadowSocksConfigModel(QObject *parent) : QAbstractListModel(parent) { } @@ -22,8 +19,8 @@ bool ShadowSocksConfigModel::setData(const QModelIndex &index, const QVariant &v } switch (role) { - case Roles::PortRole: m_newShadowsocksProtocolConfig.serverProtocolConfig.port = value.toString(); break; - case Roles::CipherRole: m_newShadowsocksProtocolConfig.serverProtocolConfig.cipher = value.toString(); break; + case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break; + case Roles::CipherRole: m_protocolConfig.insert(config_key::cipher, value.toString()); break; } emit dataChanged(index, index, QList { role }); @@ -37,34 +34,36 @@ QVariant ShadowSocksConfigModel::data(const QModelIndex &index, int role) const } switch (role) { - case Roles::PortRole: return m_newShadowsocksProtocolConfig.serverProtocolConfig.port; - case Roles::CipherRole: return m_newShadowsocksProtocolConfig.serverProtocolConfig.cipher; - case Roles::IsPortEditableRole: return true; // TODO: implement container check if needed - case Roles::IsCipherEditableRole: return true; // TODO: implement container check if needed + case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort); + case Roles::CipherRole: return m_protocolConfig.value(config_key::cipher).toString(protocols::shadowsocks::defaultCipher); + case Roles::IsPortEditableRole: return m_container == DockerContainer::ShadowSocks ? true : false; + case Roles::IsCipherEditableRole: return m_container == DockerContainer::ShadowSocks ? true : false; } return QVariant(); } -void ShadowSocksConfigModel::updateModel(const ShadowsocksProtocolConfig shadowsocksProtocolConfig) +void ShadowSocksConfigModel::updateModel(const QJsonObject &config) { beginResetModel(); - m_newShadowsocksProtocolConfig = shadowsocksProtocolConfig; - m_oldShadowsocksProtocolConfig = shadowsocksProtocolConfig; + m_container = ContainerProps::containerFromString(config.value(config_key::container).toString()); + + m_fullConfig = config; + QJsonObject protocolConfig = config.value(config_key::shadowsocks).toObject(); + + auto defaultTransportProto = ProtocolProps::transportProtoToString(ProtocolProps::defaultTransportProto(Proto::ShadowSocks), Proto::ShadowSocks); + m_protocolConfig.insert(config_key::transport_proto, + protocolConfig.value(config_key::transport_proto).toString(defaultTransportProto)); + m_protocolConfig.insert(config_key::cipher, protocolConfig.value(config_key::cipher).toString(protocols::shadowsocks::defaultCipher)); + m_protocolConfig.insert(config_key::port, protocolConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort)); + endResetModel(); } -QSharedPointer ShadowSocksConfigModel::getConfig() +QJsonObject ShadowSocksConfigModel::getConfig() { - if (m_oldShadowsocksProtocolConfig.hasEqualServerSettings(m_newShadowsocksProtocolConfig)) { - m_newShadowsocksProtocolConfig.clearClientSettings(); - } - return QSharedPointer::create(m_newShadowsocksProtocolConfig); -} - -bool ShadowSocksConfigModel::isServerSettingsEqual() -{ - return m_oldShadowsocksProtocolConfig.hasEqualServerSettings(m_newShadowsocksProtocolConfig); + m_fullConfig.insert(config_key::shadowsocks, m_protocolConfig); + return m_fullConfig; } QHash ShadowSocksConfigModel::roleNames() const @@ -78,4 +77,3 @@ QHash ShadowSocksConfigModel::roleNames() const return roles; } - diff --git a/client/ui/models/protocols/shadowsocksConfigModel.h b/client/ui/models/protocols/shadowsocksConfigModel.h index 21b05b31..566df768 100644 --- a/client/ui/models/protocols/shadowsocksConfigModel.h +++ b/client/ui/models/protocols/shadowsocksConfigModel.h @@ -5,7 +5,6 @@ #include #include "containers/containers_defs.h" -#include "core/models/protocols/shadowsocksProtocolConfig.h" class ShadowSocksConfigModel : public QAbstractListModel { @@ -27,17 +26,16 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; public slots: - void updateModel(const ShadowsocksProtocolConfig shadowsocksProtocolConfig); - QSharedPointer getConfig(); - - bool isServerSettingsEqual(); + void updateModel(const QJsonObject &config); + QJsonObject getConfig(); protected: QHash roleNames() const override; private: - ShadowsocksProtocolConfig m_newShadowsocksProtocolConfig; - ShadowsocksProtocolConfig m_oldShadowsocksProtocolConfig; + DockerContainer m_container; + QJsonObject m_protocolConfig; + QJsonObject m_fullConfig; }; #endif // SHADOWSOCKSCONFIGMODEL_H diff --git a/client/ui/models/protocols/wireguardConfigModel.cpp b/client/ui/models/protocols/wireguardConfigModel.cpp index a8b02272..1c8e1341 100644 --- a/client/ui/models/protocols/wireguardConfigModel.cpp +++ b/client/ui/models/protocols/wireguardConfigModel.cpp @@ -4,10 +4,7 @@ #include "protocols/protocols_defs.h" -WireGuardConfigModel::WireGuardConfigModel(QObject *parent) - : QAbstractListModel(parent), - m_newWireGuardProtocolConfig(QJsonObject(), ProtocolProps::protoToString(Proto::WireGuard)), - m_oldWireGuardProtocolConfig(QJsonObject(), ProtocolProps::protoToString(Proto::WireGuard)) +WireGuardConfigModel::WireGuardConfigModel(QObject *parent) : QAbstractListModel(parent) { } @@ -24,9 +21,9 @@ bool WireGuardConfigModel::setData(const QModelIndex &index, const QVariant &val } switch (role) { - case Roles::SubnetAddressRole: m_newWireGuardProtocolConfig.serverProtocolConfig.subnetAddress = value.toString(); break; - case Roles::PortRole: m_newWireGuardProtocolConfig.serverProtocolConfig.port = value.toString(); break; - case Roles::ClientMtuRole: m_newWireGuardProtocolConfig.clientProtocolConfig.wireGuardData.mtu = value.toString(); break; + case Roles::SubnetAddressRole: m_serverProtocolConfig.insert(config_key::subnet_address, value.toString()); break; + case Roles::PortRole: m_serverProtocolConfig.insert(config_key::port, value.toString()); break; + case Roles::ClientMtuRole: m_clientProtocolConfig.insert(config_key::mtu, value.toString()); break; } emit dataChanged(index, index, QList { role }); @@ -40,33 +37,62 @@ QVariant WireGuardConfigModel::data(const QModelIndex &index, int role) const } switch (role) { - case Roles::SubnetAddressRole: return m_newWireGuardProtocolConfig.serverProtocolConfig.subnetAddress; - case Roles::PortRole: return m_newWireGuardProtocolConfig.serverProtocolConfig.port; - case Roles::ClientMtuRole: return m_newWireGuardProtocolConfig.clientProtocolConfig.wireGuardData.mtu; + case Roles::SubnetAddressRole: return m_serverProtocolConfig.value(config_key::subnet_address).toString(); + case Roles::PortRole: return m_serverProtocolConfig.value(config_key::port).toString(); + case Roles::ClientMtuRole: return m_clientProtocolConfig.value(config_key::mtu); } return QVariant(); } -void WireGuardConfigModel::updateModel(const WireGuardProtocolConfig wireGuardProtocolConfig) +void WireGuardConfigModel::updateModel(const QJsonObject &config) { beginResetModel(); - m_newWireGuardProtocolConfig = wireGuardProtocolConfig; - m_oldWireGuardProtocolConfig = wireGuardProtocolConfig; + m_container = ContainerProps::containerFromString(config.value(config_key::container).toString()); + + m_fullConfig = config; + QJsonObject serverProtocolConfig = config.value(config_key::wireguard).toObject(); + + auto defaultTransportProto = + ProtocolProps::transportProtoToString(ProtocolProps::defaultTransportProto(Proto::WireGuard), Proto::WireGuard); + 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::port] = serverProtocolConfig.value(config_key::port).toString(protocols::wireguard::defaultPort); + + auto lastConfig = m_serverProtocolConfig.value(config_key::last_config).toString(); + QJsonObject clientProtocolConfig = QJsonDocument::fromJson(lastConfig.toUtf8()).object(); + m_clientProtocolConfig[config_key::mtu] = clientProtocolConfig[config_key::mtu].toString(protocols::wireguard::defaultMtu); + endResetModel(); } -QSharedPointer WireGuardConfigModel::getConfig() +QJsonObject WireGuardConfigModel::getConfig() { - if (m_oldWireGuardProtocolConfig.hasEqualServerSettings(m_newWireGuardProtocolConfig)) { - m_newWireGuardProtocolConfig.clearClientSettings(); + const WgConfig oldConfig(m_fullConfig.value(config_key::wireguard).toObject()); + const WgConfig newConfig(m_serverProtocolConfig); + + if (!oldConfig.hasEqualServerSettings(newConfig)) { + m_serverProtocolConfig.remove(config_key::last_config); + } else { + auto lastConfig = m_serverProtocolConfig.value(config_key::last_config).toString(); + QJsonObject jsonConfig = QJsonDocument::fromJson(lastConfig.toUtf8()).object(); + jsonConfig[config_key::mtu] = m_clientProtocolConfig[config_key::mtu]; + + m_serverProtocolConfig[config_key::last_config] = QString(QJsonDocument(jsonConfig).toJson()); } - return QSharedPointer::create(m_newWireGuardProtocolConfig); + + m_fullConfig.insert(config_key::wireguard, m_serverProtocolConfig); + return m_fullConfig; } bool WireGuardConfigModel::isServerSettingsEqual() { - return m_oldWireGuardProtocolConfig.hasEqualServerSettings(m_newWireGuardProtocolConfig); + const WgConfig oldConfig(m_fullConfig.value(config_key::wireguard).toObject()); + const WgConfig newConfig(m_serverProtocolConfig); + + return oldConfig.hasEqualServerSettings(newConfig); } QHash WireGuardConfigModel::roleNames() const @@ -79,3 +105,29 @@ QHash WireGuardConfigModel::roleNames() const return roles; } + +WgConfig::WgConfig(const QJsonObject &serverProtocolConfig) +{ + auto lastConfig = serverProtocolConfig.value(config_key::last_config).toString(); + QJsonObject clientProtocolConfig = QJsonDocument::fromJson(lastConfig.toUtf8()).object(); + clientMtu = clientProtocolConfig[config_key::mtu].toString(protocols::wireguard::defaultMtu); + + subnetAddress = serverProtocolConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress); + port = serverProtocolConfig.value(config_key::port).toString(protocols::wireguard::defaultPort); +} + +bool WgConfig::hasEqualServerSettings(const WgConfig &other) const +{ + if (subnetAddress != other.subnetAddress || port != other.port) { + return false; + } + return true; +} + +bool WgConfig::hasEqualClientSettings(const WgConfig &other) const +{ + if (clientMtu != other.clientMtu) { + return false; + } + return true; +} diff --git a/client/ui/models/protocols/wireguardConfigModel.h b/client/ui/models/protocols/wireguardConfigModel.h index 9010d9b4..b1ce2d61 100644 --- a/client/ui/models/protocols/wireguardConfigModel.h +++ b/client/ui/models/protocols/wireguardConfigModel.h @@ -5,7 +5,19 @@ #include #include "containers/containers_defs.h" -#include "core/models/protocols/wireguardProtocolConfig.h" + +struct WgConfig +{ + WgConfig(const QJsonObject &jsonConfig); + + QString subnetAddress; + QString port; + QString clientMtu; + + bool hasEqualServerSettings(const WgConfig &other) const; + bool hasEqualClientSettings(const WgConfig &other) const; + +}; class WireGuardConfigModel : public QAbstractListModel { @@ -26,8 +38,8 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; public slots: - void updateModel(const WireGuardProtocolConfig wireGuardProtocolConfig); - QSharedPointer getConfig(); + void updateModel(const QJsonObject &config); + QJsonObject getConfig(); bool isServerSettingsEqual(); @@ -35,8 +47,10 @@ protected: QHash roleNames() const override; private: - WireGuardProtocolConfig m_newWireGuardProtocolConfig; - WireGuardProtocolConfig m_oldWireGuardProtocolConfig; + DockerContainer m_container; + QJsonObject m_serverProtocolConfig; + QJsonObject m_clientProtocolConfig; + QJsonObject m_fullConfig; }; #endif // WIREGUARDCONFIGMODEL_H diff --git a/client/ui/models/protocols/xrayConfigModel.cpp b/client/ui/models/protocols/xrayConfigModel.cpp index fdcf15e3..3917b544 100644 --- a/client/ui/models/protocols/xrayConfigModel.cpp +++ b/client/ui/models/protocols/xrayConfigModel.cpp @@ -2,10 +2,7 @@ #include "protocols/protocols_defs.h" -XrayConfigModel::XrayConfigModel(QObject *parent) - : QAbstractListModel(parent), - m_newXrayProtocolConfig(QJsonObject(), ProtocolProps::protoToString(Proto::Xray)), - m_oldXrayProtocolConfig(QJsonObject(), ProtocolProps::protoToString(Proto::Xray)) +XrayConfigModel::XrayConfigModel(QObject *parent) : QAbstractListModel(parent) { } @@ -22,8 +19,8 @@ bool XrayConfigModel::setData(const QModelIndex &index, const QVariant &value, i } switch (role) { - case Roles::SiteRole: m_newXrayProtocolConfig.serverProtocolConfig.site = value.toString(); break; - case Roles::PortRole: m_newXrayProtocolConfig.serverProtocolConfig.port = value.toString(); break; + 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 }); @@ -37,32 +34,34 @@ QVariant XrayConfigModel::data(const QModelIndex &index, int role) const } switch (role) { - case Roles::SiteRole: return m_newXrayProtocolConfig.serverProtocolConfig.site; - case Roles::PortRole: return m_newXrayProtocolConfig.serverProtocolConfig.port; + 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(); } -void XrayConfigModel::updateModel(const XrayProtocolConfig xrayProtocolConfig) +void XrayConfigModel::updateModel(const QJsonObject &config) { beginResetModel(); - m_newXrayProtocolConfig = xrayProtocolConfig; - m_oldXrayProtocolConfig = xrayProtocolConfig; + m_container = ContainerProps::containerFromString(config.value(config_key::container).toString()); + + m_fullConfig = config; + QJsonObject protocolConfig = config.value(config_key::xray).toObject(); + + auto defaultTransportProto = ProtocolProps::transportProtoToString(ProtocolProps::defaultTransportProto(Proto::Xray), Proto::Xray); + m_protocolConfig.insert(config_key::transport_proto, + protocolConfig.value(config_key::transport_proto).toString(defaultTransportProto)); + m_protocolConfig.insert(config_key::port, protocolConfig.value(config_key::port).toString(protocols::xray::defaultPort)); + m_protocolConfig.insert(config_key::site, protocolConfig.value(config_key::site).toString(protocols::xray::defaultSite)); + endResetModel(); } -QSharedPointer XrayConfigModel::getConfig() +QJsonObject XrayConfigModel::getConfig() { - if (m_oldXrayProtocolConfig.hasEqualServerSettings(m_newXrayProtocolConfig)) { - m_newXrayProtocolConfig.clearClientSettings(); - } - return QSharedPointer::create(m_newXrayProtocolConfig); -} - -bool XrayConfigModel::isServerSettingsEqual() -{ - return m_oldXrayProtocolConfig.hasEqualServerSettings(m_newXrayProtocolConfig); + m_fullConfig.insert(config_key::xray, m_protocolConfig); + return m_fullConfig; } QHash XrayConfigModel::roleNames() const @@ -74,4 +73,3 @@ QHash XrayConfigModel::roleNames() const return roles; } - diff --git a/client/ui/models/protocols/xrayConfigModel.h b/client/ui/models/protocols/xrayConfigModel.h index 519d7fb4..41aac589 100644 --- a/client/ui/models/protocols/xrayConfigModel.h +++ b/client/ui/models/protocols/xrayConfigModel.h @@ -5,7 +5,6 @@ #include #include "containers/containers_defs.h" -#include "core/models/protocols/xrayProtocolConfig.h" class XrayConfigModel : public QAbstractListModel { @@ -25,17 +24,16 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; public slots: - void updateModel(const XrayProtocolConfig xrayProtocolConfig); - QSharedPointer getConfig(); - - bool isServerSettingsEqual(); + void updateModel(const QJsonObject &config); + QJsonObject getConfig(); protected: QHash roleNames() const override; private: - XrayProtocolConfig m_newXrayProtocolConfig; - XrayProtocolConfig m_oldXrayProtocolConfig; + DockerContainer m_container; + QJsonObject m_protocolConfig; + QJsonObject m_fullConfig; }; #endif // XRAYCONFIGMODEL_H diff --git a/client/ui/models/protocols_model.cpp b/client/ui/models/protocols_model.cpp index add32488..019b2d2f 100644 --- a/client/ui/models/protocols_model.cpp +++ b/client/ui/models/protocols_model.cpp @@ -1,45 +1,14 @@ #include "protocols_model.h" -#include "core/models/protocols/awgProtocolConfig.h" -#include "core/models/protocols/cloakProtocolConfig.h" -#include "core/models/protocols/openvpnProtocolConfig.h" -#include "core/models/protocols/shadowsocksProtocolConfig.h" -#include "core/models/protocols/wireguardProtocolConfig.h" -#include "core/models/protocols/xrayProtocolConfig.h" - -ProtocolsModel::ProtocolsModel(QObject *parent) : QAbstractListModel(parent) -{ -} - -ProtocolsModel::ProtocolsModel(const QSharedPointer &openVpnConfigModel, - const QSharedPointer &shadowSocksConfigModel, - const QSharedPointer &cloakConfigModel, - const QSharedPointer &wireGuardConfigModel, - const QSharedPointer &awgConfigModel, const QSharedPointer &xrayConfigModel, -#ifdef Q_OS_WINDOWS - const QSharedPointer &ikev2ConfigModel, -#endif - const QSharedPointer &sftpConfigModel, - const QSharedPointer &socks5ProxyConfigModel, QObject *parent) - : QAbstractListModel(parent), - m_openVpnConfigModel(openVpnConfigModel), - m_shadowSocksConfigModel(shadowSocksConfigModel), - m_cloakConfigModel(cloakConfigModel), - m_wireGuardConfigModel(wireGuardConfigModel), - m_awgConfigModel(awgConfigModel), - m_xrayConfigModel(xrayConfigModel), -#ifdef Q_OS_WINDOWS - m_ikev2ConfigModel(ikev2ConfigModel), -#endif - m_sftpConfigModel(sftpConfigModel), - m_socks5ProxyConfigModel(socks5ProxyConfigModel) +ProtocolsModel::ProtocolsModel(std::shared_ptr settings, QObject *parent) + : m_settings(settings), QAbstractListModel(parent) { } int ProtocolsModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); - return m_protocolConfigs.size(); + return m_content.size(); } QHash ProtocolsModel::roleNames() const @@ -58,102 +27,60 @@ QHash ProtocolsModel::roleNames() const QVariant ProtocolsModel::data(const QModelIndex &index, int role) const { - if (!index.isValid() || index.row() < 0 || index.row() >= m_protocolConfigs.size()) { + if (!index.isValid() || index.row() < 0 || index.row() >= m_content.size()) { return QVariant(); } - auto protocolConfig = m_protocolConfigs.at(index.row()); - amnezia::Proto protocol = ProtocolProps::protoFromString(protocolConfig->protocolName); - switch (role) { case ProtocolNameRole: { - return ProtocolProps::protocolHumanNames().value(protocol); + amnezia::Proto proto = ProtocolProps::protoFromString(m_content.keys().at(index.row())); + return ProtocolProps::protocolHumanNames().value(proto); } - case ServerProtocolPageRole: return static_cast(serverProtocolPage(protocol)); - case ClientProtocolPageRole: return static_cast(clientProtocolPage(protocol)); - case ProtocolIndexRole: return protocol; - // case RawConfigRole: { - // auto protocolConfig = m_content.value(ContainerProps::containerTypeToString(m_container)).toObject(); - // auto lastConfigJsonDoc = QJsonDocument::fromJson(protocolConfig.value(config_key::last_config).toString().toUtf8()); - // auto lastConfigJson = lastConfigJsonDoc.object(); + case ServerProtocolPageRole: + return static_cast(serverProtocolPage(ProtocolProps::protoFromString(m_content.keys().at(index.row())))); + case ClientProtocolPageRole: + return static_cast(clientProtocolPage(ProtocolProps::protoFromString(m_content.keys().at(index.row())))); + case ProtocolIndexRole: return ProtocolProps::protoFromString(m_content.keys().at(index.row())); + case RawConfigRole: { + auto protocolConfig = m_content.value(ContainerProps::containerTypeToString(m_container)).toObject(); + auto lastConfigJsonDoc = + QJsonDocument::fromJson(protocolConfig.value(config_key::last_config).toString().toUtf8()); + auto lastConfigJson = lastConfigJsonDoc.object(); - // QString rawConfig; - // QStringList lines = lastConfigJson.value(config_key::config).toString().replace("\r", "").split("\n"); - // for (const QString &l : lines) { - // rawConfig.append(l + "\n"); - // } - // return rawConfig; - // } + QString rawConfig; + QStringList lines = lastConfigJson.value(config_key::config).toString().replace("\r", "").split("\n"); + for (const QString &l : lines) { + rawConfig.append(l + "\n"); + } + return rawConfig; + } case IsClientProtocolExistsRole: { - auto protocolVariant = ProtocolConfig::getProtocolConfigVariant(protocolConfig); - return std::visit([](const auto &ptr) -> bool { return ptr->clientProtocolConfig.isEmpty; }, protocolVariant); + auto protocolConfig = m_content.value(ContainerProps::containerTypeToString(m_container)).toObject(); + auto lastConfigJsonDoc = + QJsonDocument::fromJson(protocolConfig.value(config_key::last_config).toString().toUtf8()); + auto lastConfigJson = lastConfigJsonDoc.object(); + + auto configString = lastConfigJson.value(config_key::config).toString(); + return !configString.isEmpty(); } } return QVariant(); } -void ProtocolsModel::updateModel(const QMap> &protocolConfigs) +void ProtocolsModel::updateModel(const QJsonObject &content) { - beginResetModel(); - m_protocolConfigs.clear(); - for (const auto &protocolConfig : protocolConfigs) { - m_protocolConfigs.push_back(protocolConfig); - } - endResetModel(); + m_container = ContainerProps::containerFromString(content.value(config_key::container).toString()); + + m_content = content; + m_content.remove(config_key::container); } -void ProtocolsModel::updateProtocolModel(amnezia::Proto protocol) +QJsonObject ProtocolsModel::getConfig() { - QSharedPointer protocolConfig; - - for (const auto &config : m_protocolConfigs) { - if (ProtocolProps::protoFromString(config->protocolName) == protocol) { - protocolConfig = config; - break; - } - } - - switch (protocol) { - case Proto::OpenVpn: m_openVpnConfigModel->updateModel(*qSharedPointerCast(protocolConfig).data()); break; - case Proto::ShadowSocks: m_shadowSocksConfigModel->updateModel(*qSharedPointerCast(protocolConfig).data()); break; - case Proto::Cloak: m_cloakConfigModel->updateModel(*qSharedPointerCast(protocolConfig).data()); break; - case Proto::WireGuard: m_wireGuardConfigModel->updateModel(*qSharedPointerCast(protocolConfig).data()); break; - case Proto::Awg: m_awgConfigModel->updateModel(*qSharedPointerCast(protocolConfig).data()); break; - case Proto::Xray: m_xrayConfigModel->updateModel(*qSharedPointerCast(protocolConfig).data()); break; -#ifdef Q_OS_WINDOWS - case Proto::Ikev2: - case Proto::L2tp: m_ikev2ConfigModel->updateModel(*qSharedPointerCast(protocolConfig).data()); break; -#endif - // case Proto::Sftp: m_sftpConfigModel->updateModel(*qSharedPointerCast(protocolConfig).data()); break; - // case Proto::Socks5Proxy: m_socks5ProxyConfigModel->updateModel(*qSharedPointerCast(protocolConfig).data()); break; - default: break; - } -} - -QMap> ProtocolsModel::getProtocolConfigs() -{ - QMap> protocolConfigs; - - for (const auto &config : m_protocolConfigs) { - switch (ProtocolProps::protoFromString(config->protocolName)) { - case Proto::OpenVpn: protocolConfigs.insert(config->protocolName, m_openVpnConfigModel->getConfig()); break; - case Proto::ShadowSocks: protocolConfigs.insert(config->protocolName, m_shadowSocksConfigModel->getConfig()); break; - case Proto::Cloak: protocolConfigs.insert(config->protocolName, m_cloakConfigModel->getConfig()); break; - case Proto::WireGuard: protocolConfigs.insert(config->protocolName, m_wireGuardConfigModel->getConfig()); break; - case Proto::Awg: protocolConfigs.insert(config->protocolName, m_awgConfigModel->getConfig()); break; - case Proto::Xray: protocolConfigs.insert(config->protocolName, m_xrayConfigModel->getConfig()); break; -#ifdef Q_OS_WINDOWS - case Proto::Ikev2: - case Proto::L2tp: protocolConfigs.insert(config->protocolName, m_awgConfigModel->getConfig()); break; -#endif - // case Proto::Sftp: protocolConfigs.insert(config->protocolName, m_awgConfigModel->getConfig()); break; - // case Proto::Socks5Proxy: protocolConfigs.insert(config->protocolName, m_awgConfigModel->getConfig()); break; - default: break; - } - } - - return protocolConfigs; + QJsonObject config = m_content; + config.insert(config_key::container, ContainerProps::containerToString(m_container)); + return config; } PageLoader::PageEnum ProtocolsModel::serverProtocolPage(Proto protocol) const @@ -167,7 +94,7 @@ PageLoader::PageEnum ProtocolsModel::serverProtocolPage(Proto protocol) const case Proto::Ikev2: return PageLoader::PageEnum::PageProtocolIKev2Settings; case Proto::L2tp: return PageLoader::PageEnum::PageProtocolIKev2Settings; case Proto::Xray: return PageLoader::PageEnum::PageProtocolXraySettings; - + // non-vpn case Proto::TorWebSite: return PageLoader::PageEnum::PageServiceTorWebsiteSettings; case Proto::Dns: return PageLoader::PageEnum::PageServiceDnsSettings; diff --git a/client/ui/models/protocols_model.h b/client/ui/models/protocols_model.h index 64599962..5c52ee86 100644 --- a/client/ui/models/protocols_model.h +++ b/client/ui/models/protocols_model.h @@ -4,19 +4,8 @@ #include #include -#include "core/models/protocols/protocolConfig.h" -#include "ui/controllers/pageController.h" -#include "ui/models/protocols/awgConfigModel.h" -#include "ui/models/protocols/cloakConfigModel.h" -#include "ui/models/protocols/openvpnConfigModel.h" -#include "ui/models/protocols/shadowsocksConfigModel.h" -#include "ui/models/protocols/wireguardConfigModel.h" -#include "ui/models/protocols/xrayConfigModel.h" -#ifdef Q_OS_WINDOWS - #include "ui/models/protocols/ikev2ConfigModel.h" -#endif -#include "ui/models/services/sftpConfigModel.h" -#include "ui/models/services/socks5ProxyConfigModel.h" +#include "../controllers/pageController.h" +#include "settings.h" class ProtocolsModel : public QAbstractListModel { @@ -31,26 +20,16 @@ public: IsClientProtocolExistsRole }; - ProtocolsModel(QObject *parent = nullptr); - ProtocolsModel(const QSharedPointer &openVpnConfigModel, - const QSharedPointer &shadowSocksConfigModel, - const QSharedPointer &cloakConfigModel, const QSharedPointer &wireGuardConfigModel, - const QSharedPointer &awgConfigModel, const QSharedPointer &xrayConfigModel, -#ifdef Q_OS_WINDOWS - const QSharedPointer &ikev2ConfigModel, -#endif - const QSharedPointer &sftpConfigModel, - const QSharedPointer &socks5ProxyConfigModel, QObject *parent = nullptr); + ProtocolsModel(std::shared_ptr settings, QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; public slots: - void updateModel(const QMap> &protocolConfigs); - void updateProtocolModel(amnezia::Proto protocol); + void updateModel(const QJsonObject &content); - QMap> getProtocolConfigs(); + QJsonObject getConfig(); protected: QHash roleNames() const override; @@ -59,19 +38,10 @@ private: PageLoader::PageEnum serverProtocolPage(Proto protocol) const; PageLoader::PageEnum clientProtocolPage(Proto protocol) const; - QVector> m_protocolConfigs; + std::shared_ptr m_settings; - QSharedPointer m_openVpnConfigModel; - QSharedPointer m_shadowSocksConfigModel; - QSharedPointer m_cloakConfigModel; - QSharedPointer m_wireGuardConfigModel; - QSharedPointer m_awgConfigModel; - QSharedPointer m_xrayConfigModel; -#ifdef Q_OS_WINDOWS - QSharedPointer m_ikev2ConfigModel; -#endif - QSharedPointer m_sftpConfigModel; - QSharedPointer m_socks5ProxyConfigModel; + DockerContainer m_container; + QJsonObject m_content; }; #endif // PROTOCOLS_MODEL_H diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index 986ade8a..22813312 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -2,16 +2,14 @@ #include "core/api/apiDefs.h" #include "core/controllers/serverController.h" -#include "core/models/servers/apiV1ServerConfig.h" -#include "core/models/servers/apiV2ServerConfig.h" -#include "core/models/servers/selfHostedServerConfig.h" -#include "core/models/servers/serverConfig.h" #include "core/networkUtilities.h" #ifdef Q_OS_IOS #include #endif +#include "core/api/apiUtils.h" + namespace { namespace configKey @@ -37,7 +35,8 @@ ServersModel::ServersModel(std::shared_ptr settings, QObject *parent) connect(this, &ServersModel::defaultServerIndexChanged, this, &ServersModel::defaultServerNameChanged); connect(this, &ServersModel::defaultServerIndexChanged, this, [this](const int serverIndex) { - auto defaultContainer = ContainerProps::containerFromString(m_servers1.at(serverIndex)->defaultContainer); + auto defaultContainer = + ContainerProps::containerFromString(m_servers.at(serverIndex).toObject().value(config_key::defaultContainer).toString()); emit ServersModel::defaultServerDefaultContainerChanged(defaultContainer); emit ServersModel::defaultServerNameChanged(); updateDefaultServerContainersModel(); @@ -50,25 +49,28 @@ ServersModel::ServersModel(std::shared_ptr settings, QObject *parent) int ServersModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); - return static_cast(m_servers1.size()); + return static_cast(m_servers.size()); } bool ServersModel::setData(const QModelIndex &index, const QVariant &value, int role) { - if (!index.isValid() || index.row() < 0 || index.row() >= static_cast(m_servers1.size())) { + if (!index.isValid() || index.row() < 0 || index.row() >= static_cast(m_servers.size())) { return false; } - QSharedPointer serverConfig = m_servers1.at(index.row()); - ServerConfigVariant variant = ServerConfig::getServerConfigVariant(serverConfig); + QJsonObject server = m_servers.at(index.row()).toObject(); + const auto configVersion = server.value(config_key::configVersion).toInt(); switch (role) { case NameRole: { - std::visit([&value](const auto &ptr) -> void { ptr->name = value.toString(); }, variant); - serverConfig->nameOverriddenByUser = true; - - m_settings->editServer(index.row(), serverConfig->toJson()); - m_servers1.replace(index.row(), serverConfig); + if (configVersion) { + server.insert(config_key::name, value.toString()); + } 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) { emit defaultServerNameChanged(); } @@ -91,21 +93,29 @@ bool ServersModel::setData(const int index, const QVariant &value, int role) QVariant ServersModel::data(const QModelIndex &index, int role) const { - if (!index.isValid() || index.row() < 0 || index.row() >= static_cast(m_servers1.size())) { + if (!index.isValid() || index.row() < 0 || index.row() >= static_cast(m_servers.size())) { return QVariant(); } - QSharedPointer serverConfig = m_servers1.at(index.row()); - ServerConfigVariant variant = ServerConfig::getServerConfigVariant(serverConfig); - + const QJsonObject server = m_servers.at(index.row()).toObject(); + const auto apiConfig = server.value(configKey::apiConfig).toObject(); + const auto configVersion = server.value(config_key::configVersion).toInt(); switch (role) { case NameRole: { - return std::visit([](const auto &ptr) -> QString { return ptr->name; }, variant); + if (configVersion) { + return server.value(config_key::name).toString(); + } + auto name = server.value(config_key::description).toString(); + if (name.isEmpty()) { + return server.value(config_key::hostName).toString(); + } + return name; } case ServerDescriptionRole: { - return getServerDescription(index.row()); + auto description = getServerDescription(server, index.row()); + return configVersion ? description : description + server.value(config_key::hostName).toString(); } - case HostNameRole: return serverConfig->hostName; + case HostNameRole: return server.value(config_key::hostName).toString(); case CredentialsRole: return QVariant::fromValue(serverCredentials(index.row())); case CredentialsLoginRole: return serverCredentials(index.row()).userName; case IsDefaultRole: return index.row() == m_defaultServerIndex; @@ -115,28 +125,36 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const return (!credentials.userName.isEmpty() && !credentials.secretData.isEmpty()); } case ContainsAmneziaDnsRole: { - return serverConfig->dns1 == protocols::dns::amneziaDnsIp; + QString primaryDns = server.value(config_key::dns1).toString(); + return primaryDns == protocols::dns::amneziaDnsIp; } case DefaultContainerRole: { - return ContainerProps::containerFromString(serverConfig->defaultContainer); + return ContainerProps::containerFromString(server.value(config_key::defaultContainer).toString()); } case HasInstalledContainers: { return serverHasInstalledContainers(index.row()); } case IsServerFromTelegramApiRole: { - return serverConfig->type == amnezia::ServerConfigType::ApiV1; + return server.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::Telegram; } case IsServerFromGatewayApiRole: { - return serverConfig->type == amnezia::ServerConfigType::ApiV2; + return server.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway; + } + case ApiConfigRole: { + return apiConfig; } case IsCountrySelectionAvailableRole: { - return !qSharedPointerCast(serverConfig)->apiConfig.availableCountries.isEmpty(); + return !apiConfig.value(configKey::availableCountries).toArray().isEmpty(); } case ApiAvailableCountriesRole: { - return QVariant::fromValue(qSharedPointerCast(serverConfig)->apiConfig.availableCountries); + return apiConfig.value(configKey::availableCountries).toArray(); } case ApiServerCountryCodeRole: { - return qSharedPointerCast(serverConfig)->apiConfig.serverCountryCode; + return apiConfig.value(configKey::serverCountryCode).toString(); + } + case HasAmneziaDns: { + QString primaryDns = server.value(config_key::dns1).toString(); + return primaryDns == protocols::dns::amneziaDnsIp; } } @@ -152,15 +170,9 @@ QVariant ServersModel::data(const int index, int role) const void ServersModel::resetModel() { beginResetModel(); - auto servers = m_settings->serversArray(); + m_servers = m_settings->serversArray(); m_defaultServerIndex = m_settings->defaultServerIndex(); m_processedServerIndex = m_defaultServerIndex; - - for (auto server : servers) { - auto serverConfig = ServerConfig::createServerConfig(server.toObject()); - m_servers1.push_back(serverConfig); - } - endResetModel(); emit defaultServerIndexChanged(m_defaultServerIndex); } @@ -182,64 +194,58 @@ const QString ServersModel::getDefaultServerName() return qvariant_cast(data(m_defaultServerIndex, NameRole)); } -QString ServersModel::getServerDescription(const int index) const +QString ServersModel::getServerDescription(const QJsonObject &server, const int index) const { - auto serverConfig = m_servers1.at(index); - switch (serverConfig->type) { - case amnezia::ServerConfigType::ApiV1: return qSharedPointerCast(serverConfig)->description; - case amnezia::ServerConfigType::ApiV2: { - auto apiV2ServerConfig = qSharedPointerCast(serverConfig); - if (apiV2ServerConfig->apiConfig.serverCountryCode.isEmpty()) { - return apiV2ServerConfig->description; - } else { - return apiV2ServerConfig->apiConfig.serverCountryName; + const auto configVersion = server.value(config_key::configVersion).toInt(); + const auto apiConfig = server.value(configKey::apiConfig).toObject(); + + QString description; + + if (configVersion && !apiConfig.value(configKey::serverCountryCode).toString().isEmpty()) { + return apiConfig.value(configKey::serverCountryName).toString(); + } else if (configVersion) { + return server.value(config_key::description).toString(); + } else if (data(index, HasWriteAccessRole).toBool()) { + if (m_isAmneziaDnsEnabled && isAmneziaDnsContainerInstalled(index)) { + description += "Amnezia DNS | "; + } + } else { + if (data(index, HasAmneziaDns).toBool()) { + description += "Amnezia DNS | "; } } - case amnezia::ServerConfigType::SelfHosted: { - QString description; - if (data(index, HasWriteAccessRole).toBool()) { - if (m_isAmneziaDnsEnabled && isAmneziaDnsContainerInstalled(index)) { - description += "Amnezia DNS | " + serverConfig->hostName; - } - } else { - if (data(index, ContainsAmneziaDnsRole).toBool()) { - description += "Amnezia DNS | " + serverConfig->hostName; - } - } - return description; - } - } + return description; } const QString ServersModel::getDefaultServerDescriptionCollapsed() { - auto serverConfig = m_servers1.at(m_defaultServerIndex); - auto description = getServerDescription(m_defaultServerIndex); - auto containerName = ContainerProps::containerFromString(serverConfig->defaultContainer); - - if (serverConfig->type != ServerConfigType::SelfHosted) { + const QJsonObject server = m_servers.at(m_defaultServerIndex).toObject(); + const auto configVersion = server.value(config_key::configVersion).toInt(); + auto description = getServerDescription(server, m_defaultServerIndex); + if (configVersion) { return description; } - return description += ContainerProps::containerHumanNames().value(containerName) + " | " + serverConfig->hostName; + auto container = ContainerProps::containerFromString(server.value(config_key::defaultContainer).toString()); + + return description += ContainerProps::containerHumanNames().value(container) + " | " + server.value(config_key::hostName).toString(); } const QString ServersModel::getDefaultServerDescriptionExpanded() { - auto serverConfig = m_servers1.at(m_defaultServerIndex); - auto description = getServerDescription(m_defaultServerIndex); - auto containerName = ContainerProps::containerFromString(serverConfig->defaultContainer); - - if (serverConfig->type != ServerConfigType::SelfHosted) { + const QJsonObject server = m_servers.at(m_defaultServerIndex).toObject(); + const auto configVersion = server.value(config_key::configVersion).toInt(); + auto description = getServerDescription(server, m_defaultServerIndex); + if (configVersion) { return description; } - return description += serverConfig->hostName; + return description += server.value(config_key::hostName).toString(); } const int ServersModel::getServersCount() { - return m_servers1.count(); + return m_servers.count(); } bool ServersModel::hasServerWithWriteAccess() @@ -301,22 +307,18 @@ bool ServersModel::isDefaultServerHasWriteAccess() return qvariant_cast(data(m_defaultServerIndex, HasWriteAccessRole)); } -void ServersModel::addServer(const QSharedPointer &serverConfig) +void ServersModel::addServer(const QJsonObject &server) { beginResetModel(); - m_settings->addServer(serverConfig->toJson()); - auto servers = m_settings->serversArray(); - for (auto server : servers) { - auto serverConfig = ServerConfig::createServerConfig(server.toObject()); - m_servers1.push_back(serverConfig); - } + m_settings->addServer(server); + m_servers = m_settings->serversArray(); endResetModel(); } -void ServersModel::editServer(const QSharedPointer &serverConfig, const int serverIndex) +void ServersModel::editServer(const QJsonObject &server, const int serverIndex) { - m_settings->editServer(serverIndex, serverConfig->toJson()); - m_servers1[serverIndex] = serverConfig; + m_settings->editServer(serverIndex, server); + m_servers.replace(serverIndex, m_settings->serversArray().at(serverIndex)); emit dataChanged(index(serverIndex, 0), index(serverIndex, 0)); if (serverIndex == m_defaultServerIndex) { @@ -330,20 +332,30 @@ void ServersModel::editServer(const QSharedPointer &serverConfig, } } -void ServersModel::removeProcessedServer() +void ServersModel::removeServer() { - removeServer(m_processedServerIndex); + beginResetModel(); + m_settings->removeServer(m_processedServerIndex); + m_servers = m_settings->serversArray(); + + if (m_settings->defaultServerIndex() == m_processedServerIndex) { + setDefaultServerIndex(0); + } else if (m_settings->defaultServerIndex() > m_processedServerIndex) { + setDefaultServerIndex(m_settings->defaultServerIndex() - 1); + } + + if (m_settings->serversCount() == 0) { + setDefaultServerIndex(-1); + } + setProcessedServerIndex(m_defaultServerIndex); + endResetModel(); } void ServersModel::removeServer(const int serverIndex) { beginResetModel(); m_settings->removeServer(serverIndex); - auto servers = m_settings->serversArray(); - for (auto server : servers) { - auto serverConfig = ServerConfig::createServerConfig(server.toObject()); - m_servers1.push_back(serverConfig); - } + m_servers = m_settings->serversArray(); if (m_settings->defaultServerIndex() == serverIndex) { setDefaultServerIndex(0); @@ -385,6 +397,7 @@ QHash ServersModel::roleNames() const roles[IsServerFromTelegramApiRole] = "isServerFromTelegramApi"; roles[IsServerFromGatewayApiRole] = "isServerFromGatewayApi"; + roles[ApiConfigRole] = "apiConfig"; roles[IsCountrySelectionAvailableRole] = "isCountrySelectionAvailable"; roles[ApiAvailableCountriesRole] = "apiAvailableCountries"; roles[ApiServerCountryCodeRole] = "apiServerCountryCode"; @@ -393,33 +406,97 @@ QHash ServersModel::roleNames() const ServerCredentials ServersModel::serverCredentials(int index) const { - const auto serverConfig = m_servers1.at(index); - return qSharedPointerCast(serverConfig)->serverCredentials; + const QJsonObject &s = m_servers.at(index).toObject(); + + ServerCredentials credentials; + credentials.hostName = s.value(config_key::hostName).toString(); + credentials.userName = s.value(config_key::userName).toString(); + credentials.secretData = s.value(config_key::password).toString(); + credentials.port = s.value(config_key::port).toInt(); + + return credentials; } void ServersModel::updateContainersModel() { - auto containerConfigs = m_servers1.at(m_processedServerIndex)->containerConfigs; - emit containersUpdated(containerConfigs); + auto containers = m_servers.at(m_processedServerIndex).toObject().value(config_key::containers).toArray(); + emit containersUpdated(containers); } void ServersModel::updateDefaultServerContainersModel() { - auto containerConfigs = m_servers1.at(m_defaultServerIndex)->containerConfigs; - emit defaultServerContainersUpdated(containerConfigs); + auto containers = m_servers.at(m_defaultServerIndex).toObject().value(config_key::containers).toArray(); + emit defaultServerContainersUpdated(containers); } -QSharedPointer ServersModel::getServerConfig(const int serverIndex) +QJsonObject ServersModel::getServerConfig(const int serverIndex) const { - return m_servers1.at(serverIndex); + return m_servers.at(serverIndex).toObject(); +} + +void ServersModel::reloadDefaultServerContainerConfig() +{ + QJsonObject server = m_servers.at(m_defaultServerIndex).toObject(); + auto container = ContainerProps::containerFromString(server.value(config_key::defaultContainer).toString()); + + auto containers = server.value(config_key::containers).toArray(); + + auto config = m_settings->containerConfig(m_defaultServerIndex, container); + for (auto i = 0; i < containers.size(); i++) { + auto c = ContainerProps::containerFromString(containers.at(i).toObject().value(config_key::container).toString()); + if (c == container) { + containers.replace(i, config); + break; + } + } + + server.insert(config_key::containers, containers); + editServer(server, m_defaultServerIndex); +} + +void ServersModel::updateContainerConfig(const int containerIndex, const QJsonObject config) +{ + auto container = static_cast(containerIndex); + QJsonObject server = m_servers.at(m_processedServerIndex).toObject(); + + auto containers = server.value(config_key::containers).toArray(); + for (auto i = 0; i < containers.size(); i++) { + auto c = ContainerProps::containerFromString(containers.at(i).toObject().value(config_key::container).toString()); + if (c == container) { + containers.replace(i, config); + break; + } + } + + server.insert(config_key::containers, containers); + editServer(server, m_processedServerIndex); +} + +void ServersModel::addContainerConfig(const int containerIndex, const QJsonObject config) +{ + auto container = static_cast(containerIndex); + QJsonObject server = m_servers.at(m_processedServerIndex).toObject(); + + auto containers = server.value(config_key::containers).toArray(); + containers.push_back(config); + + server.insert(config_key::containers, containers); + + auto defaultContainer = server.value(config_key::defaultContainer).toString(); + if (ContainerProps::containerFromString(defaultContainer) == DockerContainer::None + && ContainerProps::containerService(container) != ServiceType::Other && ContainerProps::isSupportedByCurrentPlatform(container)) { + server.insert(config_key::defaultContainer, ContainerProps::containerToString(container)); + } + + editServer(server, m_processedServerIndex); } void ServersModel::setDefaultContainer(const int serverIndex, const int containerIndex) { auto container = static_cast(containerIndex); - auto serverConfig = m_servers1.at(serverIndex); - serverConfig->defaultContainer = ContainerProps::containerToString(container); - editServer(serverConfig, serverIndex); + QJsonObject s = m_servers.at(serverIndex).toObject(); + s.insert(config_key::defaultContainer, ContainerProps::containerToString(container)); + editServer(s, serverIndex); // check } const QString ServersModel::getDefaultServerDefaultContainerName() @@ -430,19 +507,25 @@ const QString ServersModel::getDefaultServerDefaultContainerName() ErrorCode ServersModel::removeAllContainers(const QSharedPointer &serverController) { + ErrorCode errorCode = serverController->removeAllContainers(m_settings->serverCredentials(m_processedServerIndex)); if (errorCode == ErrorCode::NoError) { - auto serverConfig = m_servers1.at(m_processedServerIndex); - serverConfig->containerConfigs.clear(); - editServer(serverConfig, m_processedServerIndex); + QJsonObject s = m_servers.at(m_processedServerIndex).toObject(); + s.insert(config_key::containers, {}); + s.insert(config_key::defaultContainer, ContainerProps::containerToString(DockerContainer::None)); + + editServer(s, m_processedServerIndex); } return errorCode; } ErrorCode ServersModel::rebootServer(const QSharedPointer &serverController) { - ErrorCode errorCode = serverController->rebootServer(m_settings->serverCredentials(m_processedServerIndex)); + + auto credentials = m_settings->serverCredentials(m_processedServerIndex); + + ErrorCode errorCode = serverController->rebootServer(credentials); return errorCode; } @@ -455,19 +538,30 @@ ErrorCode ServersModel::removeContainer(const QSharedPointer & ErrorCode errorCode = serverController->removeContainer(credentials, dockerContainer); if (errorCode == ErrorCode::NoError) { - auto serverConfig = m_servers1.at(m_processedServerIndex); - serverConfig->containerConfigs.remove(ContainerProps::containerToString(dockerContainer)); + QJsonObject server = m_servers.at(m_processedServerIndex).toObject(); - auto defaultContainer = ContainerProps::containerFromString(serverConfig->defaultContainer); - if (defaultContainer == containerIndex) { - if (serverConfig->containerConfigs.empty()) { - serverConfig->defaultContainer = ContainerProps::containerToString(DockerContainer::None); - } else { - serverConfig->defaultContainer = serverConfig->containerConfigs.begin()->containerName; + auto containers = server.value(config_key::containers).toArray(); + for (auto it = containers.begin(); it != containers.end(); it++) { + if (it->toObject().value(config_key::container).toString() == ContainerProps::containerToString(dockerContainer)) { + containers.erase(it); + break; } } - editServer(serverConfig, m_processedServerIndex); + server.insert(config_key::containers, containers); + + auto defaultContainer = ContainerProps::containerFromString(server.value(config_key::defaultContainer).toString()); + if (defaultContainer == containerIndex) { + if (containers.empty()) { + defaultContainer = DockerContainer::None; + } else { + defaultContainer = + ContainerProps::containerFromString(containers.begin()->toObject().value(config_key::container).toString()); + } + server.insert(config_key::defaultContainer, ContainerProps::containerToString(defaultContainer)); + } + + editServer(server, m_processedServerIndex); } return errorCode; } @@ -475,9 +569,7 @@ ErrorCode ServersModel::removeContainer(const QSharedPointer & void ServersModel::clearCachedProfile(const DockerContainer container) { m_settings->clearLastConnectionConfig(m_processedServerIndex, container); - auto serverConfig = ServerConfig::createServerConfig(m_settings->server(m_processedServerIndex)); - - m_servers1.replace(m_processedServerIndex, serverConfig); + m_servers.replace(m_processedServerIndex, m_settings->server(m_processedServerIndex)); if (m_processedServerIndex == m_defaultServerIndex) { updateDefaultServerContainersModel(); } @@ -486,9 +578,10 @@ void ServersModel::clearCachedProfile(const DockerContainer container) bool ServersModel::isAmneziaDnsContainerInstalled(const int serverIndex) const { - auto serverConfig = m_servers1.at(serverIndex); - for (const auto &container : serverConfig->containerConfigs) { - if (container.containerName == ContainerProps::containerToString(DockerContainer::Dns)) { + QJsonObject server = m_servers.at(serverIndex).toObject(); + auto containers = server.value(config_key::containers).toArray(); + for (auto it = containers.begin(); it != containers.end(); it++) { + if (it->toObject().value(config_key::container).toString() == ContainerProps::containerToString(DockerContainer::Dns)) { return true; } } @@ -499,16 +592,17 @@ QPair ServersModel::getDnsPair(int serverIndex) { QPair dns; - auto serverConfig = m_servers1.at(serverIndex); + const QJsonObject &server = m_servers.at(m_processedServerIndex).toObject(); + const auto containers = server.value(config_key::containers).toArray(); bool isDnsContainerInstalled = false; - for (const auto &container : serverConfig->containerConfigs) { - if (container.containerName == ContainerProps::containerToString(DockerContainer::Dns)) { + for (const QJsonValue &container : containers) { + if (ContainerProps::containerFromString(container.toObject().value(config_key::container).toString()) == DockerContainer::Dns) { isDnsContainerInstalled = true; } } - dns.first = serverConfig->dns1; - dns.second = serverConfig->dns2; + dns.first = server.value(config_key::dns1).toString(); + dns.second = server.value(config_key::dns2).toString(); if (dns.first.isEmpty() || !NetworkUtilities::checkIPv4Format(dns.first)) { if (m_isAmneziaDnsEnabled && isDnsContainerInstalled) { @@ -527,17 +621,18 @@ QPair ServersModel::getDnsPair(int serverIndex) QStringList ServersModel::getAllInstalledServicesName(const int serverIndex) { QStringList servicesName; - auto serverConfig = m_servers1.at(serverIndex); - for (const auto &container : serverConfig->containerConfigs) { - auto containerType = ContainerProps::containerFromString(container.containerName); - if (ContainerProps::containerService(containerType) == ServiceType::Other) { - if (containerType == DockerContainer::Dns) { + QJsonObject server = m_servers.at(serverIndex).toObject(); + const auto containers = server.value(config_key::containers).toArray(); + for (auto it = containers.begin(); it != containers.end(); it++) { + auto container = ContainerProps::containerFromString(it->toObject().value(config_key::container).toString()); + if (ContainerProps::containerService(container) == ServiceType::Other) { + if (container == DockerContainer::Dns) { servicesName.append("DNS"); - } else if (containerType == DockerContainer::Sftp) { + } else if (container == DockerContainer::Sftp) { servicesName.append("SFTP"); - } else if (containerType == DockerContainer::TorWebSite) { + } else if (container == DockerContainer::TorWebSite) { servicesName.append("TOR"); - } else if (containerType == DockerContainer::Socks5Proxy) { + } else if (container == DockerContainer::Socks5Proxy) { servicesName.append("SOCKS5"); } } @@ -554,8 +649,8 @@ void ServersModel::toggleAmneziaDns(bool enabled) bool ServersModel::isServerFromApiAlreadyExists(const quint16 crc) { - for (const auto &server : std::as_const(m_servers1)) { - if (static_cast(server->crc) == crc) { + for (const auto &server : std::as_const(m_servers)) { + if (static_cast(server.toObject().value(config_key::crc).toInt()) == crc) { return true; } } @@ -564,10 +659,11 @@ bool ServersModel::isServerFromApiAlreadyExists(const quint16 crc) bool ServersModel::isServerFromApiAlreadyExists(const QString &userCountryCode, const QString &serviceType, const QString &serviceProtocol) { - for (const auto &serverConfig : std::as_const(m_servers1)) { - const auto apiV2ServerConfig = qSharedPointerCast(serverConfig); - if (apiV2ServerConfig->apiConfig.userCountryCode == userCountryCode && apiV2ServerConfig->apiConfig.serviceType == serviceType - && apiV2ServerConfig->apiConfig.serviceProtocol == serviceProtocol) { + for (const auto &server : std::as_const(m_servers)) { + const auto apiConfig = server.toObject().value(configKey::apiConfig).toObject(); + if (apiConfig.value(configKey::userCountryCode).toString() == userCountryCode + && apiConfig.value(configKey::serviceType).toString() == serviceType + && apiConfig.value(configKey::serviceProtocol).toString() == serviceProtocol) { return true; } } @@ -576,14 +672,14 @@ bool ServersModel::isServerFromApiAlreadyExists(const QString &userCountryCode, bool ServersModel::serverHasInstalledContainers(const int serverIndex) const { - auto server = m_servers1.at(serverIndex); - const auto containers = server->containerConfigs; - for (const auto &container : containers) { - auto dockerContainer = ContainerProps::containerFromString(container.containerName); - if (ContainerProps::containerService(dockerContainer) == ServiceType::Vpn) { + QJsonObject server = m_servers.at(serverIndex).toObject(); + const auto containers = server.value(config_key::containers).toArray(); + for (auto it = containers.begin(); it != containers.end(); it++) { + auto container = ContainerProps::containerFromString(it->toObject().value(config_key::container).toString()); + if (ContainerProps::containerService(container) == ServiceType::Vpn) { return true; } - if (dockerContainer == DockerContainer::SSXray) { + if (container == DockerContainer::SSXray) { return true; } } @@ -628,46 +724,27 @@ bool ServersModel::setProcessedServerData(const QString &roleString, const QVari bool ServersModel::isDefaultServerDefaultContainerHasSplitTunneling() { - auto serverConfig = m_servers1.at(m_defaultServerIndex); - auto defaultContainer = ContainerProps::containerFromString(serverConfig->defaultContainer); + auto server = m_servers.at(m_defaultServerIndex).toObject(); + auto defaultContainer = ContainerProps::containerFromString(server.value(config_key::defaultContainer).toString()); - for (const auto &container : serverConfig->containerConfigs) { - if (container.containerName != serverConfig->defaultContainer) { + auto containers = server.value(config_key::containers).toArray(); + for (auto i = 0; i < containers.size(); i++) { + auto container = containers.at(i).toObject(); + if (container.value(config_key::container).toString() != ContainerProps::containerToString(defaultContainer)) { continue; } if (defaultContainer == DockerContainer::Awg || defaultContainer == DockerContainer::WireGuard) { - auto protocolConfigVariant = ProtocolConfig::getProtocolConfigVariant(container.protocolConfigs[serverConfig->defaultContainer]); - return std::visit( - [](const auto &ptr) -> bool { - if constexpr (requires { - ptr->clientProtocolConfig; - ptr->clientProtocolConfig.wireGuardData; - }) { - const auto nativeConfig = ptr->clientProtocolConfig.nativeConfig; - const auto allowedIps = ptr->clientProtocolConfig.wireGuardData.allowedIps; - - return (nativeConfig.contains("AllowedIPs") && !nativeConfig.contains("AllowedIPs = 0.0.0.0/0, ::/0")) - || (!allowedIps.isEmpty() && !allowedIps.contains("0.0.0.0/0")); - } else { - return false; - } - }, - protocolConfigVariant); + QJsonObject serverProtocolConfig = container.value(ContainerProps::containerTypeToString(defaultContainer)).toObject(); + QString clientProtocolConfigString = serverProtocolConfig.value(config_key::last_config).toString(); + QJsonObject clientProtocolConfig = QJsonDocument::fromJson(clientProtocolConfigString.toUtf8()).object(); + return (clientProtocolConfigString.contains("AllowedIPs") && !clientProtocolConfigString.contains("AllowedIPs = 0.0.0.0/0, ::/0")) + || (!clientProtocolConfig.value(config_key::allowed_ips).toArray().isEmpty() + && !clientProtocolConfig.value(config_key::allowed_ips).toArray().contains("0.0.0.0/0")); } else if (defaultContainer == DockerContainer::Cloak || defaultContainer == DockerContainer::OpenVpn || defaultContainer == DockerContainer::ShadowSocks) { - auto protocolConfigVariant = ProtocolConfig::getProtocolConfigVariant( - container.protocolConfigs[ContainerProps::containerTypeToString(DockerContainer::OpenVpn)]); - return std::visit( - [](const auto &ptr) -> bool { - if constexpr (requires { ptr->clientProtocolConfig; }) { - const auto nativeConfig = ptr->clientProtocolConfig.nativeConfig; - - return (!nativeConfig.isEmpty() && !nativeConfig.contains("redirect-gateway")); - } else { - return false; - } - }, - protocolConfigVariant); + auto serverProtocolConfig = container.value(ContainerProps::containerTypeToString(DockerContainer::OpenVpn)).toObject(); + QString clientProtocolConfigString = serverProtocolConfig.value(config_key::last_config).toString(); + return !clientProtocolConfigString.isEmpty() && !clientProtocolConfigString.contains("redirect-gateway"); } } return false; @@ -680,62 +757,67 @@ bool ServersModel::isServerFromApi(const int serverIndex) bool ServersModel::isApiKeyExpired(const int serverIndex) { - // auto serverConfig = m_servers1.at(serverIndex); - // auto apiConfig = serverConfig.value(configKey::apiConfig).toObject(); + auto serverConfig = m_servers.at(serverIndex).toObject(); + auto apiConfig = serverConfig.value(configKey::apiConfig).toObject(); - // auto publicKeyInfo = apiConfig.value(configKey::publicKeyInfo).toObject(); - // const QString expiresAt = publicKeyInfo.value(configKey::expiresAt).toString(); - // if (expiresAt.isEmpty()) { - // publicKeyInfo.insert(configKey::expiresAt, QDateTime::currentDateTimeUtc().addDays(1).toString(Qt::ISODate)); - // apiConfig.insert(configKey::publicKeyInfo, publicKeyInfo); - // serverConfig.insert(configKey::apiConfig, apiConfig); - // editServer(serverConfig, serverIndex); + auto publicKeyInfo = apiConfig.value(configKey::publicKeyInfo).toObject(); + const QString expiresAt = publicKeyInfo.value(configKey::expiresAt).toString(); + if (expiresAt.isEmpty()) { + publicKeyInfo.insert(configKey::expiresAt, QDateTime::currentDateTimeUtc().addDays(1).toString(Qt::ISODate)); + apiConfig.insert(configKey::publicKeyInfo, publicKeyInfo); + serverConfig.insert(configKey::apiConfig, apiConfig); + editServer(serverConfig, serverIndex); - // return false; - // } + return false; + } - // auto expiresAtDateTime = QDateTime::fromString(expiresAt, Qt::ISODate).toUTC(); - // if (expiresAtDateTime < QDateTime::currentDateTimeUtc()) { - // return true; - // } - // return false; + auto expiresAtDateTime = QDateTime::fromString(expiresAt, Qt::ISODate).toUTC(); + if (expiresAtDateTime < QDateTime::currentDateTimeUtc()) { + return true; + } + return false; } void ServersModel::removeApiConfig(const int serverIndex) { -// auto serverConfig = m_servers1.at(serverIndex); + auto serverConfig = getServerConfig(serverIndex); -// #ifdef Q_OS_IOS -// QString vpncName = QString("%1 (%2) %3") -// .arg(serverConfig[config_key::description].toString()) -// .arg(serverConfig[config_key::hostName].toString()) -// .arg(serverConfig[config_key::vpnproto].toString()); +#ifdef Q_OS_IOS + QString vpncName = QString("%1 (%2) %3") + .arg(serverConfig[config_key::description].toString()) + .arg(serverConfig[config_key::hostName].toString()) + .arg(serverConfig[config_key::vpnproto].toString()); -// AmneziaVPN::removeVPNC(vpncName.toStdString()); -// #endif + AmneziaVPN::removeVPNC(vpncName.toStdString()); +#endif -// serverConfig.remove(config_key::dns1); -// serverConfig.remove(config_key::dns2); -// serverConfig.remove(config_key::containers); -// serverConfig.remove(config_key::hostName); + serverConfig.remove(config_key::dns1); + serverConfig.remove(config_key::dns2); + serverConfig.remove(config_key::containers); + serverConfig.remove(config_key::hostName); -// auto apiConfig = serverConfig.value(configKey::apiConfig).toObject(); -// apiConfig.remove(configKey::publicKeyInfo); -// serverConfig.insert(configKey::apiConfig, apiConfig); + auto apiConfig = serverConfig.value(configKey::apiConfig).toObject(); + apiConfig.remove(configKey::publicKeyInfo); + serverConfig.insert(configKey::apiConfig, apiConfig); -// serverConfig.insert(config_key::defaultContainer, ContainerProps::containerToString(DockerContainer::None)); + serverConfig.insert(config_key::defaultContainer, ContainerProps::containerToString(DockerContainer::None)); -// editServer(serverConfig, serverIndex); + editServer(serverConfig, serverIndex); } const QString ServersModel::getDefaultServerImagePathCollapsed() { - // const auto server = m_servers.at(m_defaultServerIndex).toObject(); - // const auto apiConfig = server.value(configKey::apiConfig).toObject(); - // const auto countryCode = apiConfig.value(configKey::serverCountryCode).toString(); + const auto server = m_servers.at(m_defaultServerIndex).toObject(); + const auto apiConfig = server.value(configKey::apiConfig).toObject(); + const auto countryCode = apiConfig.value(configKey::serverCountryCode).toString(); - // if (countryCode.isEmpty()) { - // return ""; - // } - // return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(countryCode.toUpper()); + if (countryCode.isEmpty()) { + return ""; + } + 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 447b720e..c36b6534 100644 --- a/client/ui/models/servers_model.h +++ b/client/ui/models/servers_model.h @@ -4,7 +4,6 @@ #include #include "core/controllers/serverController.h" -#include "core/models/servers/serverConfig.h" #include "settings.h" class ServersModel : public QAbstractListModel @@ -37,7 +36,9 @@ public: ApiConfigRole, IsCountrySelectionAvailableRole, ApiAvailableCountriesRole, - ApiServerCountryCodeRole + ApiServerCountryCodeRole, + + HasAmneziaDns }; ServersModel(std::shared_ptr settings, QObject *parent = nullptr); @@ -62,6 +63,9 @@ 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); @@ -86,12 +90,16 @@ public slots: const ServerCredentials getProcessedServerCredentials(); const ServerCredentials getServerCredentials(const int index); - void addServer(const QSharedPointer &serverConfig); - void editServer(const QSharedPointer &serverConfig, const int serverIndex); - void removeProcessedServer(); + void addServer(const QJsonObject &server); + void editServer(const QJsonObject &server, const int serverIndex); + void removeServer(); void removeServer(const int serverIndex); - QSharedPointer getServerConfig(const int serverIndex); + QJsonObject getServerConfig(const int serverIndex) const; + + void reloadDefaultServerContainerConfig(); + void updateContainerConfig(const int containerIndex, const QJsonObject config); + void addContainerConfig(const int containerIndex, const QJsonObject config); void clearCachedProfile(const DockerContainer container); @@ -132,8 +140,8 @@ signals: void defaultServerNameChanged(); void defaultServerDescriptionChanged(); - void containersUpdated(const QMap &containerConfigs); - void defaultServerContainersUpdated(const QMap &containerConfigs); + void containersUpdated(const QJsonArray &containers); + void defaultServerContainersUpdated(const QJsonArray &containers); void defaultServerDefaultContainerChanged(const int containerIndex); void updateApiCountryModel(); @@ -145,13 +153,13 @@ private: void updateContainersModel(); void updateDefaultServerContainersModel(); - QString getServerDescription(const int index) const; + QString getServerDescription(const QJsonObject &server, const int index) const; bool isAmneziaDnsContainerInstalled(const int serverIndex) const; bool serverHasInstalledContainers(const int serverIndex) const; - QVector> m_servers1; + QJsonArray m_servers; std::shared_ptr m_settings; diff --git a/client/ui/qml/Components/AwgTextField.qml b/client/ui/qml/Components/AwgTextField.qml new file mode 100644 index 00000000..87b023d9 --- /dev/null +++ b/client/ui/qml/Components/AwgTextField.qml @@ -0,0 +1,15 @@ +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/Pages2/PageProtocolAwgClientSettings.qml b/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml index b8cf5f93..d97d09e8 100644 --- a/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml @@ -115,14 +115,10 @@ PageType { KeyNavigation.tab: junkPacketCountTextField.textField } - TextFieldWithHeaderType { + AwgTextField { 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) { @@ -130,19 +126,13 @@ PageType { } } - checkEmptyText: true - KeyNavigation.tab: junkPacketMinSizeTextField.textField } - TextFieldWithHeaderType { + AwgTextField { 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) { @@ -150,28 +140,144 @@ PageType { } } - checkEmptyText: true - KeyNavigation.tab: junkPacketMaxSizeTextField.textField } - TextFieldWithHeaderType { + AwgTextField { 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 } } + } - checkEmptyText: true + AwgTextField { + id: specialJunk1TextField + headerText: qsTr("I1 - First special junk packet") + textField.text: clientSpecialJunk1 + textField.validator: null + checkEmptyText: false + 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 { @@ -181,82 +287,78 @@ PageType { text: qsTr("Server settings") } - TextFieldWithHeaderType { + AwgTextField { id: portTextField - Layout.fillWidth: true - Layout.topMargin: 8 - enabled: false headerText: qsTr("Port") textField.text: port } - TextFieldWithHeaderType { + AwgTextField { id: initPacketJunkSizeTextField - Layout.fillWidth: true - Layout.topMargin: 16 - enabled: false headerText: "S1 - Init packet junk size" textField.text: serverInitPacketJunkSize } - TextFieldWithHeaderType { + AwgTextField { id: responsePacketJunkSizeTextField - Layout.fillWidth: true - Layout.topMargin: 16 - enabled: false headerText: "S2 - Response packet junk size" textField.text: serverResponsePacketJunkSize } - TextFieldWithHeaderType { - id: initPacketMagicHeaderTextField - Layout.fillWidth: true - Layout.topMargin: 16 + // 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 { + id: initPacketMagicHeaderTextField enabled: false headerText: "H1 - Init packet magic header" textField.text: serverInitPacketMagicHeader } - TextFieldWithHeaderType { + AwgTextField { id: responsePacketMagicHeaderTextField - Layout.fillWidth: true - Layout.topMargin: 16 - enabled: false headerText: "H2 - Response packet magic header" textField.text: serverResponsePacketMagicHeader } - TextFieldWithHeaderType { + AwgTextField { id: underloadPacketMagicHeaderTextField - Layout.fillWidth: true - Layout.topMargin: 16 - enabled: false headerText: "H3 - Underload packet magic header" textField.text: serverUnderloadPacketMagicHeader } - TextFieldWithHeaderType { + AwgTextField { 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 e8fd2b94..699ae724 100644 --- a/client/ui/qml/Pages2/PageProtocolAwgSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolAwgSettings.qml @@ -138,184 +138,139 @@ PageType { checkEmptyText: true } - TextFieldWithHeaderType { + AwgTextField { 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 } - TextFieldWithHeaderType { + AwgTextField { 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 } - TextFieldWithHeaderType { + AwgTextField { 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 } - TextFieldWithHeaderType { + AwgTextField { 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() - } - } } - TextFieldWithHeaderType { + AwgTextField { 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() - } - } } - TextFieldWithHeaderType { - id: initPacketMagicHeaderTextField - Layout.fillWidth: true - Layout.topMargin: 16 + // 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 { + id: initPacketMagicHeaderTextField 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 } - TextFieldWithHeaderType { + AwgTextField { 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 } - 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 { + AwgTextField { 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 } + AwgTextField { + id: transportPacketMagicHeaderTextField + headerText: qsTr("H4 - Transport packet magic header") + textField.text: serverTransportPacketMagicHeader + + textField.onEditingFinished: { + if (textField.text !== serverTransportPacketMagicHeader) { + serverTransportPacketMagicHeader = textField.text + } + } + } + + BasicButtonType { id: saveRestartButton @@ -328,6 +283,8 @@ PageType { responsePacketMagicHeaderTextField.errorText === "" && initPacketMagicHeaderTextField.errorText === "" && responsePacketJunkSizeTextField.errorText === "" && + // cookieReplyHeaderJunkTextField.errorText === "" && + // transportHeaderJunkTextField.errorText === "" && initPacketJunkSizeTextField.errorText === "" && junkPacketMaxSizeTextField.errorText === "" && junkPacketMinSizeTextField.errorText === "" && @@ -360,6 +317,13 @@ 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 7a0fafbd..8e5129b0 100644 --- a/client/ui/qml/Pages2/PageProtocolCloakSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml @@ -59,10 +59,13 @@ PageType { model: CloakConfigModel delegate: Item { - implicitWidth: listview.width - implicitHeight: col.implicitHeight + id: delegateItem property alias trafficFromField: trafficFromField + property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess() + + implicitWidth: listview.width + implicitHeight: col.implicitHeight ColumnLayout { id: col @@ -78,7 +81,6 @@ PageType { BaseHeaderType { Layout.fillWidth: true - headerText: qsTr("Cloak settings") } @@ -88,6 +90,8 @@ PageType { Layout.fillWidth: true Layout.topMargin: 32 + enabled: delegateItem.isEnabled + headerText: qsTr("Disguised as traffic from") textField.text: site @@ -104,6 +108,8 @@ PageType { } } } + + checkEmptyText: true } TextFieldWithHeaderType { @@ -112,6 +118,8 @@ PageType { Layout.fillWidth: true Layout.topMargin: 16 + enabled: delegateItem.isEnabled + headerText: qsTr("Port") textField.text: port textField.maximumLength: 5 @@ -122,6 +130,8 @@ PageType { port = textField.text } } + + checkEmptyText: true } DropDownType { @@ -129,6 +139,8 @@ PageType { Layout.fillWidth: true Layout.topMargin: 16 + enabled: delegateItem.isEnabled + descriptionText: qsTr("Cipher") headerText: qsTr("Cipher") @@ -166,25 +178,46 @@ PageType { } BasicButtonType { - id: saveRestartButton + id: saveButton Layout.fillWidth: true Layout.topMargin: 24 Layout.bottomMargin: 24 + enabled: trafficFromField.errorText === "" && + portTextField.errorText === "" + text: qsTr("Save") clickedFunc: function() { forceActiveFocus() - if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { - PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection")) - return + 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()) } - PageController.goToPage(PageEnum.PageSetupWizardInstalling); - InstallController.updateContainer(CloakConfigModel.getConfig()) + var noButtonFunction = function() { + if (!GC.isMobile()) { + saveButton.forceActiveFocus() + } + } + + showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) } + + 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 2e00d54a..62cbd1f6 100644 --- a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml @@ -58,10 +58,13 @@ PageType { model: OpenVpnConfigModel delegate: Item { - implicitWidth: listview.width - implicitHeight: col.implicitHeight + id: delegateItem property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField + property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess() + + implicitWidth: listview.width + implicitHeight: col.implicitHeight ColumnLayout { id: col @@ -77,7 +80,6 @@ PageType { BaseHeaderType { Layout.fillWidth: true - headerText: qsTr("OpenVPN settings") } @@ -87,6 +89,8 @@ PageType { Layout.fillWidth: true Layout.topMargin: 32 + enabled: delegateItem.isEnabled + headerText: qsTr("VPN address subnet") textField.text: subnetAddress @@ -97,6 +101,8 @@ PageType { subnetAddress = textField.text } } + + checkEmptyText: true } ParagraphTextType { @@ -134,7 +140,7 @@ PageType { Layout.topMargin: 40 parentFlickable: fl - enabled: isPortEditable + enabled: delegateItem.isEnabled headerText: qsTr("Port") textField.text: port @@ -146,6 +152,8 @@ PageType { port = textField.text } } + + checkEmptyText: true } SwitcherType { @@ -388,26 +396,45 @@ PageType { } BasicButtonType { - id: saveRestartButton + id: saveButton Layout.fillWidth: true Layout.topMargin: 24 Layout.bottomMargin: 24 + enabled: vpnAddressSubnetTextField.errorText === "" && + portTextField.errorText === "" + text: qsTr("Save") parentFlickable: fl - clickedFunc: function() { + onClicked: function() { forceActiveFocus() - if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { - PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection")) - return - } + 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") - PageController.goToPage(PageEnum.PageSetupWizardInstalling); - InstallController.updateContainer(OpenVpnConfigModel.getConfig()) + 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()) + } + var noButtonFunction = function() { + if (!GC.isMobile()) { + saveButton.forceActiveFocus() + } + } + showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) } + + 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 63e60dcb..92df3ec7 100644 --- a/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml @@ -57,15 +57,13 @@ 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 @@ -80,7 +78,6 @@ PageType { BaseHeaderType { Layout.fillWidth: true - headerText: qsTr("Shadowsocks settings") } @@ -90,7 +87,7 @@ PageType { Layout.fillWidth: true Layout.topMargin: 40 - enabled: isPortEditable + enabled: delegateItem.isEnabled headerText: qsTr("Port") textField.text: port @@ -102,6 +99,8 @@ PageType { port = textField.text } } + + checkEmptyText: true } DropDownType { @@ -109,7 +108,7 @@ PageType { Layout.fillWidth: true Layout.topMargin: 20 - enabled: isCipherEditable + enabled: delegateItem.isEnabled descriptionText: qsTr("Cipher") headerText: qsTr("Cipher") @@ -149,27 +148,43 @@ PageType { } BasicButtonType { - id: saveRestartButton + id: saveButton Layout.fillWidth: true Layout.topMargin: 24 Layout.bottomMargin: 24 - enabled: isPortEditable | isCipherEditable + enabled: portTextField.errorText === "" text: qsTr("Save") clickedFunc: function() { forceActiveFocus() - if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { - PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection")) - return - } + 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") - PageController.goToPage(PageEnum.PageSetupWizardInstalling); - InstallController.updateContainer(ShadowSocksConfigModel.getConfig()) + 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()) + } + var noButtonFunction = function() { + if (!GC.isMobile()) { + saveButton.forceActiveFocus() + } + } + showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) } + + 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 7b5180f3..21b35bc1 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()) { - saveRestartButton.forceActiveFocus() + saveButton.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 d22e31a2..0bcd14de 100644 --- a/client/ui/qml/Pages2/PageProtocolXraySettings.qml +++ b/client/ui/qml/Pages2/PageProtocolXraySettings.qml @@ -58,7 +58,10 @@ PageType { model: XrayConfigModel delegate: Item { + id: delegateItem + property alias focusItemId: textFieldWithHeaderType.textField + property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess() implicitWidth: listview.width implicitHeight: col.implicitHeight @@ -85,6 +88,8 @@ PageType { Layout.fillWidth: true Layout.topMargin: 32 + enabled: delegateItem.isEnabled + headerText: qsTr("Disguised as traffic from") textField.text: site @@ -101,6 +106,8 @@ PageType { } } } + + checkEmptyText: true } TextFieldWithHeaderType { @@ -130,23 +137,38 @@ PageType { Layout.topMargin: 24 Layout.bottomMargin: 24 + enabled: portTextField.errorText === "" + text: qsTr("Save") - onClicked: { + onClicked: function() { forceActiveFocus() - if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { - PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection")) - return - } + 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") - PageController.goToPage(PageEnum.PageSetupWizardInstalling); - InstallController.updateContainer(XrayConfigModel.getConfig()) - focusItem.forceActiveFocus() + 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() + } + var noButtonFunction = function() { + if (!GC.isMobile()) { + saveButton.forceActiveFocus() + } + } + showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) } - Keys.onEnterPressed: basicButton.clicked() - Keys.onReturnPressed: basicButton.clicked() + Keys.onEnterPressed: saveButton.clicked() + Keys.onReturnPressed: saveButton.clicked() } } } diff --git a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml index 93118755..75832fa6 100644 --- a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml @@ -158,6 +158,32 @@ 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 diff --git a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml index 1ffcc8cf..d6d73b20 100644 --- a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml +++ b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml @@ -82,7 +82,8 @@ PageType { Layout.rightMargin: 16 visible: false - enabled: false //SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected + enabled: false + // enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected checked: SettingsController.strictKillSwitchEnabled text: qsTr("Strict KillSwitch") diff --git a/client/ui/qml/Pages2/PageSettingsServerData.qml b/client/ui/qml/Pages2/PageSettingsServerData.qml index 995ca74b..82552958 100644 --- a/client/ui/qml/Pages2/PageSettingsServerData.qml +++ b/client/ui/qml/Pages2/PageSettingsServerData.qml @@ -260,7 +260,7 @@ PageType { LabelWithButtonType { id: labelWithButton6 - visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") + visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") && ServersModel.processedServerIsPremium Layout.fillWidth: true text: qsTr("Switch to the new Amnezia Premium subscription") @@ -273,7 +273,7 @@ PageType { } DividerType { - visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") + visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") && ServersModel.processedServerIsPremium } } } diff --git a/client/ui/qml/Pages2/PageSettingsServerProtocol.qml b/client/ui/qml/Pages2/PageSettingsServerProtocol.qml index 706ab091..fce9b2a3 100644 --- a/client/ui/qml/Pages2/PageSettingsServerProtocol.qml +++ b/client/ui/qml/Pages2/PageSettingsServerProtocol.qml @@ -101,7 +101,10 @@ PageType { clickedFunction: function() { if (isClientProtocolExists) { - ProtocolsModel.updateProtocolModel(protocolIndex) + switch (protocolIndex) { + case ProtocolEnum.WireGuard: WireGuardConfigModel.updateModel(ProtocolsModel.getConfig()); break; + case ProtocolEnum.Awg: AwgConfigModel.updateModel(ProtocolsModel.getConfig()); break; + } PageController.goToPage(clientProtocolPage); } else { PageController.showNotificationMessage(qsTr("Click the \"connect\" button to create a connection configuration")) @@ -129,7 +132,17 @@ PageType { visible: delegateContent.isServerSettingsVisible clickedFunction: function() { - ProtocolsModel.updateProtocolModel(protocolIndex) + switch (protocolIndex) { + case ProtocolEnum.OpenVpn: OpenVpnConfigModel.updateModel(ProtocolsModel.getConfig()); break; + case ProtocolEnum.ShadowSocks: ShadowSocksConfigModel.updateModel(ProtocolsModel.getConfig()); break; + case ProtocolEnum.Cloak: CloakConfigModel.updateModel(ProtocolsModel.getConfig()); break; + case ProtocolEnum.WireGuard: WireGuardConfigModel.updateModel(ProtocolsModel.getConfig()); break; + case ProtocolEnum.Awg: AwgConfigModel.updateModel(ProtocolsModel.getConfig()); break; + case ProtocolEnum.Xray: XrayConfigModel.updateModel(ProtocolsModel.getConfig()); break; + case ProtocolEnum.Sftp: SftpConfigModel.updateModel(ProtocolsModel.getConfig()); break; + case ProtocolEnum.Ipsec: Ikev2ConfigModel.updateModel(ProtocolsModel.getConfig()); break; + case ProtocolEnum.Socks5Proxy: Socks5ProxyConfigModel.updateModel(ProtocolsModel.getConfig()); break; + } PageController.goToPage(serverProtocolPage); } diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index 48f74acf..0f0976bc 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -429,6 +429,11 @@ 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 new file mode 100644 index 00000000..8cbcf6f4 Binary files /dev/null and b/deploy/DeveloperIDG2CA.cer differ diff --git a/deploy/build_macos.sh b/deploy/build_macos.sh old mode 100755 new mode 100644 index 5f6e9786..03f286fc --- a/deploy/build_macos.sh +++ b/deploy/build_macos.sh @@ -1,4 +1,15 @@ #!/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 @@ -14,10 +25,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 @@ -28,39 +39,45 @@ 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.4.3; -QIF_VERSION=4.6 +QT_VERSION=6.8.3; 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 "____________________________________" @@ -69,102 +86,159 @@ 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 -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 +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/" -if [ "${MAC_CERT_PW+x}" ]; then +if [ "${MAC_APP_CERT_PW+x}" ]; then - CERTIFICATE_P12=$DEPLOY_DIR/PrivacyTechAppleCertDeveloperId.p12 - WWDRCA=$DEPLOY_DIR/WWDRCA.cer - KEYCHAIN=amnezia.build.macos.keychain - TEMP_PASS=tmp_pass + # Path to the p12 that contains the Developer ID *Application* certificate + CERTIFICATE_P12=$DEPLOY_DIR/DeveloperIdApplicationCertificate.p12 - security create-keychain -p $TEMP_PASS $KEYCHAIN || true - security default-keychain -s $KEYCHAIN - security unlock-keychain -p $TEMP_PASS $KEYCHAIN + # 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 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 + # Show available signing identities (useful for debugging) + security find-identity -p codesigning || true echo "Signing App bundle..." - /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 + /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 - 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..." -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 +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 -chmod a+x $INSTALLER_DATA_DIR/post_install.sh $INSTALLER_DATA_DIR/post_uninstall.sh +mkdir -p "$PKG_ROOT/Applications" "$SCRIPTS_DIR" "$RESOURCES_DIR" "$UNINSTALL_SCRIPTS_DIR" -cd $BUNDLE_DIR -tar czf $INSTALLER_DATA_DIR/$APP_NAME.tar.gz ./ +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" -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 +cat > "$SCRIPTS_DIR/postinstall" <<'EOS' +#!/bin/bash +SCRIPT_DIR="$(dirname "$0")" +bash "$SCRIPT_DIR/post_install.sh" +exit 0 +EOS -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 +chmod +x "$SCRIPTS_DIR"/* +chmod +x "$UNINSTALL_SCRIPTS_DIR"/* +chmod +x "$RESOURCES_DIR/scripts"/* +cp "$PROJECT_DIR/LICENSE" "$RESOURCES_DIR/LICENSE" - 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 +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" fi -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 +if [ "${MAC_INSTALL_CERT_PW+x}" ]; then + /usr/bin/codesign --verify -vvvv "$FINAL_PKG" || true + spctl -a -vvvv "$FINAL_PKG" || true fi -echo "Finished, artifact is $DMG_FILENAME" +# 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 -# restore keychain -security default-keychain -s login.keychain +# 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" diff --git a/deploy/data/macos/check_install.sh b/deploy/data/macos/check_install.sh new file mode 100755 index 00000000..adf63550 --- /dev/null +++ b/deploy/data/macos/check_install.sh @@ -0,0 +1,5 @@ +#!/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 new file mode 100755 index 00000000..e7a6f7e0 --- /dev/null +++ b/deploy/data/macos/check_uninstall.sh @@ -0,0 +1,5 @@ +#!/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 new file mode 100644 index 00000000..c0a1dc68 --- /dev/null +++ b/deploy/data/macos/distribution.xml @@ -0,0 +1,17 @@ + + + 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 new file mode 100644 index 00000000..cf8932b9 --- /dev/null +++ b/deploy/data/macos/distribution_uninstall.xml @@ -0,0 +1,13 @@ + + Uninstall AmneziaVPN + + + + + + + + + + AmneziaVPN_uninstall_component.pkg + diff --git a/deploy/data/macos/post_install.sh b/deploy/data/macos/post_install.sh index acd3f93f..053c8e13 100755 --- a/deploy/data/macos/post_install.sh +++ b/deploy/data/macos/post_install.sh @@ -7,29 +7,42 @@ LOG_FOLDER=/var/log/$APP_NAME LOG_FILE="$LOG_FOLDER/post-install.log" APP_PATH=/Applications/$APP_NAME.app -if launchctl list "$APP_NAME-service" &> /dev/null; then - launchctl unload $LAUNCH_DAEMONS_PLIST_NAME - rm -f $LAUNCH_DAEMONS_PLIST_NAME +# 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 -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/ +if launchctl list "$APP_NAME-service" &> /dev/null; then + 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/" rm -rf $LOG_FOLDER mkdir -p $LOG_FOLDER echo "`date` Script started" > $LOG_FILE -killall -9 $APP_NAME-service 2>> $LOG_FILE +echo "Requesting ${APP_NAME} to quit gracefully" >> "$LOG_FILE" +osascript -e 'tell application "AmneziaVPN" to quit' -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 +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 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 de7846db..d6c5cdbd 100755 --- a/deploy/data/macos/post_uninstall.sh +++ b/deploy/data/macos/post_uninstall.sh @@ -9,6 +9,19 @@ 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" @@ -32,3 +45,40 @@ 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 new file mode 100644 index 00000000..f5b8bb63 --- /dev/null +++ b/deploy/data/macos/uninstall_conclusion.html @@ -0,0 +1,7 @@ + +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 new file mode 100644 index 00000000..9f3d97cb --- /dev/null +++ b/deploy/data/macos/uninstall_welcome.html @@ -0,0 +1,7 @@ + +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/installer/config.cmake b/deploy/installer/config.cmake index 13f09986..3c33a33c 100644 --- a/deploy/installer/config.cmake +++ b/deploy/installer/config.cmake @@ -4,11 +4,6 @@ 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 2a53074e..03ab570c 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=@CMAKE_PROJECT_VERSION@ +Version=1.0 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 deleted file mode 100644 index 3888d08d..00000000 --- a/deploy/installer/config/macos.xml.in +++ /dev/null @@ -1,27 +0,0 @@ - - - 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 c44bd6a2..d0cba03a 100644 --- a/service/server/killswitch.cpp +++ b/service/server/killswitch.cpp @@ -192,7 +192,14 @@ bool KillSwitch::addAllowedRange(const QStringList &ranges) { bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) { #ifdef Q_OS_WIN InterfaceConfig config; - config.m_dnsServer = configStr.value(amnezia::config_key::dns1).toString(); + + 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_serverPublicKey = "openvpn"; config.m_serverIpv4Gateway = configStr.value("vpnGateway").toString(); config.m_serverIpv4AddrIn = configStr.value("vpnServer").toString(); @@ -255,6 +262,9 @@ 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 @@ -304,8 +314,14 @@ 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()); - dnsServers.append(configStr.value(amnezia::config_key::dns2).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("127.0.0.1"); dnsServers.append("127.0.0.53"); @@ -342,7 +358,11 @@ bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIn QStringList dnsServers; dnsServers.append(configStr.value(amnezia::config_key::dns1).toString()); - dnsServers.append(configStr.value(amnezia::config_key::dns2).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()); + } for (auto dns : configStr.value(amnezia::config_key::allowedDnsServers).toArray()) { if (!dns.isString()) {