From 548959752ca6e346fecb2a8745ff1b58e58e4d6e Mon Sep 17 00:00:00 2001 From: root Date: Wed, 14 Feb 2024 20:34:13 +0700 Subject: [PATCH 01/65] rename interface and config file name to awg0 change base docker image to amneziavpn/amneziawg-go:latest --- client/server_scripts/awg/Dockerfile | 2 +- client/server_scripts/awg/configure_container.sh | 8 ++++---- client/server_scripts/awg/start.sh | 14 +++++++------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/client/server_scripts/awg/Dockerfile b/client/server_scripts/awg/Dockerfile index 8c536fc7..851d090c 100644 --- a/client/server_scripts/awg/Dockerfile +++ b/client/server_scripts/awg/Dockerfile @@ -1,4 +1,4 @@ -FROM amneziavpn/amnezia-wg:latest +FROM amneziavpn/amneziawg-go:latest LABEL maintainer="AmneziaVPN" diff --git a/client/server_scripts/awg/configure_container.sh b/client/server_scripts/awg/configure_container.sh index 322cc38f..10be8d21 100644 --- a/client/server_scripts/awg/configure_container.sh +++ b/client/server_scripts/awg/configure_container.sh @@ -1,15 +1,15 @@ mkdir -p /opt/amnezia/awg cd /opt/amnezia/awg -WIREGUARD_SERVER_PRIVATE_KEY=$(wg genkey) +WIREGUARD_SERVER_PRIVATE_KEY=$(awg genkey) echo $WIREGUARD_SERVER_PRIVATE_KEY > /opt/amnezia/awg/wireguard_server_private_key.key -WIREGUARD_SERVER_PUBLIC_KEY=$(echo $WIREGUARD_SERVER_PRIVATE_KEY | wg pubkey) +WIREGUARD_SERVER_PUBLIC_KEY=$(echo $WIREGUARD_SERVER_PRIVATE_KEY | awg pubkey) echo $WIREGUARD_SERVER_PUBLIC_KEY > /opt/amnezia/awg/wireguard_server_public_key.key -WIREGUARD_PSK=$(wg genpsk) +WIREGUARD_PSK=$(awg genpsk) echo $WIREGUARD_PSK > /opt/amnezia/awg/wireguard_psk.key -cat > /opt/amnezia/awg/wg0.conf < /opt/amnezia/awg/awg0.conf < Date: Wed, 21 Feb 2024 19:06:16 +0500 Subject: [PATCH 02/65] updated the paths to awg files and interfaces to match the new docker container --- client/configurators/wireguard_configurator.cpp | 8 ++++++-- client/configurators/wireguard_configurator.h | 3 +++ client/protocols/protocols_defs.h | 9 ++++++++- client/ui/models/clientManagementModel.cpp | 14 +++++++++----- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/client/configurators/wireguard_configurator.cpp b/client/configurators/wireguard_configurator.cpp index 8bfd5e75..809d8550 100644 --- a/client/configurators/wireguard_configurator.cpp +++ b/client/configurators/wireguard_configurator.cpp @@ -33,6 +33,10 @@ WireguardConfigurator::WireguardConfigurator(std::shared_ptr settings, m_protocolName = m_isAwg ? config_key::awg : config_key::wireguard; m_defaultPort = m_isAwg ? protocols::wireguard::defaultPort : protocols::awg::defaultPort; + + m_interfaceName = m_isAwg ? protocols::awg::interfaceName : protocols::wireguard::interfaceName; + m_wgBinaryName = m_isAwg ? protocols::awg::wgBinaryName : protocols::wireguard::wgBinaryName; + m_wgQuickBinaryName = m_isAwg ? protocols::awg::wgQuickBinaryName : protocols::wireguard::wgQuickBinaryName; } WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys() @@ -167,8 +171,8 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon return connData; } - QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'") - .arg(m_serverConfigPath); + QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c '%4 syncconf %2 <(%3 strip %1)'") + .arg(m_serverConfigPath, m_interfaceName, m_wgQuickBinaryName, m_wgBinaryName); e = serverController.runScript( credentials, serverController.replaceVars(script, serverController.genVarsForScript(credentials, container))); diff --git a/client/configurators/wireguard_configurator.h b/client/configurators/wireguard_configurator.h index d2422981..d1180637 100644 --- a/client/configurators/wireguard_configurator.h +++ b/client/configurators/wireguard_configurator.h @@ -44,6 +44,9 @@ private: amnezia::ProtocolScriptType m_configTemplate; QString m_protocolName; QString m_defaultPort; + QString m_interfaceName; + QString m_wgBinaryName; + QString m_wgQuickBinaryName; }; #endif // WIREGUARD_CONFIGURATOR_H diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index f83a0067..c88bad15 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -152,6 +152,9 @@ namespace amnezia constexpr char serverPublicKeyPath[] = "/opt/amnezia/wireguard/wireguard_server_public_key.key"; constexpr char serverPskKeyPath[] = "/opt/amnezia/wireguard/wireguard_psk.key"; + constexpr char interfaceName[] = "wg0"; + constexpr char wgBinaryName[] = "wg"; + constexpr char wgQuickBinaryName[] = "wg-quick"; } namespace sftp @@ -164,7 +167,7 @@ namespace amnezia { constexpr char defaultPort[] = "55424"; - constexpr char serverConfigPath[] = "/opt/amnezia/awg/wg0.conf"; + constexpr char serverConfigPath[] = "/opt/amnezia/awg/awg0.conf"; constexpr char serverPublicKeyPath[] = "/opt/amnezia/awg/wireguard_server_public_key.key"; constexpr char serverPskKeyPath[] = "/opt/amnezia/awg/wireguard_psk.key"; @@ -177,6 +180,10 @@ namespace amnezia constexpr char defaultResponsePacketMagicHeader[] = "3288052141"; constexpr char defaultTransportPacketMagicHeader[] = "2528465083"; constexpr char defaultUnderloadPacketMagicHeader[] = "1766607858"; + + constexpr char interfaceName[] = "awg0"; + constexpr char wgBinaryName[] = "awg"; + constexpr char wgQuickBinaryName[] = "awg-quick"; } } // namespace protocols diff --git a/client/ui/models/clientManagementModel.cpp b/client/ui/models/clientManagementModel.cpp index 7c81c80e..0ea55fd3 100644 --- a/client/ui/models/clientManagementModel.cpp +++ b/client/ui/models/clientManagementModel.cpp @@ -167,8 +167,8 @@ ErrorCode ClientManagementModel::getWireGuardClients(ServerController &serverCon { ErrorCode error = ErrorCode::NoError; - const QString wireGuardConfigFile = - QString("opt/amnezia/%1/wg0.conf").arg(container == DockerContainer::WireGuard ? "wireguard" : "awg"); + const QString wireGuardConfigFile = DockerContainer::WireGuard ? amnezia::protocols::wireguard::serverConfigPath + : amnezia::protocols::awg::serverConfigPath; const QString wireguardConfigString = serverController.getTextFileFromContainer(container, credentials, wireGuardConfigFile, &error); if (error != ErrorCode::NoError) { @@ -379,8 +379,8 @@ ErrorCode ClientManagementModel::revokeWireGuard(const int row, const DockerCont ErrorCode error; ServerController serverController(m_settings); - const QString wireGuardConfigFile = - QString("/opt/amnezia/%1/wg0.conf").arg(container == DockerContainer::WireGuard ? "wireguard" : "awg"); + const QString wireGuardConfigFile = DockerContainer::WireGuard ? amnezia::protocols::wireguard::serverConfigPath + : amnezia::protocols::awg::serverConfigPath; const QString wireguardConfigString = serverController.getTextFileFromContainer(container, credentials, wireGuardConfigFile, &error); if (error != ErrorCode::NoError) { @@ -425,7 +425,11 @@ ErrorCode ClientManagementModel::revokeWireGuard(const int row, const DockerCont return error; } - const QString script = "sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'"; + QString interfaceName = DockerContainer::WireGuard ? protocols::wireguard::interfaceName : protocols::awg::interfaceName; + QString wgBinaryName = DockerContainer::WireGuard ? protocols::wireguard::wgBinaryName : protocols::awg::wgBinaryName; + QString wgQuickBinaryName = DockerContainer::WireGuard ? protocols::wireguard::wgQuickBinaryName : protocols::awg::wgQuickBinaryName; + QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c '%4 syncconf %2 <(%3 strip %1)'") + .arg(wireGuardConfigFile, interfaceName, wgQuickBinaryName, wgBinaryName); error = serverController.runScript( credentials, serverController.replaceVars(script.arg(wireGuardConfigFile), From 10933ce4660a4c3c32d0357252e06ea46a8a14e8 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Sat, 24 Feb 2024 14:34:47 +0500 Subject: [PATCH 03/65] added backward compatibility for the old awg container --- .../configurators/wireguard_configurator.cpp | 34 +++++++++++----- client/core/controllers/serverController.cpp | 32 ++++++++++++++- client/core/controllers/serverController.h | 2 + client/ui/models/clientManagementModel.cpp | 40 +++++++++++++------ 4 files changed, 85 insertions(+), 23 deletions(-) diff --git a/client/configurators/wireguard_configurator.cpp b/client/configurators/wireguard_configurator.cpp index 809d8550..440a4386 100644 --- a/client/configurators/wireguard_configurator.cpp +++ b/client/configurators/wireguard_configurator.cpp @@ -13,23 +13,22 @@ #include #include "containers/containers_defs.h" +#include "core/controllers/serverController.h" #include "core/scripts_registry.h" #include "core/server_defs.h" -#include "core/controllers/serverController.h" #include "settings.h" #include "utilities.h" WireguardConfigurator::WireguardConfigurator(std::shared_ptr settings, bool isAwg, QObject *parent) : ConfiguratorBase(settings, parent), m_isAwg(isAwg) { - m_serverConfigPath = m_isAwg ? amnezia::protocols::awg::serverConfigPath - : amnezia::protocols::wireguard::serverConfigPath; - m_serverPublicKeyPath = m_isAwg ? amnezia::protocols::awg::serverPublicKeyPath - : amnezia::protocols::wireguard::serverPublicKeyPath; - m_serverPskKeyPath = m_isAwg ? amnezia::protocols::awg::serverPskKeyPath - : amnezia::protocols::wireguard::serverPskKeyPath; - m_configTemplate = m_isAwg ? ProtocolScriptType::awg_template - : ProtocolScriptType::wireguard_template; + m_serverConfigPath = + m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath; + m_serverPublicKeyPath = + m_isAwg ? amnezia::protocols::awg::serverPublicKeyPath : amnezia::protocols::wireguard::serverPublicKeyPath; + m_serverPskKeyPath = + m_isAwg ? amnezia::protocols::awg::serverPskKeyPath : amnezia::protocols::wireguard::serverPskKeyPath; + m_configTemplate = m_isAwg ? ProtocolScriptType::awg_template : ProtocolScriptType::wireguard_template; m_protocolName = m_isAwg ? config_key::awg : config_key::wireguard; m_defaultPort = m_isAwg ? protocols::wireguard::defaultPort : protocols::awg::defaultPort; @@ -88,6 +87,20 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon ErrorCode e = ErrorCode::NoError; ServerController serverController(m_settings); + if (container == DockerContainer::Awg) { + if (serverController.isNewAwgContainer(credentials)) { + m_serverConfigPath = amnezia::protocols::awg::serverConfigPath; + m_interfaceName = protocols::awg::interfaceName; + m_wgBinaryName = protocols::awg::wgBinaryName; + m_wgQuickBinaryName = protocols::awg::wgQuickBinaryName; + } else { + m_serverConfigPath = "/opt/amnezia/awg/wg0.conf"; + m_interfaceName = protocols::wireguard::interfaceName; + m_wgBinaryName = protocols::wireguard::wgBinaryName; + m_wgQuickBinaryName = protocols::wireguard::wgQuickBinaryName; + } + } + // Get list of already created clients (only IP addresses) QString nextIpNumber; { @@ -181,7 +194,8 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon } QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &credentials, DockerContainer container, - const QJsonObject &containerConfig, QString &clientId, ErrorCode *errorCode) + const QJsonObject &containerConfig, QString &clientId, + ErrorCode *errorCode) { ServerController serverController(m_settings); QString scriptData = amnezia::scriptData(m_configTemplate, container); diff --git a/client/core/controllers/serverController.cpp b/client/core/controllers/serverController.cpp index 9a170a85..736f43ac 100644 --- a/client/core/controllers/serverController.cpp +++ b/client/core/controllers/serverController.cpp @@ -855,7 +855,16 @@ ErrorCode ServerController::getAlreadyInstalledContainers(const ServerCredential containerConfig.insert(config_key::transport_proto, transportProto); if (protocol == Proto::Awg) { - QString serverConfig = getTextFileFromContainer(container, credentials, protocols::awg::serverConfigPath, &errorCode); + QString serverConfigPath; + if (container == DockerContainer::Awg) { + if (isNewAwgContainer(credentials)) { + serverConfigPath = amnezia::protocols::awg::serverConfigPath; + } else { + serverConfigPath = "/opt/amnezia/awg/wg0.conf"; + } + } + + QString serverConfig = getTextFileFromContainer(container, credentials, serverConfigPath, &errorCode); QMap serverConfigMap; auto serverConfigLines = serverConfig.split("\n"); @@ -960,3 +969,24 @@ ErrorCode ServerController::getDecryptedPrivateKey(const ServerCredentials &cred auto error = m_sshClient.getDecryptedPrivateKey(credentials, decryptedPrivateKey, callback); return error; } + +bool ServerController::isNewAwgContainer(const ServerCredentials &credentials) +{ + QString stdOut; + auto cbReadStdOut = [&](const QString &data, libssh::Client &) { + stdOut += data + "\n"; + return ErrorCode::NoError; + }; + + auto cbReadStdErr = [&](const QString &data, libssh::Client &) { + stdOut += data + "\n"; + return ErrorCode::NoError; + }; + + QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'type awg'"); + + runScript(credentials, replaceVars(script, genVarsForScript(credentials, DockerContainer::Awg)), cbReadStdOut, cbReadStdErr); + + return stdOut.contains("/usr/bin/awg"); + +} diff --git a/client/core/controllers/serverController.h b/client/core/controllers/serverController.h index 16569dbb..f3db7602 100644 --- a/client/core/controllers/serverController.h +++ b/client/core/controllers/serverController.h @@ -62,6 +62,8 @@ public: ErrorCode getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey, const std::function &callback); + bool isNewAwgContainer(const ServerCredentials &credentials); + private: ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container); ErrorCode prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, diff --git a/client/ui/models/clientManagementModel.cpp b/client/ui/models/clientManagementModel.cpp index 0ea55fd3..5f543cde 100644 --- a/client/ui/models/clientManagementModel.cpp +++ b/client/ui/models/clientManagementModel.cpp @@ -10,7 +10,8 @@ namespace { Logger logger("ClientManagementModel"); - namespace configKey { + namespace configKey + { constexpr char clientId[] = "clientId"; constexpr char clientName[] = "clientName"; constexpr char container[] = "container"; @@ -61,7 +62,6 @@ void ClientManagementModel::migration(const QByteArray &clientsTableString) m_clientsTable.push_back(client); } - } ErrorCode ClientManagementModel::updateModel(DockerContainer container, ServerCredentials credentials) @@ -121,7 +121,8 @@ ErrorCode ClientManagementModel::updateModel(DockerContainer container, ServerCr return error; } -ErrorCode ClientManagementModel::getOpenVpnClients(ServerController &serverController, DockerContainer container, ServerCredentials credentials, int &count) +ErrorCode ClientManagementModel::getOpenVpnClients(ServerController &serverController, DockerContainer container, + ServerCredentials credentials, int &count) { ErrorCode error = ErrorCode::NoError; QString stdOut; @@ -163,7 +164,8 @@ ErrorCode ClientManagementModel::getOpenVpnClients(ServerController &serverContr return error; } -ErrorCode ClientManagementModel::getWireGuardClients(ServerController &serverController, DockerContainer container, ServerCredentials credentials, int &count) +ErrorCode ClientManagementModel::getWireGuardClients(ServerController &serverController, DockerContainer container, + ServerCredentials credentials, int &count) { ErrorCode error = ErrorCode::NoError; @@ -314,13 +316,16 @@ ErrorCode ClientManagementModel::revokeClient(const int row, const DockerContain QJsonArray containers = server.value(config_key::containers).toArray(); for (auto i = 0; i < containers.size(); i++) { auto containerConfig = containers.at(i).toObject(); - auto containerType = ContainerProps::containerFromString(containerConfig.value(config_key::container).toString()); + auto containerType = + ContainerProps::containerFromString(containerConfig.value(config_key::container).toString()); if (containerType == container) { QJsonObject protocolConfig; if (container == DockerContainer::ShadowSocks || container == DockerContainer::Cloak) { - protocolConfig = containerConfig.value(ContainerProps::containerTypeToString(DockerContainer::OpenVpn)).toObject(); + protocolConfig = + containerConfig.value(ContainerProps::containerTypeToString(DockerContainer::OpenVpn)).toObject(); } else { - protocolConfig = containerConfig.value(ContainerProps::containerTypeToString(containerType)).toObject(); + protocolConfig = + containerConfig.value(ContainerProps::containerTypeToString(containerType)).toObject(); } if (protocolConfig.value(config_key::last_config).toString().contains(clientId)) { @@ -379,8 +384,17 @@ ErrorCode ClientManagementModel::revokeWireGuard(const int row, const DockerCont ErrorCode error; ServerController serverController(m_settings); - const QString wireGuardConfigFile = DockerContainer::WireGuard ? amnezia::protocols::wireguard::serverConfigPath - : amnezia::protocols::awg::serverConfigPath; + QString wireGuardConfigFile; + if (container == DockerContainer::Awg) { + if (serverController.isNewAwgContainer(credentials)) { + wireGuardConfigFile = amnezia::protocols::awg::serverConfigPath; + } else { + wireGuardConfigFile = "/opt/amnezia/awg/wg0.conf"; + } + } else { + wireGuardConfigFile = amnezia::protocols::wireguard::serverConfigPath; + } + const QString wireguardConfigString = serverController.getTextFileFromContainer(container, credentials, wireGuardConfigFile, &error); if (error != ErrorCode::NoError) { @@ -425,9 +439,11 @@ ErrorCode ClientManagementModel::revokeWireGuard(const int row, const DockerCont return error; } - QString interfaceName = DockerContainer::WireGuard ? protocols::wireguard::interfaceName : protocols::awg::interfaceName; - QString wgBinaryName = DockerContainer::WireGuard ? protocols::wireguard::wgBinaryName : protocols::awg::wgBinaryName; - QString wgQuickBinaryName = DockerContainer::WireGuard ? protocols::wireguard::wgQuickBinaryName : protocols::awg::wgQuickBinaryName; + QString interfaceName = + DockerContainer::WireGuard ? protocols::wireguard::interfaceName : protocols::awg::interfaceName; + QString wgBinaryName = DockerContainer::WireGuard ? protocols::wireguard::wgBinaryName : protocols::awg::wgBinaryName; + QString wgQuickBinaryName = + DockerContainer::WireGuard ? protocols::wireguard::wgQuickBinaryName : protocols::awg::wgQuickBinaryName; QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c '%4 syncconf %2 <(%3 strip %1)'") .arg(wireGuardConfigFile, interfaceName, wgQuickBinaryName, wgBinaryName); error = serverController.runScript( From f1b045f8a89f050e915368cedc01aea73b5a0fb6 Mon Sep 17 00:00:00 2001 From: Nethius Date: Sun, 30 Mar 2025 12:53:26 +0700 Subject: [PATCH 04/65] fixed selecting the default button on PageSetupWizardEasy (#1502) --- client/ui/qml/Pages2/PageSetupWizardEasy.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ui/qml/Pages2/PageSetupWizardEasy.qml b/client/ui/qml/Pages2/PageSetupWizardEasy.qml index 353eeb32..ae04f635 100644 --- a/client/ui/qml/Pages2/PageSetupWizardEasy.qml +++ b/client/ui/qml/Pages2/PageSetupWizardEasy.qml @@ -78,7 +78,7 @@ PageType { height: containers.contentItem.height spacing: 16 - currentIndex: 1 + currentIndex: 0 clip: true interactive: false model: proxyContainersModel From 9e1298550fcf241c62a032a4b521743b0aa76c9f Mon Sep 17 00:00:00 2001 From: lunardunno <126363523+lunardunno@users.noreply.github.com> Date: Tue, 1 Apr 2025 03:04:55 +0400 Subject: [PATCH 05/65] Linux version to stdOut Print the Linux version to stdOut for subsequent checking by the server controller. --- client/server_scripts/install_docker.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/server_scripts/install_docker.sh b/client/server_scripts/install_docker.sh index 619b08d6..8e60acb5 100644 --- a/client/server_scripts/install_docker.sh +++ b/client/server_scripts/install_docker.sh @@ -20,4 +20,5 @@ if [ "$(systemctl is-active docker)" != "active" ]; then \ sleep 5; sudo systemctl start docker; sleep 5;\ fi;\ if ! command -v sudo > /dev/null 2>&1; then echo "Failed to install sudo, command not found"; exit 1; fi;\ -docker --version +docker --version;\ +uname -sr From d61e2bc8f60f357565360813324cca8cef8f46c9 Mon Sep 17 00:00:00 2001 From: lunardunno <126363523+lunardunno@users.noreply.github.com> Date: Tue, 1 Apr 2025 03:11:51 +0400 Subject: [PATCH 06/65] revert change: Linux versions of stdOut --- client/server_scripts/install_docker.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/server_scripts/install_docker.sh b/client/server_scripts/install_docker.sh index 8e60acb5..619b08d6 100644 --- a/client/server_scripts/install_docker.sh +++ b/client/server_scripts/install_docker.sh @@ -20,5 +20,4 @@ if [ "$(systemctl is-active docker)" != "active" ]; then \ sleep 5; sudo systemctl start docker; sleep 5;\ fi;\ if ! command -v sudo > /dev/null 2>&1; then echo "Failed to install sudo, command not found"; exit 1; fi;\ -docker --version;\ -uname -sr +docker --version From ef8fb89eb35c99351d77cf96247a48421955e69d Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Mon, 7 Apr 2025 23:30:11 +0800 Subject: [PATCH 07/65] feature: warning when importing openvpn configurations --- client/ui/controllers/importController.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/client/ui/controllers/importController.cpp b/client/ui/controllers/importController.cpp index 4ca12e21..622d8b3a 100644 --- a/client/ui/controllers/importController.cpp +++ b/client/ui/controllers/importController.cpp @@ -56,7 +56,7 @@ namespace } else if ((config.contains(xrayConfigPatternInbound)) && (config.contains(xrayConfigPatternOutbound))) { return ConfigTypes::Xray; } else if (config.contains(openVpnConfigPatternCli) - && (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) { + && (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) { return ConfigTypes::OpenVpn; } return ConfigTypes::Invalid; @@ -658,6 +658,7 @@ void ImportController::checkForMaliciousStrings(const QJsonObject &serverConfig) if ((containerName == ContainerProps::containerToString(DockerContainer::OpenVpn)) || (containerName == ContainerProps::containerToString(DockerContainer::Cloak)) || (containerName == ContainerProps::containerToString(DockerContainer::ShadowSocks))) { + QString protocolConfig = containerConfig[ProtocolProps::protoToString(Proto::OpenVpn)].toObject()[config_key::last_config].toString(); QString protocolConfigJson = QJsonDocument::fromJson(protocolConfig.toUtf8()).object()[config_key::config].toString(); @@ -679,8 +680,11 @@ void ImportController::checkForMaliciousStrings(const QJsonObject &serverConfig) } } + m_maliciousWarningText = tr("This configuration contains an OpenVPN setup. OpenVPN configurations can include malicious " + "scripts, so only add it if you fully trust the provider of this config. "); + if (maliciousStrings.size() >= dangerousTagsMaxCount) { - m_maliciousWarningText = tr("In the imported configuration, potentially dangerous lines were found:"); + m_maliciousWarningText.push_back(tr("
In the imported configuration, potentially dangerous lines were found:")); for (const auto &string : maliciousStrings) { m_maliciousWarningText.push_back(QString("
%1").arg(string)); } From 27cb17c640c42a59d8846cd2f44b7e5615e2db91 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Mon, 7 Apr 2025 23:35:24 +0800 Subject: [PATCH 08/65] chore: clear warning text before extract --- client/ui/controllers/importController.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/ui/controllers/importController.cpp b/client/ui/controllers/importController.cpp index 622d8b3a..be66d8f3 100644 --- a/client/ui/controllers/importController.cpp +++ b/client/ui/controllers/importController.cpp @@ -94,6 +94,8 @@ bool ImportController::extractConfigFromFile(const QString &fileName) bool ImportController::extractConfigFromData(QString data) { + m_maliciousWarningText.clear(); + QString config = data; QString prefix; QString errormsg; From 76fe203767a371922b34bbe18ea751971674e4ca Mon Sep 17 00:00:00 2001 From: pokamest Date: Mon, 7 Apr 2025 18:05:08 +0100 Subject: [PATCH 09/65] Update go version in actions to 1.24 --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 35e740b0..23df2d8f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -190,7 +190,7 @@ jobs: - name: 'Install go' uses: actions/setup-go@v5 with: - go-version: '1.22.1' + go-version: '1.24' cache: false - name: 'Setup gomobile' From 9d571a4c7152fa1fb703c0a785a051094c314594 Mon Sep 17 00:00:00 2001 From: Nethius Date: Tue, 8 Apr 2025 12:07:31 +0700 Subject: [PATCH 10/65] feature: new mirrors support (#1519) --- client/ui/models/languageModel.cpp | 18 ++++++++---------- client/ui/models/languageModel.h | 2 +- client/ui/qml/Components/AdLabel.qml | 2 +- client/ui/qml/Pages2/PageSettingsAbout.qml | 2 +- .../qml/Pages2/PageSetupWizardCredentials.qml | 2 +- 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/client/ui/models/languageModel.cpp b/client/ui/models/languageModel.cpp index 0041fdd0..09755e06 100644 --- a/client/ui/models/languageModel.cpp +++ b/client/ui/models/languageModel.cpp @@ -1,13 +1,11 @@ #include "languageModel.h" -LanguageModel::LanguageModel(std::shared_ptr settings, QObject *parent) - : m_settings(settings), QAbstractListModel(parent) +LanguageModel::LanguageModel(std::shared_ptr settings, QObject *parent) : m_settings(settings), QAbstractListModel(parent) { QMetaEnum metaEnum = QMetaEnum::fromType(); for (int i = 0; i < metaEnum.keyCount(); i++) { - m_availableLanguages.push_back( - LanguageModelData {getLocalLanguageName(static_cast(i)), - static_cast(i) }); + m_availableLanguages.push_back(LanguageModelData { getLocalLanguageName(static_cast(i)), + static_cast(i) }); } } @@ -50,8 +48,7 @@ QString LanguageModel::getLocalLanguageName(const LanguageSettings::AvailableLan case LanguageSettings::AvailableLanguageEnum::Burmese: strLanguage = "မြန်မာဘာသာ"; break; case LanguageSettings::AvailableLanguageEnum::Urdu: strLanguage = "اُرْدُوْ"; break; case LanguageSettings::AvailableLanguageEnum::Hindi: strLanguage = "हिन्दी"; break; - default: - break; + default: break; } return strLanguage; @@ -104,11 +101,12 @@ QString LanguageModel::getCurrentLanguageName() return m_availableLanguages[getCurrentLanguageIndex()].name; } -QString LanguageModel::getCurrentSiteUrl() +QString LanguageModel::getCurrentSiteUrl(const QString &path) { auto language = static_cast(getCurrentLanguageIndex()); switch (language) { - case LanguageSettings::AvailableLanguageEnum::Russian: return "https://storage.googleapis.com/amnezia/amnezia.org"; - default: return "https://amnezia.org"; + case LanguageSettings::AvailableLanguageEnum::Russian: + return "https://storage.googleapis.com/amnezia/amnezia.org" + (path.isEmpty() ? "" : (QString("?m-path=/%1").arg(path))); + default: return QString("https://amnezia.org") + (path.isEmpty() ? "" : (QString("/%1").arg(path))); } } diff --git a/client/ui/models/languageModel.h b/client/ui/models/languageModel.h index 2c80880a..d1a2e7d5 100644 --- a/client/ui/models/languageModel.h +++ b/client/ui/models/languageModel.h @@ -59,7 +59,7 @@ public slots: int getCurrentLanguageIndex(); int getLineHeightAppend(); QString getCurrentLanguageName(); - QString getCurrentSiteUrl(); + QString getCurrentSiteUrl(const QString &path = ""); signals: void updateTranslations(const QLocale &locale); diff --git a/client/ui/qml/Components/AdLabel.qml b/client/ui/qml/Components/AdLabel.qml index 91e1a42c..3ef0fc69 100644 --- a/client/ui/qml/Components/AdLabel.qml +++ b/client/ui/qml/Components/AdLabel.qml @@ -29,7 +29,7 @@ Rectangle { cursorShape: Qt.PointingHandCursor onClicked: function() { - Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/premium") + Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl("premium")) } } diff --git a/client/ui/qml/Pages2/PageSettingsAbout.qml b/client/ui/qml/Pages2/PageSettingsAbout.qml index 37327313..6342ce66 100644 --- a/client/ui/qml/Pages2/PageSettingsAbout.qml +++ b/client/ui/qml/Pages2/PageSettingsAbout.qml @@ -252,7 +252,7 @@ PageType { text: qsTr("Privacy Policy") clickedFunc: function() { - Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/policy") + Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl("policy")) } } } diff --git a/client/ui/qml/Pages2/PageSetupWizardCredentials.qml b/client/ui/qml/Pages2/PageSetupWizardCredentials.qml index ca7e3a7c..cdafa47f 100644 --- a/client/ui/qml/Pages2/PageSetupWizardCredentials.qml +++ b/client/ui/qml/Pages2/PageSetupWizardCredentials.qml @@ -175,7 +175,7 @@ PageType { leftImageSource: "qrc:/images/controls/help-circle.svg" onClicked: { - Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/starter-guide") + Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl("starter-guide")) } } } From b3b0fec2e14f145eccedb59e7076f0a24376c42d Mon Sep 17 00:00:00 2001 From: Nethius Date: Wed, 9 Apr 2025 10:47:33 +0700 Subject: [PATCH 11/65] feature: additional logs for proxy bypass (#1518) --- client/core/api/apiUtils.cpp | 1 + client/core/controllers/gatewayController.cpp | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/client/core/api/apiUtils.cpp b/client/core/api/apiUtils.cpp index 9f518b52..be19a166 100644 --- a/client/core/api/apiUtils.cpp +++ b/client/core/api/apiUtils.cpp @@ -66,6 +66,7 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList &ssl return amnezia::ErrorCode::NoError; } else if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError || reply->error() == QNetworkReply::NetworkError::TimeoutError) { + qDebug() << reply->error(); return amnezia::ErrorCode::ApiConfigTimeoutError; } else { QString err = reply->errorString(); diff --git a/client/core/controllers/gatewayController.cpp b/client/core/controllers/gatewayController.cpp index be42ad4d..f8c23c1a 100644 --- a/client/core/controllers/gatewayController.cpp +++ b/client/core/controllers/gatewayController.cpp @@ -251,6 +251,9 @@ QStringList GatewayController::getProxyUrls() } return endpoints; } else { + apiUtils::checkNetworkReplyErrors(sslErrors, reply); + qDebug() << "go to the next storage endpoint"; + reply->deleteLater(); } } @@ -261,26 +264,29 @@ bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray const QByteArray &iv, const QByteArray &salt) { if (reply->error() == QNetworkReply::NetworkError::OperationCanceledError || reply->error() == QNetworkReply::NetworkError::TimeoutError) { - qDebug() << "Timeout occurred"; + qDebug() << "timeout occurred"; + qDebug() << reply->error(); return true; } else if (responseBody.contains("html")) { - qDebug() << "The response contains an html tag"; + qDebug() << "the response contains an html tag"; return true; } else if (reply->error() == QNetworkReply::NetworkError::ContentNotFoundError) { if (responseBody.contains(errorResponsePattern1) || responseBody.contains(errorResponsePattern2) || responseBody.contains(errorResponsePattern3)) { return false; } else { + qDebug() << reply->error(); return true; } } else if (reply->error() != QNetworkReply::NetworkError::NoError) { + qDebug() << reply->error(); return true; } else if (checkEncryption) { try { QSimpleCrypto::QBlockCipher blockCipher; static_cast(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt)); } catch (...) { - qDebug() << "Failed to decrypt the data"; + qDebug() << "failed to decrypt the data"; return true; } } @@ -301,7 +307,7 @@ void GatewayController::bypassProxy(const QString &endpoint, QNetworkReply *repl QByteArray responseBody; for (const QString &proxyUrl : proxyUrls) { - qDebug() << "Go to the next endpoint"; + qDebug() << "go to the next proxy endpoint"; reply->deleteLater(); // delete the previous reply reply = requestFunction(endpoint.arg(proxyUrl)); From c24531833951cfabd7248fc5b981289c00b62993 Mon Sep 17 00:00:00 2001 From: aiamnezia Date: Thu, 10 Apr 2025 11:24:33 +0400 Subject: [PATCH 12/65] bugfix: empty split tunneling list (#1520) * Disable split tunneling with empty list * Fix bug with Amnezia DNS in split tunneling list * update ubuntu version for linux deploy pipeline * Fix deploy script --- .github/workflows/deploy.yml | 4 ++-- client/vpnconnection.cpp | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 23df2d8f..a6f7c4c6 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -10,7 +10,7 @@ env: jobs: Build-Linux-Ubuntu: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 env: QT_VERSION: 6.6.2 @@ -47,7 +47,7 @@ jobs: - name: 'Build project' run: | - sudo apt-get install libxkbcommon-x11-0 + sudo apt-get install libxkbcommon-x11-0 libsecret-1-dev export QT_BIN_DIR=${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64/bin export QIF_BIN_DIR=${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin bash deploy/build_linux.sh diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 042c51c7..ff875b39 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -351,8 +351,10 @@ void VpnConnection::appendSplitTunnelingConfig() sitesJsonArray.append(site); } - // Allow traffic to Amnezia DNS - if (sitesRouteMode == Settings::VpnOnlyForwardSites) { + if (sitesJsonArray.isEmpty()) { + sitesRouteMode = Settings::RouteMode::VpnAllSites; + } else if (sitesRouteMode == Settings::VpnOnlyForwardSites) { + // Allow traffic to Amnezia DNS sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns1).toString()); sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns2).toString()); } @@ -371,6 +373,10 @@ void VpnConnection::appendSplitTunnelingConfig() for (const auto &app : apps) { appsJsonArray.append(app.appPath.isEmpty() ? app.packageName : app.appPath); } + + if (appsJsonArray.isEmpty()) { + appsRouteMode = Settings::AppsRouteMode::VpnAllApps; + } } m_vpnConfiguration.insert(config_key::appSplitTunnelType, appsRouteMode); From d5b3da6ba3dc0a73a50346aa6ea980ad07b23a9f Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Thu, 10 Apr 2025 18:57:56 -0700 Subject: [PATCH 13/65] Use older Ubuntu version for build job (#1523) --- .github/workflows/deploy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a6f7c4c6..ae8768a6 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -10,7 +10,7 @@ env: jobs: Build-Linux-Ubuntu: - runs-on: ubuntu-24.04 + runs-on: ubuntu-22.04 env: QT_VERSION: 6.6.2 @@ -47,7 +47,7 @@ jobs: - name: 'Build project' run: | - sudo apt-get install libxkbcommon-x11-0 libsecret-1-dev + sudo apt-get install libxkbcommon-x11-0 export QT_BIN_DIR=${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64/bin export QIF_BIN_DIR=${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin bash deploy/build_linux.sh From d00f64e6ad22efff95a9121479fc2d22362b437c Mon Sep 17 00:00:00 2001 From: Nethius Date: Fri, 11 Apr 2025 12:29:28 +0700 Subject: [PATCH 14/65] feature: added export logs button on start page (#1525) --- .../Pages2/PageSetupWizardConfigSource.qml | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml index 38a1da52..bcbb7fb2 100644 --- a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml +++ b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml @@ -3,6 +3,8 @@ import QtQuick.Controls import QtQuick.Layouts import QtQuick.Dialogs +import QtCore + import PageEnum 1.0 import Style 1.0 @@ -101,6 +103,34 @@ PageType { } } + LabelWithButtonType { + Layout.fillWidth: true + + text: qsTr("Export client logs") + rightImageSource: "qrc:/images/controls/chevron-right.svg" + + visible: PageController.isStartPageVisible() + + clickedFunction: function() { + var fileName = "" + if (GC.isMobile()) { + fileName = "AmneziaVPN.log" + } else { + fileName = SystemController.getFileName(qsTr("Save"), + qsTr("Logs files (*.log)"), + StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/AmneziaVPN", + true, + ".log") + } + if (fileName !== "") { + PageController.showBusyIndicator(true) + SettingsController.exportLogsFile(fileName) + PageController.showBusyIndicator(false) + PageController.showNotificationMessage(qsTr("Logs file saved")) + } + } + } + LabelWithButtonType { id: supportUuid Layout.fillWidth: true From 6977a8ecbcbef67ea5c721aa3134f97dda5dda82 Mon Sep 17 00:00:00 2001 From: Nethius Date: Fri, 11 Apr 2025 12:59:06 +0700 Subject: [PATCH 15/65] chore: bump version and update translation files (#1526) --- CMakeLists.txt | 4 +- client/translations/amneziavpn_ar_EG.ts | 1294 +++++++++++++++------- client/translations/amneziavpn_fa_IR.ts | 1279 ++++++++++++++++------ client/translations/amneziavpn_hi_IN.ts | 1263 +++++++++++++++------- client/translations/amneziavpn_my_MM.ts | 1305 ++++++++++++++++------- client/translations/amneziavpn_ru_RU.ts | 241 +++-- client/translations/amneziavpn_uk_UA.ts | 1289 +++++++++++++++------- client/translations/amneziavpn_ur_PK.ts | 1240 ++++++++++++++------- client/translations/amneziavpn_zh_CN.ts | 1264 +++++++++++++++------- 9 files changed, 6518 insertions(+), 2661 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b1246970..0896973e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR) set(PROJECT AmneziaVPN) -project(${PROJECT} VERSION 4.8.5.0 +project(${PROJECT} VERSION 4.8.6.0 DESCRIPTION "AmneziaVPN" HOMEPAGE_URL "https://amnezia.org/" ) @@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d") set(RELEASE_DATE "${CURRENT_DATE}") set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}) -set(APP_ANDROID_VERSION_CODE 2082) +set(APP_ANDROID_VERSION_CODE 2083) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(MZ_PLATFORM_NAME "linux") diff --git a/client/translations/amneziavpn_ar_EG.ts b/client/translations/amneziavpn_ar_EG.ts index 773f5d05..4b84502b 100644 --- a/client/translations/amneziavpn_ar_EG.ts +++ b/client/translations/amneziavpn_ar_EG.ts @@ -4,60 +4,129 @@ AdLabel - - Amnezia Premium - for access to any website + + Amnezia Premium - for access to all websites and online resources + + ApiAccountInfoModel + + + + Active + + + + + Inactive + + + + + %1 out of %2 + + + + + Classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. Speeds up to 200 Mbps + + + + + Free unlimited access to a basic set of websites such as Facebook, Instagram, Twitter (X), Discord, Telegram and more. YouTube is not included in the free plan. + + + + + amnezia_free_support_bot + + + + + amnezia_premium_support_bot + + + + + ApiConfigsController + + + %1 installed successfully. + تم تحميل %1 بنجاح + + + + API config reloaded + تمت إعادة تحميل تكوين API + + + + Successfully changed the country of connection to %1 + تم تغيير بلد الاتصال بنجاح إلى %1 + + ApiServicesModel - Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s - شبكة VPN كلاسيكية للعمل المريح وتنزيل الملفات الكبيرة ومشاهدة مقاطع الفيديو. تعمل مع أي موقع. تصل السرعة إلى %1 ميجابت/ثانية + شبكة VPN كلاسيكية للعمل المريح وتنزيل الملفات الكبيرة ومشاهدة مقاطع الفيديو. تعمل مع أي موقع. تصل السرعة إلى %1 ميجابت/ثانية - VPN to access blocked sites in regions with high levels of Internet censorship. - شبكة VPN للولوج للمواقع المحظورة في بلاد ذو مستوي عالي من الرقابة علي الانترنت. + شبكة VPN للولوج للمواقع المحظورة في بلاد ذو مستوي عالي من الرقابة علي الانترنت. - + <p><a style="color: #EB5757;">Not available in your region. If you have VPN enabled, disable it, return to the previous screen, and try again.</a> - Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship. - Amenzia Premium - شبكة VPN للعمل المريح, تحميل ملفات كبيرة الحجم, ومشاهدة مقاطع الفيديو ب جودة عالية. تعمل لجميع المواقع, حتي في البلاد ذو مستوي عالي من الرقابة علي الانترنت + Amenzia Premium - شبكة VPN للعمل المريح, تحميل ملفات كبيرة الحجم, ومشاهدة مقاطع الفيديو ب جودة عالية. تعمل لجميع المواقع, حتي في البلاد ذو مستوي عالي من الرقابة علي الانترنت - Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship - Amnezia Free هو VPN مجاني لتخطي الحظر في البلاد ذو مستوي عالي من الرقابة علي الانترنت + Amnezia Free هو VPN مجاني لتخطي الحظر في البلاد ذو مستوي عالي من الرقابة علي الانترنت - + + Amnezia Premium is classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. Speeds up to %1 Mbps. + + + + + + AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan. + + + + + Amnezia Premium is classic VPN for for seamless work, downloading large files, and watching videos. Access all websites and online resources. + + + + %1 MBit/s %1 ميجابت/ثانية - + %1 days %1 ايام - + VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, <a href="%1/free" style="color: #FBB26A;">more details on the website.</a> سيقوم VPN فقط بفتح المواقع المشهورة المحظورة في بلدك, مثل Instagram, Facebook, Twitter و مواقع اخري. المواقع الاخري ستٌفتح من عنوان ال IP الحقيقي الخاص بك, <a href="%1/free" style="color: #FBB26A;">معلومات اخري علي الموقع.</a> - + Free مجاني - + %1 $/month %1 دولار/الشهر @@ -96,63 +165,60 @@ ConnectionController - - - - + + + + Connect اتصل - VPN Protocols is not installed. Please install VPN container at first - لم يتم تثبيت بروتوكولات VPN, من فضلك قم بتنزيل حاوية VPN اولاً + لم يتم تثبيت بروتوكولات VPN, من فضلك قم بتنزيل حاوية VPN اولاً - + Connecting... اتصال... - + Connected تم الاتصال - + Reconnecting... إعادة الاتصال... - + Disconnecting... إنهاء الاتصال... - + Preparing... جاري التحضير... - + Settings updated successfully, reconnnection... تم تحديث الاعدادات بنجاح, جاري إعادة الاتصال... - + Settings updated successfully تم تحديث الاعدادات بنجاح - The selected protocol is not supported on the current platform - البروتوكول المحدد غير مدعوم علي المنصة الحالية + البروتوكول المحدد غير مدعوم علي المنصة الحالية - unable to create configuration - غير قادر علي إنشاء تكوين + غير قادر علي إنشاء تكوين @@ -269,37 +335,46 @@ Can't be disabled for current server ملف تكوين غير صحيح - + Scanned %1 of %2. تم فحص%1 من %2. - + + This configuration contains an OpenVPN setup. OpenVPN configurations can include malicious scripts, so only add it if you fully trust the provider of this config. + + + + + <br>In the imported configuration, potentially dangerous lines were found: + + + In the imported configuration, potentially dangerous lines were found: - في التكوين المستورد، تم العثور على سطور يحتمل أن تكون خطرة: + في التكوين المستورد، تم العثور على سطور يحتمل أن تكون خطرة: InstallController - + %1 installed successfully. %1 تم التثبيت بنجاح. - + %1 is already installed on the server. %1 بالفعل مٌثبت علي الخادم. - + Added containers that were already installed on the server تمت إضافة الحاويات التي كانت مٌثبتة بالفعل علي الخادم - + Already installed containers were found on the server. All installed containers have been added to the application @@ -307,64 +382,61 @@ Already installed containers were found on the server. All installed containers تمت إضافة جميع الحاويات المٌثبتة إلي التطبيق - + Settings updated successfully تم تحديث الاعدادات بنجاح - + Server '%1' was rebooted تمت إعادة تشغيل الخادم%1 - + Server '%1' was removed تمت إزالة الخادم '%1' - + All containers from server '%1' have been removed قد تم حذفها '%1' جميع الحاويات من الخادم - + %1 has been removed from the server '%2' %1 تم حدف '%2' اسم الخادم - + Api config removed تم حذف تكوين Api - + %1 cached profile cleared تم مسح ملف تعريف %1 المخزن مؤقتًا - + Please login as the user من فضلك قم بتسجيل الدخول كمستخدم - + Server added successfully تمت إضافة الخادم بنجاح - %1 installed successfully. - تم تحميل %1 بنجاح + تم تحميل %1 بنجاح - API config reloaded - تمت إعادة تحميل تكوين API + تمت إعادة تحميل تكوين API - Successfully changed the country of connection to %1 - تم تغيير بلد الاتصال بنجاح إلى %1 + تم تغيير بلد الاتصال بنجاح إلى %1 @@ -476,12 +548,12 @@ Already installed containers were found on the server. All installed containers تقسيم الانفاق مٌعطل - + VPN protocol بروتوكول VPN - + Servers الخوادم @@ -1291,22 +1363,22 @@ Already installed containers were found on the server. All installed containers تطبيق - + Backup نسخة احتياطية - + About AmneziaVPN عن AmneziaVPN - + Dev console وحدة تحكم التطوير - + Close application إغلاق التطبيق @@ -1314,17 +1386,17 @@ Already installed containers were found on the server. All installed containers PageSettingsAbout - + Support Amnezia دعم Amenzia - + Amnezia is a free and open-source application. You can support the developers if you like it. هو تطبيق مجاني ومفتوح المصدر يمكنك دعم مطورين Amnezia إذا اعجبك. - + Contacts التواصل @@ -1358,139 +1430,507 @@ Already installed containers were found on the server. All installed containers لل مراجعات والابلاغات عن المشاكل - - Copied + + mailto:support@amnezia.org - + GitHub GitHub - + Discover the source code - + https://github.com/amnezia-vpn/amnezia-client - + Website موقع - + Visit official website - + Software version: %1 %1 :إصدار البرنامج - + Check for updates تحقق من وجود تحديثات - + Privacy Policy سياسات الخصوصية - PageSettingsApiLanguageList + PageSettingsApiAvailableCountries - + + Location for connection + + + + Unable change server location while there is an active connection - PageSettingsApiServerInfo + PageSettingsApiDevices - - For the region - للمنطقة - - - - Price - السعر - - - - Work period - مدة العمل - - - - Valid until + + Active Devices - + + Manage currently connected devices + + + + + You can find the identifier on the Support tab or, for older versions of the app, by tapping '+' and then the three dots at the top of the page. + + + + + (current device) + + + + + Support tag: + + + + + Last updated: + + + + + Cannot unlink device during active connection + + + + + Are you sure you want to unlink this device? + + + + + This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect. + + + + + Continue + واصل + + + + Cancel + إلغاء + + + + PageSettingsApiInstructions + + + Windows + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#windows + + + + + macOS + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#macos + + + + + Android + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#android + + + + + AndroidTV + + + + + https://docs.amnezia.org/ru/documentation/instructions/android_tv_connect/ + + + + + iOS + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#ios + + + + + Linux + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#linux + + + + + Routers + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#routers + + + + + How to connect on another device + + + + + Setup guides on the Amnezia website + + + + + PageSettingsApiNativeConfigs + + + Save AmneziaVPN config + احفظ تكوين AmneziaVPN + + + + Configuration Files + + + + + For router setup or the AmneziaWG app + + + + + The configuration needs to be reissued + + + + + configuration file + + + + + Generate a new configuration file + + + + + The previously created one will stop working + + + + + Revoke the current configuration file + + + + + Config file saved + + + + + The config has been revoked + + + + + Generate a new %1 configuration file? + + + + + Revoke the current %1 configuration file? + + + + + Your previous configuration file will no longer work, and it will not be possible to connect using it + + + + + Download + + + + + Continue + واصل + + + + Cancel + إلغاء + + + + PageSettingsApiServerInfo + + For the region + للمنطقة + + + Price + السعر + + + Work period + مدة العمل + + Speed - السرعة + السرعة - Support tag - علامة الدعم + علامة الدعم - Copied - تم النسخ + تم النسخ - + + Subscription Status + + + + + Valid Until + + + + + Active Connections + + + + + Configurations have been updated for some countries. Download and install the updated configuration files + + + + + Subscription Key + + + + + Amnezia Premium subscription key + + + + + Save VPN key as a file + + + + + Copy VPN key + + + + + Configuration Files + + + + + Manage configuration files + + + + + Active Devices + + + + + Manage currently connected devices + + + + + Support + + + + + How to connect on another device + + + + Reload API config إعادة تحميل تكوين API - + Reload API config? إعادة تحميل تكوين API - - + + + Continue واصل - - + + + Cancel إلغاء - + Cannot reload API config during active connection لا يمكن إعادة تحميل تكوين API اثناء تواجد اتصال نشط - + + Unlink this device + + + + + Are you sure you want to unlink this device? + + + + + This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect. + + + + + Cannot unlink device during active connection + + + + Remove from application احذف من التطبيق - + Remove from application? احذف من التطبيق؟ - + Cannot remove server during active connection لا يمكن إزالة الخادم أثناء الاتصال النشط + + PageSettingsApiSupport + + + Telegram + + + + + Email + + + + + support@amnezia.org + + + + + Email Billing & Orders + + + + + help@vpnpay.io + + + + + Website + موقع + + + + amnezia.org + + + + + Support + + + + + Our technical support specialists are available to assist you at any time + + + + + Support tag + علامة الدعم + + + + Copied + + + PageSettingsAppSplitTunneling @@ -1790,8 +2230,12 @@ Already installed containers were found on the server. All installed containers + Cannot change KillSwitch settings during active connection + + + Cannot change killSwitch settings during active connection - لا يمكن تغيير إعدادات KillSwitch اثناء تواجد اتصال فعال + لا يمكن تغيير إعدادات KillSwitch اثناء تواجد اتصال فعال @@ -1882,20 +2326,20 @@ Already installed containers were found on the server. All installed containers افتح مجلد يحتوي علي سجلات - - + + Save احفظ - - + + Logs files (*.log) ملفات الولوج (*.log) - - + + Logs file saved تم حفظ ملف السجل @@ -1929,32 +2373,32 @@ Already installed containers were found on the server. All installed containers تم مسح السجلات - + Client logs - + AmneziaVPN logs - + Open logs folder - + Export logs - + Service logs - + AmneziaVPN-service logs @@ -1977,12 +2421,12 @@ Already installed containers were found on the server. All installed containers لم يتم العثور علي اي خدمات مٌثبتة سابقاً - + Do you want to reboot the server? هل تريد إعادة تشغيل الخادم؟ - + Do you want to clear server from Amnezia software? هل تريد حذف الخادم من Amnezia? @@ -1992,93 +2436,93 @@ Already installed containers were found on the server. All installed containers - - - - + + + + Continue واصل - - - - + + + + Cancel إلغاء - + Check the server for previously installed Amnezia services افحص الخادم عن اي خدمات Amnezia مٌثبتة سابقاُ - + Add them to the application if they were not displayed اضفهم إلي التطبيق إذا لم يكونو ظاهرين - + Reboot server إعادة تشغيل الخادم - + The reboot process may take approximately 30 seconds. Are you sure you wish to proceed? عملية إعادة التشغيل قد تستغرق 30 ثانية, هل تريد الاستكمال؟ - + Cannot reboot server during active connection لا يمكن إعادة تشغيل الخادم أثناء الاتصال النشط - + Remove server from application احذف خادم من التطبيق - + Do you want to remove the server from application? هل تريد حذف الخادم من التطبيق؟ - + Cannot remove server during active connection لا يمكن إزالة الخادم أثناء الاتصال النشط - + All users whom you shared a connection with will no longer be able to connect to it. جميع المستخدمين الذين شاركت معهم اتصال لن يستطيعو الاتصال مرة اخري. - + Cannot clear server from Amnezia software during active connection لا يمكن مسح الخادم من برنامج Amnezia أثناء الاتصال النشط - + Reset API config إعادة تكوين API - + Do you want to reset API config? هل تريد إعادة تكوين API? - + Cannot reset API config during active connection لا يمكن إعادة تعيين تكوين API أثناء الاتصال النشط - + All installed AmneziaVPN services will still remain on the server. جميع خدمات AmneziaVPN المٌثبتة ستظل علي الخادم. - + Clear server from Amnezia software احذف خادم من Amnezia @@ -2086,32 +2530,25 @@ Already installed containers were found on the server. All installed containers PageSettingsServerInfo - - Subscription is valid until - - - - Server name - اسم الخادم + اسم الخادم - Save - احفظ + احفظ - + Protocols البروتوكولات - + Services الخدمات - + Management الإدارة @@ -2231,17 +2668,17 @@ Already installed containers were found on the server. All installed containers وضع - + Remove احذف - + Continue واصل - + Cancel إلغاء @@ -2256,17 +2693,17 @@ Already installed containers were found on the server. All installed containers لا يمكن تغير إعدادات تقسيم الانفاق بينما هناك اتصال مٌفعل - + website or IP موقع او IP - + Import / Export Sites - + Import استرد @@ -2282,29 +2719,29 @@ Already installed containers were found on the server. All installed containers - - + + Sites files (*.json) - + Import a list of sites استرد قائمة من المواقع - + Replace site list تبديل قائمة المواقع - - + + Open sites file افتح ملف المواقع - + Add imported sites to existing ones إضافة المواقع المستردة للمواقع الموجودة @@ -2408,85 +2845,70 @@ Already installed containers were found on the server. All installed containers اختيارات اتصال اخري - + Site Amnezia - + VPN by Amnezia VPN بواسطة Amnezia - + Connect to classic paid and free VPN services from Amnezia اتصل بخدمات VPN الكلاسيكية المدفوعة والمجانية من Amnezia - + Self-hosted VPN VPN ذاتية الاستضافة - + Configure Amnezia VPN on your own server قم بتكوين Amnezia VPN على الخادم الخاص بك - + Restore from backup استرجاع من ملف يحتوي علي نسخة احتياطية - + - + Open backup file افتح ملف نسخ احتياطي - + Backup files (*.backup) ملفات نٌسخ احتياطية (*.backup) - + File with connection settings ملف إعدادات اتصال - - - - - - + Open config file افتح ملف تكوين - + QR code رمز QR - - - - - - + I have nothing ليس لدي اي شئ - - - - - PageSetupWizardCredentials @@ -2496,62 +2918,62 @@ Already installed containers were found on the server. All installed containers تكوين الخادم الخاص بك - + Server IP address [:port] عنوان خادم IP [:منفذ] - + Continue واصل - + All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties ستظل جميع البيانات التي تدخلها سرية للغاية ولن تتم مشاركتها أو الكشف عنها ل Amnezia أو أي طرف ثالث - + 255.255.255.255:22 - + SSH Username - + Password or SSH private key كلمة مرور او مفتاح SSH خاص - + How to run your VPN server كيف تقوم بتشغيل خادم ال VPN الخاص بك - + Where to get connection data, step-by-step instructions for buying a VPS اين تحصل علي بيانات الاتصال, تعليمات خطوة ب خطوة لشراء VPS - + Ip address cannot be empty لا يمكن لعنوان IP ان يكون فارغ - + Enter the address in the format 255.255.255.255:88 ادخل العنوان في شكل 255.255.255.255:88 - + Login cannot be empty تسجيل دخول لا يمكن ان يكون فارغ - + Password/private key cannot be empty كلمة مرور/مفتاح خاص لأ يمكن ان يكونو فارغين @@ -2559,22 +2981,31 @@ Already installed containers were found on the server. All installed containers PageSetupWizardEasy - What is the level of internet control in your region? - ما هو مستوي التحكم في الانترنت في منطقتك؟ + ما هو مستوي التحكم في الانترنت في منطقتك؟ + + + + Choose Installation Type + + Manual + + + + Choose a VPN protocol اختر بروتوكول VPN - + Skip setup تخطي الإعداد - + Continue واصل @@ -2750,174 +3181,174 @@ Already installed containers were found on the server. All installed containers PageShare - + Save OpenVPN config احفظ تكوين OpenVPN - + Save WireGuard config احفظ تكوين WireGuard - + Save AmneziaWG config احفظ تكوين AmneziaWG - + Save Shadowsocks config احفظ تكوين Shadowsocks - + Save Cloak config احفظ تكوين Cloak - + Save XRay config حفظ تكوين XRay - + For the AmneziaVPN app AmneziaVPN من اجل تطبيق - + OpenVPN native format تنسيق OpenVPN الاصلي - + WireGuard native format تنسيق WireGuard الاصلي - + AmneziaWG native format تنسيق AmneziaWG اصلي - + Shadowsocks native format تنسيق Shadowsocks الاصلي - + Cloak native format تنسيق Cloak الاصلي - + XRay native format الشكل الاصلي ل XRay - + Share VPN Access شارك اتصال VPN - + Share full access to the server and VPN شارك ولوج كامل للخادم و ال VPN - + Use for your own devices, or share with those you trust to manage the server. استخدمه للأجهزة الخاصة بك، أو شاركه مع من تثق بهم لإدارة الخادم. - - + + Users المستخدمين - + Share VPN access without the ability to manage the server شارك اتصال VPN بدون القدرة علي إدارة الخادم - + Search ابحث - + Creation date: %1 تاريخ الإنشاء: %1 - + Latest handshake: %1 اخر تصافح: %1 - + Data received: %1 البيانات المستلمة: %1 - + Data sent: %1 البيانات المٌرسلة: %1 - + Allowed IPs: %1 - + Rename إعادة التسمية - + Client name اسم العميل - + Save احفظ - + Revoke سحب وإبطال - + Revoke the config for a user - %1? سحب وإبطال للمستخدم - %1? - + The user will no longer be able to connect to your server. المستخدم لن يكون قادر علي الاتصال بعد الان. - + Continue واصل - + Cancel إلغاء - + Connection الاتصال + - Server خادم @@ -2927,8 +3358,8 @@ Already installed containers were found on the server. All installed containers ملف بإعدادات إلي + - Protocol بروتوكول @@ -2943,19 +3374,19 @@ Already installed containers were found on the server. All installed containers تم سحب وإبطال التكوين - + User name اسم المستخدم + - Connection format تنسيق الاتصال - - + + Share شارك @@ -3019,17 +3450,17 @@ Already installed containers were found on the server. All installed containers PageStart - + Logging was disabled after 14 days, log files were deleted تم تعطيل التسجيل بعد 14 يومًا، وتم حذف ملفات السجل - + Settings restored from backup file تم تحميل الإعدادات من ملف نسخة احتياطية - + Logging is enabled. Note that logs will be automaticallydisabled after 14 days, and all log files will be deleted. @@ -3287,7 +3718,7 @@ Already installed containers were found on the server. All installed containers - + SOCKS5 proxy server @@ -3308,87 +3739,88 @@ Already installed containers were found on the server. All installed containers لم يتم تنفيذ الوظيفة - + Server check failed فشل في فحص الخادم - + Server port already used. Check for another software منفذ الخادم بالفعل مٌستخدم, تحقق من باقي التطبيقات - + Server error: Docker container missing خطأ من الخادم: حاوية Docker مفقودة - + Server error: Docker failed خطأ من الخادم: فشل Docker - + Installation canceled by user تم اغلاق التثبيت بواسطة المستخدم - + The user is not a member of the sudo group المستخدم ليس عضوًا في مجموعة sudo - + SSH request was denied طلب SSH محظو - + SSH request was interrupted إنقطع طلب SSH - + SSH internal error مشكلة داخلية SSH - + Invalid private key or invalid passphrase entered مفتا ح خاص غير صحيح او عبارة مرور غير صحيحة - + The selected private key format is not supported, use openssh ED25519 key types or PEM key types التنسيق المٌحدد للمفتاح الخاص غير مدعوم, استخدم نوع مفتاح openssh ED25519 او نوع مفتاح PEM - + Timeout connecting to server انتهت مدة الاتصال بالخادم - + VPN connection error - + + Error when retrieving configuration from API خطأ عند استرداد التكوين من API - + This config has already been added to the application هذا التكوين بالفعل تمت إضافتة للبرنامج - + ErrorCode: %1. - + OpenVPN config missing OpenVPN تكوين مفقود @@ -3398,132 +3830,168 @@ Already installed containers were found on the server. All installed containers خدمة الخلفية ليست قيد التشغيل - + + The selected protocol is not supported on the current platform + البروتوكول المحدد غير مدعوم علي المنصة الحالية + + + Server error: Package manager error خطأ في الخادم: خطأ في مدير الحزم - + + The sudo package is not pre-installed on the server + + + + + The server user's home directory is not accessible + + + + + Action not allowed in sudoers + + + + + The user's password is required + + + + SCP error: Generic failure خطأ SCP: فشل عام - + OpenVPN management server error OpenVPN خطأ في إدارة الخادم - + OpenVPN executable missing OpenVPN executable مفقود - + Shadowsocks (ss-local) executable missing Shadowsocks (ss-local) executable مفقود - + Cloak (ck-client) executable missing Cloak (ck-client) executable مفقود - + Amnezia helper service error خطأ في خدمة مٌساعد Amnezia - + OpenSSL failed فشل OpenSSL - + Can't connect: another VPN connection is active لا يمكن الاتصال: هناك اتصال VPN اخر بالفعل يعمل - + Can't setup OpenVPN TAP network adapter لا يمك نتثبيت محول شبكة OpenVPN TAP - + VPN pool error: no available addresses VPN pool error: لا يوجد عنواين مٌتاحة - + The config does not contain any containers and credentials for connecting to the server التكوين لا يحتوي علي اي حاويات و اعتماد للأتصال بالخادم - + Unable to open config file - + + VPN Protocols is not installed. + Please install VPN container at first + لم يتم تثبيت بروتوكولات VPN, من فضلك قم بتنزيل حاوية VPN اولاً + + + In the response from the server, an empty config was received في الاستجابة من الخادم، تم تلقي تكوين فارغ - + SSL error occurred حدث خطأ SSL - + Server response timeout on api request انتهت مهلة استجابة الخادم عند طلب واجهة برمجة التطبيقات - + Missing AGW public key مفتاح AGW عام مفقود - + Failed to decrypt response payload - + Missing list of available services - + + The limit of allowed configurations per subscription has been exceeded + + + + QFile error: The file could not be opened خطأ QFile: لا يمكن فتح الملف - + QFile error: An error occurred when reading from the file خطأ QFile: ظهر خطأ اثناء القراءه من الملف - + QFile error: The file could not be accessed خطأ QFile: لا يمكن الوصول للملف - + QFile error: An unspecified error occurred خطأ QFile: ظهر خطأ غير محدد - + QFile error: A fatal error occurred خطأ QFile: حدث خطأ فادح - + QFile error: The operation was aborted خطأ QFile: تم إحباط العملية - + Internal error خطأ داخلي @@ -3534,7 +4002,7 @@ Already installed containers were found on the server. All installed containers - + Website in Tor network موقع في شبكة Tor @@ -3555,33 +4023,120 @@ Already installed containers were found on the server. All installed containers + Shadowsocks masks VPN traffic, making it resemble normal web traffic, but it may still be detected by certain analysis systems. + + + + + OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against active-probing detection. It is very resistant to detection, but offers low speed. + + + + + WireGuard - popular VPN protocol with high performance, high speed and low power consumption. + + + + + AmneziaWG is a special protocol from Amnezia based on WireGuard. It provides high connection speed and ensures stable operation even in the most challenging network conditions. + + + + + XRay with REALITY masks VPN traffic as web traffic and protects against active probing. It is highly resistant to detection and offers high speed. + + + + + OpenVPN stands as one of the most popular and time-tested VPN protocols available. +It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, catering to a wide range of devices and operating systems. Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, which continually reinforces its security. With a strong balance of performance, security, and compatibility, OpenVPN remains a top choice for privacy-conscious individuals and businesses alike. + +* Available in the AmneziaVPN across all platforms +* Normal power consumption on mobile devices +* Flexible customisation to suit user needs to work with different operating systems and devices +* Recognised by DPI systems and therefore susceptible to blocking +* Can operate over both TCP and UDP network protocols. + + + + + This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for protecting against detection. + +OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client and the server. + +Cloak protects OpenVPN from detection. + +Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, and also protects the VPN from detection by Active Probing. This makes it very resistant to being detected + +Immediately after receiving the first data packet, Cloak authenticates the incoming connection. If authentication fails, the plugin masks the server as a fake website and your VPN becomes invisible to analysis systems. + +* Available in the AmneziaVPN across all platforms +* High power consumption on mobile devices +* Flexible settings +* Not recognised by detection systems +* Works over TCP network protocol, 443 port. + + + + + + A relatively new popular VPN protocol with a simplified architecture. +WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. +WireGuard is very susceptible to detection and blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. + +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Easily recognised by DPI analysis systems, susceptible to blocking +* Works over UDP network protocol. + + + + + A modern iteration of the popular VPN protocol, AmneziaWG builds upon the foundation set by WireGuard, retaining its simplified architecture and high-performance capabilities across devices. +While WireGuard is known for its efficiency, it had issues with being easily detected due to its distinct packet signatures. AmneziaWG solves this problem by using better obfuscation methods, making its traffic blend in with regular internet traffic. +This means that AmneziaWG keeps the fast performance of the original while adding an extra layer of stealth, making it a great choice for those wanting a fast and discreet VPN connection. + +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Not recognised by traffic analysis systems +* Works over UDP network protocol. + + + + + The REALITY protocol, a pioneering development by the creators of XRay, is designed to provide the highest level of protection against detection through its innovative approach to security and privacy. +It uniquely identifies attackers during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting attackers to genuine websites, thus presenting an authentic TLS certificate and data. +This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. +Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security. This makes REALITY a robust solution for maintaining internet freedom. + + + Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions. - بروتوكول Shadowsocks- يتنكر في حركة مرور VPN, يبدو ك حركة مرور الويب العادية + بروتوكول Shadowsocks- يتنكر في حركة مرور VPN, يبدو ك حركة مرور الويب العادية ولكن قد يتم التعرف عليه من خلال أنظمة التحليل في بعض المناطق شديدة الرقابة. - OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against active-probing detection. Ideal for bypassing blocking in regions with the highest levels of censorship. - بروتوكول OpenVPN over Cloak هو OpenVPN مع VPN يتنكر كحركة مرور على الويب ويوفر الحماية + بروتوكول OpenVPN over Cloak هو OpenVPN مع VPN يتنكر كحركة مرور على الويب ويوفر الحماية ضد عمليات الكشف النشط. مثالية لتجاوز الحجب في المناطق ذات أعلى مستويات الرقابة. - XRay with REALITY - Suitable for countries with the highest level of internet censorship. Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods. - XRay مع REALITY - مناسبة للبلدان التي لديها أعلى مستوى من الرقابة على الإنترنت. إخفاء حركة المرور كحركة مرور على الويب على مستوى TLS، والحماية من الكشف عن طريق طرق التحقيق النشطة. + XRay مع REALITY - مناسبة للبلدان التي لديها أعلى مستوى من الرقابة على الإنترنت. إخفاء حركة المرور كحركة مرور على الويب على مستوى TLS، والحماية من الكشف عن طريق طرق التحقيق النشطة. - + IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss. It has native support on the latest versions of Android and iOS. IKEv2/IPsec - بروتوكول مستقر حديث، أسرع قليلاً من البروتوكولات الأخرى، يستعيد الاتصال بعد فقدان الإشارة. يتمتع بدعم أصلي على أحدث إصدارات Android وiOS. - + Create a file vault on your server to securely store and transfer files. انشأ مخزن ملفات علي الخادم الخاص بك حتي تخزن الملفات و تنقلها بسرية. - This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for protecting against blocking. OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client and the server. @@ -3600,7 +4155,7 @@ If there is a extreme level of Internet censorship in your region, we advise you * Not recognised by DPI analysis systems * Works over TCP network protocol, 443 port. - هذه مجموعة من بروتوكول OpenVPN و برنامج Cloak المساعد مٌصمم خصيصاً للحماية ضد الحجب + هذه مجموعة من بروتوكول OpenVPN و برنامج Cloak المساعد مٌصمم خصيصاً للحماية ضد الحجب يوفر OpenVPN اتصال VPN امن عن طريق تشفير جميع حركات المرور بين العميل والخادم @@ -3622,7 +4177,6 @@ Cloak يحمي OpenVPN من ان يٌكتشف والحجب - A relatively new popular VPN protocol with a simplified architecture. WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. WireGuard is very susceptible to blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. @@ -3632,7 +4186,7 @@ WireGuard is very susceptible to blocking due to its distinct packet signatures. * Minimum number of settings * Easily recognised by DPI analysis systems, susceptible to blocking * Works over UDP network protocol. - بروتوكول VPN جديد وشارع ذو بنية مبسطة. + بروتوكول VPN جديد وشارع ذو بنية مبسطة. يوفر WireGuard اتصال VPN مستقر و اداء عالي علي جميع الاجهزة. يستعمل إعدادات تشفير معقدة. WireGuard مٌقارنة مع OpenVPN يتمتع بزمن وصول أقل وتحسين إنتاجية نقل البيانات. بسبب توقيعات الحزمة المميزة WireGuard عرضة جداُ للحجب. علي عكس باقي برتوكولات VPN التي تستعمل تقنيات تشويش. حزمة أنماط التوقيع المتسقة الخاصة ب WireGuard يمكن التعرف عليها بسهولة ولذلك تٌحجب بواسطة أنظمة الفحص العميق للحزم (DPI) المتقدمة وأدوات مراقبة الشبكة الأخرى. @@ -3643,18 +4197,17 @@ WireGuard is very susceptible to blocking due to its distinct packet signatures. * يعمل عبر بروتوكول شبكة UDP. - The REALITY protocol, a pioneering development by the creators of XRay, is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion. It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, thus presenting an authentic TLS certificate and data. This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship. - تم تصميم بروتوكول REALITY، وهو تطور رائد قام به مبدعو XRay، خصيصًا لمواجهة أعلى مستويات الرقابة على الإنترنت من خلال نهجه الجديد في التهرب. + تم تصميم بروتوكول REALITY، وهو تطور رائد قام به مبدعو XRay، خصيصًا لمواجهة أعلى مستويات الرقابة على الإنترنت من خلال نهجه الجديد في التهرب. فهو يحدد بشكل فريد الرقباء أثناء مرحلة مصافحة TLS، ويعمل بسلاسة كوكيل للعملاء الشرعيين بينما يحول الرقباء إلى مواقع الويب الأصلية مثل google.com، وبالتالي يقدم شهادة وبيانات TLS أصلية. هذه الإمكانية المتقدمة تميز REALITY عن التقنيات المشابهة من خلال قدرتها على إخفاء حركة مرور الويب على أنها قادمة من مواقع عشوائية وشرعية دون الحاجة إلى تكوينات محددة. على عكس البروتوكولات القديمة مثل VMess وVLESS ونقل XTLS-Vision، فإن التعرف المبتكر على "الصديق أو العدو" من REALITY عند مصافحة TLS يعزز الأمان ويتحايل على الكشف بواسطة أنظمة DPI المتطورة التي تستخدم تقنيات التحقيق النشطة. وهذا يجعل من REALITY حلاً قويًا للحفاظ على حرية الإنترنت في البيئات التي تخضع لرقابة صارمة. - + After installation, Amnezia will create a file storage on your server. You will be able to access it using @@ -3673,27 +4226,24 @@ For more detailed information, you can إيجادها في قسم الدعم تحت "انشاء مخزن ملفات SFTP." - WireGuard - New popular VPN protocol with high performance, high speed and low power consumption. Recommended for regions with low levels of censorship. - بروتوكول WireGuard - بروتوكول شائع ب اداء عالي, سرعة عالية واستهلاك قليل للطاقة. ينصح للمناطق ذات مستوي منخفض من الرقابة. + بروتوكول WireGuard - بروتوكول شائع ب اداء عالي, سرعة عالية واستهلاك قليل للطاقة. ينصح للمناطق ذات مستوي منخفض من الرقابة. - AmneziaWG - Special protocol from Amnezia, based on WireGuard. It's fast like WireGuard, but very resistant to blockages. Recommended for regions with high levels of censorship. - بروتوكول AmneziaWG - بروتوكول خاص من Amnezia, يعتمد علي WireGuard. سريع مثل WireGuard, لكن مقاوم جداً للحجب. ينصح للمناطق ذات مستوي عالي من الرقابة. + بروتوكول AmneziaWG - بروتوكول خاص من Amnezia, يعتمد علي WireGuard. سريع مثل WireGuard, لكن مقاوم جداً للحجب. ينصح للمناطق ذات مستوي عالي من الرقابة. - + Deploy a WordPress site on the Tor network in two clicks. انشر موقع WordPress علي شبكة Tor في ضغطتين. - + Replace the current DNS server with your own. This will increase your privacy level. استبدل خادم ال DNS الحالي مع الخادم الخاص بك, هذا سيزيد من خصوصيتك. - OpenVPN stands as one of the most popular and time-tested VPN protocols available. It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, catering to a wide range of devices and operating systems. Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, which continually reinforces its security. With a strong balance of performance, security, and compatibility, OpenVPN remains a top choice for privacy-conscious individuals and businesses alike. @@ -3702,7 +4252,7 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for * Flexible customisation to suit user needs to work with different operating systems and devices * Recognised by DPI analysis systems and therefore susceptible to blocking * Can operate over both TCP and UDP network protocols. - يبقا OpenVPN كأحد اشهر بروتوكولات VPN و التي تم اختبارها عبر الزمن. + يبقا OpenVPN كأحد اشهر بروتوكولات VPN و التي تم اختبارها عبر الزمن. ينشأ بروتوكول امان مميز, يستفيد من SSL/TLS للتشفير و تغير المفاتيح. واكثر من ذلك, OpenVPN يدعم تعدد طرق المصادقة يجعلة متعدد الاستخدامات وقابلة للتكيف, تلبية مجموعة واسعة من الأجهزة وأنظمة التشغيل. بسبب طبيعتة مفتوحة المصدر, يستفيد OpenVPN من التدقيق الشامل من قبل المجتمع العالمي, مما يعزز أمنها باستمرار. مع توازن قوي بين الأداء والأمان والتوافق, يظل OpenVPN الخيار الأفضل للأفراد والشركات المهتمين بالخصوصية على حدٍ سواء. * مٌتاح في AmneziaVPN عبر جميع المنصات @@ -3712,7 +4262,7 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for * يمكن ان يعمل علي بروتوكولات شبكة TCP و UDP. - + Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection.However, certain traffic analysis systems might still detect a Shadowsocks connection. Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol. * Available in the AmneziaVPN only on desktop platforms @@ -3727,7 +4277,6 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for * يعمل عبر بروتوكول شبكة TCP. - A modern iteration of the popular VPN protocol, AmneziaWG builds upon the foundation set by WireGuard, retaining its simplified architecture and high-performance capabilities across devices. While WireGuard is known for its efficiency, it had issues with being easily detected due to its distinct packet signatures. AmneziaWG solves this problem by using better obfuscation methods, making its traffic blend in with regular internet traffic. This means that AmneziaWG keeps the fast performance of the original while adding an extra layer of stealth, making it a great choice for those wanting a fast and discreet VPN connection. @@ -3737,7 +4286,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin * Minimum number of settings * Not recognised by DPI analysis systems, resistant to blocking * Works over UDP network protocol. - لفة سريعة من بروتوكولات VPN الحديثة والشائعة, يٌبني AmneziaWG علي الاساس الموضع من قبل WireGuard, مع الاحتفاظ ببنيته المبسطة وقدرات الأداء العالي عبر الاجهزة. + لفة سريعة من بروتوكولات VPN الحديثة والشائعة, يٌبني AmneziaWG علي الاساس الموضع من قبل WireGuard, مع الاحتفاظ ببنيته المبسطة وقدرات الأداء العالي عبر الاجهزة. بينما WireGuard معروف بأدائة العالي. لدية مشاكل مع سهولة التعرف علية بسبب توقيعات الحزمة المميزة الخاصة بة. يٌصلح AmneziaWG هذه المشكلة عن طريق استخدام طرق تشويش افضل, يجعل حركة المرور تبقا مع حركة مرور انترنت عادية. هذا يعني ان AmneziaWG يبقا الاداء العالي الاساسي بينما يضيف طبقة من العزل, هذا يجعلة اختيار ممتاز لهولاء الذين يريدون اتصال VPN سريع و متخفي. @@ -3748,7 +4297,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin * يعمل عبر بروتوكول شبكة UDP. - + IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol. One of its distinguishing features is its ability to swiftly switch between networks and devices, making it particularly adaptive in dynamic network environments. While it offers a blend of security, stability, and speed, it's essential to note that IKEv2 can be easily detected and is susceptible to blocking. @@ -3768,7 +4317,7 @@ While it offers a blend of security, stability, and speed, it's essential t * يعمل عبر بروتوكول شبكة UDP, منفذ 500 و منفذ 4500. - + DNS Service خدمة ال DNS @@ -3959,6 +4508,19 @@ While it offers a blend of security, stability, and speed, it's essential t لا يمكن العثور على فاصل النقطتين بين اسم المستضيف و المنفذ + + RenameServerDrawer + + + Server name + اسم الخادم + + + + Save + احفظ + + SelectLanguageDrawer @@ -4005,24 +4567,24 @@ While it offers a blend of security, stability, and speed, it's essential t ShareConnectionDrawer - - + + Save AmneziaVPN config احفظ تكوين AmneziaVPN - + Share شارك - + Copy انسخ - - + + Copied تم النسخ @@ -4032,12 +4594,12 @@ While it offers a blend of security, stability, and speed, it's essential t انسخ نص التكوين - + Show connection settings اظهر إعدادات الاتصال - + To read the QR code in the Amnezia app, select "Add server" → "I have data to connect" → "QR code, key or settings file" حتي تقرأ رمز ال QR في تطبيق Amnezia, اختار "إضافة خادم" - "لدي بيانات الاتصال" - "رمز Qr, او مفتاح تعريف او ملف إعدادات" @@ -4050,37 +4612,37 @@ While it offers a blend of security, stability, and speed, it's essential t اسم المضيف لا يشبه عنوان IP أو اسم ال domain - + New site added: %1 تمت إضافة موقع جديد: %1 - + Site removed: %1 تم حذف الموقع: %1 - + Can't open file: %1 لا يمكن فتح ملف: %1 - + Failed to parse JSON data from file: %1 فشل قراءه بيانات JSON من الملف: %1 - + The JSON data is not an array in file: %1 بيانات ال JSON ليست مصفوفة في الملف: %1 - + Import completed اكتمل الاستيراد - + Export completed اكتمل التصدير @@ -4121,7 +4683,7 @@ While it offers a blend of security, stability, and speed, it's essential t TextFieldWithHeaderType - + The field can't be empty الحقل لا يمكن ان يكون فارغ @@ -4129,7 +4691,7 @@ While it offers a blend of security, stability, and speed, it's essential t VpnConnection - + Mbps @@ -4180,24 +4742,30 @@ While it offers a blend of security, stability, and speed, it's essential t amnezia::ContainerProps - Low - منخفض + منخفض + + + High + متوسط او عالي + + + I just want to increase the level of my privacy. + انا فقط اريد زيادة مستوي الخصوصية. + + + I want to bypass censorship. This option recommended in most cases. + أريد تجاوز الرقابة. يوصى بهذا الخيار في معظم الحالات. + + + + Automatic + - High - متوسط او عالي - - - - I just want to increase the level of my privacy. - انا فقط اريد زيادة مستوي الخصوصية. - - - - I want to bypass censorship. This option recommended in most cases. - أريد تجاوز الرقابة. يوصى بهذا الخيار في معظم الحالات. + AmneziaWG protocol will be installed. It provides high connection speed and ensures stable operation even in the most challenging network conditions. + diff --git a/client/translations/amneziavpn_fa_IR.ts b/client/translations/amneziavpn_fa_IR.ts index c1bfce7e..c1155b9b 100644 --- a/client/translations/amneziavpn_fa_IR.ts +++ b/client/translations/amneziavpn_fa_IR.ts @@ -4,60 +4,129 @@ AdLabel - - Amnezia Premium - for access to any website + + Amnezia Premium - for access to all websites and online resources + + ApiAccountInfoModel + + + + Active + + + + + Inactive + + + + + %1 out of %2 + + + + + Classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. Speeds up to 200 Mbps + + + + + Free unlimited access to a basic set of websites such as Facebook, Instagram, Twitter (X), Discord, Telegram and more. YouTube is not included in the free plan. + + + + + amnezia_free_support_bot + + + + + amnezia_premium_support_bot + + + + + ApiConfigsController + + + %1 installed successfully. + %1 با موفقیت نصب شد. + + + + API config reloaded + پیکربندی API دوباره بارگذاری شد. + + + + Successfully changed the country of connection to %1 + کشور اتصال با موفقیت به %1 تغییر یافت. + + ApiServicesModel - Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s - برای کار راحت، دانلود فایل‌های بزرگ و تماشای ویدیوها، از VPN کلاسیک استفاده کنید. این VPN برای هر سایتی کار می‌کند و سرعت آن تا %1 مگابیت بر ثانیه است. + برای کار راحت، دانلود فایل‌های بزرگ و تماشای ویدیوها، از VPN کلاسیک استفاده کنید. این VPN برای هر سایتی کار می‌کند و سرعت آن تا %1 مگابیت بر ثانیه است. - VPN to access blocked sites in regions with high levels of Internet censorship. - وی پی ان برای دسترسی به سایت‌های مسدود شده در مناطق با سانسور شدید اینترنت. + وی پی ان برای دسترسی به سایت‌های مسدود شده در مناطق با سانسور شدید اینترنت. - + <p><a style="color: #EB5757;">Not available in your region. If you have VPN enabled, disable it, return to the previous screen, and try again.</a> - Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship. - امنزیا پریمیوم - یک وی پی ان کلاسیک برای کار راحت، دانلود فایل‌های بزرگ و تماشای ویدیو با کیفیت بالا. قابل استفاده برای تمامی سایت‌ها، حتی در کشورهایی با بالاترین سطح سانسور اینترنت. + امنزیا پریمیوم - یک وی پی ان کلاسیک برای کار راحت، دانلود فایل‌های بزرگ و تماشای ویدیو با کیفیت بالا. قابل استفاده برای تمامی سایت‌ها، حتی در کشورهایی با بالاترین سطح سانسور اینترنت. - Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship - امنزیا رایگان یک وی پی ان رایگان برای دور زدن مسدودیت‌ها در کشورهایی با سطح بالای سانسور اینترنت است. + امنزیا رایگان یک وی پی ان رایگان برای دور زدن مسدودیت‌ها در کشورهایی با سطح بالای سانسور اینترنت است. - + + Amnezia Premium is classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. Speeds up to %1 Mbps. + + + + + + AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan. + + + + + Amnezia Premium is classic VPN for for seamless work, downloading large files, and watching videos. Access all websites and online resources. + + + + %1 MBit/s %1 MBit/s - + %1 days %1 روز - + VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, <a href="%1/free" style="color: #FBB26A;">more details on the website.</a> وی پی ان فقط سایت‌های محبوبی را که در منطقه شما مسدود شده‌اند، مانند اینستاگرام، فیسبوک، توییتر و غیره باز می‌کند. سایر سایت‌ها با آدرس آی‌پی واقعی شما باز خواهند شد. <a href="%1/free" style="color: #FBB26A;">more details on the website.</a> - + Free رایگان - + %1 $/month %1 $/ماه @@ -96,63 +165,60 @@ ConnectionController - VPN Protocols is not installed. Please install VPN container at first - پروتکل وی‎پی‎ان نصب نشده است + پروتکل وی‎پی‎ان نصب نشده است لطفا کانتینر وی‎پی‎ان را نصب کنید - + Connecting... در حال ارتباط... - + Connected متصل - + Preparing... در حال آماده‌سازی... - + Settings updated successfully, reconnnection... تنظیمات به روز رسانی شد در حال اتصال دوباره... - + Settings updated successfully تنظیمات با موفقیت به‎روز‎رسانی شدند - The selected protocol is not supported on the current platform - پروتکل انتخاب‌شده در پلتفرم فعلی پشتیبانی نمی‌شود. + پروتکل انتخاب‌شده در پلتفرم فعلی پشتیبانی نمی‌شود. - unable to create configuration - نمی‌توان پیکربندی را ایجاد کرد. + نمی‌توان پیکربندی را ایجاد کرد. - + Reconnecting... اتصال دوباره... - - - - + + + + Connect اتصال - + Disconnecting... قطع ارتباط... @@ -274,101 +340,107 @@ Can't be disabled for current server فایل پیکربندی نامعتبر است. - + Scanned %1 of %2. ارزیابی %1 از %2. - + + This configuration contains an OpenVPN setup. OpenVPN configurations can include malicious scripts, so only add it if you fully trust the provider of this config. + + + + + <br>In the imported configuration, potentially dangerous lines were found: + + + In the imported configuration, potentially dangerous lines were found: - در پیکربندی وارد شده، خطوطی که ممکن است خطرناک باشند، یافت شدند: + در پیکربندی وارد شده، خطوطی که ممکن است خطرناک باشند، یافت شدند: InstallController - + %1 installed successfully. %1 با موفقیت نصب شد. - + %1 is already installed on the server. %1 در حال حاضر بر روی سرور نصب شده است. - + Added containers that were already installed on the server کانتینرهایی که بر روی سرور موجود بودند اضافه شدند - + Already installed containers were found on the server. All installed containers have been added to the application کانتینرهای نصب شده بر روی سرور شناسایی شدند. تمام کانتینترهای نصب شده به نرم افزار اضافه شدند - + Settings updated successfully تنظیمات با موفقیت به‎روز‎رسانی شدند - + Server '%1' was rebooted سرور %1 راه اندازی مجدد شد - + Server '%1' was removed سرور %1 حذف شد - + All containers from server '%1' have been removed تمام کانتینترها از سرور %1 حذف شدند - + %1 has been removed from the server '%2' %1 از سرور %2 حذف شد - + Api config removed پیکربندی API حذف شد. - + %1 cached profile cleared %1 پروفایل ذخیره شده پاک شد. - + Please login as the user لطفا به عنوان کاربر وارد شوید - + Server added successfully سرور با موفقیت اضافه شد - %1 installed successfully. - %1 با موفقیت نصب شد. + %1 با موفقیت نصب شد. - API config reloaded - پیکربندی API دوباره بارگذاری شد. + پیکربندی API دوباره بارگذاری شد. - Successfully changed the country of connection to %1 - کشور اتصال با موفقیت به %1 تغییر یافت. + کشور اتصال با موفقیت به %1 تغییر یافت. @@ -480,12 +552,12 @@ Already installed containers were found on the server. All installed containers تونل تقسیم‌شده غیرفعال شده - + VPN protocol پروتکل وی‎پی‎ان - + Servers سرورها @@ -1370,22 +1442,22 @@ Already installed containers were found on the server. All installed containers نرم‎افزار - + Backup بک‎آپ - + About AmneziaVPN درباره Amnezia - + Dev console - + Close application بستن نرم‎افزار @@ -1393,17 +1465,17 @@ Already installed containers were found on the server. All installed containers PageSettingsAbout - + Support Amnezia پشتیبانی از Amnezia - + Amnezia is a free and open-source application. You can support the developers if you like it. Amnezia یک برنامه رایگان و متن باز است. اگر دوست دارید می توانید از توسعه دهندگان حمایت کنید. - + Contacts مخاطب @@ -1437,32 +1509,36 @@ Already installed containers were found on the server. All installed containers برای ارائه نظرات و گزارشات باگ - Copied - کپی شد + کپی شد - + + mailto:support@amnezia.org + + + + GitHub GitHub - + Discover the source code - + https://github.com/amnezia-vpn/amnezia-client https://github.com/amnezia-vpn/amnezia-client - + Website وب سایت - + Visit official website @@ -1471,109 +1547,473 @@ Already installed containers were found on the server. All installed containers https://amnezia.org - + Software version: %1 %1 :نسخه نرم‎افزار - + Check for updates بررسی بروز‎رسانی - + Privacy Policy - PageSettingsApiLanguageList + PageSettingsApiAvailableCountries - + + Location for connection + + + + Unable change server location while there is an active connection + + PageSettingsApiDevices + + + Active Devices + + + + + Manage currently connected devices + + + + + You can find the identifier on the Support tab or, for older versions of the app, by tapping '+' and then the three dots at the top of the page. + + + + + (current device) + + + + + Support tag: + + + + + Last updated: + + + + + Cannot unlink device during active connection + + + + + Are you sure you want to unlink this device? + + + + + This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect. + + + + + Continue + + + + + Cancel + + + + + PageSettingsApiInstructions + + + Windows + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#windows + + + + + macOS + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#macos + + + + + Android + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#android + + + + + AndroidTV + + + + + https://docs.amnezia.org/ru/documentation/instructions/android_tv_connect/ + + + + + iOS + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#ios + + + + + Linux + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#linux + + + + + Routers + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#routers + + + + + How to connect on another device + + + + + Setup guides on the Amnezia website + + + + + PageSettingsApiNativeConfigs + + + Save AmneziaVPN config + ذخیره تنظیمات AmneziaVPN + + + + Configuration Files + + + + + For router setup or the AmneziaWG app + + + + + The configuration needs to be reissued + + + + + configuration file + + + + + Generate a new configuration file + + + + + The previously created one will stop working + + + + + Revoke the current configuration file + + + + + Config file saved + + + + + The config has been revoked + + + + + Generate a new %1 configuration file? + + + + + Revoke the current %1 configuration file? + + + + + Your previous configuration file will no longer work, and it will not be possible to connect using it + + + + + Download + + + + + Continue + + + + + Cancel + + + PageSettingsApiServerInfo - For the region - برای منطقه + برای منطقه - Price - قیمت + قیمت - Work period - مدت زمان کار + مدت زمان کار - - Valid until - - - - Speed - سرعت + سرعت - - Support tag + Copied + کپی شد + + + + Subscription Status - - Copied - کپی شد + + Valid Until + - + + Active Connections + + + + + Configurations have been updated for some countries. Download and install the updated configuration files + + + + + Subscription Key + + + + + Amnezia Premium subscription key + + + + + Save VPN key as a file + + + + + Copy VPN key + + + + + Configuration Files + + + + + Manage configuration files + + + + + Active Devices + + + + + Manage currently connected devices + + + + + Support + + + + + How to connect on another device + + + + Reload API config بارگذاری مجدد پیکربندی API - + Reload API config? آیا می‌خواهید پیکربندی API را دوباره بارگذاری کنید؟ - - + + + Continue ادامه دهید - - + + + Cancel لغو - + Cannot reload API config during active connection نمی‌توان پیکربندی API را در حین اتصال فعال دوباره بارگذاری کرد. - + + Unlink this device + + + + + Are you sure you want to unlink this device? + + + + + This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect. + + + + + Cannot unlink device during active connection + + + + Remove from application حذف از برنامه - + Remove from application? آیا می‌خواهید از برنامه حذف کنید؟ - + Cannot remove server during active connection نمی‌توان سرور را در حین اتصال فعال حذف کرد. + + PageSettingsApiSupport + + + Telegram + + + + + Email + + + + + support@amnezia.org + + + + + Email Billing & Orders + + + + + help@vpnpay.io + + + + + Website + وب سایت + + + + amnezia.org + + + + + Support + + + + + Our technical support specialists are available to assist you at any time + + + + + Support tag + + + + + Copied + کپی شد + + PageSettingsAppSplitTunneling @@ -1858,8 +2298,12 @@ Already installed containers were found on the server. All installed containers + Cannot change KillSwitch settings during active connection + + + Cannot change killSwitch settings during active connection - نمی‌توان تنظیمات Kill Switch را در حین اتصال فعال تغییر داد. + نمی‌توان تنظیمات Kill Switch را در حین اتصال فعال تغییر داد. @@ -1965,20 +2409,20 @@ Already installed containers were found on the server. All installed containers باز کردن پوشه گزارشات - - + + Save ذخیره - - + + Logs files (*.log) Logs files (*.log) - - + + Logs file saved فایل گزارشات ذخیره شد @@ -2012,32 +2456,32 @@ Already installed containers were found on the server. All installed containers گزارشات پاک شدند - + Client logs - + AmneziaVPN logs - + Open logs folder - + Export logs - + Service logs - + AmneziaVPN-service logs @@ -2077,103 +2521,103 @@ Already installed containers were found on the server. All installed containers - - - - + + + + Continue ادامه - - - - + + + + Cancel کنسل - + Check the server for previously installed Amnezia services چک کردن سرویس‎های نصب شده Amnezia بر روی سرور - + Add them to the application if they were not displayed اضافه کردن آنها به نرم‎افزار اگر نمایش داده نشده‎اند - + Reboot server سرور را دوباره راه‌اندازی کنید - + Do you want to reboot the server? آیا می‌خواهید سرور را دوباره راه‌اندازی کنید؟ - + The reboot process may take approximately 30 seconds. Are you sure you wish to proceed? فرآیند راه‌اندازی ممکن است حدود ۳۰ ثانیه طول بکشد. آیا مطمئن هستید که می‌خواهید ادامه دهید؟ - + Cannot reboot server during active connection نمی‌توان سرور را در حین اتصال فعال راه‌اندازی مجدد کرد. - + Do you want to remove the server from application? آیا می‌خواهید سرور را از برنامه حذف کنید؟ - + Cannot remove server during active connection نمی‌توان سرور را در حین اتصال فعال حذف کرد. - + Do you want to clear server from Amnezia software? آیا می‌خواهید سرور را از نرم‌افزار Amnezia پاک کنید؟ - + All users whom you shared a connection with will no longer be able to connect to it. همه کاربرانی که با آن‌ها ارتباطی به اشتراک گذاشته‌اید دیگر قادر به اتصال به آن نخواهند بود. - + Cannot clear server from Amnezia software during active connection نمی‌توان سرور را در حین اتصال فعال از نرم‌افزار Amnezia پاک کرد. - + Reset API config تنظیمات API را بازنشانی کنید - + Do you want to reset API config? آیا می خواهید پیکربندی API را بازنشانی کنید؟ - + Cannot reset API config during active connection نمی‌توان پیکربندی API را در حین اتصال فعال بازنشانی کرد. - + Remove server from application حذف کردن سرور از نرم‎افزار - + All installed AmneziaVPN services will still remain on the server. تمام سرویس‎های نصب‎شده Amnezia همچنان بر روی سرور باقی خواهند ماند. - + Clear server from Amnezia software پاک کردن سرور از نرم‎افزار Amnezia @@ -2181,32 +2625,25 @@ Already installed containers were found on the server. All installed containers PageSettingsServerInfo - - Subscription is valid until - - - - Server name - نام سرور + نام سرور - Save - ذخیره + ذخیره - + Protocols پروتکل‎ها - + Services سرویس‎ها - + Management مدیریت @@ -2326,17 +2763,17 @@ Already installed containers were found on the server. All installed containers حالت - + Remove حذف - + Continue ادامه - + Cancel کنسل @@ -2351,17 +2788,17 @@ Already installed containers were found on the server. All installed containers تنها سایت‌های موجود در اینجا از طریق VPN دسترسی داده خواهند شد - + website or IP وب‌سایت یا آدرس IP - + Import / Export Sites وارد کردن / صادر کردن وب‌سایت‌ها - + Import بارگذاری @@ -2377,29 +2814,29 @@ Already installed containers were found on the server. All installed containers - - + + Sites files (*.json) Sites files (*.json) - + Import a list of sites بارگذاری لیست سایت‎ها - + Replace site list جایگزین کردن لیست سایت - - + + Open sites file باز کردن فایل سایت‎ها - + Add imported sites to existing ones اضافه کردن سایت‎های بارگذاری شده به سایت‎های موجود @@ -2473,7 +2910,7 @@ It's okay as long as it's from someone you trust. چی داری؟ - + File with connection settings فایل شامل تنظیمات اتصال @@ -2532,80 +2969,65 @@ It's okay as long as it's from someone you trust. گزینه‌های اتصال دیگر - + Site Amnezia - + VPN by Amnezia VPN توسط Amnezia - + Connect to classic paid and free VPN services from Amnezia اتصال به سرویس‌های VPN کلاسیک پولی و رایگان از Amnezia - + Self-hosted VPN Self-hosted VPN - + Configure Amnezia VPN on your own server پیکربندی VPN Amnezia بر روی سرور خودتان - + Restore from backup بازیابی از پشتیبان - + - + Open backup file باز کردن فایل پشتیبان - + Backup files (*.backup) Backup files (*.backup) - - - - - - + Open config file باز کردن فایل تنظیمات - + QR code QR-Code - - - - - - + I have nothing من هیچی ندارم - - - - - Key as text متن شامل کلید @@ -2614,17 +3036,17 @@ It's okay as long as it's from someone you trust. PageSetupWizardCredentials - + Server IP address [:port] آدرس آی‎پی سرور (:پورت) - + Continue ادامه - + Enter the address in the format 255.255.255.255:88 آدرس را با فرمت 255.255.255.255:88 وارد کنید @@ -2634,47 +3056,47 @@ It's okay as long as it's from someone you trust. سرور خود را پیکربندی کنید - + 255.255.255.255:22 - + SSH Username - + Password or SSH private key رمز عبور یا کلید خصوصی SSH - + All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties تمام داده‎هایی که شما وارد می‎کنید به شدت محرمانه‎ است و با Amnezia یا هر شخص ثالث دیگری به اشتراک گذاشته نمی‎شود - + How to run your VPN server چگونه سرور VPN خود را اجرا کنید - + Where to get connection data, step-by-step instructions for buying a VPS داده‌های اتصال را از کجا دریافت کنید و دستورالعمل‌های مرحله به مرحله برای خرید یک VPS - + Ip address cannot be empty آدرس آی‎پی نمی‎تواند خالی باشد - + Login cannot be empty نام‎کاربری نمی‎تواند خالی باشد - + Password/private key cannot be empty پسورد یا کلید خصوصی نمی‎تواند خالی باشد @@ -2682,22 +3104,31 @@ It's okay as long as it's from someone you trust. PageSetupWizardEasy - What is the level of internet control in your region? - سطح کنترل اینترنت در منطقه شما چگونه است؟ + سطح کنترل اینترنت در منطقه شما چگونه است؟ + + + + Choose Installation Type + + Manual + + + + Choose a VPN protocol یک پروتکل VPN را انتخاب کنید - + Skip setup رد شدن از تنظیم - + Continue ادامه @@ -2892,23 +3323,23 @@ It's okay as long as it's from someone you trust. PageShare - + OpenVPN native format فرمت OpenVPN - + WireGuard native format فرمت WireGuard - + Connection ارتباط + - Server سرور @@ -2928,113 +3359,113 @@ It's okay as long as it's from someone you trust. فایل شامل تنظیمات ارتباط با - + Save OpenVPN config ذخیره تنظیمات OpenVPN - + Save WireGuard config ذخیره تنظیمات WireGuard - + Save AmneziaWG config تنظیمات AmneziaWG را ذخیره کنید - + Save Shadowsocks config ذخیره تنظیمات Shadowsocks - + Save Cloak config ذخیره تنظیمات Cloak - + Save XRay config ذخیره پیکربندی XRay - + For the AmneziaVPN app برای نرم‎افزار AmneziaVPN - + AmneziaWG native format فرمت بومی AmneziaWG - + Shadowsocks native format فرمت Shadowsocks - + Cloak native format فرمت Cloak - + XRay native format فرمت بومی XRay - + Share VPN Access اتصال vpn را به اشتراک بگذارید - + Share full access to the server and VPN به اشتراک گذاشتن دسترسی کامل به سرور و وی‎پی‎ان - + Use for your own devices, or share with those you trust to manage the server. برای دستگاه‎های خودتان استفاده کنید یا با آنهایی که برای مدیریت سرور به آن‎ها اعتماد دارید به اشتراک بگذارید. - - + + Users کاربران - + User name نام کاربری - + Search جستجو - + Creation date: %1 تاریخ ایجاد: %1 - + Latest handshake: %1 آخرین ارتباط: %1 - + Data received: %1 داده‌های دریافت شده: %1 - + Data sent: %1 داده‌های ارسال شده: %1 - + Allowed IPs: %1 @@ -3043,65 +3474,65 @@ It's okay as long as it's from someone you trust. تاریخ ایجاد: - + Rename تغییر نام - + Client name نام کلاینت - + Save ذخیره - + Revoke ابطال - + Revoke the config for a user - %1? لغو پیکربندی برای یک کاربر - %1? - + The user will no longer be able to connect to your server. کاربر دیگر نمی‎تواند به سرور وصل شود. - + Continue ادامه - + Cancel کنسل - + Share VPN access without the ability to manage the server به اشتراک گذاشتن دسترسی وی‎پی‎ان بدون امکان مدیریت سرور + - Protocol پروتکل + - Connection format فرمت ارتباط - - + + Share اشتراک‎گذاری @@ -3165,17 +3596,17 @@ It's okay as long as it's from someone you trust. PageStart - + Logging was disabled after 14 days, log files were deleted ثبت وقایع پس از ۱۴ روز غیرفعال شد و فایل‌های ثبت وقایع حذف شدند - + Settings restored from backup file تنظیمات از فایل پشتیبان بازیابی شد - + Logging is enabled. Note that logs will be automaticallydisabled after 14 days, and all log files will be deleted. @@ -3442,62 +3873,62 @@ It's okay as long as it's from someone you trust. Function not implemented - + Server check failed Server check failed - + Server port already used. Check for another software Server port already used. Check for another software - + Server error: Docker container missing Server error: Docker container missing - + Server error: Docker failed Server error: Docker failed - + Installation canceled by user Installation canceled by user - + The user is not a member of the sudo group کاربر عضو گروه sudo نیست - + SSH request was denied SSH request was denied - + SSH request was interrupted SSH request was interrupted - + SSH internal error SSH internal error - + Invalid private key or invalid passphrase entered Invalid private key or invalid passphrase entered - + The selected private key format is not supported, use openssh ED25519 key types or PEM key types The selected private key format is not supported, use openssh ED25519 key types or PEM key types - + Timeout connecting to server Timeout connecting to server @@ -3554,32 +3985,33 @@ It's okay as long as it's from someone you trust. Sftp error: No media was in remote drive - + The config does not contain any containers and credentials for connecting to the server تنظیمات شامل هیچ کانتینر یا اعتبارنامه‎ای برای اتصال به سرور نیست - + VPN connection error خطای اتصال VPN - + + Error when retrieving configuration from API خطا هنگام بازیابی پیکربندی از API - + This config has already been added to the application این پیکربندی قبلاً به برنامه اضافه شده است - + ErrorCode: %1. کد خطا: %1. - + OpenVPN config missing OpenVPN config missing @@ -3589,127 +4021,164 @@ It's okay as long as it's from someone you trust. Background service is not running - + + The selected protocol is not supported on the current platform + + + + Server error: Package manager error خطای سرور: خطای مدیر بسته - + + The sudo package is not pre-installed on the server + + + + + The server user's home directory is not accessible + + + + + Action not allowed in sudoers + + + + + The user's password is required + + + + SCP error: Generic failure SCP error: Generic failure - + OpenVPN management server error OpenVPN management server error - + OpenVPN executable missing OpenVPN executable missing - + Shadowsocks (ss-local) executable missing Shadowsocks (ss-local) executable missing - + Cloak (ck-client) executable missing Cloak (ck-client) executable missing - + Amnezia helper service error Amnezia helper service error - + OpenSSL failed OpenSSL failed - + Can't connect: another VPN connection is active Can't connect: another VPN connection is active - + Can't setup OpenVPN TAP network adapter Can't setup OpenVPN TAP network adapter - + VPN pool error: no available addresses VPN pool error: no available addresses - + Unable to open config file - + + VPN Protocols is not installed. + Please install VPN container at first + پروتکل وی‎پی‎ان نصب نشده است +لطفا کانتینر وی‎پی‎ان را نصب کنید + + + In the response from the server, an empty config was received در پاسخ از سرور، پیکربندی خالی دریافت شد - + SSL error occurred SSL error occurred - + Server response timeout on api request Server response timeout on api request - + Missing AGW public key - + Failed to decrypt response payload - + Missing list of available services - + + The limit of allowed configurations per subscription has been exceeded + + + + QFile error: The file could not be opened - + QFile error: An error occurred when reading from the file - + QFile error: The file could not be accessed - + QFile error: An unspecified error occurred - + QFile error: A fatal error occurred - + QFile error: The operation was aborted - + Internal error Internal error @@ -3719,32 +4188,28 @@ It's okay as long as it's from someone you trust. IPsec - Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions. - شدوساکس - ترافیک VPN را پنهان می کند، به طوری که مشابه ترافیک وب عادی می شود، اما ممکن است توسط سیستم های تجزیه و تحلیل در برخی از مناطق با سانسور شدید شناسایی شود. + شدوساکس - ترافیک VPN را پنهان می کند، به طوری که مشابه ترافیک وب عادی می شود، اما ممکن است توسط سیستم های تجزیه و تحلیل در برخی از مناطق با سانسور شدید شناسایی شود. - OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against active-probing detection. Ideal for bypassing blocking in regions with the highest levels of censorship. - OpenVPN روی Cloak - OpenVPN با VPN که به عنوان ترافیک وب پنهان می‌شود و مقاومت در برابر تشخیص فعال از طریق پیشرفته. ایده‌آل برای دور زدن مسدود کردن در مناطق با بالاترین سطوح سانسور. + OpenVPN روی Cloak - OpenVPN با VPN که به عنوان ترافیک وب پنهان می‌شود و مقاومت در برابر تشخیص فعال از طریق پیشرفته. ایده‌آل برای دور زدن مسدود کردن در مناطق با بالاترین سطوح سانسور. + + + XRay with REALITY - Suitable for countries with the highest level of internet censorship. Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods. + XRay با REALITY - مناسب برای کشورهایی با بالاترین سطح سانسور اینترنت. استتار ترافیک به عنوان ترافیک وب در سطح TLS و حفاظت در برابر شناسایی با روش‌های پروب فعال. - XRay with REALITY - Suitable for countries with the highest level of internet censorship. Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods. - XRay با REALITY - مناسب برای کشورهایی با بالاترین سطح سانسور اینترنت. استتار ترافیک به عنوان ترافیک وب در سطح TLS و حفاظت در برابر شناسایی با روش‌های پروب فعال. - - - IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss. It has native support on the latest versions of Android and iOS. IKEv2/IPsec - پروتکل مدرن و پایدار، کمی سریع‌تر از سایرین است و پس از قطع شدن سیگنال، اتصال را بازیابی می‌کند. از پشتیبانی بومی در آخرین نسخه‌های Android و iOS برخوردار است. - + Create a file vault on your server to securely store and transfer files. ساختن یک گنجانده فایل بر روی سرور شما برای ذخیره و انتقال ایمن فایل‌ها. - This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for protecting against blocking. OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client and the server. @@ -3763,7 +4228,7 @@ If there is a extreme level of Internet censorship in your region, we advise you * Not recognised by DPI analysis systems * Works over TCP network protocol, 443 port. - این ترکیبی از پروتکل OpenVPN و پلاگین Cloak به طور خاص برای محافظت در برابر مسدود کردن طراحی شده است. + این ترکیبی از پروتکل OpenVPN و پلاگین Cloak به طور خاص برای محافظت در برابر مسدود کردن طراحی شده است. OpenVPN ارتباط امن VPN را با رمزگذاری تمام ترافیک اینترنتی بین مشتری و سرور فراهم می‌کند. @@ -3783,7 +4248,6 @@ Cloak می‌تواند اطلاعات فراداده بسته را تغییر - A relatively new popular VPN protocol with a simplified architecture. WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. WireGuard is very susceptible to blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. @@ -3793,7 +4257,7 @@ WireGuard is very susceptible to blocking due to its distinct packet signatures. * Minimum number of settings * Easily recognised by DPI analysis systems, susceptible to blocking * Works over UDP network protocol. - یک پروتکل VPN محبوب نسبتا جدید با معماری ساده شده. + یک پروتکل VPN محبوب نسبتا جدید با معماری ساده شده. WireGuard اتصال VPN پایدار و عملکرد بالا را در همه دستگاه ها فراهم می کند. از تنظیمات رمزگذاری سخت کد شده استفاده می کند. WireGuard در مقایسه با OpenVPN دارای تأخیر کمتر و توان انتقال داده بهتر است. WireGuard به دلیل امضاهای بسته متمایز خود، بسیار مستعد مسدود شدن است. برخلاف برخی دیگر از پروتکل‌های VPN که از تکنیک‌های مبهم سازی استفاده می‌کنند، الگوهای امضای ثابت بسته‌های WireGuard را می‌توان به راحتی شناسایی کرد و بنابراین توسط Deep پیشرفته مسدود شدسیستم های بازرسی بسته (DPI) و سایر ابزارهای نظارت بر شبکه. @@ -3804,19 +4268,18 @@ WireGuard به دلیل امضاهای بسته متمایز خود، بسیار * روی پروتکل شبکه UDP کار می کند. - The REALITY protocol, a pioneering development by the creators of XRay, is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion. It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, thus presenting an authentic TLS certificate and data. This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship. - پروتکل REALITY، یک توسعه پیشگامانه توسط خالقان XRay، به‌طور خاص برای مقابله با بالاترین سطح سانسور اینترنتی طراحی شده است و از رویکرد نوآورانه‌ای برای دور زدن محدودیت‌ها استفاده می‌کند. + پروتکل REALITY، یک توسعه پیشگامانه توسط خالقان XRay، به‌طور خاص برای مقابله با بالاترین سطح سانسور اینترنتی طراحی شده است و از رویکرد نوآورانه‌ای برای دور زدن محدودیت‌ها استفاده می‌کند. REALITY به‌طور منحصربه‌فردی سانسورچیان را در مرحله دست‌دهی TLS شناسایی می‌کند و به‌صورت یکپارچه به‌عنوان پراکسی برای کاربران قانونی عمل می‌کند، در حالی که سانسورچیان را به سایت‌های معتبر مانند google.com هدایت می‌کند و در نتیجه یک گواهی TLS واقعی و داده‌های اصلی ارائه می‌دهد. این قابلیت پیشرفته، REALITY را از فناوری‌های مشابه متمایز می‌کند، زیرا می‌تواند ترافیک وب را بدون نیاز به پیکربندی‌های خاص، به‌عنوان ترافیک از سایت‌های تصادفی و معتبر جا بزند. برخلاف پروتکل‌های قدیمی‌تر مانند VMess، VLESS و انتقال XTLS-Vision، تشخیص نوآورانه "دوست یا دشمن" REALITY در مرحله دست‌دهی TLS امنیت را افزایش داده و از شناسایی توسط سیستم‌های پیشرفته DPI که از تکنیک‌های پروب فعال استفاده می‌کنند، جلوگیری می‌کند. این ویژگی REALITY را به یک راه‌حل قوی برای حفظ آزادی اینترنت در محیط‌هایی با سانسور شدید تبدیل می‌کند. - + IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol. One of its distinguishing features is its ability to swiftly switch between networks and devices, making it particularly adaptive in dynamic network environments. While it offers a blend of security, stability, and speed, it's essential to note that IKEv2 can be easily detected and is susceptible to blocking. @@ -3837,7 +4300,7 @@ While it offers a blend of security, stability, and speed, it's essential t * روی پروتکل شبکه UDP، پورت‎های 500 و 4500 کار می‎کند. - + DNS Service سرویس DNS @@ -3848,7 +4311,7 @@ While it offers a blend of security, stability, and speed, it's essential t - + Website in Tor network وب سایت در شبکه Tor @@ -3863,31 +4326,119 @@ While it offers a blend of security, stability, and speed, it's essential t پروتکل OpenVPN یکی از پروتکل‎های وی‎پی‎ان محبوب می‎باشد با تنظیمات و پیکربندی‎های قابل تغییر. از پروتکل امنیتی داخلی خود با تبادل کلید SSL/TLS استفاده می‎کند. - - WireGuard - New popular VPN protocol with high performance, high speed and low power consumption. Recommended for regions with low levels of censorship. - پروتکل WireGuard یک پروتکل وی‎پی‎ان جدید با عملکرد بسیار خوب، سرعت بالا و مصرف انرژی پایین. برای مناطقی که سطح سانسور پایینی دارند پیشنهاد می‎شود. + + Shadowsocks masks VPN traffic, making it resemble normal web traffic, but it may still be detected by certain analysis systems. + + + + + OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against active-probing detection. It is very resistant to detection, but offers low speed. + + + + + WireGuard - popular VPN protocol with high performance, high speed and low power consumption. + + + + + AmneziaWG is a special protocol from Amnezia based on WireGuard. It provides high connection speed and ensures stable operation even in the most challenging network conditions. + + + + + XRay with REALITY masks VPN traffic as web traffic and protects against active probing. It is highly resistant to detection and offers high speed. + + + + + OpenVPN stands as one of the most popular and time-tested VPN protocols available. +It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, catering to a wide range of devices and operating systems. Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, which continually reinforces its security. With a strong balance of performance, security, and compatibility, OpenVPN remains a top choice for privacy-conscious individuals and businesses alike. + +* Available in the AmneziaVPN across all platforms +* Normal power consumption on mobile devices +* Flexible customisation to suit user needs to work with different operating systems and devices +* Recognised by DPI systems and therefore susceptible to blocking +* Can operate over both TCP and UDP network protocols. + + + + + This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for protecting against detection. + +OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client and the server. + +Cloak protects OpenVPN from detection. + +Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, and also protects the VPN from detection by Active Probing. This makes it very resistant to being detected + +Immediately after receiving the first data packet, Cloak authenticates the incoming connection. If authentication fails, the plugin masks the server as a fake website and your VPN becomes invisible to analysis systems. + +* Available in the AmneziaVPN across all platforms +* High power consumption on mobile devices +* Flexible settings +* Not recognised by detection systems +* Works over TCP network protocol, 443 port. + + + + + + A relatively new popular VPN protocol with a simplified architecture. +WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. +WireGuard is very susceptible to detection and blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. + +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Easily recognised by DPI analysis systems, susceptible to blocking +* Works over UDP network protocol. + + + + + A modern iteration of the popular VPN protocol, AmneziaWG builds upon the foundation set by WireGuard, retaining its simplified architecture and high-performance capabilities across devices. +While WireGuard is known for its efficiency, it had issues with being easily detected due to its distinct packet signatures. AmneziaWG solves this problem by using better obfuscation methods, making its traffic blend in with regular internet traffic. +This means that AmneziaWG keeps the fast performance of the original while adding an extra layer of stealth, making it a great choice for those wanting a fast and discreet VPN connection. + +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Not recognised by traffic analysis systems +* Works over UDP network protocol. + + + + + The REALITY protocol, a pioneering development by the creators of XRay, is designed to provide the highest level of protection against detection through its innovative approach to security and privacy. +It uniquely identifies attackers during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting attackers to genuine websites, thus presenting an authentic TLS certificate and data. +This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. +Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security. This makes REALITY a robust solution for maintaining internet freedom. + + + + WireGuard - New popular VPN protocol with high performance, high speed and low power consumption. Recommended for regions with low levels of censorship. + پروتکل WireGuard یک پروتکل وی‎پی‎ان جدید با عملکرد بسیار خوب، سرعت بالا و مصرف انرژی پایین. برای مناطقی که سطح سانسور پایینی دارند پیشنهاد می‎شود. - AmneziaWG - Special protocol from Amnezia, based on WireGuard. It's fast like WireGuard, but very resistant to blockages. Recommended for regions with high levels of censorship. - پروتکل AmneziaWG یک پروتکل اختصاصی Amnezia که بر اساس WireGaurd کار میکند. به اندازه WireGaurd پرسرعت است و در عین حال بسیار مقاوم به بلاک شدن توسط شبکه ست. مناسب برای مناطق با سطح سانسور بالاست. + پروتکل AmneziaWG یک پروتکل اختصاصی Amnezia که بر اساس WireGaurd کار میکند. به اندازه WireGaurd پرسرعت است و در عین حال بسیار مقاوم به بلاک شدن توسط شبکه ست. مناسب برای مناطق با سطح سانسور بالاست. IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss. پروتکل IKEv2/IPsec پروتکلی پایدار و مدرن که مقداری سریعتر از سایر پروتکل‎هاست. بعد از قطع سیگنال دوباره اتصال را بازیابی می‎کند. - + Deploy a WordPress site on the Tor network in two clicks. با دو کلیک یک سایت وردپرس در شبکه Tor راه‎اندازی کنید. - + Replace the current DNS server with your own. This will increase your privacy level. سرور DNS را با مال خودتان جایگزین کنید. این کار سطح حریم خصوصی شما را افزایش می‎دهد. - OpenVPN stands as one of the most popular and time-tested VPN protocols available. It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, catering to a wide range of devices and operating systems. Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, which continually reinforces its security. With a strong balance of performance, security, and compatibility, OpenVPN remains a top choice for privacy-conscious individuals and businesses alike. @@ -3896,7 +4447,7 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for * Flexible customisation to suit user needs to work with different operating systems and devices * Recognised by DPI analysis systems and therefore susceptible to blocking * Can operate over both TCP and UDP network protocols. - پروتکل OpenVPN یکی از پروتکل‎های محبوب و تست شده در دسترس می‎باشد که از پروتکل امنیتی مخصوص خودش استفاده میکند. + پروتکل OpenVPN یکی از پروتکل‎های محبوب و تست شده در دسترس می‎باشد که از پروتکل امنیتی مخصوص خودش استفاده میکند. از امتیازات SSL/TLS برای رمزنگاری و تبادل کلید استفاده میکند. همچنین OpenVPN از روش‎های چندگانه‎ای برای احراز هویت پشتیبانی می‎کند که آن را قابل انطباق و منعطف میکند. از طیف وسیعی از دستگاه‎ها و سیستم عامل‎ها نیز پشتیبانی می‎کند. @@ -3910,7 +4461,7 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for * امکان کار بر روی دو پروتکل TCP و UDP - + Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection.However, certain traffic analysis systems might still detect a Shadowsocks connection. Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol. * Available in the AmneziaVPN only on desktop platforms @@ -3925,7 +4476,6 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for * عملکرد بر روی پروتکل شبکه TCP - A modern iteration of the popular VPN protocol, AmneziaWG builds upon the foundation set by WireGuard, retaining its simplified architecture and high-performance capabilities across devices. While WireGuard is known for its efficiency, it had issues with being easily detected due to its distinct packet signatures. AmneziaWG solves this problem by using better obfuscation methods, making its traffic blend in with regular internet traffic. This means that AmneziaWG keeps the fast performance of the original while adding an extra layer of stealth, making it a great choice for those wanting a fast and discreet VPN connection. @@ -3935,7 +4485,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin * Minimum number of settings * Not recognised by DPI analysis systems, resistant to blocking * Works over UDP network protocol. - یک نسخه مدرن از پروتکل وی‎پی‎ان محبوب، AmneziaWG بر روی پایه‎های WireGuard ساخته شده و معماری ساده و عملکرد بالای آن را بر روی تمام دستگاه‎ها حفظ کرده است. + یک نسخه مدرن از پروتکل وی‎پی‎ان محبوب، AmneziaWG بر روی پایه‎های WireGuard ساخته شده و معماری ساده و عملکرد بالای آن را بر روی تمام دستگاه‎ها حفظ کرده است. در حالی‎که WireGuard به دلیل بازدهی آن شناخته می‎شود اما امکان شناسایی شدن بالا به دلیل امضای ثابت بسته داده‎های آن یکی از مشکلات آن است. AmneziaWG این مشکل را با استفاده از متدهای مخفی سازی حل کرده و در نتیجه ترافیک آن همانند با ترافیک عادی اینترنت است. این بدین معنی است که AmneziaWG عملکرد سریع اصلی را حفظ کرده و یک لایه پنهان سازی به آن اضافه کرده که باعث می‎شود که به انتخابی عالی برای آنها که وی‎پی‎ان امن و سریع می‎خواهند تبدیل شود. @@ -3946,7 +4496,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin * کار بر روی پروتکل شبکه UDP - + After installation, Amnezia will create a file storage on your server. You will be able to access it using @@ -3972,7 +4522,7 @@ For more detailed information, you can - + SOCKS5 proxy server سرور پروکسی SOCKS5 @@ -4163,6 +4713,19 @@ For more detailed information, you can + + RenameServerDrawer + + + Server name + نام سرور + + + + Save + ذخیره + + SelectLanguageDrawer @@ -4213,24 +4776,24 @@ For more detailed information, you can ShareConnectionDrawer - - + + Save AmneziaVPN config ذخیره تنظیمات AmneziaVPN - + Share اشتراک‎گذاری - + Copy کپی - - + + Copied کپی شد @@ -4240,12 +4803,12 @@ For more detailed information, you can کپی‎کردن متن تنظیمات - + Show connection settings نمایش تنظیمات ارتباط - + To read the QR code in the Amnezia app, select "Add server" → "I have data to connect" → "QR code, key or settings file" برای خواندن QR Code در نرم‎افزار AmneziaVPN "اضافه کردن سرور" -> "من داده برای اتصال دارم" -> "QR Code، کلید یا فایل تنظیمات" @@ -4258,37 +4821,37 @@ For more detailed information, you can فرمت هاست شبیه آدرس آی‎پی یا نام دامنه نیست - + New site added: %1 سایت جدید اضافه‎شد: %1 - + Site removed: %1 سایت حذف شد: %1 - + Can't open file: %1 فایل باز نشد: %1 - + Failed to parse JSON data from file: %1 مشکل در تحلیل داده‎های JSON در فایل: %1 - + The JSON data is not an array in file: %1 داده‎های JSON در فایل به صورت آرایه نیستند: %1 - + Import completed بارگذاری کامل شد - + Export completed خروجی گرفتن کامل شد @@ -4329,7 +4892,7 @@ For more detailed information, you can TextFieldWithHeaderType - + The field can't be empty این فیلد نمی‌تواند خالی باشد. @@ -4337,7 +4900,7 @@ For more detailed information, you can VpnConnection - + Mbps Mbps @@ -4388,33 +4951,39 @@ For more detailed information, you can amnezia::ContainerProps - Low - پایین + پایین - High - متوسط یا بالا + متوسط یا بالا Extreme شدید - I just want to increase the level of my privacy. - من فقط میخواهم سطح حریم شخصی خودم را بالا ببرم + من فقط میخواهم سطح حریم شخصی خودم را بالا ببرم - I want to bypass censorship. This option recommended in most cases. - من میخواهم از سانسور عبور کنم. این گزینه در اکثر موارد توصیه می‎‌شود + من میخواهم از سانسور عبور کنم. این گزینه در اکثر موارد توصیه می‎‌شود Most VPN protocols are blocked. Recommended if other options are not working. اکثر پروتکل‎های وی‎پی‎ان مسدود شده‎اند. در مواردی که بقیه گزینه‎ها کار نمی‎کنند توصی می‎شود. + + + Automatic + + + + + AmneziaWG protocol will be installed. It provides high connection speed and ensures stable operation even in the most challenging network conditions. + + main2 diff --git a/client/translations/amneziavpn_hi_IN.ts b/client/translations/amneziavpn_hi_IN.ts index a3fe2011..18dcfc65 100644 --- a/client/translations/amneziavpn_hi_IN.ts +++ b/client/translations/amneziavpn_hi_IN.ts @@ -4,60 +4,113 @@ AdLabel - - Amnezia Premium - for access to any website + + Amnezia Premium - for access to all websites and online resources + + + + + ApiAccountInfoModel + + + + Active + + + + + Inactive + + + + + %1 out of %2 + + + + + Classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. Speeds up to 200 Mbps + + + + + Free unlimited access to a basic set of websites such as Facebook, Instagram, Twitter (X), Discord, Telegram and more. YouTube is not included in the free plan. + + + + + amnezia_free_support_bot + + + + + amnezia_premium_support_bot + + + + + ApiConfigsController + + + %1 installed successfully. + + + + + API config reloaded + + + + + Successfully changed the country of connection to %1 ApiServicesModel - - Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s - - - - - VPN to access blocked sites in regions with high levels of Internet censorship. - - - - + <p><a style="color: #EB5757;">Not available in your region. If you have VPN enabled, disable it, return to the previous screen, and try again.</a> - - Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship. + + Amnezia Premium is classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. Speeds up to %1 Mbps. - - Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship + + + AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan. - + + Amnezia Premium is classic VPN for for seamless work, downloading large files, and watching videos. Access all websites and online resources. + + + + %1 MBit/s - + %1 days - + VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, <a href="%1/free" style="color: #FBB26A;">more details on the website.</a> - + Free - + %1 $/month @@ -96,62 +149,59 @@ ConnectionController - - - - + + + + Connect कनेक्ट - VPN Protocols is not installed. Please install VPN container at first - पीएन प्रोटोकॉल स्थापित नहीं है. + पीएन प्रोटोकॉल स्थापित नहीं है. कृपया पहले वीपीएन कंटेनर स्थापित करें - + Connected जुड़ा हुआ - The selected protocol is not supported on the current platform - चयनित प्रोटोकॉल वर्तमान प्लेटफ़ॉर्म पर समर्थित नहीं है + चयनित प्रोटोकॉल वर्तमान प्लेटफ़ॉर्म पर समर्थित नहीं है - unable to create configuration - कॉन्फ़िगरेशन बनाने में असमर्थ + कॉन्फ़िगरेशन बनाने में असमर्थ - + Connecting... कनेक्ट... - + Reconnecting... पुनः कनेक्ट हो रहा है... - + Disconnecting... डिस्कनेक्ट हो रहा है... - + Preparing... तैयार कर रहे हैं... - + Settings updated successfully, reconnnection... सेटिंग्स सफलतापूर्वक अपडेट हो गईं... - + Settings updated successfully सेटिंग्स सफलतापूर्वक अपडेट हो गईं @@ -270,102 +320,92 @@ Can't be disabled for current server अमान्य कॉन्फ़िगरेशन फ़ाइल - + Scanned %1 of %2. %2 में से %1 स्कैन किया गया. - - In the imported configuration, potentially dangerous lines were found: + + This configuration contains an OpenVPN setup. OpenVPN configurations can include malicious scripts, so only add it if you fully trust the provider of this config. + + + + + <br>In the imported configuration, potentially dangerous lines were found: InstallController - + %1 installed successfully. %1 सफलतापूर्वक स्थापित हुआ. - + %1 is already installed on the server. %1 पहले से ही सर्वर पर स्थापित है. - + Added containers that were already installed on the server सर्वर पर पहले से स्थापित कंटेनर जोड़े गए - + Already installed containers were found on the server. All installed containers have been added to the application सर्वर पर पहले से स्थापित कंटेनर पाए गए। सभी स्थापित कंटेनरों को एप्लिकेशन में जोड़ दिया गया है - + Settings updated successfully सेटिंग्स सफलतापूर्वक अपडेट हो गईं - + Server '%1' was rebooted सर्वर '%1' रीबूट किया गया था - + Server '%1' was removed सर्वर '%1' रीबूट किया गया था - + All containers from server '%1' have been removed सर्वर '%1' से सभी कंटेनर हटा दिए गए हैं - + %1 has been removed from the server '%2' %1 को सर्वर '%2' से हटा दिया गया है - + Api config removed - + %1 cached profile cleared %1 कैश्ड प्रोफ़ाइल साफ़ की गई - + Please login as the user कृपया उपयोगकर्ता के रूप में लॉगिन करें - + Server added successfully सर्वर सफलतापूर्वक जोड़ा गया - - - %1 installed successfully. - - - - - API config reloaded - - - - - Successfully changed the country of connection to %1 - - InstalledAppsDrawer @@ -476,12 +516,12 @@ Already installed containers were found on the server. All installed containers स्प्लिट टनलिंग अक्षम - + VPN protocol VPN प्रोटोकॉल - + Servers सर्वर @@ -1331,22 +1371,22 @@ Already installed containers were found on the server. All installed containers एप्लिकेशन - + Backup बैकअप - + About AmneziaVPN AmneziaVPN के बारे में - + Dev console - + Close application एप्लिकेशन बंद करो @@ -1354,17 +1394,17 @@ Already installed containers were found on the server. All installed containers PageSettingsAbout - + Support Amnezia Amnezia का समर्थन करें - + Amnezia is a free and open-source application. You can support the developers if you like it. एमनेज़िया एक निःशुल्क और ओपन-सोर्स एप्लिकेशन है। यदि आपको यह पसंद है तो आप डेवलपर्स का समर्थन कर सकते हैं।. - + Contacts संपर्क @@ -1398,139 +1438,491 @@ Already installed containers were found on the server. All installed containers समीक्षाओं और बग रिपोर्टों के लिए - Copied - कॉपी किया गया + कॉपी किया गया - + + mailto:support@amnezia.org + + + + GitHub GitHub - + Discover the source code - + https://github.com/amnezia-vpn/amnezia-client https://github.com/amnezia-vpn/amnezia-client - + Website वेबसाइट - + Visit official website - + Software version: %1 सॉफ़्टवेयर संस्करण: %1 - + Check for updates अद्यतन के लिए जाँच - + Privacy Policy गोपनीयता नीति - PageSettingsApiLanguageList + PageSettingsApiAvailableCountries - + + Location for connection + + + + Unable change server location while there is an active connection - PageSettingsApiServerInfo + PageSettingsApiDevices - - For the region + + Active Devices - - Price + + Manage currently connected devices - - Work period + + You can find the identifier on the Support tab or, for older versions of the app, by tapping '+' and then the three dots at the top of the page. - - Valid until + + (current device) - - Speed + + Support tag: - - Support tag + + Last updated: - - Copied - कॉपी किया गया - - - - Reload API config + + Cannot unlink device during active connection - - Reload API config? + + Are you sure you want to unlink this device? - - + + This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect. + + + + Continue जारी रखना - - + + Cancel + रद्द करना + + + + PageSettingsApiInstructions + + + Windows + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#windows + + + + + macOS + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#macos + + + + + Android + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#android + + + + + AndroidTV + + + + + https://docs.amnezia.org/ru/documentation/instructions/android_tv_connect/ + + + + + iOS + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#ios + + + + + Linux + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#linux + + + + + Routers + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#routers + + + + + How to connect on another device + + + + + Setup guides on the Amnezia website + + + + + PageSettingsApiNativeConfigs + + + Save AmneziaVPN config + AmneziaVPN कॉन्फ़िगरेशन सहेजें + + + + Configuration Files + + + + + For router setup or the AmneziaWG app + + + + + The configuration needs to be reissued + + + + + configuration file + + + + + Generate a new configuration file + + + + + The previously created one will stop working + + + + + Revoke the current configuration file + + + + + Config file saved + + + + + The config has been revoked + + + + + Generate a new %1 configuration file? + + + + + Revoke the current %1 configuration file? + + + + + Your previous configuration file will no longer work, and it will not be possible to connect using it + + + + + Download + + + + + Continue + जारी रखना + + + + Cancel + रद्द करना + + + + PageSettingsApiServerInfo + + Copied + कॉपी किया गया + + + + Subscription Status + + + + + Valid Until + + + + + Active Connections + + + + + Configurations have been updated for some countries. Download and install the updated configuration files + + + + + Subscription Key + + + + + Amnezia Premium subscription key + + + + + Save VPN key as a file + + + + + Copy VPN key + + + + + Configuration Files + + + + + Manage configuration files + + + + + Active Devices + + + + + Manage currently connected devices + + + + + Support + + + + + How to connect on another device + + + + + Reload API config + + + + + Reload API config? + + + + + + + Continue + जारी रखना + + + + + Cancel रद्द करना - + Cannot reload API config during active connection - + + Unlink this device + + + + + Are you sure you want to unlink this device? + + + + + This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect. + + + + + Cannot unlink device during active connection + + + + Remove from application - + Remove from application? - + Cannot remove server during active connection सक्रिय कनेक्शन के दौरान सर्वर को हटाया नहीं जा सकता + + PageSettingsApiSupport + + + Telegram + + + + + Email + + + + + support@amnezia.org + + + + + Email Billing & Orders + + + + + help@vpnpay.io + + + + + Website + वेबसाइट + + + + amnezia.org + + + + + Support + + + + + Our technical support specialists are available to assist you at any time + + + + + Support tag + + + + + Copied + कॉपी किया गया + + PageSettingsAppSplitTunneling @@ -1838,8 +2230,12 @@ Already installed containers were found on the server. All installed containers + Cannot change KillSwitch settings during active connection + + + Cannot change killSwitch settings during active connection - सक्रिय कनेक्शन के दौरान किलस्विच सेटिंग्स को नहीं बदला जा सकता + सक्रिय कनेक्शन के दौरान किलस्विच सेटिंग्स को नहीं बदला जा सकता @@ -1930,20 +2326,20 @@ Already installed containers were found on the server. All installed containers लॉग के साथ फ़ोल्डर खोलें - - + + Save सहेजें - - + + Logs files (*.log) लॉग फ़ाइलें (*.log) - - + + Logs file saved लॉग फ़ाइल सहेजी गई @@ -1977,32 +2373,32 @@ Already installed containers were found on the server. All installed containers लॉग साफ़ कर दिए गए हैं - + Client logs - + AmneziaVPN logs - + Open logs folder - + Export logs - + Service logs - + AmneziaVPN-service logs @@ -2025,12 +2421,12 @@ Already installed containers were found on the server. All installed containers कोई नया स्थापित कंटेनर नहीं मिला - + Do you want to reboot the server? क्या आप सर्वर को रीबूट करना चाहते हैं? - + Do you want to clear server from Amnezia software? क्या आप एमनेज़िया सॉफ़्टवेयर से सर्वर साफ़ करना चाहते हैं? @@ -2040,93 +2436,93 @@ Already installed containers were found on the server. All installed containers - - - - + + + + Continue जारी रखना - - - - + + + + Cancel रद्द करना - + Check the server for previously installed Amnezia services पहले से स्थापित एमनेज़िया सेवाओं के लिए सर्वर की जाँच करें - + Add them to the application if they were not displayed यदि वे प्रदर्शित नहीं थे तो उन्हें एप्लिकेशन में जोड़ें - + Reboot server सर्वर रीबूट करें - + The reboot process may take approximately 30 seconds. Are you sure you wish to proceed? रीबूट प्रक्रिया में लगभग 30 सेकंड लग सकते हैं। आप निश्चित है आप आगे बढ़ना चाहते है? - + Cannot reboot server during active connection सक्रिय कनेक्शन के दौरान सर्वर को रीबूट नहीं किया जा सकता - + Remove server from application एप्लिकेशन से सर्वर हटाएं - + Do you want to remove the server from application? क्या आप एप्लिकेशन से सर्वर हटाना चाहते हैं? - + Cannot remove server during active connection सक्रिय कनेक्शन के दौरान सर्वर को हटाया नहीं जा सकता - + All users whom you shared a connection with will no longer be able to connect to it. वे सभी उपयोगकर्ता जिनके साथ आपने कनेक्शन साझा किया था, वे अब इससे कनेक्ट नहीं हो पाएंगे. - + Cannot clear server from Amnezia software during active connection सक्रिय कनेक्शन के दौरान एमनेज़िया सॉफ़्टवेयर से सर्वर साफ़ नहीं किया जा सकता - + Reset API config एपीआई कॉन्फिगरेशन रीसेट करें - + Do you want to reset API config? क्या आप एपीआई कॉन्फिगरेशन रीसेट करना चाहते हैं? - + Cannot reset API config during active connection सक्रिय कनेक्शन के दौरान एपीआई कॉन्फिगरेशन को रीसेट नहीं किया जा सकता - + All installed AmneziaVPN services will still remain on the server. सभी स्थापित AmneziaVPN सेवाएँ अभी भी सर्वर पर रहेंगी. - + Clear server from Amnezia software एमनेज़िया सॉफ़्टवेयर से सर्वर साफ़ करें @@ -2134,32 +2530,25 @@ Already installed containers were found on the server. All installed containers PageSettingsServerInfo - - Subscription is valid until - - - - Server name - सर्वर का नाम + सर्वर का नाम - Save - सहेजें + सहेजें - + Protocols प्रोटोकॉल - + Services सेवाएं - + Management प्रबंध @@ -2279,17 +2668,17 @@ Already installed containers were found on the server. All installed containers तरीका - + Remove निकालना - + Continue जारी रखना - + Cancel रद्द करना @@ -2304,17 +2693,17 @@ Already installed containers were found on the server. All installed containers सक्रिय कनेक्शन के दौरान स्प्लिट टनलिंग सेटिंग्स को नहीं बदला जा सकता - + website or IP वेबसाइट या आईपी - + Import / Export Sites आयात/निर्यात साइटें - + Import आयात @@ -2330,29 +2719,29 @@ Already installed containers were found on the server. All installed containers - - + + Sites files (*.json) - + Import a list of sites साइटों की सूची आयात करें - + Replace site list साइट सूची बदलें - - + + Open sites file साइट फ़ाइल खोलें - + Add imported sites to existing ones आयातित साइटों को मौजूदा साइटों में जोड़ें @@ -2472,85 +2861,70 @@ Already installed containers were found on the server. All installed containers - + Site Amnezia - + VPN by Amnezia - + Connect to classic paid and free VPN services from Amnezia - + Self-hosted VPN - + Configure Amnezia VPN on your own server - + Restore from backup बैकअप से बहाल करना - + - + Open backup file बैकअप फ़ाइल खोलें - + Backup files (*.backup) बैकअप फ़ाइलें (*.backup) - + File with connection settings कनेक्शन सेटिंग्स वाली फ़ाइल - - - - - - + Open config file कॉन्फ़िग फ़ाइल खोलें - + QR code क्यू आर संहिता - - - - - - + I have nothing मेरे पास कुछ नहीं है - - - - - Key as text पाठ के रूप में कुंजी @@ -2564,62 +2938,62 @@ Already installed containers were found on the server. All installed containers अपना सर्वर कॉन्फ़िगर करें - + Server IP address [:port] सर्वर आईपी पता [:पोर्ट] - + Continue जारी रखना - + All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties आपके द्वारा दर्ज किया गया सभी डेटा पूरी तरह से गोपनीय रहेगा और एमनेज़िया या किसी तीसरे पक्ष को साझा या प्रकट नहीं किया जाएगा - + 255.255.255.255:22 255.255.255.255:22 - + SSH Username SSH उपयोगकर्ता नाम - + Password or SSH private key पासवर्ड या SSH निजी कुंजी - + How to run your VPN server - + Where to get connection data, step-by-step instructions for buying a VPS - + Ip address cannot be empty आईपी ​​पता खाली नहीं हो सकता - + Enter the address in the format 255.255.255.255:88 पता 255.255.255.255:88 प्रारूप में दर्ज करें - + Login cannot be empty लॉगिन खाली नहीं हो सकता - + Password/private key cannot be empty पासवर्ड/निजी कुंजी खाली नहीं हो सकती @@ -2627,22 +3001,31 @@ Already installed containers were found on the server. All installed containers PageSetupWizardEasy - What is the level of internet control in your region? - आपके क्षेत्र में इंटरनेट नियंत्रण का स्तर क्या है? + आपके क्षेत्र में इंटरनेट नियंत्रण का स्तर क्या है? + + + + Choose Installation Type + + Manual + + + + Choose a VPN protocol एक वीपीएन प्रोटोकॉल चुनें - + Continue जारी रखना - + Skip setup सेटअप छोड़ें @@ -2837,37 +3220,37 @@ Already installed containers were found on the server. All installed containers PageShare - + Save OpenVPN config OpenVPN कॉन्फ़िगरेशन सहेजें - + Save WireGuard config वायरगार्ड कॉन्फ़िगरेशन सहेजें - + Save AmneziaWG config AmneziaWG कॉन्फ़िगरेशन सहेजें - + Save Shadowsocks config शैडोसॉक्स कॉन्फ़िगरेशन सहेजें - + Save Cloak config क्लोक कॉन्फ़िगरेशन सहेजें - + Save XRay config एक्सरे कॉन्फिगरेशन सहेजें - + For the AmneziaVPN app AmneziaVPN ऐप के लिए @@ -2876,83 +3259,83 @@ Already installed containers were found on the server. All installed containers OpenVpn मूल स्वरूप - + WireGuard native format वायरगार्ड मूल प्रारूप - + AmneziaWG native format AmneziaWG मूल प्रारूप - + Shadowsocks native format शैडोसॉक्स मूल प्रारूप - + Cloak native format लबादा देशी स्वरूप - + XRay native format एक्सरे देशी प्रारूप - + Share VPN Access VPN एक्सेस साझा करें - + Share full access to the server and VPN सर्वर और वीपीएन तक पूर्ण पहुंच साझा करें - + Use for your own devices, or share with those you trust to manage the server. अपने स्वयं के उपकरणों के लिए उपयोग करें, या सर्वर को प्रबंधित करने के लिए उन लोगों के साथ साझा करें जिन पर आप भरोसा करते हैं. - - + + Users उपयोगकर्ताओं - + Share VPN access without the ability to manage the server सर्वर को प्रबंधित करने की क्षमता के बिना वीपीएन एक्सेस साझा करें - + Search खोज - + Creation date: %1 निर्माण दिनांक: %1 - + Latest handshake: %1 नवीनतम हाथ मिलाना: %1 - + Data received: %1 प्राप्त डेटा: %1 - + Data sent: %1 डेटा भेजा गया: %1 - + Allowed IPs: %1 @@ -2961,53 +3344,53 @@ Already installed containers were found on the server. All installed containers निर्माण तिथि: - + Rename नाम बदलें - + Client name ग्राहक नाम - + Save सहेजें - + Revoke निरस्त करें - + Revoke the config for a user - %1? किसी उपयोक्ता के लिए कॉन्फ़िगरेशन निरस्त करें - %1? - + The user will no longer be able to connect to your server. उपयोगकर्ता अब आपके सर्वर से कनेक्ट नहीं हो पाएगा. - + Continue जारी रखना - + Cancel रद्द करना - + Connection कनेक्शन + - Server सर्वर @@ -3017,8 +3400,8 @@ Already installed containers were found on the server. All installed containers कनेक्शन सेटिंग्स वाली फ़ाइल + - Protocol शिष्टाचार @@ -3033,24 +3416,24 @@ Already installed containers were found on the server. All installed containers कॉन्फ़िगरेशन निरस्त कर दिया गया - + OpenVPN native format - + User name उपयोगकर्ता नाम + - Connection format कनेक्शन प्रारूप - - + + Share शेयर करना @@ -3114,17 +3497,17 @@ Already installed containers were found on the server. All installed containers PageStart - + Logging was disabled after 14 days, log files were deleted 14 दिनों के बाद लॉगिंग अक्षम कर दी गई, लॉग फ़ाइलें हटा दी गईं - + Settings restored from backup file बैकअप फ़ाइल से सेटिंग्स पुनर्स्थापित की गईं - + Logging is enabled. Note that logs will be automaticallydisabled after 14 days, and all log files will be deleted. @@ -3382,7 +3765,7 @@ Already installed containers were found on the server. All installed containers - + SOCKS5 proxy server @@ -3408,217 +3791,255 @@ Already installed containers were found on the server. All installed containers - + + The selected protocol is not supported on the current platform + चयनित प्रोटोकॉल वर्तमान प्लेटफ़ॉर्म पर समर्थित नहीं है + + + Server check failed सर्वर जाँच विफल रही - + Server port already used. Check for another software सर्वर पोर्ट पहले ही उपयोग किया जा चुका है. किसी अन्य सॉफ़्टवेयर की जाँच करें - + Server error: Docker container missing सर्वर त्रुटि: डॉकर कंटेनर गायब है - + Server error: Docker failed सर्वर त्रुटि: डॉकर विफल - + Installation canceled by user उपयोगकर्ता द्वारा इंस्टॉलेशन रद्द कर दिया गया - + The user is not a member of the sudo group उपयोगकर्ता sudo समूह का सदस्य नहीं है - + Server error: Package manager error सर्वर त्रुटि: पैकेज प्रबंधक त्रुटि + + + The sudo package is not pre-installed on the server + + + The server user's home directory is not accessible + + + + + Action not allowed in sudoers + + + + + The user's password is required + + + + SSH request was denied SSH अनुरोध अस्वीकार कर दिया गया - + SSH request was interrupted SSH अनुरोध बाधित हो गया था - + SSH internal error SSH आंतरिक त्रुटि - + Invalid private key or invalid passphrase entered अमान्य निजी कुंजी या अमान्य पासफ़्रेज़ दर्ज किया गया - + The selected private key format is not supported, use openssh ED25519 key types or PEM key types चयनित निजी कुंजी प्रारूप समर्थित नहीं है, ओपनश ED25519 कुंजी प्रकार या PEM कुंजी प्रकार का उपयोग करें - + Timeout connecting to server सर्वर से कनेक्ट होने का समय समाप्त - + Unable to open config file - + + VPN Protocols is not installed. + Please install VPN container at first + पीएन प्रोटोकॉल स्थापित नहीं है. +कृपया पहले वीपीएन कंटेनर स्थापित करें + + + VPN connection error VPN कनेक्शन त्रुटि - + + Error when retrieving configuration from API एपीआई से कॉन्फ़िगरेशन पुनर्प्राप्त करते समय त्रुटि - + This config has already been added to the application यह कॉन्फ़िगरेशन पहले ही एप्लिकेशन में जोड़ा जा चुका है - + In the response from the server, an empty config was received - + SSL error occurred - + Server response timeout on api request - + Missing AGW public key - + Failed to decrypt response payload - + Missing list of available services - + + The limit of allowed configurations per subscription has been exceeded + + + + ErrorCode: %1. ErrorCode: %1. - + OpenVPN config missing OpenVPN प्रबंधन सर्वर त्रुटि - + SCP error: Generic failure एससीपी त्रुटि: सामान्य विफलता - + OpenVPN management server error OpenVPN प्रबंधन सर्वर त्रुटि - + OpenVPN executable missing OpenVPN निष्पादन योग्य गायब है - + Shadowsocks (ss-local) executable missing शैडोसॉक्स (एसएस-स्थानीय) निष्पादन योग्य गायब है - + Cloak (ck-client) executable missing क्लोक (सीके-क्लाइंट) निष्पादन योग्य गायब है - + Amnezia helper service error Amnezia भूलने की बीमारी सहायक सेवा त्रुटि - + OpenSSL failed ओपनएसएसएल विफल रहा - + Can't connect: another VPN connection is active कनेक्ट नहीं हो सकता: कोई अन्य वीपीएन कनेक्शन सक्रिय है - + Can't setup OpenVPN TAP network adapter OpenVPN TAP नेटवर्क एडाप्टर सेटअप नहीं कर सकता - + VPN pool error: no available addresses VPN pool error: لا يوجد عنواين مٌتاحة - + The config does not contain any containers and credentials for connecting to the server कॉन्फ़िगरेशन में सर्वर से कनेक्ट करने के लिए कोई कंटेनर और क्रेडेंशियल नहीं है - + QFile error: The file could not be opened Qफ़ाइल त्रुटि: फ़ाइल खोली नहीं जा सकी - + QFile error: An error occurred when reading from the file Qफ़ाइल त्रुटि: फ़ाइल से पढ़ते समय एक त्रुटि उत्पन्न हुई - + QFile error: The file could not be accessed Qफ़ाइल त्रुटि: फ़ाइल तक नहीं पहुंचा जा सका - + QFile error: An unspecified error occurred Qफ़ाइल त्रुटि: एक अनिर्दिष्ट त्रुटि उत्पन्न हुई - + QFile error: A fatal error occurred Qफ़ाइल त्रुटि: एक घातक त्रुटि उत्पन्न हुई - + QFile error: The operation was aborted Qफ़ाइल त्रुटि: ऑपरेशन निरस्त कर दिया गया था - + Internal error आंतरिक त्रुटि @@ -3629,7 +4050,7 @@ Already installed containers were found on the server. All installed containers - + Website in Tor network टोर नेटवर्क में वेबसाइट @@ -3650,31 +4071,118 @@ Already installed containers were found on the server. All installed containers - Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions. - शैडोसॉक्स - वीपीएन ट्रैफ़िक को मास्क करता है, जिससे यह सामान्य वेब ट्रैफ़िक के समान हो जाता है, लेकिन इसे कुछ अत्यधिक सेंसर किए गए क्षेत्रों में विश्लेषण प्रणालियों द्वारा पहचाना जा सकता है. + Shadowsocks masks VPN traffic, making it resemble normal web traffic, but it may still be detected by certain analysis systems. + + + + + OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against active-probing detection. It is very resistant to detection, but offers low speed. + + + + + WireGuard - popular VPN protocol with high performance, high speed and low power consumption. + + + + + AmneziaWG is a special protocol from Amnezia based on WireGuard. It provides high connection speed and ensures stable operation even in the most challenging network conditions. + + + + + XRay with REALITY masks VPN traffic as web traffic and protects against active probing. It is highly resistant to detection and offers high speed. + + + + + OpenVPN stands as one of the most popular and time-tested VPN protocols available. +It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, catering to a wide range of devices and operating systems. Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, which continually reinforces its security. With a strong balance of performance, security, and compatibility, OpenVPN remains a top choice for privacy-conscious individuals and businesses alike. + +* Available in the AmneziaVPN across all platforms +* Normal power consumption on mobile devices +* Flexible customisation to suit user needs to work with different operating systems and devices +* Recognised by DPI systems and therefore susceptible to blocking +* Can operate over both TCP and UDP network protocols. + + + + + This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for protecting against detection. + +OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client and the server. + +Cloak protects OpenVPN from detection. + +Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, and also protects the VPN from detection by Active Probing. This makes it very resistant to being detected + +Immediately after receiving the first data packet, Cloak authenticates the incoming connection. If authentication fails, the plugin masks the server as a fake website and your VPN becomes invisible to analysis systems. + +* Available in the AmneziaVPN across all platforms +* High power consumption on mobile devices +* Flexible settings +* Not recognised by detection systems +* Works over TCP network protocol, 443 port. + + + + + + A relatively new popular VPN protocol with a simplified architecture. +WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. +WireGuard is very susceptible to detection and blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. + +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Easily recognised by DPI analysis systems, susceptible to blocking +* Works over UDP network protocol. + + + + + A modern iteration of the popular VPN protocol, AmneziaWG builds upon the foundation set by WireGuard, retaining its simplified architecture and high-performance capabilities across devices. +While WireGuard is known for its efficiency, it had issues with being easily detected due to its distinct packet signatures. AmneziaWG solves this problem by using better obfuscation methods, making its traffic blend in with regular internet traffic. +This means that AmneziaWG keeps the fast performance of the original while adding an extra layer of stealth, making it a great choice for those wanting a fast and discreet VPN connection. + +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Not recognised by traffic analysis systems +* Works over UDP network protocol. + + + + + The REALITY protocol, a pioneering development by the creators of XRay, is designed to provide the highest level of protection against detection through its innovative approach to security and privacy. +It uniquely identifies attackers during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting attackers to genuine websites, thus presenting an authentic TLS certificate and data. +This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. +Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security. This makes REALITY a robust solution for maintaining internet freedom. + + + + Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions. + शैडोसॉक्स - वीपीएन ट्रैफ़िक को मास्क करता है, जिससे यह सामान्य वेब ट्रैफ़िक के समान हो जाता है, लेकिन इसे कुछ अत्यधिक सेंसर किए गए क्षेत्रों में विश्लेषण प्रणालियों द्वारा पहचाना जा सकता है. - OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against active-probing detection. Ideal for bypassing blocking in regions with the highest levels of censorship. - क्लोक पर ओपनवीपीएन - ओपनवीपीएन वीपीएन के साथ वेब ट्रैफिक और सक्रिय-जांच पहचान के खिलाफ सुरक्षा का मुखौटा लगाता है। उच्चतम स्तर की सेंसरशिप वाले क्षेत्रों में अवरोध को दूर करने के लिए आदर्श. + क्लोक पर ओपनवीपीएन - ओपनवीपीएन वीपीएन के साथ वेब ट्रैफिक और सक्रिय-जांच पहचान के खिलाफ सुरक्षा का मुखौटा लगाता है। उच्चतम स्तर की सेंसरशिप वाले क्षेत्रों में अवरोध को दूर करने के लिए आदर्श. + + + XRay with REALITY - Suitable for countries with the highest level of internet censorship. Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods. + वास्तविकता के साथ एक्सरे - उच्चतम स्तर की इंटरनेट सेंसरशिप वाले देशों के लिए उपयुक्त। टीएलएस स्तर पर ट्रैफ़िक को वेब ट्रैफ़िक के रूप में छिपाया जाता है, और सक्रिय जांच विधियों द्वारा पता लगाने से सुरक्षा प्रदान की जाती है. - XRay with REALITY - Suitable for countries with the highest level of internet censorship. Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods. - वास्तविकता के साथ एक्सरे - उच्चतम स्तर की इंटरनेट सेंसरशिप वाले देशों के लिए उपयुक्त। टीएलएस स्तर पर ट्रैफ़िक को वेब ट्रैफ़िक के रूप में छिपाया जाता है, और सक्रिय जांच विधियों द्वारा पता लगाने से सुरक्षा प्रदान की जाती है. - - - IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss. It has native support on the latest versions of Android and iOS. - + Create a file vault on your server to securely store and transfer files. फ़ाइलों को सुरक्षित रूप से संग्रहीत और स्थानांतरित करने के लिए अपने सर्वर पर एक फ़ाइल वॉल्ट बनाएं. - This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for protecting against blocking. OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client and the server. @@ -3693,7 +4201,7 @@ If there is a extreme level of Internet censorship in your region, we advise you * Not recognised by DPI analysis systems * Works over TCP network protocol, 443 port. - यह OpenVPN प्रोटोकॉल और क्लॉक प्लगइन का एक संयोजन है जिसे विशेष रूप से ब्लॉकिंग से बचाने के लिए डिज़ाइन किया गया है। + यह OpenVPN प्रोटोकॉल और क्लॉक प्लगइन का एक संयोजन है जिसे विशेष रूप से ब्लॉकिंग से बचाने के लिए डिज़ाइन किया गया है। OpenVPN क्लाइंट और सर्वर के बीच सभी इंटरनेट ट्रैफ़िक को एन्क्रिप्ट करके एक सुरक्षित वीपीएन कनेक्शन प्रदान करता है। @@ -3713,7 +4221,6 @@ OpenVPN क्लाइंट और सर्वर के बीच सभी - A relatively new popular VPN protocol with a simplified architecture. WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. WireGuard is very susceptible to blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. @@ -3723,7 +4230,7 @@ WireGuard is very susceptible to blocking due to its distinct packet signatures. * Minimum number of settings * Easily recognised by DPI analysis systems, susceptible to blocking * Works over UDP network protocol. - सरलीकृत वास्तुकला के साथ एक अपेक्षाकृत नया लोकप्रिय वीपीएन प्रोटोकॉल। + सरलीकृत वास्तुकला के साथ एक अपेक्षाकृत नया लोकप्रिय वीपीएन प्रोटोकॉल। वायरगार्ड सभी उपकरणों पर स्थिर वीपीएन कनेक्शन और उच्च प्रदर्शन प्रदान करता है। यह हार्ड-कोडित एन्क्रिप्शन सेटिंग्स का उपयोग करता है। ओपनवीपीएन की तुलना में वायरगार्ड में कम विलंबता और बेहतर डेटा ट्रांसफर थ्रूपुट है। वायरगार्ड अपने विशिष्ट पैकेट हस्ताक्षरों के कारण अवरुद्ध होने के प्रति अतिसंवेदनशील है। कुछ अन्य वीपीएन प्रोटोकॉल के विपरीत, जो अस्पष्टता तकनीकों को नियोजित करते हैं, वायरगार्ड पैकेट के सुसंगत हस्ताक्षर पैटर्न को अधिक आसानी से पहचाना जा सकता है और इस प्रकार उन्नत डीप पैकेट निरीक्षण (डीपीआई) सिस्टम और अन्य नेटवर्क निगरानी उपकरणों द्वारा अवरुद्ध किया जा सकता है। @@ -3734,7 +4241,7 @@ WireGuard is very susceptible to blocking due to its distinct packet signatures. * यूडीपी नेटवर्क प्रोटोकॉल पर काम करता है।. - + After installation, Amnezia will create a file storage on your server. You will be able to access it using @@ -3753,31 +4260,28 @@ For more detailed information, you can इसे "एसएफटीपी फ़ाइल संग्रहण बनाएं" के अंतर्गत सहायता अनुभाग में ढूंढें - WireGuard - New popular VPN protocol with high performance, high speed and low power consumption. Recommended for regions with low levels of censorship. - वायरगार्ड - उच्च प्रदर्शन, उच्च गति और कम बिजली की खपत के साथ नया लोकप्रिय वीपीएन प्रोटोकॉल। सेंसरशिप के निम्न स्तर वाले क्षेत्रों के लिए अनुशंसित. + वायरगार्ड - उच्च प्रदर्शन, उच्च गति और कम बिजली की खपत के साथ नया लोकप्रिय वीपीएन प्रोटोकॉल। सेंसरशिप के निम्न स्तर वाले क्षेत्रों के लिए अनुशंसित. - AmneziaWG - Special protocol from Amnezia, based on WireGuard. It's fast like WireGuard, but very resistant to blockages. Recommended for regions with high levels of censorship. - AmneziaWG - वायरगार्ड पर आधारित Amnezia का विशेष प्रोटोकॉल। यह वायरगार्ड की तरह तेज़ है, लेकिन रुकावटों के प्रति बहुत प्रतिरोधी है। उच्च स्तर की सेंसरशिप वाले क्षेत्रों के लिए अनुशंसित. + AmneziaWG - वायरगार्ड पर आधारित Amnezia का विशेष प्रोटोकॉल। यह वायरगार्ड की तरह तेज़ है, लेकिन रुकावटों के प्रति बहुत प्रतिरोधी है। उच्च स्तर की सेंसरशिप वाले क्षेत्रों के लिए अनुशंसित. IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss. IKEv2/IPsec - आधुनिक स्थिर प्रोटोकॉल, दूसरों की तुलना में थोड़ा तेज़, सिग्नल हानि के बाद कनेक्शन पुनर्स्थापित करता है। - + Deploy a WordPress site on the Tor network in two clicks. दो क्लिक में टोर नेटवर्क पर एक वर्डप्रेस साइट तैनात करें।. - + Replace the current DNS server with your own. This will increase your privacy level. वर्तमान DNS सर्वर को अपने स्वयं के DNS सर्वर से बदलें। इससे आपकी गोपनीयता का स्तर बढ़ जाएगा. - OpenVPN stands as one of the most popular and time-tested VPN protocols available. It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, catering to a wide range of devices and operating systems. Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, which continually reinforces its security. With a strong balance of performance, security, and compatibility, OpenVPN remains a top choice for privacy-conscious individuals and businesses alike. @@ -3786,7 +4290,7 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for * Flexible customisation to suit user needs to work with different operating systems and devices * Recognised by DPI analysis systems and therefore susceptible to blocking * Can operate over both TCP and UDP network protocols. - OpenVPN उपलब्ध सबसे लोकप्रिय और समय-परीक्षणित वीपीएन प्रोटोकॉल में से एक है। + OpenVPN उपलब्ध सबसे लोकप्रिय और समय-परीक्षणित वीपीएन प्रोटोकॉल में से एक है। यह एन्क्रिप्शन और कुंजी विनिमय के लिए एसएसएल/टीएलएस की ताकत का लाभ उठाते हुए, अपने अद्वितीय सुरक्षा प्रोटोकॉल को नियोजित करता है। इसके अलावा, कई प्रमाणीकरण विधियों के लिए ओपनवीपीएन का समर्थन इसे उपकरणों और ऑपरेटिंग सिस्टम की एक विस्तृत श्रृंखला को पूरा करते हुए बहुमुखी और अनुकूलनीय बनाता है। अपनी ओपन-सोर्स प्रकृति के कारण, ओपनवीपीएन को वैश्विक समुदाय द्वारा व्यापक जांच से लाभ मिलता है, जो लगातार इसकी सुरक्षा को मजबूत करता है। प्रदर्शन, सुरक्षा और अनुकूलता के मजबूत संतुलन के साथ, ओपनवीपीएन गोपनीयता के प्रति जागरूक व्यक्तियों और व्यवसायों के लिए शीर्ष विकल्प बना हुआ है। * सभी प्लेटफार्मों पर AmneziaVPN में उपलब्ध है @@ -3796,7 +4300,7 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for * टीसीपी और यूडीपी दोनों नेटवर्क प्रोटोकॉल पर काम कर सकता है।. - + Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection.However, certain traffic analysis systems might still detect a Shadowsocks connection. Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol. * Available in the AmneziaVPN only on desktop platforms @@ -3811,7 +4315,6 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for * टीसीपी नेटवर्क प्रोटोकॉल पर काम करता है।. - A modern iteration of the popular VPN protocol, AmneziaWG builds upon the foundation set by WireGuard, retaining its simplified architecture and high-performance capabilities across devices. While WireGuard is known for its efficiency, it had issues with being easily detected due to its distinct packet signatures. AmneziaWG solves this problem by using better obfuscation methods, making its traffic blend in with regular internet traffic. This means that AmneziaWG keeps the fast performance of the original while adding an extra layer of stealth, making it a great choice for those wanting a fast and discreet VPN connection. @@ -3821,7 +4324,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin * Minimum number of settings * Not recognised by DPI analysis systems, resistant to blocking * Works over UDP network protocol. - लोकप्रिय वीपीएन प्रोटोकॉल का एक आधुनिक पुनरावृत्ति, एमनेज़ियाडब्ल्यूजी वायरगार्ड द्वारा निर्धारित नींव पर आधारित है, जो सभी उपकरणों में इसकी सरलीकृत वास्तुकला और उच्च-प्रदर्शन क्षमताओं को बरकरार रखता है। + लोकप्रिय वीपीएन प्रोटोकॉल का एक आधुनिक पुनरावृत्ति, एमनेज़ियाडब्ल्यूजी वायरगार्ड द्वारा निर्धारित नींव पर आधारित है, जो सभी उपकरणों में इसकी सरलीकृत वास्तुकला और उच्च-प्रदर्शन क्षमताओं को बरकरार रखता है। जबकि वायरगार्ड अपनी दक्षता के लिए जाना जाता है, इसके विशिष्ट पैकेट हस्ताक्षरों के कारण इसे आसानी से पहचाने जाने में समस्याएँ थीं। AmneziaWG बेहतर अस्पष्टीकरण विधियों का उपयोग करके इस समस्या को हल करता है, जिससे इसका ट्रैफ़िक नियमित इंटरनेट ट्रैफ़िक के साथ मिश्रित हो जाता है। इसका मतलब यह है कि AmneziaWG स्टील्थ की एक अतिरिक्त परत जोड़ते हुए मूल के तेज़ प्रदर्शन को बनाए रखता है, जिससे यह तेज़ और विवेकपूर्ण वीपीएन कनेक्शन चाहने वालों के लिए एक बढ़िया विकल्प बन जाता है। @@ -3832,18 +4335,17 @@ This means that AmneziaWG keeps the fast performance of the original while addin * यूडीपी नेटवर्क प्रोटोकॉल पर काम करता है।. - The REALITY protocol, a pioneering development by the creators of XRay, is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion. It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, thus presenting an authentic TLS certificate and data. This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship. - रियलिटी प्रोटोकॉल, एक्सरे के रचनाकारों द्वारा एक अग्रणी विकास, विशेष रूप से चोरी के अपने नए दृष्टिकोण के माध्यम से इंटरनेट सेंसरशिप के उच्चतम स्तर का प्रतिकार करने के लिए डिज़ाइन किया गया है। + रियलिटी प्रोटोकॉल, एक्सरे के रचनाकारों द्वारा एक अग्रणी विकास, विशेष रूप से चोरी के अपने नए दृष्टिकोण के माध्यम से इंटरनेट सेंसरशिप के उच्चतम स्तर का प्रतिकार करने के लिए डिज़ाइन किया गया है। यह टीएलएस हैंडशेक चरण के दौरान सेंसर की विशिष्ट रूप से पहचान करता है, सेंसर को google.com जैसी वास्तविक वेबसाइटों की ओर मोड़ते हुए वैध ग्राहकों के लिए प्रॉक्सी के रूप में निर्बाध रूप से काम करता है, इस प्रकार एक प्रामाणिक टीएलएस प्रमाणपत्र और डेटा प्रस्तुत करता है। यह उन्नत क्षमता विशिष्ट कॉन्फ़िगरेशन की आवश्यकता के बिना यादृच्छिक, वैध साइटों से आने वाले वेब ट्रैफ़िक को छिपाने की क्षमता के कारण REALITY को समान तकनीकों से अलग करती है। VMess, VLESS और XTLS-Vision ट्रांसपोर्ट जैसे पुराने प्रोटोकॉल के विपरीत, TLS हैंडशेक पर REALITY की अभिनव "दोस्त या दुश्मन" पहचान सुरक्षा को बढ़ाती है और सक्रिय जांच तकनीकों को नियोजित करने वाले परिष्कृत DPI सिस्टम द्वारा पहचान को रोकती है। यह REALITY को कठोर सेंसरशिप वाले वातावरण में इंटरनेट की स्वतंत्रता बनाए रखने के लिए एक मजबूत समाधान बनाता है. - + IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol. One of its distinguishing features is its ability to swiftly switch between networks and devices, making it particularly adaptive in dynamic network environments. While it offers a blend of security, stability, and speed, it's essential to note that IKEv2 can be easily detected and is susceptible to blocking. @@ -3864,7 +4366,7 @@ While it offers a blend of security, stability, and speed, it's essential t * यूडीपी नेटवर्क प्रोटोकॉल, पोर्ट 500 और 4500 पर काम करता है. - + DNS Service DNS सेवाएँ @@ -4055,6 +4557,19 @@ While it offers a blend of security, stability, and speed, it's essential t + + RenameServerDrawer + + + Server name + सर्वर का नाम + + + + Save + सहेजें + + SelectLanguageDrawer @@ -4101,24 +4616,24 @@ While it offers a blend of security, stability, and speed, it's essential t ShareConnectionDrawer - - + + Save AmneziaVPN config AmneziaVPN कॉन्फ़िगरेशन सहेजें - + Share शेयर करना - + Copy कॉपी - - + + Copied कॉपी किया गया @@ -4128,12 +4643,12 @@ While it offers a blend of security, stability, and speed, it's essential t कॉन्फिग स्ट्रिंग कॉपी करें - + Show connection settings कनेक्शन सेटिंग दिखाएं - + To read the QR code in the Amnezia app, select "Add server" → "I have data to connect" → "QR code, key or settings file" एमनेज़िया ऐप में क्यूआर कोड पढ़ने के लिए, "सर्वर जोड़ें" → "मेरे पास कनेक्ट करने के लिए डेटा है" → "क्यूआर कोड, कुंजी या सेटिंग्स फ़ाइल" चुनें। @@ -4146,37 +4661,37 @@ While it offers a blend of security, stability, and speed, it's essential t होस्टनाम आईपी एड्रेस या डोमेन नाम जैसा नहीं दिखता - + New site added: %1 नई साइट जोड़ी गई: %1 - + Site removed: %1 साइट हटाई गई: %1 - + Can't open file: %1 फ़ाइल नहीं खुल सकती: %1 - + Failed to parse JSON data from file: %1 फ़ाइल से JSON डेटा पार्स करने में विफल:%1 - + The JSON data is not an array in file: %1 JSON डेटा फ़ाइल में कोई सरणी नहीं है: %1 - + Import completed आयात पूरा हुआ - + Export completed निर्यात पूरा हुआ @@ -4217,7 +4732,7 @@ While it offers a blend of security, stability, and speed, it's essential t TextFieldWithHeaderType - + The field can't be empty फ़ील्ड खाली नहीं हो सकती @@ -4225,7 +4740,7 @@ While it offers a blend of security, stability, and speed, it's essential t VpnConnection - + Mbps @@ -4276,9 +4791,8 @@ While it offers a blend of security, stability, and speed, it's essential t amnezia::ContainerProps - Low - कम + कम Medium or High @@ -4289,24 +4803,27 @@ While it offers a blend of security, stability, and speed, it's essential t चरम - - High - - - - I just want to increase the level of my privacy. - मैं बस अपनी गोपनीयता का स्तर बढ़ाना चाहता हूं. + मैं बस अपनी गोपनीयता का स्तर बढ़ाना चाहता हूं. - I want to bypass censorship. This option recommended in most cases. - मैं सेंसरशिप को दरकिनार करना चाहता हूं। अधिकांश मामलों में इस विकल्प की अनुशंसा की जाती है. + मैं सेंसरशिप को दरकिनार करना चाहता हूं। अधिकांश मामलों में इस विकल्प की अनुशंसा की जाती है. Most VPN protocols are blocked. Recommended if other options are not working. अधिकांश वीपीएन प्रोटोकॉल अवरुद्ध हैं। यदि अन्य विकल्प काम नहीं कर रहे हों तो अनुशंसित. + + + Automatic + + + + + AmneziaWG protocol will be installed. It provides high connection speed and ensures stable operation even in the most challenging network conditions. + + main2 diff --git a/client/translations/amneziavpn_my_MM.ts b/client/translations/amneziavpn_my_MM.ts index 09819cfe..1e81c5aa 100644 --- a/client/translations/amneziavpn_my_MM.ts +++ b/client/translations/amneziavpn_my_MM.ts @@ -4,60 +4,129 @@ AdLabel - - Amnezia Premium - for access to any website + + Amnezia Premium - for access to all websites and online resources + + ApiAccountInfoModel + + + + Active + + + + + Inactive + + + + + %1 out of %2 + + + + + Classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. Speeds up to 200 Mbps + + + + + Free unlimited access to a basic set of websites such as Facebook, Instagram, Twitter (X), Discord, Telegram and more. YouTube is not included in the free plan. + + + + + amnezia_free_support_bot + + + + + amnezia_premium_support_bot + + + + + ApiConfigsController + + + %1 installed successfully. + %1 ခုကို အောင်မြင်စွာ ထည့်သွင်းပြီးပါပြီ. + + + + API config reloaded + API config ကို ပြန်လည်စတင်လိုက်ပါပြီ + + + + Successfully changed the country of connection to %1 + ချိတ်ဆက်မှုနိုင်ငံကို %1 သို့ အောင်မြင်စွာ ပြောင်းလဲလိုက်ပါပြီ + + ApiServicesModel - Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s - သက်တောင့်သက်သာအလုပ်လုပ်နိုင်ဖို့အတွက်နှင့် ကြီးမားသောဖိုင်များကိုဒေါင်းလုဒ်လုပ်ခြင်းနှင့် ဗီဒီယိုများကြည့်ရှုခြင်းတို့အတွက် အသုံးပြုနိုင်သော VPN ဖြစ်ပါတယ်။ မည်သည့်ဆိုက်များအတွက်မဆိုအလုပ်လုပ်ပြီး လိုင်းအရှိန် %1 MBit/s အထိအသုံးပြုနိုင်ပါတယ်။ + သက်တောင့်သက်သာအလုပ်လုပ်နိုင်ဖို့အတွက်နှင့် ကြီးမားသောဖိုင်များကိုဒေါင်းလုဒ်လုပ်ခြင်းနှင့် ဗီဒီယိုများကြည့်ရှုခြင်းတို့အတွက် အသုံးပြုနိုင်သော VPN ဖြစ်ပါတယ်။ မည်သည့်ဆိုက်များအတွက်မဆိုအလုပ်လုပ်ပြီး လိုင်းအရှိန် %1 MBit/s အထိအသုံးပြုနိုင်ပါတယ်။ - VPN to access blocked sites in regions with high levels of Internet censorship. - အင်တာနက် ဆင်ဆာဖြတ်တောက်မှု မြင့်မားသော ဒေသများရှိ ပိတ်ဆို့ထားသော ဆိုက်များကို ဝင်ရောက်ရန် VPN။. + အင်တာနက် ဆင်ဆာဖြတ်တောက်မှု မြင့်မားသော ဒေသများရှိ ပိတ်ဆို့ထားသော ဆိုက်များကို ဝင်ရောက်ရန် VPN။. - + <p><a style="color: #EB5757;">Not available in your region. If you have VPN enabled, disable it, return to the previous screen, and try again.</a> - Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship. - Amnezia Premium - သက်တောင့်သက်သာအလုပ်လုပ်နိုင်ဖို့အတွက်နှင့် ကြီးမားသောဖိုင်များကိုဒေါင်းလုဒ်လုပ်ခြင်းနှင့် ဗီဒီယိုများကိုကြည်လင်ပြတ်သားစွာကြည့်ရှုခြင်းတို့အတွက် အသုံးပြုနိုင်သော VPN ဖြစ်ပါတယ်။ အင်တာနက်ဆင်ဆာဖြတ်မှု အဆင့်အမြင့်ဆုံးနိုင်ငံများတွင်ပင် မည်သည့်ဆိုက်များအတွက်မဆို အလုပ်လုပ်ပါသည်။. + Amnezia Premium - သက်တောင့်သက်သာအလုပ်လုပ်နိုင်ဖို့အတွက်နှင့် ကြီးမားသောဖိုင်များကိုဒေါင်းလုဒ်လုပ်ခြင်းနှင့် ဗီဒီယိုများကိုကြည်လင်ပြတ်သားစွာကြည့်ရှုခြင်းတို့အတွက် အသုံးပြုနိုင်သော VPN ဖြစ်ပါတယ်။ အင်တာနက်ဆင်ဆာဖြတ်မှု အဆင့်အမြင့်ဆုံးနိုင်ငံများတွင်ပင် မည်သည့်ဆိုက်များအတွက်မဆို အလုပ်လုပ်ပါသည်။. - Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship - Amnezia Free သည် အင်တာနက်ဆင်ဆာဖြတ်တောက်မှု မြင့်မားသောနိုင်ငံများတွင် ပိတ်ဆို့ခြင်းကို ကျော်ဖြတ်ရန်အတွက် အခမဲ့ VPN တစ်ခုဖြစ်ပါသည်။ + Amnezia Free သည် အင်တာနက်ဆင်ဆာဖြတ်တောက်မှု မြင့်မားသောနိုင်ငံများတွင် ပိတ်ဆို့ခြင်းကို ကျော်ဖြတ်ရန်အတွက် အခမဲ့ VPN တစ်ခုဖြစ်ပါသည်။ - + + Amnezia Premium is classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. Speeds up to %1 Mbps. + + + + + + AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan. + + + + + Amnezia Premium is classic VPN for for seamless work, downloading large files, and watching videos. Access all websites and online resources. + + + + %1 MBit/s %1 MBit/s - + %1 days %1 ရက် - + VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, <a href="%1/free" style="color: #FBB26A;">more details on the website.</a> ဤ VPN သည် သင့်ဒေသရှိ Instagram၊ Facebook၊ Twitter နှင့် အခြားသော လူကြိုက်များသော ဆိုက်များကိုသာ ဖွင့်ပေးပါမည်။ အခြားဝဘ်ဆိုက်များကိုမူ သင်၏ IP လိပ်စာအစစ်အမှန်ဖြင့်သာ ဖွင့်ပေးပါမည်၊ <a href="%1/free" style="color: #FBB26A;">နောက်ထပ်အသေးစိတ်အချက်အလက်များကို ဝဘ်ဆိုဒ်ပေါ်တွင်ကြည့်ရန်</a> - + Free အခမဲ့ - + %1 $/month %1 $/တစ်လ @@ -96,62 +165,59 @@ ConnectionController - VPN Protocols is not installed. Please install VPN container at first - VPN ပရိုတိုကောများကို မထည့်သွင်းရသေးပါ။ + VPN ပရိုတိုကောများကို မထည့်သွင်းရသေးပါ။ ကျေးဇူးပြု၍ VPN ကွန်တိန်နာကို အရင်ထည့်သွင်းပါ။ - + Connecting... ချိတ်ဆက်နေပါပြီ... - + Connected ချိတ်ဆက်ပြီးသွားပါပြီ - + Preparing... ပြင်ဆင်နေသည်... - + Settings updated successfully, reconnnection... ဆက်တင်များကို အောင်မြင်စွာ အပ်ဒိတ်လုပ်ပြီးပါပြီ၊ ပြန်လည်ချိတ်ဆက်နေပါသည်... - + Settings updated successfully ဆက်တင်များကို အောင်မြင်စွာ အပ်ဒိတ်လုပ်ပြီးပါပြီ - The selected protocol is not supported on the current platform - ရွေးချယ်ထားသော ပရိုတိုကောကို လက်ရှိပလက်ဖောင်းပေါ်တွင် အ‌ထောက်အပံ့မပေးထားပါ + ရွေးချယ်ထားသော ပရိုတိုကောကို လက်ရှိပလက်ဖောင်းပေါ်တွင် အ‌ထောက်အပံ့မပေးထားပါ - unable to create configuration - configuration ဖန်တီး၍မရပါ + configuration ဖန်တီး၍မရပါ - + Reconnecting... ပြန်လည်ချိတ်ဆက်နေပါသည်... - - - - + + + + Connect ချိတ်ဆက်မည် - + Disconnecting... အဆက်အသွယ်ဖြတ်နေပါသည်... @@ -270,101 +336,107 @@ Can't be disabled for current server Configuration ဖိုင် မမှန်ကန်ပါ - + Scanned %1 of %2. %2 ၏ %1 ကို စကင်န်ဖတ်ထားသည်. - + + This configuration contains an OpenVPN setup. OpenVPN configurations can include malicious scripts, so only add it if you fully trust the provider of this config. + + + + + <br>In the imported configuration, potentially dangerous lines were found: + + + In the imported configuration, potentially dangerous lines were found: - တင်သွင်းသည့် configuration တွင်၊ အန္တရာယ်ရှိနိုင်သည့်စာလိုင်းများကို တွေ့ရှိခဲ့သည်: + တင်သွင်းသည့် configuration တွင်၊ အန္တရာယ်ရှိနိုင်သည့်စာလိုင်းများကို တွေ့ရှိခဲ့သည်: InstallController - + %1 installed successfully. %1 ကို အောင်မြင်စွာ ထည့်သွင်းပြီးပါပြီ. - + %1 is already installed on the server. %1 ကို ဆာဗာတွင် ထည့်သွင်းပြီးဖြစ်သည်. - + Added containers that were already installed on the server ဆာဗာတွင် ထည့်သွင်းပြီးသား ကွန်တိန်နာများကို ပေါင်းထည့်ပြီးပါပြီ။ - + Already installed containers were found on the server. All installed containers have been added to the application ထည့်သွင်းပြီးသား ကွန်တိန်နာများကို ဆာဗာပေါ်တွင် တွေ့ရှိခဲ့သည်။ ထည့်သွင်းထားသည့် ကွန်တိန်နာအားလုံးကို အပလီကေးရှင်းထဲသို့ ပေါင်းထည့်ပြီးပါပြီ။ - + Settings updated successfully ဆက်တင်များကို အောင်မြင်စွာ အပ်ဒိတ်လုပ်ပြီးပါပြီ။ - + Server '%1' was rebooted ဆာဗာ '%1' ကို ပြန်လည်စတင်ခဲ့သည်။ - + Server '%1' was removed ဆာဗာ '%1' ကို ဖယ်ရှားခဲ့သည်။ - + All containers from server '%1' have been removed ဆာဗာ '%1' မှ ကွန်တိန်နာအားလုံးကို ဖယ်ရှားလိုက်ပါပြီ။ - + %1 has been removed from the server '%2' %1 ကို ဆာဗာ '%2' မှ ဖယ်ရှားလိုက်ပါပြီ - + Api config removed Api config ကိုဖယ်ရှားလိုက်သည် - + %1 cached profile cleared ကက်ရှ်လုပ်ထားတဲ့ ပရိုဖိုင် %1 ခုကို ရှင်းပြီးပါပြီ - + Please login as the user အသုံးပြုသူအဖြစ် log in ဝင်ရောက်ပါ - + Server added successfully ဆာဗာကို အောင်မြင်စွာ ထည့်သွင်းပြီးပါပြီ - %1 installed successfully. - %1 ခုကို အောင်မြင်စွာ ထည့်သွင်းပြီးပါပြီ. + %1 ခုကို အောင်မြင်စွာ ထည့်သွင်းပြီးပါပြီ. - API config reloaded - API config ကို ပြန်လည်စတင်လိုက်ပါပြီ + API config ကို ပြန်လည်စတင်လိုက်ပါပြီ - Successfully changed the country of connection to %1 - ချိတ်ဆက်မှုနိုင်ငံကို %1 သို့ အောင်မြင်စွာ ပြောင်းလဲလိုက်ပါပြီ + ချိတ်ဆက်မှုနိုင်ငံကို %1 သို့ အောင်မြင်စွာ ပြောင်းလဲလိုက်ပါပြီ @@ -476,12 +548,12 @@ Already installed containers were found on the server. All installed containers split tunnelling ပိတ်ထားပါသည် - + VPN protocol VPN ပရိုတိုကော - + Servers ဆာဗာများ @@ -1299,22 +1371,22 @@ Already installed containers were found on the server. All installed containers အပလီကေးရှင်း - + Backup backup ယူမည် - + About AmneziaVPN AmneziaVPN အကြောင်း - + Dev console ဒက်ဗယ်လော်ပါ console - + Close application အပလီကေးရှင်းကို ပိတ်မည် @@ -1322,17 +1394,17 @@ Already installed containers were found on the server. All installed containers PageSettingsAbout - + Support Amnezia Amnezia ကိုကူညီပံ့ပိုးမည် - + Amnezia is a free and open-source application. You can support the developers if you like it. Amnezia သည် အခမဲ့ open-source application တစ်ခုဖြစ်သည်။ သင်နှစ်သက်ပါက developer များကို ပံ့ပိုးနိုင်ပါသည်. - + Contacts ဆက်သွယ်ရန်လိပ်စာများ @@ -1366,139 +1438,511 @@ Already installed containers were found on the server. All installed containers သုံးသပ်ချက်များနှင့် ချွတ်ယွင်းချက်အစီရင်ခံစာများအတွက် - Copied - ကူးယူပြီးပါပြီ + ကူးယူပြီးပါပြီ - + + mailto:support@amnezia.org + + + + GitHub GitHub - + Discover the source code - + https://github.com/amnezia-vpn/amnezia-client https://github.com/amnezia-vpn/amnezia-client - + Website ဝဘ်ဆိုက် - + Visit official website - + Software version: %1 ဆော့ဖ်ဝဲဗားရှင်း: %1 - + Check for updates အပ်ဒိတ်များရှိမရှိ စစ်ဆေးမည် - + Privacy Policy ကိုယ်ရေးအချက်အလက်မူဝါဒ - PageSettingsApiLanguageList + PageSettingsApiAvailableCountries - + + Location for connection + + + + Unable change server location while there is an active connection - PageSettingsApiServerInfo + PageSettingsApiDevices - - For the region - ဒေသအတွက် - - - - Price - စျေးနှုန်း - - - - Work period - အလုပ်လုပ်မည့်ကာလ - - - - Valid until + + Active Devices - + + Manage currently connected devices + + + + + You can find the identifier on the Support tab or, for older versions of the app, by tapping '+' and then the three dots at the top of the page. + + + + + (current device) + + + + + Support tag: + + + + + Last updated: + + + + + Cannot unlink device during active connection + + + + + Are you sure you want to unlink this device? + + + + + This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect. + + + + + Continue + ဆက်လက်လုပ်ဆောင်မည် + + + + Cancel + ပယ်ဖျက်မည် + + + + PageSettingsApiInstructions + + + Windows + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#windows + + + + + macOS + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#macos + + + + + Android + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#android + + + + + AndroidTV + + + + + https://docs.amnezia.org/ru/documentation/instructions/android_tv_connect/ + + + + + iOS + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#ios + + + + + Linux + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#linux + + + + + Routers + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#routers + + + + + How to connect on another device + + + + + Setup guides on the Amnezia website + + + + + PageSettingsApiNativeConfigs + + + Save AmneziaVPN config + AmneziaWG config ကိုသိမ်းဆည်းမည် + + + + Configuration Files + + + + + For router setup or the AmneziaWG app + + + + + The configuration needs to be reissued + + + + + configuration file + + + + + Generate a new configuration file + + + + + The previously created one will stop working + + + + + Revoke the current configuration file + + + + + Config file saved + + + + + The config has been revoked + + + + + Generate a new %1 configuration file? + + + + + Revoke the current %1 configuration file? + + + + + Your previous configuration file will no longer work, and it will not be possible to connect using it + + + + + Download + + + + + Continue + ဆက်လက်လုပ်ဆောင်မည် + + + + Cancel + ပယ်ဖျက်မည် + + + + PageSettingsApiServerInfo + + For the region + ဒေသအတွက် + + + Price + စျေးနှုန်း + + + Work period + အလုပ်လုပ်မည့်ကာလ + + Speed - မြန်နှုန်း + မြန်နှုန်း - Support tag - ကူညီပံ့ပိုးမှု tag + ကူညီပံ့ပိုးမှု tag - Copied - ကူးယူပြီးပါပြီ + ကူးယူပြီးပါပြီ - + + Subscription Status + + + + + Valid Until + + + + + Active Connections + + + + + Configurations have been updated for some countries. Download and install the updated configuration files + + + + + Subscription Key + + + + + Amnezia Premium subscription key + + + + + Save VPN key as a file + + + + + Copy VPN key + + + + + Configuration Files + + + + + Manage configuration files + + + + + Active Devices + + + + + Manage currently connected devices + + + + + Support + + + + + How to connect on another device + + + + Reload API config API config ကို ပြန်လည်စတင်မည် - + Reload API config? API config ကို ပြန်လည်စတင်မည်လား? - - + + + Continue ဆက်လက်လုပ်ဆောင်မည် - - + + + Cancel ပယ်ဖျက်မည် - + Cannot reload API config during active connection ချိတ်ဆက်မှုရှိနေချိန်အတွင်း API config ကို ပြန်လည်စတင်၍မရပါ - + + Unlink this device + + + + + Are you sure you want to unlink this device? + + + + + This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect. + + + + + Cannot unlink device during active connection + + + + Remove from application အပလီကေးရှင်းမှဖယ်ရှားမည် - + Remove from application? အပလီကေးရှင်းမှဖယ်ရှားမည်လား? - + Cannot remove server during active connection ချိတ်ဆက်မှုရှိနေချိန်အတွင်း ဆာဗာကို ဖယ်ရှား၍မရပါ + + PageSettingsApiSupport + + + Telegram + + + + + Email + + + + + support@amnezia.org + + + + + Email Billing & Orders + + + + + help@vpnpay.io + + + + + Website + ဝဘ်ဆိုက် + + + + amnezia.org + + + + + Support + + + + + Our technical support specialists are available to assist you at any time + + + + + Support tag + ကူညီပံ့ပိုးမှု tag + + + + Copied + ကူးယူပြီးပါပြီ + + PageSettingsAppSplitTunneling @@ -1783,8 +2227,12 @@ Already installed containers were found on the server. All installed containers + Cannot change KillSwitch settings during active connection + + + Cannot change killSwitch settings during active connection - လက်ရှိချိတ်ဆက်မှုတစ်ခုရှိနေချိန်တွင် killSwitch ဆက်တင်များကို ပြောင်းလဲ၍မရပါ + လက်ရှိချိတ်ဆက်မှုတစ်ခုရှိနေချိန်တွင် killSwitch ဆက်တင်များကို ပြောင်းလဲ၍မရပါ @@ -1890,21 +2338,21 @@ Already installed containers were found on the server. All installed containers မှတ်တမ်းများရှိသောဖိုင်တွဲကိုဖွင့်မည် - - + + Save သိမ်းဆည်းမည် - - + + Logs files (*.log) မှတ်တမ်းဖိုင်များ (*.log) မှတ်တမ်းဖိုင်များ (*.log) - - + + Logs file saved မှတ်တမ်းဖိုင်များသိမ်းဆည်းပြီးပါပြီ @@ -1938,32 +2386,32 @@ Already installed containers were found on the server. All installed containers မှတ်တမ်းများကို ရှင်းလင်းပြီးပါပြီ - + Client logs - + AmneziaVPN logs - + Open logs folder - + Export logs - + Service logs - + AmneziaVPN-service logs @@ -1991,103 +2439,103 @@ Already installed containers were found on the server. All installed containers - - - - + + + + Continue ဆက်လက်လုပ်ဆောင်မည် - - - - + + + + Cancel ပယ်ဖျက်မည် - + Check the server for previously installed Amnezia services ယခင်က ထည့်သွင်းထားသော Amnezia ဝန်ဆောင်မှုများရှိမရှိ ဆာဗာကို စစ်ဆေးမည် - + Add them to the application if they were not displayed ဖော်ဆောင်ပြသခြင်းမရှိပါက ၎င်းတို့ကို အပလီကေးရှင်းထဲသို့ ထည့်မည် - + Reboot server ဆာဗာကို ပြန်လည်စတင်မည် - + Do you want to reboot the server? ဆာဗာကို ပြန်လည်စတင်ချင်ပါသလား? - + The reboot process may take approximately 30 seconds. Are you sure you wish to proceed? ပြန်လည်စတင်သည့် လုပ်ငန်းစဉ်သည် စက္ကန့် 30 ခန့် ကြာနိုင်သည်. ဆက်လက်လုပ်ဆောင်လိုပါသလား? - + Cannot reboot server during active connection ချိတ်ဆက်မှုရှိနေချိန်အတွင်း ဆာဗာကို ပြန်လည်စတင်၍မရပါ - + Do you want to remove the server from application? ဆာဗာကို အပလီကေးရှင်းမှဖယ်ရှားချင်ပါသလား? - + Cannot remove server during active connection ချိတ်ဆက်မှုရှိနေချိန်အတွင်း ဆာဗာကို ဖယ်ရှား၍မရပါ - + Do you want to clear server from Amnezia software? ဆာဗာကို Amnezia ဆော့ဖ်ဝဲလ်မှ ရှင်းလင်းလိုပါသလား? - + All users whom you shared a connection with will no longer be able to connect to it. သင်၏ချိတ်ဆက်မှကို မျှဝေထားသည့် အသုံးပြုသူအားလုံး ချိတ်ဆက်နိုင်တော့မည်မဟုတ်ပါ. - + Cannot clear server from Amnezia software during active connection လက်ရှိချိတ်ဆက်မှုတစ်ခုရှိနေချိန်တွင် ဆာဗာကို Amnezia ဆော့ဖ်ဝဲလ်မှ ရှင်းလင်း၍မရပါ - + Reset API config API config ကို ပြန်လည်သတ်မှတ်မည် - + Do you want to reset API config? API config ကို ပြန်လည်သတ်မှတ်ချင်ပါသလား? - + Cannot reset API config during active connection လက်ရှိချိတ်ဆက်မှုတစ်ခုရှိနေချိန်တွင် API config ကို ပြန်လည်သတ်မှတ်၍မရပါ - + Remove server from application ဆာဗာကို အပလီကေးရှင်းမှဖယ်ရှားမည် - + All installed AmneziaVPN services will still remain on the server. ထည့်သွင်းထားသော AmneziaVPN ဝန်ဆောင်မှုများအားလုံးသည် ဆာဗာပေါ်တွင် ဆက်လက်ရှိနေမည်ဖြစ်သည်. - + Clear server from Amnezia software ဆာဗာကို Amnezia ဆော့ဖ်ဝဲလ်မှ ရှင်းလင်းမည် @@ -2095,32 +2543,25 @@ Already installed containers were found on the server. All installed containers PageSettingsServerInfo - - Subscription is valid until - - - - Server name - ဆာဗာအမည် + ဆာဗာအမည် - Save - သိမ်းဆည်းမည် + သိမ်းဆည်းမည် - + Protocols ပရိုတိုကောများ - + Services ဝန်ဆောင်မှုများ - + Management စီမံခန့်ခွဲမှု @@ -2240,22 +2681,22 @@ Already installed containers were found on the server. All installed containers Mode - + Remove ဖယ်ရှားမည် - + Continue ဆက်လက်လုပ်ဆောင်မည် - + Cancel ပယ်ဖျက်မည် - + Import / Export Sites ဆိုက်များ သွင်း/ထုတ်မည် @@ -2270,12 +2711,12 @@ Already installed containers were found on the server. All installed containers လက်ရှိချိတ်ဆက်မှုတစ်ခုရှိနေချိန်တွင် split tunneling ဆက်တင်များကို ပြောင်းလဲ၍မရပါ - + website or IP ဝဘ်ဆိုက် သို့မဟုတ် IP - + Import တင်သွင်းမည် @@ -2291,29 +2732,29 @@ Already installed containers were found on the server. All installed containers - - + + Sites files (*.json) ဆိုက်ဖိုင်များ (*.json) - + Import a list of sites ဆိုက်စာရင်းတစ်ခု တင်သွင်းမည် - + Replace site list ဆိုက်စာရင်းကို အစားထိုးမည် - - + + Open sites file ဆိုက်ဖိုင်များ ဖွင့်မည် - + Add imported sites to existing ones တင်သွင်းထားသော ဆိုက်များကို ရှိပြီးသားဆိုက်များထဲသို့ ထည့်မည် @@ -2367,7 +2808,7 @@ Already installed containers were found on the server. All installed containers PageSetupWizardConfigSource - + File with connection settings ချိတ်ဆက်မှုဆက်တင်များပါဝင်သောဖိုင် @@ -2422,95 +2863,80 @@ Already installed containers were found on the server. All installed containers အခြားချိတ်ဆက်မှုရွေးချယ်စရာများ - + Site Amnezia - + VPN by Amnezia Amnezia မှ VPN - + Connect to classic paid and free VPN services from Amnezia Amnezia မှ အခပေးနှင့် အခမဲ့ မူလ VPN ဝန်ဆောင်မှုများသို့ ချိတ်ဆက်မည် - + Self-hosted VPN ကိုယ်တိုင် host လုပ်ထားသော VPN - + Configure Amnezia VPN on your own server Amnezia VPN ကို သင်၏ကိုယ်ပိုင်ဆာဗာပေါ်တွင် စီစဥ်ချိန်ညှိမည် - + Restore from backup အရံဖိုင်မှ ပြန်လည်ရယူမည် - + - + Open backup file အရံဖိုင်ကို ဖွင့်မည် - + Backup files (*.backup) အရံဖိုင်များ (*.backup) - - - - - - + Open config file config ဖိုင်ကိုဖွင့်မည် - + QR code QR-ကုဒ် - - - - - - + I have nothing ကျွန်ုပ်တွင်ဘာမှမရှိပါ - - - - - PageSetupWizardCredentials - + Server IP address [:port] ဆာဗာ IP လိပ်စာ [:port] - + Continue ဆက်လက်လုပ်ဆောင်မည် - + Enter the address in the format 255.255.255.255:88 လိပ်စာကို 255.255.255.255:88 ဖော်မတ်ဖြင့် ထည့်ပါ @@ -2520,47 +2946,47 @@ Already installed containers were found on the server. All installed containers သင်၏ဆာဗာကို စီစဉ်ချိန်ညှိမည် - + 255.255.255.255:22 255.255.255.255:22 - + SSH Username SSH အသုံးပြုသူအမည် - + Password or SSH private key စကားဝှက် သိုမဟုတ် SSH private key - + All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties သင်ထည့်သွင်းသည့်ဒေတာအားလုံးကို တင်းကြပ်လုံခြုံစွာလျှို့ဝှက်ထားမည်ဖြစ်ပြီး Amnezia သို့မဟုတ် မည်သည့်ပြင်ပအဖွဲ့အစည်းကိုမျှ မျှဝေမည် သို့မဟုတ် ထုတ်ဖော်မည်မဟုတ်ပါ - + How to run your VPN server သင်၏ဆာဗာကို လည်ပတ်ပုံလည်ပတ်နည်း - + Where to get connection data, step-by-step instructions for buying a VPS ချိတ်ဆက်မှုဒေတာကို ဘယ်မှာရနိုင်မလဲ၊ VPS ဝယ်ယူပုံဝယ်ယူနည်းအတွက် အဆင့်ဆင့် ညွှန်ကြားချက်များ - + Ip address cannot be empty IP လိပ်စာသည် ဗလာမဖြစ်ရပါ - + Login cannot be empty လော့ဂ်အင်အချက်အလက်သည် ဗလာမဖြစ်ရပါ - + Password/private key cannot be empty စကားဝှက်/private key သည် ဗလာမဖြစ်ရပါ @@ -2568,22 +2994,31 @@ Already installed containers were found on the server. All installed containers PageSetupWizardEasy - What is the level of internet control in your region? - သင့်ဒေသရှိ အင်တာနက်ထိန်းချုပ်မှုအဆင့်က ဘယ်လောက်ရှိပါသလဲ? + သင့်ဒေသရှိ အင်တာနက်ထိန်းချုပ်မှုအဆင့်က ဘယ်လောက်ရှိပါသလဲ? + + + + Choose Installation Type + + Manual + + + + Choose a VPN protocol VPN ပရိုတိုကောကို ရွေးပါ - + Skip setup စနစ်ထည့်သွင်းမှုကို ကျော်မည် - + Continue ဆက်လက်လုပ်ဆောင်မည် @@ -2758,23 +3193,23 @@ Already installed containers were found on the server. All installed containers PageShare - + OpenVPN native format OpenVPN မူရင်းဖောမတ် - + WireGuard native format WireGuard မူရင်းဖော်မတ် - + Connection ချိတ်ဆက်မှု + - Server ဆာဗာ @@ -2794,176 +3229,176 @@ Already installed containers were found on the server. All installed containers ဤဆာဗာနှင့်ချိတ်ဆက်မှု ဆက်တင်များပါရှိသော ဖိုင် - + Save OpenVPN config OpenVPN config ကိုသိမ်းဆည်းမည် - + Save WireGuard config WireGuard config ကိုသိမ်းဆည်းမည် - + Save AmneziaWG config AmneziaWG config ကိုသိမ်းဆည်းမည် - + Save Shadowsocks config Shadowsocks config ကိုသိမ်းဆည်းမည် - + Save Cloak config Cloak config ကိုသိမ်းဆည်းမည် - + Save XRay config XRay config ကိုသိမ်းဆည်းမည် - + For the AmneziaVPN app AmneziaVPN အက်ပ်အတွက် - + AmneziaWG native format AmneziaWG မူရင်းဖော်မတ် - + Shadowsocks native format Shadowsocks မူရင်းဖောမတ် - + Cloak native format Cloak မူရင်းဖော်မတ် - + XRay native format XRay မူရင်းဖော်မတ် - + Share VPN Access VPN အသုံးပြုခွင့်ကိုမျှဝေမည် - + Share full access to the server and VPN ဆာဗာနှင့် VPN သို့ အပြည့်အဝဝင်ရောက်ခွင့်ကို မျှဝေမည် - + Use for your own devices, or share with those you trust to manage the server. သင့်ကိုယ်ပိုင်စက်ပစ္စည်းများအတွက် အသုံးပြုရန် သို့မဟုတ် ဆာဗာကို စီမံခန့်ခွဲရန် သင်ယုံကြည်ရသူများနှင့် မျှဝေရန်. - - + + Users အသုံးပြုသူများ - + User name အသုံးပြုသူနာမည် - + Search ရှာဖွေမည် - + Creation date: %1 ဖန်တီးပြုလုပ်သည့်ရက်စွဲ: %1 - + Latest handshake: %1 နောက်ဆုံး handshake လုပ်ခြင်း: %1 - + Data received: %1 လက်ခံရရှိသည့်ဒေတာ: %1 - + Data sent: %1 ပေးပို့လိုက်သည့်ဒေတာ: %1 - + Allowed IPs: %1 - + Rename အမည်ပြောင်းမည် - + Client name ကလိုင်းရင့်အမည် - + Save သိမ်းဆည်းမည် - + Revoke ပြန်ရုပ်သိမ်းမည် - + Revoke the config for a user - %1? အသုံးပြုသူ %1 အတွက် config ကို ပြန်လည်ရုပ်သိမ်းမည်လား? - + The user will no longer be able to connect to your server. ဤအသုံးပြုသူသည် သင့်ဆာဗာသို့ ချိတ်ဆက်နိုင်တော့မည်မဟုတ်ပါ. - + Continue ဆက်လက်လုပ်ဆောင်မည် - + Cancel ပယ်ဖျက်မည် - + Share VPN access without the ability to manage the server ဆာဗာကို စီမံခန့်ခွဲနိုင်စွမ်းမပါရှိဘဲ VPN အသုံးပြုခွင့်ကို မျှဝေမည် + - Protocol ပရိုတိုကော + - Connection format ချိတ်ဆက်မှုဖောမတ် - - + + Share မျှဝေမည် @@ -3027,17 +3462,17 @@ Already installed containers were found on the server. All installed containers PageStart - + Logging was disabled after 14 days, log files were deleted ၁၄ ရက်အကြာတွင် Logging ကို ပိတ်ခဲ့သည်၊ မှတ်တမ်းဖိုင်များကို ဖျက်ပစ်လိုက်ပြီဖြစ်သည် - + Settings restored from backup file ဆက်တင်များကို အရံဖိုင်မှ ပြန်လည်ရယူပြီးပါပြီ - + Logging is enabled. Note that logs will be automaticallydisabled after 14 days, and all log files will be deleted. @@ -3304,87 +3739,88 @@ Already installed containers were found on the server. All installed containers လုပ်ဆောင်ချက်ကို မတတ်ဆင်ရသေးပါ - + Server check failed ဆာဗာစစ်ဆေးမှု မအောင်မြင်ပါ - + Server port already used. Check for another software ဆာဗာ Port ကို အသုံးပြုပြီးဖြစ်သည်. အခြားဆော့ဖ်ဝဲရှိမရှိ စစ်ဆေးပါ - + Server error: Docker container missing ဆာဗာ မှားယွင်းမှု: Docker ကွန်တိန်နာ ပျောက်နေသည် - + Server error: Docker failed ဆာဗာ မှားယွင်းမှု: Docker မအောင်မြင်ပါ - + Installation canceled by user ထည့်သွင်းမှုကို အသုံးပြုသူမှ ပယ်ဖျက်လိုက်သည် - + The user is not a member of the sudo group ဤအသုံးပြုသူသည် sudo အုပ်စု၏အဖွဲ့ဝင်မဟုတ်ပါ - + SSH request was denied SSH တောင်းဆိုမှု ငြင်းဆိုခံလိုက်ရပါသည် - + SSH request was interrupted SSH တောင်းဆိုမှု အနှောက်အယက်ခံလိုက်ရပါသည် - + SSH internal error စက်တွင်းဖြစ်သော SSH မှားယွင်းမှု - + Invalid private key or invalid passphrase entered မမှန်ကန်သော ကိုယ်ပိုင် key သို့မဟုတ် မမှန်ကန်သော စကားဝှက်ကို ထည့်သွင်းထားသည် - + The selected private key format is not supported, use openssh ED25519 key types or PEM key types ရွေးချယ်ထားသော ကိုယ်ပိုင် key ဖော်မတ်ကို ထောက်ပံ့မှုမပေးပါ၊ openssh ED25519 key အမျိုးအစားများ သို့မဟုတ် PEM သော့အမျိုးအစားများကို အသုံးပြုပါ - + Timeout connecting to server ဆာဗာသို့ ချိတ်ဆက်ခြင်း အချိန်ကုန်သွားသည် - + The config does not contain any containers and credentials for connecting to the server Config တွင် ဆာဗာသို့ချိတ်ဆက်ရန်အတွက် ကွန်တိန်နာများနှင့် အထောက်အထားများ မပါဝင်ပါ - + + Error when retrieving configuration from API API မှ စီစဉ်သတ်မှတ်မှုကို ရယူသည့်အခါ အမှားအယွင်းဖြစ်ပေါ်နေသည် - + This config has already been added to the application ဤ config ကို အပလီကေးရှင်းထဲသို့ ထည့်သွင်းပြီးဖြစ်သည် - + ErrorCode: %1. မှားယွင်းမှုကုတ်: %1. - + OpenVPN config missing OpenVPN config ပျောက်ဆုံးနေပါသည် @@ -3394,132 +3830,169 @@ Already installed containers were found on the server. All installed containers နောက်ခံဝန်ဆောင်မှု လည်ပတ်နေခြင်းမရှိပါ - + + The selected protocol is not supported on the current platform + ရွေးချယ်ထားသော ပရိုတိုကောကို လက်ရှိပလက်ဖောင်းပေါ်တွင် အ‌ထောက်အပံ့မပေးထားပါ + + + Server error: Package manager error ဆာဗာ အမှား- Package manager အမှား - + + The sudo package is not pre-installed on the server + + + + + The server user's home directory is not accessible + + + + + Action not allowed in sudoers + + + + + The user's password is required + + + + SCP error: Generic failure SCP မှားယွင်းမှု: ယေဘုယ မအောင်မြင်ခြင်း - + OpenVPN management server error OpenVPN စီမံခန့်ခွဲမှုဆာဗာ အမှားအယွင်း - + OpenVPN executable missing OpenVPN စီမံလုပ်ဆောင်နိုင်မှု ပျောက်ဆုံးနေပါသည် - + Shadowsocks (ss-local) executable missing Shadowsocks (ss-local) executable ပျောက်နေပါသည် - + Cloak (ck-client) executable missing Cloak (ck-client) စီမံလုပ်ဆောင်နိုင်မှု ပျောက်ဆုံးနေပါသည် - + Amnezia helper service error Amnezia helper ဝန်ဆောင်မှု မှားယွင်းမှု - + OpenSSL failed OpenSSL မအောင်မြင်ပါ - + Can't connect: another VPN connection is active ချိတ်ဆက်၍မရပါ: အခြား VPN ချိတ်ဆက်မှုတစ်ခုရှိနေပါသည် - + Can't setup OpenVPN TAP network adapter OpenVPN TAP ကွန်ရက် adapter ကို စနစ်တည်ဆောက်၍မရပါ - + VPN pool error: no available addresses VPN pool မှားယွင်းမှု: ရရှိနိုင်သောလိပ်စာများမရှိပါ - + Unable to open config file - + + VPN Protocols is not installed. + Please install VPN container at first + VPN ပရိုတိုကောများကို မထည့်သွင်းရသေးပါ။ +ကျေးဇူးပြု၍ VPN ကွန်တိန်နာကို အရင်ထည့်သွင်းပါ။ + + + VPN connection error VPN ချိတ်ဆက်မှုမှားယွင်းနေပါသည် - + In the response from the server, an empty config was received ဆာဗာမှ တုံ့ပြန်မှုတွင်၊ config အလွတ်တစ်ခုကို လက်ခံရရှိခဲ့သည် - + SSL error occurred SSL မှားယွင်းမှုဖြစ်သွားသည် - + Server response timeout on api request Api တောင်းဆိုမှုတွင် ဆာဗာတုံ့ပြန်မှု အချိန်ကုန်သွားသည် - + Missing AGW public key AGW public key ပျောက်ဆုံးနေသည် - + Failed to decrypt response payload - + Missing list of available services - + + The limit of allowed configurations per subscription has been exceeded + + + + QFile error: The file could not be opened QFile မှားယွင်းမှု: ဖိုင်ကို ဖွင့်၍မရပါ - + QFile error: An error occurred when reading from the file QFile မှားယွင်းမှု: ဖိုင်ကိုဖတ်နေစဥ်အတွင်း မှားယွင်းမှုဖြစ်သွားသည် - + QFile error: The file could not be accessed QFile မှားယွင်းမှု: ဖိုင်ကို ဝင်၍မရပါ - + QFile error: An unspecified error occurred QFile မှားယွင်းမှု: သတ်မှတ်မထားသော မှားယွင်းမှုတစ်ခု ဖြစ်ပွားခဲ့သည် - + QFile error: A fatal error occurred QFile မှားယွင်းမှု: ကြီးမားသော မှားယွင်းမှုတစ်ခု ဖြစ်ပွားခဲ့သည် - + QFile error: The operation was aborted QFile မှားယွင်းမှု: လုပ်ငန်းစဥ်ကို ဖျက်သိမ်းလိုက်ရသည် - + Internal error စက်တွင်းဖြစ်သော မှားယွင်းမှု @@ -3529,32 +4002,28 @@ Already installed containers were found on the server. All installed containers IPsec - Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions. - Shadowsocks - ၎င်းသည် ပုံမှန်ဝဘ်လမ်းကြောင်းနှင့် ဆင်တူစေရန် VPN အသွားအလာကို ဖုံးကွယ်ထားသော်လည်း ၎င်းကို အချို့သော ဆင်ဆာဖြတ်ထားသော ဒေသများရှိ ခွဲခြမ်းစိတ်ဖြာမှုစနစ်များက ထောက်လှန်းသိရှိနိုင်ပါသည်. + Shadowsocks - ၎င်းသည် ပုံမှန်ဝဘ်လမ်းကြောင်းနှင့် ဆင်တူစေရန် VPN အသွားအလာကို ဖုံးကွယ်ထားသော်လည်း ၎င်းကို အချို့သော ဆင်ဆာဖြတ်ထားသော ဒေသများရှိ ခွဲခြမ်းစိတ်ဖြာမှုစနစ်များက ထောက်လှန်းသိရှိနိုင်ပါသည်. - OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against active-probing detection. Ideal for bypassing blocking in regions with the highest levels of censorship. - OpenVPN over Cloak - ဝဘ်အသွားအလာအဖြစ် ဟန်ဆောင်ထားသည့် VPN ပါသော OpenVPN နှင့် active-probing ထောက်လှမ်းခြင်းမှ ကာကွယ်ပေးခြင်း. ဆင်ဆာဖြတ်တောက်မှု အမြင့်ဆုံးအဆင့်ရှိသော ဒေသများတွင် ပိတ်ဆို့ခြင်းများကို ကျော်ဖြတ်ရန်အတွက် အကောင်းဆုံးဖြစ်သည်. + OpenVPN over Cloak - ဝဘ်အသွားအလာအဖြစ် ဟန်ဆောင်ထားသည့် VPN ပါသော OpenVPN နှင့် active-probing ထောက်လှမ်းခြင်းမှ ကာကွယ်ပေးခြင်း. ဆင်ဆာဖြတ်တောက်မှု အမြင့်ဆုံးအဆင့်ရှိသော ဒေသများတွင် ပိတ်ဆို့ခြင်းများကို ကျော်ဖြတ်ရန်အတွက် အကောင်းဆုံးဖြစ်သည်. + + + XRay with REALITY - Suitable for countries with the highest level of internet censorship. Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods. + REALITY ပါဝင်သော XRay - အင်တာနက်ဆင်ဆာဖြတ်တောက်မှုအပြင်းထန်ဆုံးနိုင်ငံများအတွက် သင့်လျော်သည်။ Web traffic အဖြစ် အသွားအလာကို TLS အဆင့်ဖြင့် ဖုံးကွယ်ပေးထားခြင်း၊ Active probing နည်းလမ်းများဖြင့် ထောက်လှမ်းခံရခြင်းမှ ကာကွယ်ပေးခြင်းများ။. - XRay with REALITY - Suitable for countries with the highest level of internet censorship. Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods. - REALITY ပါဝင်သော XRay - အင်တာနက်ဆင်ဆာဖြတ်တောက်မှုအပြင်းထန်ဆုံးနိုင်ငံများအတွက် သင့်လျော်သည်။ Web traffic အဖြစ် အသွားအလာကို TLS အဆင့်ဖြင့် ဖုံးကွယ်ပေးထားခြင်း၊ Active probing နည်းလမ်းများဖြင့် ထောက်လှမ်းခံရခြင်းမှ ကာကွယ်ပေးခြင်းများ။. - - - IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss. It has native support on the latest versions of Android and iOS. IKEv2/IPsec - ခေတ်မီပြီးတည်ငြိမ်သော ပရိုတိုကော၊ အခြားပရိုတိုကောများထက် အနည်းငယ်ပိုမြန်သည်၊ Signal ဆုံးရှုံးပြီးနောက် ချိတ်ဆက်မှုကို ပြန်လည်ရယူနိုင်သည်။ Android နှင့် iOS ၏ နောက်ဆုံးဗားရှင်းများတွင် native ပံ့ပိုးမှုရရှိသည်။. - + Create a file vault on your server to securely store and transfer files. ဖိုင်များကို လုံခြုံစွာသိမ်းဆည်းရန်နှင့် လွှဲပြောင်းရန်အတွက် သင့်ဆာဗာပေါ်တွင် fire vault တစ်ခု ဖန်တီးပါ. - This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for protecting against blocking. OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client and the server. @@ -3573,7 +4042,7 @@ If there is a extreme level of Internet censorship in your region, we advise you * Not recognised by DPI analysis systems * Works over TCP network protocol, 443 port. - ဤပစ္စည်းသည်ပိတ်ဆို့ခြင်းမှကာကွယ်ရန်အတွက် အထူးထုတ်လုပ်ထားသည့် OpenVPN ပရိုတိုကောနှင့် Cloak plugin ၏ပေါင်းစပ်မှုဖြစ်သည်. + ဤပစ္စည်းသည်ပိတ်ဆို့ခြင်းမှကာကွယ်ရန်အတွက် အထူးထုတ်လုပ်ထားသည့် OpenVPN ပရိုတိုကောနှင့် Cloak plugin ၏ပေါင်းစပ်မှုဖြစ်သည်. OpenVPN သည် ကလိုင်းယင့်နှင့် ဆာဗာကြားရှိ အင်တာနက်အသွားအလာအားလုံးကို ကုဒ်ဝှက်ခြင်းဖြင့် လုံခြုံသော VPN ချိတ်ဆက်မှုကို ပံ့ပိုးပေးပါသည် @@ -3593,7 +4062,6 @@ Cloak သည် ပက်ကတ်မက်တာဒေတာကို မွမ - A relatively new popular VPN protocol with a simplified architecture. WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. WireGuard is very susceptible to blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. @@ -3603,7 +4071,7 @@ WireGuard is very susceptible to blocking due to its distinct packet signatures. * Minimum number of settings * Easily recognised by DPI analysis systems, susceptible to blocking * Works over UDP network protocol. - ရိုးရှင်းသော တည်ဆောက်ပုံဖြင့် အတော်လေးနာမည်ကြီးသော VPN ပရိုတိုကောအသစ်။ + ရိုးရှင်းသော တည်ဆောက်ပုံဖြင့် အတော်လေးနာမည်ကြီးသော VPN ပရိုတိုကောအသစ်။ WireGuard သည် ၎င်းအားအသုံးပြုထားသောစက်အားလုံးကို တည်ငြိမ်သော VPN ချိတ်ဆက်မှုနှင့် စွမ်းဆောင်ရည်မြင့်မားမှုကို ရရှိစေပါသည်။ Hard-coded encryption ဆက်တင်များကို အသုံးပြုထားပါသည်။ OpenVPN နှင့် နှိုင်းယှဉ်ပါက WireGuard သည် latency နည်းပါးပြီး ဒေတာလွှဲပြောင်းမှု ပိုမိုကောင်းမွန်ပါသည်။ WireGuard သည် ၎င်း၏ ကွဲပြားသော packet လက်မှတ်များ ကြောင့် ပိတ်ဆို့ခြင်းကို အလွန်ခံရနိုင်ချေရှိသည်။ ရှုပ်ထွေးသောနည်းပညာများကို အသုံးပြုသည့် အခြားသော VPN ပရိုတိုကောများနှင့် မတူဘဲ၊ WireGuard ပက်ကတ်များ၏ တသမတ်တည်း လက်မှတ်ပုံစံများကြောင့် ၎င်းတို့ကိုပိုမိုလွယ်ကူစွာ ရှာဖွေဖော်ထုတ်နိုင်ကာ အဆင့်မြင့် Deep Packet Inspection (DPI) စနစ်များနှင့် အခြားသော ကွန်ရက်စောင့်ကြည့်ရေးကိရိယာများဖြင့် ပိတ်ဆို့ထားနိုင်သည်။ @@ -3614,18 +4082,17 @@ WireGuard သည် ၎င်း၏ ကွဲပြားသော packet လက * UDP ကွန်ရက်ပရိုတိုကောပေါ်တွင် အလုပ်လုပ်သည်။. - The REALITY protocol, a pioneering development by the creators of XRay, is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion. It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, thus presenting an authentic TLS certificate and data. This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship. - The REALITY protocol, a pioneering development by the creators of XRay, is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion. + The REALITY protocol, a pioneering development by the creators of XRay, is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion. It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, thus presenting an authentic TLS certificate and data. This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship. - + IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol. One of its distinguishing features is its ability to swiftly switch between networks and devices, making it particularly adaptive in dynamic network environments. While it offers a blend of security, stability, and speed, it's essential to note that IKEv2 can be easily detected and is susceptible to blocking. @@ -3646,7 +4113,7 @@ IKEv2 သည် လုံခြုံရေး၊ တည်ငြိမ်မှ * UDP ကွန်ရက်ပရိုတိုကော၊ port 500 နှင့် 4500 ကျော်တွင် အလုပ်လုပ်သည်။. - + DNS Service DNS ဝန်ဆောင်မှု @@ -3657,7 +4124,7 @@ IKEv2 သည် လုံခြုံရေး၊ တည်ငြိမ်မှ - + Website in Tor network Tor ကွန်ရက်ထဲရှိ ဝဘ်ဆိုဒ် @@ -3672,27 +4139,115 @@ IKEv2 သည် လုံခြုံရေး၊ တည်ငြိမ်မှ OpenVPN သည် ပြောင်းလွယ်ပြင်လွယ် ဖွဲ့စည်းမှုရွေးချယ်စရာများပါရှိသော လူကြိုက်အများဆုံး VPN ပရိုတိုကောဖြစ်သည်. ၎င်းသည် key လဲလှယ်မှုအတွက် SSL/TLS ဖြင့် ၎င်း၏ကိုယ်ပိုင်လုံခြုံရေးပရိုတိုကောကို အသုံးပြုသည်. - + + Shadowsocks masks VPN traffic, making it resemble normal web traffic, but it may still be detected by certain analysis systems. + + + + + OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against active-probing detection. It is very resistant to detection, but offers low speed. + + + + + WireGuard - popular VPN protocol with high performance, high speed and low power consumption. + + + + + AmneziaWG is a special protocol from Amnezia based on WireGuard. It provides high connection speed and ensures stable operation even in the most challenging network conditions. + + + + + XRay with REALITY masks VPN traffic as web traffic and protects against active probing. It is highly resistant to detection and offers high speed. + + + + + OpenVPN stands as one of the most popular and time-tested VPN protocols available. +It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, catering to a wide range of devices and operating systems. Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, which continually reinforces its security. With a strong balance of performance, security, and compatibility, OpenVPN remains a top choice for privacy-conscious individuals and businesses alike. + +* Available in the AmneziaVPN across all platforms +* Normal power consumption on mobile devices +* Flexible customisation to suit user needs to work with different operating systems and devices +* Recognised by DPI systems and therefore susceptible to blocking +* Can operate over both TCP and UDP network protocols. + + + + + This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for protecting against detection. + +OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client and the server. + +Cloak protects OpenVPN from detection. + +Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, and also protects the VPN from detection by Active Probing. This makes it very resistant to being detected + +Immediately after receiving the first data packet, Cloak authenticates the incoming connection. If authentication fails, the plugin masks the server as a fake website and your VPN becomes invisible to analysis systems. + +* Available in the AmneziaVPN across all platforms +* High power consumption on mobile devices +* Flexible settings +* Not recognised by detection systems +* Works over TCP network protocol, 443 port. + + + + + + A relatively new popular VPN protocol with a simplified architecture. +WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. +WireGuard is very susceptible to detection and blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. + +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Easily recognised by DPI analysis systems, susceptible to blocking +* Works over UDP network protocol. + + + + + A modern iteration of the popular VPN protocol, AmneziaWG builds upon the foundation set by WireGuard, retaining its simplified architecture and high-performance capabilities across devices. +While WireGuard is known for its efficiency, it had issues with being easily detected due to its distinct packet signatures. AmneziaWG solves this problem by using better obfuscation methods, making its traffic blend in with regular internet traffic. +This means that AmneziaWG keeps the fast performance of the original while adding an extra layer of stealth, making it a great choice for those wanting a fast and discreet VPN connection. + +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Not recognised by traffic analysis systems +* Works over UDP network protocol. + + + + + The REALITY protocol, a pioneering development by the creators of XRay, is designed to provide the highest level of protection against detection through its innovative approach to security and privacy. +It uniquely identifies attackers during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting attackers to genuine websites, thus presenting an authentic TLS certificate and data. +This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. +Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security. This makes REALITY a robust solution for maintaining internet freedom. + + + WireGuard - New popular VPN protocol with high performance, high speed and low power consumption. Recommended for regions with low levels of censorship. - WireGuard - မြင့်မားသောစွမ်းဆောင်ရည်၊ မြန်နှုန်းမြင့်နှင့် ပါဝါသုံးစွဲမှုနည်းသော လူကြိုက်များသော VPN ပရိုတိုကောအသစ်. ဆင်ဆာဖြတ်မှုအဆင့်နိမ့်သော ဒေသများတွင်အသုံးပြုရန်အကြံပြုထားသည်. + WireGuard - မြင့်မားသောစွမ်းဆောင်ရည်၊ မြန်နှုန်းမြင့်နှင့် ပါဝါသုံးစွဲမှုနည်းသော လူကြိုက်များသော VPN ပရိုတိုကောအသစ်. ဆင်ဆာဖြတ်မှုအဆင့်နိမ့်သော ဒေသများတွင်အသုံးပြုရန်အကြံပြုထားသည်. - AmneziaWG - Special protocol from Amnezia, based on WireGuard. It's fast like WireGuard, but very resistant to blockages. Recommended for regions with high levels of censorship. - AmneziaWG - WireGuard ကိုအခြေခံထားသော Amnezia မှ အထူးပရိုတိုကော. ၎င်းသည် WireGuard ကဲ့သို့မြန်ဆန်သော်ပြီး ပိတ်ဆို့ခြင်းများကိုလည်း ခံနိုင်ရည်ရှိပါသည်. ဆင်ဆာဖြတ်တောက်မှု မြင့်မားသော ဒေသများတွင်အသုံးပြုရန် အကြံပြုပါသည်. + AmneziaWG - WireGuard ကိုအခြေခံထားသော Amnezia မှ အထူးပရိုတိုကော. ၎င်းသည် WireGuard ကဲ့သို့မြန်ဆန်သော်ပြီး ပိတ်ဆို့ခြင်းများကိုလည်း ခံနိုင်ရည်ရှိပါသည်. ဆင်ဆာဖြတ်တောက်မှု မြင့်မားသော ဒေသများတွင်အသုံးပြုရန် အကြံပြုပါသည်. - + Deploy a WordPress site on the Tor network in two clicks. ကလစ်နှစ်ချက်နှိပ်ရုံဖြင့် Tor ကွန်ရက်ပေါ်တွင် WordPress ဆိုက်တစ်ခုကို ဖြန့်ကျက်လိုက်ပါ. - + Replace the current DNS server with your own. This will increase your privacy level. လက်ရှိ DNS ဆာဗာကို သင့်ကိုယ်ပိုင် DNS ဆာဗာဖြင့် အစားထိုးပါ. ဤသို့ပြုလုပ်ခြင်းသည် သင်၏ကိုယ်ရေးကိုယ်တာလုံခြုံမှုအဆင့်ကို တိုးမြှင့်ပေးလိမ့်မည်. - OpenVPN stands as one of the most popular and time-tested VPN protocols available. It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, catering to a wide range of devices and operating systems. Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, which continually reinforces its security. With a strong balance of performance, security, and compatibility, OpenVPN remains a top choice for privacy-conscious individuals and businesses alike. @@ -3701,7 +4256,7 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for * Flexible customisation to suit user needs to work with different operating systems and devices * Recognised by DPI analysis systems and therefore susceptible to blocking * Can operate over both TCP and UDP network protocols. - OpenVPN သည်ပေါ်ပြူလာအဖြစ်ဆုံးနှင့် ကာလရှည်ကြာအသုံးဝင်ခဲ့ အသုံးဝင်နေစဲဖြစ်သော VPN ပရိုတိုကောများထဲမှတစ်ခုဖြစ်သည်။ + OpenVPN သည်ပေါ်ပြူလာအဖြစ်ဆုံးနှင့် ကာလရှည်ကြာအသုံးဝင်ခဲ့ အသုံးဝင်နေစဲဖြစ်သော VPN ပရိုတိုကောများထဲမှတစ်ခုဖြစ်သည်။ ကုဒ်ဝှက်ခြင်းနှင့် key လဲလှယ်ခြင်းအတွက် SSL/TLS ၏ ခွန်အားကို အသုံးချခြင်းဖြင့် OpenVPN သည် ၎င်း၏ထူးခြားသော လုံခြုံရေးပရိုတိုကောကို အသုံးပြုထားသည်။ ထို့အပြင် OpenVPN ၏ အထောက်အထားစိစစ်ခြင်းနည်းလမ်းများစွာအတွက်အထောက်အပံ့ပေးထားမှုသည် ၎င်းကို စွယ်စုံရလိုက်လျောညီထွေဖြစ်စေပြီး စက်ပစ္စည်းများနှင့် လည်ပတ်မှုစနစ်များစွာကို အထောက်အပံ့ပေးစေပါသည်။ ၎င်း၏ open-source သဘောသဘာဝကြောင့် OpenVPN သည် ၎င်းကို ကျယ်ကျယ်ပြန့်ပြန့် စိစစ်စောင့်ကြည့်ပေးသည့် global community ကြောင့် လုံခြုံရေးပိုမိုကောင်းမွန်လာသည့်အကျိုးကျေးဇူးများ ရရှိခဲ့သည်။ စွမ်းဆောင်ရည်၊ လုံခြုံရေးနှင့် မည်သည့်စက်ပစ္စည်းနှင့်မဆိုလိုက်လျှောညီထွေရှိမှုဂုဏ်သတ္တိတို့ကို မျှတစွာပိုင်ဆိုင်ထားသော OpenVPN သည် ကိုယ်ရေးကိုယ်တာလုံခြုံမှုကိုအထူးဂရုစိုက်သော ပုဂ္ဂိုလ်များနှင့် စီးပွားရေးလုပ်ငန်းများအတွက် ထိပ်တန်းရွေးချယ်မှုတစ်ခုအဖြစ် ရပ်တည်နေစဲဖြစ်ပါသည်။ * ပလက်ဖောင်းအားလုံးရှိ AmneziaVPN တွင်အသုံးပြုနိုင်သည်။ @@ -3711,7 +4266,7 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for * TCP နှင့် UDP ကွန်ရက် ပရိုတိုကော နှစ်ခုလုံးတွင် လည်ပတ်နိုင်သည်။. - + Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection.However, certain traffic analysis systems might still detect a Shadowsocks connection. Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol. * Available in the AmneziaVPN only on desktop platforms @@ -3726,7 +4281,6 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for * TCP ကွန်ရက် ပရိုတိုကောပေါ်တွင် အလုပ်လုပ်သည်။. - A modern iteration of the popular VPN protocol, AmneziaWG builds upon the foundation set by WireGuard, retaining its simplified architecture and high-performance capabilities across devices. While WireGuard is known for its efficiency, it had issues with being easily detected due to its distinct packet signatures. AmneziaWG solves this problem by using better obfuscation methods, making its traffic blend in with regular internet traffic. This means that AmneziaWG keeps the fast performance of the original while adding an extra layer of stealth, making it a great choice for those wanting a fast and discreet VPN connection. @@ -3736,7 +4290,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin * Minimum number of settings * Not recognised by DPI analysis systems, resistant to blocking * Works over UDP network protocol. - လူကြိုက်များသော VPN ပရိုတိုကော၏ ခေတ်မီပြန်လည်တည်ဆောက်မှုတစ်ခုဖြစ်သော AmneziaWG သည် WireGuard မှသတ်မှတ်ထားသော အခြေခံအုတ်မြစ်ပေါ်တွင် တည်ဆောက်ထားပြီး ၎င်း၏ရိုးရှင်းသောတည်ဆောက်ပုံနှင့် စွမ်းဆောင်ရည်မြင့်မားသောစွမ်းရည်များကို ဆက်လက်ထိန်းသိမ်းထားပါသည်။ + လူကြိုက်များသော VPN ပရိုတိုကော၏ ခေတ်မီပြန်လည်တည်ဆောက်မှုတစ်ခုဖြစ်သော AmneziaWG သည် WireGuard မှသတ်မှတ်ထားသော အခြေခံအုတ်မြစ်ပေါ်တွင် တည်ဆောက်ထားပြီး ၎င်း၏ရိုးရှင်းသောတည်ဆောက်ပုံနှင့် စွမ်းဆောင်ရည်မြင့်မားသောစွမ်းရည်များကို ဆက်လက်ထိန်းသိမ်းထားပါသည်။ WireGuard သည် ၎င်း၏ စွမ်းဆောင်ရည်အတွက် လူသိများသော်လည်း ၎င်း၏ ထူးခြားသော packet လက်မှတ်များ ကြောင့် အလွယ်တကူ ထောက်လှန်းရှာဖွေတွေ့ရှိနိုင်သည့် ပြဿနာများ ရှိခဲ့ပါသည်။ AmneziaWG သည် ၎င်း၏ အသွားအလာကို ပုံမှန်အင်တာနက်အသွားအလာနှင့် ရောနှောကာ ပိုမိုကောင်းမွန်သော ရှုပ်ထွေးသော နည်းလမ်းများကို အသုံးပြုခြင်းဖြင့် ဤပြဿနာကို ဖြေရှင်းပေးထားပါသည်။ ဆိုလိုသည်မှာ AmneziaWG သည် နောက်ထပ်ထောက်လှန်းရခက်စေသည့်အလွှာတစ်ခုထပ်ထည့်စဉ်တွင် မူရင်းမြန်ဆန်သောစွမ်းဆောင်ရည်ကို ထိန်းသိမ်းထားနိုင်သောကြောင့် မြန်ဆန်ပြီးပါးနပ်သော VPN ချိတ်ဆက်မှုကိုလိုချင်သူများအတွက် အကောင်းဆုံးရွေးချယ်မှုတစ်ခုဖြစ်ပါသည်။ @@ -3747,7 +4301,7 @@ WireGuard သည် ၎င်း၏ စွမ်းဆောင်ရည်အ * UDP ကွန်ရက်ပရိုတိုကောပေါ်တွင် အလုပ်လုပ်သည်။. - + After installation, Amnezia will create a file storage on your server. You will be able to access it using @@ -3773,7 +4327,7 @@ For more detailed information, you can - + SOCKS5 proxy server SOCKS5 proxy ဆာဗာ @@ -3964,6 +4518,19 @@ For more detailed information, you can Hostname နှင့် port ကြားရှိ colon separator ကို ရှာမတွေ့ပါ + + RenameServerDrawer + + + Server name + ဆာဗာအမည် + + + + Save + သိမ်းဆည်းမည် + + SelectLanguageDrawer @@ -4010,24 +4577,24 @@ For more detailed information, you can ShareConnectionDrawer - - + + Save AmneziaVPN config AmneziaWG config ကိုသိမ်းဆည်းမည် - + Share မျှဝေမည် - + Copy ကူးယူမည် - - + + Copied ကူးယူပြီးပါပြီ @@ -4037,12 +4604,12 @@ For more detailed information, you can config string ကိုကူးယူမည် - + Show connection settings ချိတ်ဆက်မှုဆက်တင်များကို ပြပါ - + To read the QR code in the Amnezia app, select "Add server" → "I have data to connect" → "QR code, key or settings file" Amnezia အက်ပ်ရှိ QR ကုဒ်ကိုဖတ်ရန်အတွက်အောက်ပါအတိုင်း ရွေးချယ်ပါ "ဆာဗာထည့်ရန်" → "ချိတ်ဆက်ရန် ဒေတာရှိသည်" → "QR ကုဒ်၊ key သို့မဟုတ် ဆက်တင်ဖိုင်" @@ -4055,37 +4622,37 @@ For more detailed information, you can လက်ခံသူအမည်သည် ip လိပ်စာ သို့မဟုတ် ဒိုမိန်းအမည်နှင့် မတူပါ - + New site added: %1 ဆိုဒ်အသစ်ထပ်ထည့်ပြီးပါပြီ: %1 - + Site removed: %1 ဆိုက်ကို ဖယ်ရှားလိုက်သည်: %1 - + Can't open file: %1 ဖိုင်ကိုဖွင့်၍မရပါ: %1 - + Failed to parse JSON data from file: %1 JSON ဒေတာကို ဖိုင်မှ ခွဲခြမ်းထုပ်ယူမှု မအောင်မြင်ပါ: %1 - + The JSON data is not an array in file: %1 JSON ဒေတာသည် ဖိုင်ထဲရှိ array တစ်ခုမဟုတ်ပါ: %1 - + Import completed တင်သွင်းခြင်းပြီးဆုံးသွားပါပြီ - + Export completed ထုတ်ယူခြင်းပြီးဆုံးသွားပါပြီ @@ -4126,7 +4693,7 @@ For more detailed information, you can TextFieldWithHeaderType - + The field can't be empty ဖြည့်သွင်းရမည့်နေရာသည် အလွတ်မဖြစ်ရပါ @@ -4134,7 +4701,7 @@ For more detailed information, you can VpnConnection - + Mbps Mbps @@ -4185,24 +4752,30 @@ For more detailed information, you can amnezia::ContainerProps - Low - Low + Low + + + High + High + + + I just want to increase the level of my privacy. + ကျွန်ုပ်၏ကိုယ်ရေးကိုယ်တာလုံခြုံမှုအဆင့်ကို မြှင့်တင်လိုပါသည်. + + + I want to bypass censorship. This option recommended in most cases. + ဆင်ဆာဖြတ်တောက်ခြင်းကို ကျော်ဖြတ်ချင်ပါသည်. ဤရွေးချယ်မှုကို ကိစ္စအများစုအတွက် အကြံပြုထားသည်. + + + + Automatic + - High - High - - - - I just want to increase the level of my privacy. - ကျွန်ုပ်၏ကိုယ်ရေးကိုယ်တာလုံခြုံမှုအဆင့်ကို မြှင့်တင်လိုပါသည်. - - - - I want to bypass censorship. This option recommended in most cases. - ဆင်ဆာဖြတ်တောက်ခြင်းကို ကျော်ဖြတ်ချင်ပါသည်. ဤရွေးချယ်မှုကို ကိစ္စအများစုအတွက် အကြံပြုထားသည်. + AmneziaWG protocol will be installed. It provides high connection speed and ensures stable operation even in the most challenging network conditions. + diff --git a/client/translations/amneziavpn_ru_RU.ts b/client/translations/amneziavpn_ru_RU.ts index c4ae0ffd..9c332f17 100644 --- a/client/translations/amneziavpn_ru_RU.ts +++ b/client/translations/amneziavpn_ru_RU.ts @@ -4,9 +4,13 @@ AdLabel - Amnezia Premium - for access to any website - Amnezia Premium - для доступа к любым сайтам + Amnezia Premium - для доступа к любым сайтам + + + + Amnezia Premium - for access to all websites and online resources + @@ -56,12 +60,12 @@ %1 успешно установлен. - + API config reloaded Конфигурация API перезагружена - + Successfully changed the country of connection to %1 Страна подключения изменена на %1 @@ -90,9 +94,8 @@ Amnezia Free - это бесплатный VPN для обхода блокировок в странах с высоким уровнем интернет-цензуры - Amnezia Premium is VPN for comfortable work, downloading large files and watching videos in 8K resolution. Works for any sites with no restrictions. Speed up to %1 MBit/s. Unlimited traffic. - Amnezia Premium — VPN для комфортной работы, скачивания больших файлов и просмотра видео в высоком разрешении. Скорость до %1 Мбит/с. Безлимитный трафик. + Amnezia Premium — VPN для комфортной работы, скачивания больших файлов и просмотра видео в высоком разрешении. Скорость до %1 Мбит/с. Безлимитный трафик. @@ -101,9 +104,18 @@ AmneziaFree предоставляет бесплатный неограниченный доступ к базовому набору сайтов и приложений, таким как Facebook, Instagram, Twitter (X), Discord, Telegram и другим. YouTube не включен в бесплатный тариф. - Amnezia Premium is VPN for comfortable work, downloading large files and watching videos in 8K resolution. Works for any sites with no restrictions. - Amnezia Premium — VPN для комфортной работы, скачивания больших файлов и просмотра видео в высоком разрешении. Работает для любых сайтов без ограничений. + Amnezia Premium — VPN для комфортной работы, скачивания больших файлов и просмотра видео в высоком разрешении. Работает для любых сайтов без ограничений. + + + + Amnezia Premium is classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. Speeds up to %1 Mbps. + + + + + Amnezia Premium is classic VPN for for seamless work, downloading large files, and watching videos. Access all websites and online resources. + @@ -340,14 +352,23 @@ Can't be disabled for current server Неверный файл конфигурации - + Scanned %1 of %2. Отсканировано %1 из %2. - + + This configuration contains an OpenVPN setup. OpenVPN configurations can include malicious scripts, so only add it if you fully trust the provider of this config. + + + + + <br>In the imported configuration, potentially dangerous lines were found: + + + In the imported configuration, potentially dangerous lines were found: - В импортированной конфигурации были обнаружены потенциально опасные строки: + В импортированной конфигурации были обнаружены потенциально опасные строки: @@ -1589,9 +1610,13 @@ Already installed containers were found on the server. All installed containers PageSettingsApiDevices - Active devices - Активные устройства + Активные устройства + + + + Active Devices + @@ -1742,9 +1767,13 @@ Already installed containers were found on the server. All installed containers Сохранить конфигурацию AmneziaVPN - Configuration files - Файл конфигурации + Файл конфигурации + + + + Configuration Files + @@ -1832,9 +1861,8 @@ Already installed containers were found on the server. All installed containers Период работы - Valid until - Действует до + Действует до Speed @@ -1845,14 +1873,12 @@ Already installed containers were found on the server. All installed containers Скопировано - Subscription status - Статус подписки + Статус подписки - Active connections - Активные соединения + Активные соединения @@ -1860,9 +1886,8 @@ Already installed containers were found on the server. All installed containers Сетевые адреса одного или нескольких серверов были обновлены. Пожалуйста, удалите старые конфигурацию и загрузите новые файлы - Subscription key - Ключ для подключения + Ключ для подключения @@ -1870,9 +1895,8 @@ Already installed containers were found on the server. All installed containers Ключ подписки Amnezia Premium - Save VPN key to file - Сохранить VPN-ключ в файле + Сохранить VPN-ключ в файле @@ -1880,9 +1904,8 @@ Already installed containers were found on the server. All installed containers Скопировать VPN ключ - Configuration files - Файл конфигурации + Файл конфигурации @@ -1890,9 +1913,43 @@ Already installed containers were found on the server. All installed containers Управление файлами конфигурации - Active devices - Активные устройства + Активные устройства + + + + Subscription Status + + + + + Valid Until + + + + + Active Connections + + + + + Subscription Key + + + + + Save VPN key as a file + + + + + Configuration Files + + + + + Active Devices + @@ -1982,9 +2039,13 @@ Already installed containers were found on the server. All installed containers - Email Support - Email + Email + + + + Email + @@ -2332,8 +2393,12 @@ Already installed containers were found on the server. All installed containers + Cannot change KillSwitch settings during active connection + + + Cannot change killSwitch settings during active connection - Невозможно изменить настройки аварийного выключателя во время активного соединения + Невозможно изменить настройки аварийного выключателя во время активного соединения @@ -4046,38 +4111,58 @@ and will not be shared or disclosed to the Amnezia or any third parties Server error: Package manager error Ошибка сервера: Ошибка менеджера пакетов + + + The sudo package is not pre-installed on the server + + + + + The server user's home directory is not accessible + + + Action not allowed in sudoers + + + + + The user's password is required + + + + SSH request was denied SSH-запрос был отклонён - + SSH request was interrupted SSH-запрос был прерван - + SSH internal error Внутренняя ошибка SSH - + Invalid private key or invalid passphrase entered Введен неверный закрытый ключ или неверная парольная фраза - + The selected private key format is not supported, use openssh ED25519 key types or PEM key types Выбранный формат закрытого ключа не поддерживается, используйте типы ключей openssh ED25519 или PEM - + Timeout connecting to server Тайм-аут подключения к серверу - + SCP error: Generic failure Ошибка SCP: общий сбой @@ -4134,23 +4219,23 @@ and will not be shared or disclosed to the Amnezia or any third parties Sftp error: No media was in remote drive - + The config does not contain any containers and credentials for connecting to the server Конфигурация не содержит каких-либо контейнеров и учетных данных для подключения к серверу - - + + Error when retrieving configuration from API Ошибка при получении конфигурации из API - + This config has already been added to the application Данная конфигурация уже была добавлена в приложение - + ErrorCode: %1. Код ошибки: %1. @@ -4159,139 +4244,139 @@ and will not be shared or disclosed to the Amnezia or any third parties Failed to save config to disk - + OpenVPN config missing Отсутствует конфигурация OpenVPN - + OpenVPN management server error Серверная ошибка управлением OpenVPN - + OpenVPN executable missing Отсутствует исполняемый файл OpenVPN - + Shadowsocks (ss-local) executable missing Отсутствует исполняемый файл Shadowsocks (ss-local) - + Cloak (ck-client) executable missing Отсутствует исполняемый файл Cloak (ck-client) - + Amnezia helper service error Ошибка вспомогательной службы Amnezia - + OpenSSL failed Ошибка OpenSSL - + Can't connect: another VPN connection is active Невозможно подключиться: активно другое VPN-соединение - + Can't setup OpenVPN TAP network adapter Невозможно настроить сетевой адаптер OpenVPN TAP - + VPN pool error: no available addresses Ошибка пула VPN: нет доступных адресов - + Unable to open config file Не удалось открыть файл конфигурации - + VPN Protocols is not installed. Please install VPN container at first VPN-протоколы не установлены. Пожалуйста, установите протокол - + VPN connection error Ошибка VPN-соединения - + In the response from the server, an empty config was received В ответе от сервера была получена пустая конфигурация - + SSL error occurred Произошла ошибка SSL - + Server response timeout on api request Тайм-аут ответа сервера на запрос API - + Missing AGW public key - + Failed to decrypt response payload - + Missing list of available services - + The limit of allowed configurations per subscription has been exceeded Превышен лимит разрешенных конфигураций для одной подписки - + QFile error: The file could not be opened Ошибка QFile: не удалось открыть файл - + QFile error: An error occurred when reading from the file Ошибка QFile: произошла ошибка при чтении из файла - + QFile error: The file could not be accessed Ошибка QFile: не удалось получить доступ к файлу - + QFile error: An unspecified error occurred Ошибка QFile: произошла неизвестная ошибка - + QFile error: A fatal error occurred Ошибка QFile: произошла фатальная ошибка - + QFile error: The operation was aborted Ошибка QFile: операция была прервана - + Internal error Внутренняя ошибка @@ -5053,37 +5138,37 @@ This means that AmneziaWG keeps the fast performance of the original while addin Имя хоста не похоже на IP-адрес или доменное имя - + New site added: %1 Добавлен новый сайт: %1 - + Site removed: %1 Сайт удален: %1 - + Can't open file: %1 Невозможно открыть файл: %1 - + Failed to parse JSON data from file: %1 Не удалось разобрать JSON-данные из файла: %1 - + The JSON data is not an array in file: %1 JSON-данные не являются массивом в файле: %1 - + Import completed Импорт завершен - + Export completed Экспорт завершен @@ -5132,7 +5217,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin VpnConnection - + Mbps Мбит/с diff --git a/client/translations/amneziavpn_uk_UA.ts b/client/translations/amneziavpn_uk_UA.ts index 2875850c..3fa42c9f 100644 --- a/client/translations/amneziavpn_uk_UA.ts +++ b/client/translations/amneziavpn_uk_UA.ts @@ -4,8 +4,8 @@ AdLabel - - Amnezia Premium - for access to any website + + Amnezia Premium - for access to all websites and online resources @@ -32,55 +32,124 @@ VPN Підключено + + ApiAccountInfoModel + + + + Active + + + + + Inactive + + + + + %1 out of %2 + + + + + Classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. Speeds up to 200 Mbps + + + + + Free unlimited access to a basic set of websites such as Facebook, Instagram, Twitter (X), Discord, Telegram and more. YouTube is not included in the free plan. + + + + + amnezia_free_support_bot + + + + + amnezia_premium_support_bot + + + + + ApiConfigsController + + + %1 installed successfully. + %1 встановлено успішно. + + + + API config reloaded + Конфігурацію API перезавантажено + + + + Successfully changed the country of connection to %1 + Успішно змінено країну підключення на %1 + + ApiServicesModel - Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s - Звичайний VPN для комфортної роботи, завантаження великих файлів та перегляду відео. Працює для будь-яких сайтів. Швидкість до %1 MBit/s + Звичайний VPN для комфортної роботи, завантаження великих файлів та перегляду відео. Працює для будь-яких сайтів. Швидкість до %1 MBit/s - VPN to access blocked sites in regions with high levels of Internet censorship. - VPN для доступу до заблокованих сайтів у регіонах з високим рівнем інтернет-цензури. + VPN для доступу до заблокованих сайтів у регіонах з високим рівнем інтернет-цензури. - + <p><a style="color: #EB5757;">Not available in your region. If you have VPN enabled, disable it, return to the previous screen, and try again.</a> - Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship. - Amnezia Premium - звичайний VPN для комфортної роботи, завантаження великих файлів та перегляду відео у високій роздільній здатності. Працює для всіх вебсайтів, навіть у країнах з найвищим рівнем інтернет-цензури. + Amnezia Premium - звичайний VPN для комфортної роботи, завантаження великих файлів та перегляду відео у високій роздільній здатності. Працює для всіх вебсайтів, навіть у країнах з найвищим рівнем інтернет-цензури. - Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship - Amnezia Free — це безкоштовний VPN для обходу блокувань у країнах з високим рівнем інтернет-цензури + Amnezia Free — це безкоштовний VPN для обходу блокувань у країнах з високим рівнем інтернет-цензури - + + Amnezia Premium is classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. Speeds up to %1 Mbps. + + + + + + AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan. + + + + + Amnezia Premium is classic VPN for for seamless work, downloading large files, and watching videos. Access all websites and online resources. + + + + %1 MBit/s %1 MBit/s - + %1 days %1 днів - + VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, <a href="%1/free" style="color: #FBB26A;">more details on the website.</a> Лише популярні сайти, які заблоковані у вашому регіоні, будуть відкриватись за допомогою VPN підключення (Instagram, Facebook, Twitter та ін.). Звичайні сайти будуть відкриватися без використання VPN, <a href="%1/free" style="color: #FBB26A;">більш детально на нашому сайті.</a> - + Free Безкоштовно - + %1 $/month %1 $/місяць @@ -119,62 +188,59 @@ ConnectionController - VPN Protocols is not installed. Please install VPN container at first - VPN протоколи не встановлено. + VPN протоколи не встановлено. Будь-ласка, встановіть VPN контейнер - unable to create configuration - Неможливо створити конфігурацію + Неможливо створити конфігурацію - + Connecting... Підключення... - + Connected Підключено - + Preparing... Підготовка... - + Settings updated successfully, reconnnection... Налаштування оновлено, підключення... - + Settings updated successfully Налаштування оновлено - The selected protocol is not supported on the current platform - Вибраний протокол не підтримується на цьому пристрої + Вибраний протокол не підтримується на цьому пристрої - + Reconnecting... Перепідключення... - - - - + + + + Connect Підключитись - + Disconnecting... Відключаємось... @@ -301,100 +367,106 @@ Can't be disabled for current server Недійсний файл конфігурації - + Scanned %1 of %2. Відскановано %1 з %2. - + + This configuration contains an OpenVPN setup. OpenVPN configurations can include malicious scripts, so only add it if you fully trust the provider of this config. + + + + + <br>In the imported configuration, potentially dangerous lines were found: + + + In the imported configuration, potentially dangerous lines were found: - У імпортованій конфігурації знайдено потенційно небезпечні рядки: + У імпортованій конфігурації знайдено потенційно небезпечні рядки: InstallController - + %1 installed successfully. %1 встановлено. - + %1 is already installed on the server. %1 вже встановлено на сервері. - + Added containers that were already installed on the server Додані сервіси і протоколи, які були раніше встановлені на сервері - + Already installed containers were found on the server. All installed containers have been added to the application На сервері знайдені сервіси та протоколи, всі вони додані в застосунок - + Settings updated successfully Налаштування оновлено - + Server '%1' was rebooted Сервер '%1' перезавантажено - + Server '%1' was removed Сервер '%1' був видалений - + All containers from server '%1' have been removed Всі сервіси та протоколи були видалені з сервера '%1' - + %1 has been removed from the server '%2' %1 був видалений з сервера '%2' - + Api config removed Конфігурацію API видалено - + %1 cached profile cleared Кешований профіль %1 очищено - + Please login as the user Буль-ласка, увійдіть в систему від імені користувача - + Server added successfully Сервер додано - %1 installed successfully. - %1 встановлено успішно. + %1 встановлено успішно. - API config reloaded - Конфігурацію API перезавантажено + Конфігурацію API перезавантажено - Successfully changed the country of connection to %1 - Успішно змінено країну підключення на %1 + Успішно змінено країну підключення на %1 @@ -506,12 +578,12 @@ Already installed containers were found on the server. All installed containers Роздільне тунелювання вимкнено - + VPN protocol VPN протокол - + Servers Сервери @@ -1440,22 +1512,22 @@ Already installed containers were found on the server. All installed containers Застосунок - + Backup Резервне копіювання - + About AmneziaVPN Про AmneziaVPN - + Dev console - + Close application Закрити застосунок @@ -1467,7 +1539,7 @@ Already installed containers were found on the server. All installed containers Підтримайте проект донатом - + Support Amnezia Підтримайте Amnezia @@ -1492,12 +1564,12 @@ Already installed containers were found on the server. All installed containers Показати інші способи на Github - + Amnezia is a free and open-source application. You can support the developers if you like it. Amnezia — це безкоштовний додаток з відкритим кодом. Якщо вам подобається цей додаток, ви можете підтримати розробників. - + Contacts Контакти @@ -1531,32 +1603,36 @@ Already installed containers were found on the server. All installed containers Для відгуків і повідомлень про помилки - Copied - Скопійовано + Скопійовано - + + mailto:support@amnezia.org + + + + GitHub GitHub - + Discover the source code - + https://github.com/amnezia-vpn/amnezia-client https://github.com/amnezia-vpn/amnezia-client - + Website Веб-сайт - + Visit official website @@ -1565,109 +1641,473 @@ Already installed containers were found on the server. All installed containers https://amnezia.org - + Software version: %1 Версія ПЗ: %1 - + Check for updates Перевірити оновлення - + Privacy Policy Політика конфіденційності - PageSettingsApiLanguageList + PageSettingsApiAvailableCountries - + + Location for connection + + + + Unable change server location while there is an active connection - PageSettingsApiServerInfo + PageSettingsApiDevices - - For the region - Для регіону - - - - Price - Ціна - - - - Work period - Період роботи - - - - Valid until + + Active Devices - + + Manage currently connected devices + + + + + You can find the identifier on the Support tab or, for older versions of the app, by tapping '+' and then the three dots at the top of the page. + + + + + (current device) + + + + + Support tag: + + + + + Last updated: + + + + + Cannot unlink device during active connection + + + + + Are you sure you want to unlink this device? + + + + + This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect. + + + + + Continue + Продовжити + + + + Cancel + Відмінити + + + + PageSettingsApiInstructions + + + Windows + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#windows + + + + + macOS + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#macos + + + + + Android + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#android + + + + + AndroidTV + + + + + https://docs.amnezia.org/ru/documentation/instructions/android_tv_connect/ + + + + + iOS + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#ios + + + + + Linux + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#linux + + + + + Routers + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#routers + + + + + How to connect on another device + + + + + Setup guides on the Amnezia website + + + + + PageSettingsApiNativeConfigs + + + Save AmneziaVPN config + Зберегти config AmneziaVPN + + + + Configuration Files + + + + + For router setup or the AmneziaWG app + + + + + The configuration needs to be reissued + + + + + configuration file + + + + + Generate a new configuration file + + + + + The previously created one will stop working + + + + + Revoke the current configuration file + + + + + Config file saved + + + + + The config has been revoked + + + + + Generate a new %1 configuration file? + + + + + Revoke the current %1 configuration file? + + + + + Your previous configuration file will no longer work, and it will not be possible to connect using it + + + + + Download + + + + + Continue + Продовжити + + + + Cancel + Відмінити + + + + PageSettingsApiServerInfo + + For the region + Для регіону + + + Price + Ціна + + + Work period + Період роботи + + Speed - Швидкість + Швидкість - - Support tag - - - - Copied - Скопійовано + Скопійовано - + + Subscription Status + + + + + Valid Until + + + + + Active Connections + + + + + Configurations have been updated for some countries. Download and install the updated configuration files + + + + + Subscription Key + + + + + Amnezia Premium subscription key + + + + + Save VPN key as a file + + + + + Copy VPN key + + + + + Configuration Files + + + + + Manage configuration files + + + + + Active Devices + + + + + Manage currently connected devices + + + + + Support + + + + + How to connect on another device + + + + Reload API config Перезавантажити конфігурацію API - + Reload API config? Перезавантажити конфігурацію API? - - + + + Continue Продовжити - - + + + Cancel Відмінити - + Cannot reload API config during active connection Неможливо перезавантажити конфігурацію API під час активного підключення - + + Unlink this device + + + + + Are you sure you want to unlink this device? + + + + + This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect. + + + + + Cannot unlink device during active connection + + + + Remove from application Видалити з додатку - + Remove from application? Видалити з додатку? - + Cannot remove server during active connection Неможливо видалити сервер під час активного підключення + + PageSettingsApiSupport + + + Telegram + + + + + Email + + + + + support@amnezia.org + + + + + Email Billing & Orders + + + + + help@vpnpay.io + + + + + Website + Веб-сайт + + + + amnezia.org + + + + + Support + + + + + Our technical support specialists are available to assist you at any time + + + + + Support tag + + + + + Copied + Скопійовано + + PageSettingsAppSplitTunneling @@ -1968,8 +2408,12 @@ Already installed containers were found on the server. All installed containers + Cannot change KillSwitch settings during active connection + + + Cannot change killSwitch settings during active connection - Неможливо змінити налаштування killSwitch під час активного підключення + Неможливо змінити налаштування killSwitch під час активного підключення @@ -2083,20 +2527,20 @@ Already installed containers were found on the server. All installed containers Відкрити папку з логами - - + + Save Зберегти - - + + Logs files (*.log) Logs files (*.log) - - + + Logs file saved Файл з логами збережено @@ -2130,32 +2574,32 @@ Already installed containers were found on the server. All installed containers Логи видалено - + Client logs - + AmneziaVPN logs - + Open logs folder - + Export logs - + Service logs - + AmneziaVPN-service logs @@ -2195,88 +2639,88 @@ Already installed containers were found on the server. All installed containers - - - - + + + + Continue Продовжити - - - - + + + + Cancel Відмінити - + Check the server for previously installed Amnezia services Проверить сервер на наличие ранее установленных сервисов Amnezia - + Add them to the application if they were not displayed Додати їх в застосунок, якщо вони не були відображені - + Reboot server Перезавантажити сервер - + Do you want to reboot the server? Ви впевнені, що хочете перезавантажити сервер? - + The reboot process may take approximately 30 seconds. Are you sure you wish to proceed? Процес перезавантаження може зайняти близько 30 сек. Ви впевені, що хочете продовжити? - + Cannot reboot server during active connection Неможливо перезавантажити сервер під час активного підключення - + Remove server from application Видалити сервер з додатка - + Do you want to remove the server from application? Ви впевнені, що хочете видалити сервер із застосунку? - + Cannot remove server during active connection Неможливо видалити сервер під час активного підключення - + Clear server from Amnezia software Очистити сервер від програмного забезпечення Amnezia - + Do you want to clear server from Amnezia software? Ви дійсно хочете очистити сервер від програмного забезпечення Amnezia? - + All users whom you shared a connection with will no longer be able to connect to it. Усі користувачі, з якими ви поділилися підключенням, більше не зможуть підключитися до нього. - + Cannot clear server from Amnezia software during active connection Неможливо очистити сервер від програмного забезпечення Amnezia під час активного підключення - + Cannot reset API config during active connection Неможливо скинути конфігурацію API під час активного підключення @@ -2285,12 +2729,12 @@ Already installed containers were found on the server. All installed containers Ви хочете очистити сервер від сервісів Amnezia? - + Reset API config Скинути API конфігурацію - + Do you want to reset API config? Ви хочете скинути API конфігурацію? @@ -2303,7 +2747,7 @@ Already installed containers were found on the server. All installed containers Видалити сервер із застосунку? - + All installed AmneziaVPN services will still remain on the server. Всі встановлені сервіси та протоколи Amnezia все ще залишаться на сервері. @@ -2323,32 +2767,25 @@ Already installed containers were found on the server. All installed containers PageSettingsServerInfo - - Subscription is valid until - - - - Server name - Імя сервера + Імя сервера - Save - Зберегти + Зберегти - + Protocols Протоколи - + Services Сервіси - + Management Управління @@ -2475,17 +2912,17 @@ Already installed containers were found on the server. All installed containers Режим - + Remove Видалити - + Continue Продовжити - + Cancel Відмінити @@ -2494,7 +2931,7 @@ Already installed containers were found on the server. All installed containers Сайт чи IP - + Import / Export Sites Імпорт / Експорт Сайтів @@ -2514,12 +2951,12 @@ Already installed containers were found on the server. All installed containers - + website or IP вебсайт або IP - + Import Імпорт @@ -2535,29 +2972,29 @@ Already installed containers were found on the server. All installed containers - - + + Sites files (*.json) Sites files (*.json) - + Import a list of sites Імпортувати список із сайтами - + Replace site list Замінити список із сайтами - - + + Open sites file Відкрити список із сайтами - + Add imported sites to existing ones Додати імпортовані сайти до існуючих @@ -2631,7 +3068,7 @@ It's okay as long as it's from someone you trust. Виберіть що у вас є - + File with connection settings Файл з налаштуваннями підключення @@ -2690,80 +3127,65 @@ It's okay as long as it's from someone you trust. Інші параметри підключення - + Site Amnezia - + VPN by Amnezia VPN від Amnezia - + Connect to classic paid and free VPN services from Amnezia Підключайтеся до звичайних платних та безкоштовних VPN-сервісів від Amnezia - + Self-hosted VPN Self-hosted VPN - + Configure Amnezia VPN on your own server Налаштуйте Amnezia VPN на власному сервері - + Restore from backup Відновити із бекапа - + - + Open backup file Відкрити бекап файл - + Backup files (*.backup) Файли резервної копії (*.backup) - - - - - - + Open config file Відкрити файл з конфігурацією - + QR code QR-код - - - - - - + I have nothing У мене нічого нема - - - - - Key as text Ключ у вигляді тексту @@ -2776,7 +3198,7 @@ It's okay as long as it's from someone you trust. Підключення до сервера - + Server IP address [:port] Server IP address [:port] @@ -2789,7 +3211,7 @@ It's okay as long as it's from someone you trust. Password / SSH private key - + Continue Продовжити @@ -2800,7 +3222,7 @@ and will not be shared or disclosed to the Amnezia or any third parties і не будуть передані чи розголошені Amnezia або будь-яким третім особам - + Enter the address in the format 255.255.255.255:88 Введіть адресу в форматі 255.255.255.255:88 @@ -2814,47 +3236,47 @@ and will not be shared or disclosed to the Amnezia or any third parties Налаштувати свій сервер - + 255.255.255.255:22 255.255.255.255:22 - + SSH Username SSH Username - + Password or SSH private key Пароль або SSH ключ - + All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties Усі дані, які ви вводите, залишатимуться суворо конфіденційними та не будуть передані чи розголошені Amnezia або будь-яким третім особам - + How to run your VPN server Як запустити ваш VPN-сервер - + Where to get connection data, step-by-step instructions for buying a VPS Де отримати дані для підключення: покрокові інструкції з придбання VPS - + Ip address cannot be empty Поле IP address не може бути пустим - + Login cannot be empty Поле Login не може бути пустим - + Password/private key cannot be empty Поле Password/Private key не може бути пустим @@ -2862,17 +3284,26 @@ and will not be shared or disclosed to the Amnezia or any third parties PageSetupWizardEasy - What is the level of internet control in your region? - Який рівень контроля над інтернетом у вашому регіоні? + Який рівень контроля над інтернетом у вашому регіоні? + + + + Choose Installation Type + + Manual + + + + Choose a VPN protocol Виберіть протокол VPN - + Skip setup Пропустити налаштування @@ -2885,7 +3316,7 @@ and will not be shared or disclosed to the Amnezia or any third parties Вибрати VPN-протокол - + Continue Продовжити @@ -3100,12 +3531,12 @@ and will not be shared or disclosed to the Amnezia or any third parties PageShare - + OpenVPN native format OpenVPN нативний формат - + WireGuard native format WireGuard нативний формат @@ -3114,7 +3545,7 @@ and will not be shared or disclosed to the Amnezia or any third parties VPN-Доступ - + Connection З'єднання @@ -3127,8 +3558,8 @@ and will not be shared or disclosed to the Amnezia or any third parties Доступ до керування сервером. Користувач, з яким ви ділитесь повним доступом до підключення, зможе додавати та видаляти протоколи і служби на сервері, а також змінювати налаштування. + - Server Сервер @@ -3152,113 +3583,113 @@ and will not be shared or disclosed to the Amnezia or any third parties Файл з налаштуванням доступу до - + Save OpenVPN config Зберегти OpenVPN конфігурацію - + Save WireGuard config Збергти WireGuard конфігурацію - + Save AmneziaWG config Зберегти AmneziaWG конфігурацію - + Save Shadowsocks config Зберегти конфігурацію Shadowsocks - + Save Cloak config Зберегти конфігурацію Cloak - + Save XRay config Зберегти конфігурацію XRay - + For the AmneziaVPN app Для AmneziaVPN - + AmneziaWG native format нативний формат AmneziaWG - + Shadowsocks native format Shadowsocks нативний формат - + Cloak native format Cloak нативний формат - + XRay native format XRay нативний формат - + Share VPN Access Поділитись VPN з'єднанням - + Share full access to the server and VPN Поділитись повним доступом до серверу - + Use for your own devices, or share with those you trust to manage the server. Використовуйте для власних пристроїв або передайте керування сервером тим, кому довіряєте. - - + + Users Користувачі - + User name Ім'я користувача - + Search Пошук - + Creation date: %1 Дата створення: %1 - + Latest handshake: %1 Останнє з'єднання: %1 - + Data received: %1 Отримано даних: %1 - + Data sent: %1 Відправлено даних: %1 - + Allowed IPs: %1 @@ -3267,42 +3698,42 @@ and will not be shared or disclosed to the Amnezia or any third parties Дата створення: - + Rename Перейменувати - + Client name Назва клієнта - + Save Зберегти - + Revoke Відкликати - + Revoke the config for a user - %1? Відкликати доступ для користувача - %1? - + The user will no longer be able to connect to your server. Користувач більше не зможе підключатись до вашого сервера - + Continue Продовжити - + Cancel Відмінити @@ -3311,25 +3742,25 @@ and will not be shared or disclosed to the Amnezia or any third parties Повний доступ - + Share VPN access without the ability to manage the server Поділитись доступом до VPN, без можливості керування сервером + - Protocol Протокол + - Connection format Формат підключення - - + + Share Поділитись @@ -3392,17 +3823,17 @@ and will not be shared or disclosed to the Amnezia or any third parties PageStart - + Logging was disabled after 14 days, log files were deleted Логування було вимкнене через 14 днів, файли журналів були видалені - + Settings restored from backup file Відновлення налаштувань із бекап файлу - + Logging is enabled. Note that logs will be automaticallydisabled after 14 days, and all log files will be deleted. @@ -3674,72 +4105,97 @@ and will not be shared or disclosed to the Amnezia or any third parties - + + The selected protocol is not supported on the current platform + Вибраний протокол не підтримується на цьому пристрої + + + Server check failed Server check failed - + Server port already used. Check for another software Server port already used. Check for another software - + Server error: Docker container missing Server error: Docker container missing - + Server error: Docker failed Server error: Docker failed - + Installation canceled by user Installation canceled by user - + The user is not a member of the sudo group Користувач не входить до групи sudo - + Server error: Package manager error Помилка сервера: Помилка менеджера пакетів + + + The sudo package is not pre-installed on the server + + + The server user's home directory is not accessible + + + + + Action not allowed in sudoers + + + + + The user's password is required + + + + SSH request was denied SSH request was denied - + SSH request was interrupted SSH request was interrupted - + SSH internal error SSH internal error - + Invalid private key or invalid passphrase entered Invalid private key or invalid passphrase entered - + The selected private key format is not supported, use openssh ED25519 key types or PEM key types The selected private key format is not supported, use openssh ED25519 key types or PEM key types - + Timeout connecting to server Timeout connecting to server - + SCP error: Generic failure @@ -3796,22 +4252,23 @@ and will not be shared or disclosed to the Amnezia or any third parties Sftp error: No media was in remote drive - + The config does not contain any containers and credentials for connecting to the server Конфігурація не містить контейнерів і облікових даних для підключення до серверу - + + Error when retrieving configuration from API - + This config has already been added to the application Ця конфігурація вже була додана в застосунок - + ErrorCode: %1. @@ -3820,127 +4277,139 @@ and will not be shared or disclosed to the Amnezia or any third parties Failed to save config to disk - + OpenVPN config missing OpenVPN config missing - + OpenVPN management server error OpenVPN management server error - + OpenVPN executable missing OpenVPN executable missing - + Shadowsocks (ss-local) executable missing Shadowsocks (ss-local) executable missing - + Cloak (ck-client) executable missing Cloak (ck-client) executable missing - + Amnezia helper service error Amnezia helper service error - + OpenSSL failed OpenSSL failed - + Can't connect: another VPN connection is active Can't connect: another VPN connection is active - + Can't setup OpenVPN TAP network adapter Can't setup OpenVPN TAP network adapter - + VPN pool error: no available addresses VPN pool error: no available addresses - + Unable to open config file - + + VPN Protocols is not installed. + Please install VPN container at first + VPN протоколи не встановлено. + Будь-ласка, встановіть VPN контейнер + + + VPN connection error - + In the response from the server, an empty config was received - + SSL error occurred - + Server response timeout on api request - + Missing AGW public key - + Failed to decrypt response payload - + Missing list of available services - + + The limit of allowed configurations per subscription has been exceeded + + + + QFile error: The file could not be opened - + QFile error: An error occurred when reading from the file - + QFile error: The file could not be accessed - + QFile error: An unspecified error occurred - + QFile error: A fatal error occurred - + QFile error: The operation was aborted - + Internal error Internal error @@ -3950,27 +4419,24 @@ and will not be shared or disclosed to the Amnezia or any third parties IPsec - Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions. - Shadowsocks - маскує VPN-трафік під звичайний веб-трафік, але розпізнається системами аналізу трафіка в деяких регіонах з високим рівнем цензури. + Shadowsocks - маскує VPN-трафік під звичайний веб-трафік, але розпізнається системами аналізу трафіка в деяких регіонах з високим рівнем цензури. - OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against active-probing detection. Ideal for bypassing blocking in regions with the highest levels of censorship. - OpenVPN over Cloak - OpenVPN з маскуванням VPN під HTTPS трафік і захистом від active-probing. Підходить для регіонів з самим високим рівнем цензури. + OpenVPN over Cloak - OpenVPN з маскуванням VPN під HTTPS трафік і захистом від active-probing. Підходить для регіонів з самим високим рівнем цензури. - + IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss. It has native support on the latest versions of Android and iOS. IKEv2/IPsec — сучасний стабільний протокол, який дещо швидший за інші та відновлює підключення після втрати сигналу. Має нативну підтримку на останніх версіях Android та iOS. - + Create a file vault on your server to securely store and transfer files. Створіть на сервері файлове сховище для безпечного зберігання та передачі файлів. - This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for protecting against blocking. OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client and the server. @@ -3989,7 +4455,7 @@ If there is a extreme level of Internet censorship in your region, we advise you * Not recognised by DPI analysis systems * Works over TCP network protocol, 443 port. - Це комбінація протоколу OpenVPN та плагіна Cloak, розроблена спеціально для захисту від блокувань. + Це комбінація протоколу OpenVPN та плагіна Cloak, розроблена спеціально для захисту від блокувань. OpenVPN забезпечує безпечне VPN-підключення шляхом шифрування всього інтернет-трафіку між клієнтом і сервером. @@ -4009,7 +4475,6 @@ Cloak може змінювати метадані пакетів так, що - A relatively new popular VPN protocol with a simplified architecture. WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. WireGuard is very susceptible to blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. @@ -4019,7 +4484,7 @@ WireGuard is very susceptible to blocking due to its distinct packet signatures. * Minimum number of settings * Easily recognised by DPI analysis systems, susceptible to blocking * Works over UDP network protocol. - Відносно новий популярний VPN-протокол з спрощеною архітектурою. + Відносно новий популярний VPN-протокол з спрощеною архітектурою. WireGuard забезпечує стабільне VPN-підключення та високу продуктивність на всіх пристроях. Він використовує жорстко закодовані налаштування шифрування. Порівняно з OpenVPN, WireGuard має нижчу затримку та кращу пропускну здатність передачі даних. WireGuard дуже чутливий до блокувань через свої чіткі підписи пакетів. На відміну від деяких інших VPN-протоколів, які використовують техніки обфускації, постійні шаблони підписів пакетів WireGuard легше ідентифікуються та можуть бути заблоковані просунутими системами глибокого аналізу пакетів (DPI) та іншими інструментами моніторингу мережі. @@ -4031,18 +4496,17 @@ WireGuard дуже чутливий до блокувань через свої * Працює через UDP мережевий протокол. - The REALITY protocol, a pioneering development by the creators of XRay, is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion. It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, thus presenting an authentic TLS certificate and data. This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship. - Протокол **REALITY**, сучасна розробка XRay. Спеціально розроблений для протидії найвищим рівням інтернет-цензури завдяки новому підходу до маскування. + Протокол **REALITY**, сучасна розробка XRay. Спеціально розроблений для протидії найвищим рівням інтернет-цензури завдяки новому підходу до маскування. REALITY унікально ідентифікує цензорів під час фази TLS-handshake, працюючи як проксі для VPN клієнтів, при цьому перенаправляючи цензорів на справжні вебсайти, такі як google.com, надаючи справжній TLS-сертифікат та інші дані. Цей функціонал, відрізняє REALITY від подібних технологій, своєю здатністю маскувати веб-трафік у такий такий, що походить із випадкових справжніх сайтів без необхідності спеціальних налаштувань. На відміну від старіших протоколів, таких як VMess, VLESS та XTLS-Vision transport, продвиуте розпізнавання "Свій — Чужий" REALITY під час TLS-handshake підвищує безпеку та протидіє виявленню складними системами DPI, що використовують активні техніки аналізу. Це робить REALITY надійним рішенням для підтримання інтернет-свободи в середовищах з жорсткою цензурою. - + IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol. One of its distinguishing features is its ability to swiftly switch between networks and devices, making it particularly adaptive in dynamic network environments. While it offers a blend of security, stability, and speed, it's essential to note that IKEv2 can be easily detected and is susceptible to blocking. @@ -4063,7 +4527,7 @@ While it offers a blend of security, stability, and speed, it's essential t * Працює по мережевому протоколу UDP, порти 500 і 4500. - + DNS Service DNS Сервіс @@ -4074,7 +4538,7 @@ While it offers a blend of security, stability, and speed, it's essential t - + Website in Tor network Веб-сайт в мережі Tor @@ -4089,36 +4553,69 @@ While it offers a blend of security, stability, and speed, it's essential t OpenVPN - популярний VPN-протокол, гнучний в налаштуваннях. Має власний протокол оснований на обміні ключами SSL/TLS. - + + Shadowsocks masks VPN traffic, making it resemble normal web traffic, but it may still be detected by certain analysis systems. + + + + + OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against active-probing detection. It is very resistant to detection, but offers low speed. + + + + + WireGuard - popular VPN protocol with high performance, high speed and low power consumption. + + + + + AmneziaWG is a special protocol from Amnezia based on WireGuard. It provides high connection speed and ensures stable operation even in the most challenging network conditions. + + + + + XRay with REALITY masks VPN traffic as web traffic and protects against active probing. It is highly resistant to detection and offers high speed. + + + + + OpenVPN stands as one of the most popular and time-tested VPN protocols available. +It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, catering to a wide range of devices and operating systems. Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, which continually reinforces its security. With a strong balance of performance, security, and compatibility, OpenVPN remains a top choice for privacy-conscious individuals and businesses alike. + +* Available in the AmneziaVPN across all platforms +* Normal power consumption on mobile devices +* Flexible customisation to suit user needs to work with different operating systems and devices +* Recognised by DPI systems and therefore susceptible to blocking +* Can operate over both TCP and UDP network protocols. + + + WireGuard - New popular VPN protocol with high performance, high speed and low power consumption. Recommended for regions with low levels of censorship. - WireGuard - новий популярний VPN-протокол, з високою швидістю та низьким енергоспоживанням. Для регіонів з низьким рівнем цензури. + WireGuard - новий популярний VPN-протокол, з високою швидістю та низьким енергоспоживанням. Для регіонів з низьким рівнем цензури. - AmneziaWG - Special protocol from Amnezia, based on WireGuard. It's fast like WireGuard, but very resistant to blockages. Recommended for regions with high levels of censorship. - AmneziaWG - фірмовий протокол Amnezia, оснований на протоколі WireGuard. Такий же швидкий, як і WireGuard, але стійкий до блокувань. Рекомендується для регіонів з високим рівнем цензури. + AmneziaWG - фірмовий протокол Amnezia, оснований на протоколі WireGuard. Такий же швидкий, як і WireGuard, але стійкий до блокувань. Рекомендується для регіонів з високим рівнем цензури. - XRay with REALITY - Suitable for countries with the highest level of internet censorship. Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods. - XRay with REALITY — підходить для країн з найвищим рівнем інтернет-цензури. Маскування трафіку під веб-трафік на рівні TLS. Захист від виявлення активними методами сканування (active-probing). + XRay with REALITY — підходить для країн з найвищим рівнем інтернет-цензури. Маскування трафіку під веб-трафік на рівні TLS. Захист від виявлення активними методами сканування (active-probing). IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss. IKEv2/IPsec сучасний стабільний протокол, трішки швидше за інших відновлює підключення. - + Deploy a WordPress site on the Tor network in two clicks. Розгорніть сайт WordPress в мережі Tor в два кліка. - + Replace the current DNS server with your own. This will increase your privacy level. Замініть DNS-сервер на AmneziaDNS. Це підвищить вашу рівень захищеності в інтернеті. - OpenVPN stands as one of the most popular and time-tested VPN protocols available. It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, catering to a wide range of devices and operating systems. Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, which continually reinforces its security. With a strong balance of performance, security, and compatibility, OpenVPN remains a top choice for privacy-conscious individuals and businesses alike. @@ -4127,7 +4624,7 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for * Flexible customisation to suit user needs to work with different operating systems and devices * Recognised by DPI analysis systems and therefore susceptible to blocking * Can operate over both TCP and UDP network protocols. - OpenVPN один із самих популярних і перевірених часом протоколів VPN. + OpenVPN один із самих популярних і перевірених часом протоколів VPN. Він використовує власний протокол, який опирається на протокол SSL/TLS для шифрування та обміну ключами. Крім того, підтримка OpenVPN багатьох методів аутентифікації робить його універсальним і адаптованим до широкого спектру пристроїв і операційних систем. Завдяки відкритому коду, OpenVPN піддається ретельному аналізу зі сторони світової спільноти, що постійно підвищує його безпеку. Завдяки оптимальному співвідношенню продуктивності, безпеки та сумісності OpenVPN залишається найкращим вибором як для приватних осіб, так і для компаній. * Доступний в AmneziaVPN для всіх платформ @@ -4137,7 +4634,7 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for * Може працювати за протоколом TCP і UDP. - + Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection.However, certain traffic analysis systems might still detect a Shadowsocks connection. Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol. * Available in the AmneziaVPN only on desktop platforms @@ -4152,7 +4649,61 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for * Працює по мережевому протоколу TCP. - + + This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for protecting against detection. + +OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client and the server. + +Cloak protects OpenVPN from detection. + +Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, and also protects the VPN from detection by Active Probing. This makes it very resistant to being detected + +Immediately after receiving the first data packet, Cloak authenticates the incoming connection. If authentication fails, the plugin masks the server as a fake website and your VPN becomes invisible to analysis systems. + +* Available in the AmneziaVPN across all platforms +* High power consumption on mobile devices +* Flexible settings +* Not recognised by detection systems +* Works over TCP network protocol, 443 port. + + + + + + A relatively new popular VPN protocol with a simplified architecture. +WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. +WireGuard is very susceptible to detection and blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. + +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Easily recognised by DPI analysis systems, susceptible to blocking +* Works over UDP network protocol. + + + + + A modern iteration of the popular VPN protocol, AmneziaWG builds upon the foundation set by WireGuard, retaining its simplified architecture and high-performance capabilities across devices. +While WireGuard is known for its efficiency, it had issues with being easily detected due to its distinct packet signatures. AmneziaWG solves this problem by using better obfuscation methods, making its traffic blend in with regular internet traffic. +This means that AmneziaWG keeps the fast performance of the original while adding an extra layer of stealth, making it a great choice for those wanting a fast and discreet VPN connection. + +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Not recognised by traffic analysis systems +* Works over UDP network protocol. + + + + + The REALITY protocol, a pioneering development by the creators of XRay, is designed to provide the highest level of protection against detection through its innovative approach to security and privacy. +It uniquely identifies attackers during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting attackers to genuine websites, thus presenting an authentic TLS certificate and data. +This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. +Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security. This makes REALITY a robust solution for maintaining internet freedom. + + + + After installation, Amnezia will create a file storage on your server. You will be able to access it using @@ -4228,7 +4779,6 @@ WireGuard дуже вразливий до блокування. На відмі * Працює по протоколу UDP. - A modern iteration of the popular VPN protocol, AmneziaWG builds upon the foundation set by WireGuard, retaining its simplified architecture and high-performance capabilities across devices. While WireGuard is known for its efficiency, it had issues with being easily detected due to its distinct packet signatures. AmneziaWG solves this problem by using better obfuscation methods, making its traffic blend in with regular internet traffic. This means that AmneziaWG keeps the fast performance of the original while adding an extra layer of stealth, making it a great choice for those wanting a fast and discreet VPN connection. @@ -4238,7 +4788,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin * Minimum number of settings * Not recognised by DPI analysis systems, resistant to blocking * Works over UDP network protocol. - Сучасна ітерація популярного протоколу VPN, AmneziaWG спирається на протокол WireGuard, зберігаючи його просту архітектуру та високопродуктивні можливості на різних пристроях. + Сучасна ітерація популярного протоколу VPN, AmneziaWG спирається на протокол WireGuard, зберігаючи його просту архітектуру та високопродуктивні можливості на різних пристроях. Незважаючи на те, що WireGuard відомий своєю ефективністю, він має проблеми з легким виявленням через чіткі підписи пакетів. AmneziaWG вирішує цю проблему, використовуючи кращі методи обфускації, завдяки чому її трафік змішується зі звичайним інтернет-трафіком. Це означає, що AmneziaWG зберігає швидку роботу оригінального протоколу WireGuard, додаючи додатковий рівень скритності, що робить його чудовим вибором для тих, хто бажає швидкого та непомітного VPN-з’єднання. @@ -4264,7 +4814,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin - + SOCKS5 proxy server SOCKS5 proxy server @@ -4455,6 +5005,19 @@ This means that AmneziaWG keeps the fast performance of the original while addin + + RenameServerDrawer + + + Server name + Імя сервера + + + + Save + Зберегти + + SelectLanguageDrawer @@ -4505,24 +5068,24 @@ This means that AmneziaWG keeps the fast performance of the original while addin ShareConnectionDrawer - - + + Save AmneziaVPN config Зберегти config AmneziaVPN - + Share Поділитись - + Copy Скопіювати - - + + Copied Скопійовано @@ -4532,12 +5095,12 @@ This means that AmneziaWG keeps the fast performance of the original while addin Скопіювати стрічку конфігурації - + Show connection settings Показати налаштування підключення - + To read the QR code in the Amnezia app, select "Add server" → "I have data to connect" → "QR code, key or settings file" Для зчитування QR-коду в застосунку Amnezia виберіть "Додати сервер" → "У мене є дані підключенн" → "QR-код, ключ чи файл налаштувань" @@ -4550,37 +5113,37 @@ This means that AmneziaWG keeps the fast performance of the original while addin Ім’я хосту не схоже на ip-адресу чи доменне ім’я - + New site added: %1 Додано новий сайт %1 - + Site removed: %1 Сайт видалено %1 - + Can't open file: %1 Неможливо відкрити файл: %1 - + Failed to parse JSON data from file: %1 Не вдалося розібрати JSON-данні із файлу: %1 - + The JSON data is not an array in file: %1 Данні JSON не являються масивом в файлі: %1 - + Import completed Імпорт завершено - + Export completed Експорт завершено @@ -4621,7 +5184,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin TextFieldWithHeaderType - + The field can't be empty Поле не може бути пустим @@ -4629,7 +5192,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin VpnConnection - + Mbps Mbps @@ -4680,28 +5243,24 @@ This means that AmneziaWG keeps the fast performance of the original while addin amnezia::ContainerProps - Low - Низький + Низький - High - Високий + Високий Extreme Екстремальний - I just want to increase the level of my privacy. - Я просто хочу підвищити свій рівень безпеки в інтернеті. + Я просто хочу підвищити свій рівень безпеки в інтернеті. - I want to bypass censorship. This option recommended in most cases. - Я хочу обійти блокування. Цей варіант рекомендується в більшості випадків. + Я хочу обійти блокування. Цей варіант рекомендується в більшості випадків. Most VPN protocols are blocked. Recommended if other options are not working. @@ -4723,6 +5282,16 @@ This means that AmneziaWG keeps the fast performance of the original while addin I just want to increase the level of privacy Я просто хочу підвищити свій рівень безпеки в інтернеті. + + + Automatic + + + + + AmneziaWG protocol will be installed. It provides high connection speed and ensures stable operation even in the most challenging network conditions. + + main2 diff --git a/client/translations/amneziavpn_ur_PK.ts b/client/translations/amneziavpn_ur_PK.ts index e45ae5da..3a8b8172 100644 --- a/client/translations/amneziavpn_ur_PK.ts +++ b/client/translations/amneziavpn_ur_PK.ts @@ -4,60 +4,113 @@ AdLabel - - Amnezia Premium - for access to any website + + Amnezia Premium - for access to all websites and online resources + + + + + ApiAccountInfoModel + + + + Active + + + + + Inactive + + + + + %1 out of %2 + + + + + Classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. Speeds up to 200 Mbps + + + + + Free unlimited access to a basic set of websites such as Facebook, Instagram, Twitter (X), Discord, Telegram and more. YouTube is not included in the free plan. + + + + + amnezia_free_support_bot + + + + + amnezia_premium_support_bot + + + + + ApiConfigsController + + + %1 installed successfully. + + + + + API config reloaded + + + + + Successfully changed the country of connection to %1 ApiServicesModel - - Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s - - - - - VPN to access blocked sites in regions with high levels of Internet censorship. - - - - + <p><a style="color: #EB5757;">Not available in your region. If you have VPN enabled, disable it, return to the previous screen, and try again.</a> - - Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship. + + Amnezia Premium is classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. Speeds up to %1 Mbps. - - Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship + + + AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan. - + + Amnezia Premium is classic VPN for for seamless work, downloading large files, and watching videos. Access all websites and online resources. + + + + %1 MBit/s - + %1 days - + VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, <a href="%1/free" style="color: #FBB26A;">more details on the website.</a> - + Free - + %1 $/month @@ -96,61 +149,58 @@ ConnectionController - - - - + + + + Connect جوڑنا - The selected protocol is not supported on the current platform - منتخب کردہ پروٹوکول موجودہ پلیٹ فارم پر تعاون یافتہ نہیں ہے + منتخب کردہ پروٹوکول موجودہ پلیٹ فارم پر تعاون یافتہ نہیں ہے - VPN Protocols is not installed. Please install VPN container at first - وی پی این پروٹوکول انسٹال نہیں ہے,براہ کرم پہلےوی پی این کنٹینر انسٹال کریں + وی پی این پروٹوکول انسٹال نہیں ہے,براہ کرم پہلےوی پی این کنٹینر انسٹال کریں - unable to create configuration - تشکیل تیار کرنے میں ناکام + تشکیل تیار کرنے میں ناکام - + Connecting... جوڑاجارھاھے.... - + Connected جوڑاجارھاھے - + Reconnecting... دوبارہ جوڑنےکی کوشش... - + Disconnecting... منقطع کرنا... - + Preparing... تیاری کیا جا رہا ہے... - + Settings updated successfully, reconnnection... ترتیب ک ھوگی،دوبارہ جوڑنےکی کوشش... - + Settings updated successfully دوبارہ ترتیب تاذہ کامیاب @@ -268,102 +318,92 @@ Can't be disabled for current server غلط کنفیگریشن فائل - + Scanned %1 of %2. سکین%1 کی%2. - - In the imported configuration, potentially dangerous lines were found: + + This configuration contains an OpenVPN setup. OpenVPN configurations can include malicious scripts, so only add it if you fully trust the provider of this config. + + + + + <br>In the imported configuration, potentially dangerous lines were found: InstallController - + %1 installed successfully. %1 کامیابی سےنصب. - + %1 is already installed on the server. %1 پہلے ہی سرور پر انسٹال ہے. - + Added containers that were already installed on the server وہ کنٹینرز شامل کیے گئے جو پہلے سے سرور پر نصب تھے - + Already installed containers were found on the server. All installed containers have been added to the application سرور پر پہلے سے نصب کنٹینرز پائے گئے۔ تمام نصب کنٹینرز کو ایپلی کیشن میں شامل کر دیا گیا ہے - + Settings updated successfully ترتیب کامیابی کے ساتھ اپ ڈیٹ ہو گئی - + Server '%1' was rebooted سرور %1 دوبارہ چالو کیا گیا تھا - + Server '%1' was removed سرور %1 ہٹا دیا گیا تھا - + All containers from server '%1' have been removed سرور '%1' سے تمام کنٹینرز ہٹا دیے گئے ہیں - + %1 has been removed from the server '%2' سرور '%2' سے %1 ہٹا دیا گیا ہے - + Api config removed - + %1 cached profile cleared %1 کیش کردہ پروفائل ختم کر دی گئی - + Please login as the user براہ کرم صارف کے طور پر لاگ ان کریں - + Server added successfully سرور کامیابی سے شامل کیا گیا - - - %1 installed successfully. - - - - - API config reloaded - - - - - Successfully changed the country of connection to %1 - - InstalledAppsDrawer @@ -476,12 +516,12 @@ Already installed containers were found on the server. All installed containers سپلٹ ٹنلنگ غیر فعال ہے - + VPN protocol وی پی این پروٹوکول - + Servers سرور @@ -1335,22 +1375,22 @@ Already installed containers were found on the server. All installed containers ایپلیکیشن - + Backup بیک اپ - + About AmneziaVPN AmneziaVPN کے بارے میں - + Dev console - + Close application براہ کرم ایپلیکیشن بند کریں @@ -1358,17 +1398,17 @@ Already installed containers were found on the server. All installed containers PageSettingsAbout - + Support Amnezia Amnezia کی حمایت کریں - + Amnezia is a free and open-source application. You can support the developers if you like it. ایمنیزیا ایک مفت اور آزاد سورس ایپلیکیشن ہے۔ آپ اگر اسے پسند کریں تو ڈویلپرز کی حمایت کرسکتے ہیں. - + Contacts رابطے @@ -1402,139 +1442,483 @@ Already installed containers were found on the server. All installed containers جائزہ اور بگ رپورٹس کے لئے - - Copied + + mailto:support@amnezia.org - + GitHub گِٹ ہَب - + Discover the source code - + https://github.com/amnezia-vpn/amnezia-client https://github.com/amnezia-vpn/amnezia-client - + Website ویب سائٹ - + Visit official website - + Software version: %1 سافٹ ویئر ورژن: %1 - + Check for updates اپ ڈیٹس چیک کریں - + Privacy Policy رازداری کی پالیسی - PageSettingsApiLanguageList + PageSettingsApiAvailableCountries - + + Location for connection + + + + Unable change server location while there is an active connection + + PageSettingsApiDevices + + + Active Devices + + + + + Manage currently connected devices + + + + + You can find the identifier on the Support tab or, for older versions of the app, by tapping '+' and then the three dots at the top of the page. + + + + + (current device) + + + + + Support tag: + + + + + Last updated: + + + + + Cannot unlink device during active connection + + + + + Are you sure you want to unlink this device? + + + + + This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect. + + + + + Continue + + + + + Cancel + + + + + PageSettingsApiInstructions + + + Windows + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#windows + + + + + macOS + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#macos + + + + + Android + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#android + + + + + AndroidTV + + + + + https://docs.amnezia.org/ru/documentation/instructions/android_tv_connect/ + + + + + iOS + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#ios + + + + + Linux + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#linux + + + + + Routers + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#routers + + + + + How to connect on another device + + + + + Setup guides on the Amnezia website + + + + + PageSettingsApiNativeConfigs + + + Save AmneziaVPN config + AmneziaVPN ترتیب کو محفوظ کریں + + + + Configuration Files + + + + + For router setup or the AmneziaWG app + + + + + The configuration needs to be reissued + + + + + configuration file + + + + + Generate a new configuration file + + + + + The previously created one will stop working + + + + + Revoke the current configuration file + + + + + Config file saved + + + + + The config has been revoked + + + + + Generate a new %1 configuration file? + + + + + Revoke the current %1 configuration file? + + + + + Your previous configuration file will no longer work, and it will not be possible to connect using it + + + + + Download + + + + + Continue + + + + + Cancel + + + PageSettingsApiServerInfo - - For the region + + Subscription Status - - Price + + Valid Until - - Work period + + Active Connections - - Valid until + + Configurations have been updated for some countries. Download and install the updated configuration files - - Speed + + Subscription Key - - Support tag + + Amnezia Premium subscription key - - Copied + + Save VPN key as a file - + + Copy VPN key + + + + + Configuration Files + + + + + Manage configuration files + + + + + Active Devices + + + + + Manage currently connected devices + + + + + Support + + + + + How to connect on another device + + + + Reload API config - + Reload API config? - - + + + Continue - - + + + Cancel - + Cannot reload API config during active connection - + + Unlink this device + + + + + Are you sure you want to unlink this device? + + + + + This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect. + + + + + Cannot unlink device during active connection + + + + Remove from application - + Remove from application? - + Cannot remove server during active connection چالو کنکشن کے دوران سرور کو ہٹایا نہیں جا سکتا + + PageSettingsApiSupport + + + Telegram + + + + + Email + + + + + support@amnezia.org + + + + + Email Billing & Orders + + + + + help@vpnpay.io + + + + + Website + ویب سائٹ + + + + amnezia.org + + + + + Support + + + + + Our technical support specialists are available to assist you at any time + + + + + Support tag + + + + + Copied + + + PageSettingsAppSplitTunneling @@ -1842,7 +2226,7 @@ Already installed containers were found on the server. All installed containers - Cannot change killSwitch settings during active connection + Cannot change KillSwitch settings during active connection @@ -1934,20 +2318,20 @@ Already installed containers were found on the server. All installed containers فائلوں کے فولڈر کو کھولیں - - + + Save محفوظ - - + + Logs files (*.log) لاگ فائلیں (*.log) - - + + Logs file saved لاگ فائل محفوظ ہوگئی @@ -1981,32 +2365,32 @@ Already installed containers were found on the server. All installed containers تم مسح السجلاتلاگوں کو صاف کر دیا گیا ہے - + Client logs - + AmneziaVPN logs - + Open logs folder - + Export logs - + Service logs - + AmneziaVPN-service logs @@ -2029,12 +2413,12 @@ Already installed containers were found on the server. All installed containers کوئی نئے انسٹال شدہ کنٹینرز نہیں ملے - + Do you want to reboot the server? کیا آپ سرور کو دوبارہ چالو کرنا چاہتے ہیں؟ - + Do you want to clear server from Amnezia software? هل تريد حذف الخادم من Amnezia?کیا آپ سرور کو Amnezia سافٹ ویئر سے صاف کرنا چاہتے ہیں؟ @@ -2044,93 +2428,93 @@ Already installed containers were found on the server. All installed containers - - - - + + + + Continue براہ کرم جاری رکھیں - - - - + + + + Cancel منسوخ - + Check the server for previously installed Amnezia services سرور پر پہلے سے انسٹال کی گئی Amnezia سروسز کو چیک کریں - + Add them to the application if they were not displayed اگر وہ دکھایا نہیں گیا تو انہیں ایپلیکیشن میں شامل کریں - + Reboot server سرور کو دوبارہ چالو کریں - + The reboot process may take approximately 30 seconds. Are you sure you wish to proceed? ریبوٹ کا عمل تقریباً 30 سیکنڈ لے سکتا ہے۔ کیا آپ براہ کرم جاری رکھنا چاہتے ہیں؟ - + Cannot reboot server during active connection چالو کنکشن کے دوران سرور کو دوبارہ چالو نہیں کیا جا سکتا - + Remove server from application ایپلیکیشن سے سرور کو ہٹا دیں - + Do you want to remove the server from application? کیا آپ ایپلیکیشن سے سرور کو ہٹانا چاہتے ہیں؟ - + Cannot remove server during active connection چالو کنکشن کے دوران سرور کو ہٹایا نہیں جا سکتا - + All users whom you shared a connection with will no longer be able to connect to it. آپ نے اپنی کنکشن کا اشتراک دینے والے تمام صارفین کو اس سے جڑنے کی اجازت نہیں ہوگی. - + Cannot clear server from Amnezia software during active connection چالو کنکشن کے دوران سرور کو ایمنیزیا سافٹ ویئر سے صاف کرنا ممکن نہیں - + Reset API config API کونفیگ کو دوبارہ ترتیب دیں - + Do you want to reset API config? کیا آپ API کونفیگ کو دوبارہ ترتیب دینا چاہتے ہیں؟ - + Cannot reset API config during active connection چالو کنکشن کے دوران API ترتیبات کو دوبارہ ترتیب نہیں دی جا سکتی - + All installed AmneziaVPN services will still remain on the server. سرور پر تمام انسٹال شدہ AmneziaVPN سروسز محفوظ رہیں گے. - + Clear server from Amnezia software Amnezia سافٹ ویئر کو سرور سے صاف کریں @@ -2138,32 +2522,25 @@ Already installed containers were found on the server. All installed containers PageSettingsServerInfo - - Subscription is valid until - - - - Server name - سرور کا نام + سرور کا نام - Save - محفوظ + محفوظ - + Protocols پروٹوکولات - + Services خدمات - + Management مینجمنٹ @@ -2283,17 +2660,17 @@ Already installed containers were found on the server. All installed containers موڈ - + Remove ہٹائیں - + Continue براہ کرم جاری رکھیں - + Cancel منسوخ @@ -2308,17 +2685,17 @@ Already installed containers were found on the server. All installed containers فعال کنکشن کے دوران سپلٹ ٹنلنگ کی ترتیبات تبدیل نہیں کی جا سکتیں - + website or IP ویب سائٹ یا آئی پی - + Import / Export Sites سائٹس درآمد / برآمد - + Import درآمد @@ -2334,29 +2711,29 @@ Already installed containers were found on the server. All installed containers - - + + Sites files (*.json) سائٹس فائلیں (*.json) - + Import a list of sites ایک فہرست کو درآمد کریں - + Replace site list سائٹ کی فہرست کو بدلیں - - + + Open sites file سائٹس فائل کو کھولیں - + Add imported sites to existing ones آمدہ سائٹس کو موجودہ میں شامل کریں @@ -2476,85 +2853,70 @@ Already installed containers were found on the server. All installed containers - + Site Amnezia - + VPN by Amnezia - + Connect to classic paid and free VPN services from Amnezia - + Self-hosted VPN - + Configure Amnezia VPN on your own server - + Restore from backup بیک اپ سے بحال کریں - + - + Open backup file بیک اپ فائل کو کھولیں - + Backup files (*.backup) بیک اپ فائلیں (*.backup) - + File with connection settings کنکشن کی ترتیبات والی فائل - - - - - - + Open config file کنفیگ فائل کو کھولیں - + QR code QR کوڈ - - - - - - + I have nothing میرے پاس کچھ نہیں ہے - - - - - Key as text متن کے طور پر کلید @@ -2568,62 +2930,62 @@ Already installed containers were found on the server. All installed containers اپنے سرور کو ترتیب دیں - + Server IP address [:port] سرور آئی پی پتہ [:پورٹ] - + Continue براہ کرم جاری رکھیں - + All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties آپ جو ڈیٹا داخل کریں گے وہ بالکل خفیہ رہے گا اور نہ تو امنیزیا یا کسی تیسری شخصیت کے ساتھ اشتراک کیا جائے گا - + 255.255.255.255:22 - + SSH Username ایس ایس ایچ صارف نام - + Password or SSH private key پاس ورڈ یا SSH نجی کلید - + How to run your VPN server - + Where to get connection data, step-by-step instructions for buying a VPS - + Ip address cannot be empty آئی پی پتہ خالی نہیں ہو سکتا - + Enter the address in the format 255.255.255.255:88 ایڈریس درج کریں فارمیٹ 255.255.255.255:88 - + Login cannot be empty لاگ ان نام خالی نہیں ہو سکتا - + Password/private key cannot be empty پاس ورڈ یا نجی کلید خالی نہیں ہو سکتی @@ -2631,22 +2993,31 @@ Already installed containers were found on the server. All installed containers PageSetupWizardEasy - What is the level of internet control in your region? - آپ کے علاقے میں انٹرنیٹ کنٹرول کا سطح کیا ہے؟ + آپ کے علاقے میں انٹرنیٹ کنٹرول کا سطح کیا ہے؟ + + + + Choose Installation Type + + Manual + + + + Choose a VPN protocol ایک VPN پروٹوکول کا انتخاب کریں - + Skip setup ترتیب چھوڑیں - + Continue جاری رکھیں @@ -2841,37 +3212,37 @@ Already installed containers were found on the server. All installed containers PageShare - + Save OpenVPN config اوپن وی پی این کی ترتیبات کو محفوظ کریں - + Save WireGuard config وائر گارڈ کی ترتیبات کو محفوظ کریں - + Save AmneziaWG config ایمنیزیا ڈبلیو جی کی ترتیبات کو محفوظ کریں - + Save Shadowsocks config شیڈو ساکس کی ترتیبات کو محفوظ کریں - + Save Cloak config چادر کی ترتیبات کو محفوظ کریں - + Save XRay config "XRay کنفیگ کو محفوظ کریں - + For the AmneziaVPN app AmneziaVPN ایپ کے لئے @@ -2880,83 +3251,83 @@ Already installed containers were found on the server. All installed containers OpenVPN کا اصل فارمیٹ - + WireGuard native format وائر گارڈ کا اصل فارمیٹ - + AmneziaWG native format ایمنیزیا ڈبلیو جی کا اصل فارمیٹ - + Shadowsocks native format شیڈو ساکس کا اصل فارمیٹ - + Cloak native format Cloak کا اصل فارمیٹ - + XRay native format ایکس رے کا نیٹویٹ فارمیٹ - + Share VPN Access VPN دسترسی شیئر - + Share full access to the server and VPN سرور اور وی پی این کے لئے مکمل دسترسی کو شیئر کریں - + Use for your own devices, or share with those you trust to manage the server. اپنی خود کی ڈیوائسز کے لئے استعمال کریں، یا ان لوگوں کے ساتھ شیئر کریں جن پر آپ کا بھروسہ ہو کہ وہ سرور کو منظم کر سکیں. - - + + Users صارفین - + Share VPN access without the ability to manage the server سرور کو منظم کرنے کی صلاحیت کے بغیر وی پی این کی دسترسی شیئر - + Search تلاش - + Creation date: %1 - + Latest handshake: %1 - + Data received: %1 - + Data sent: %1 - + Allowed IPs: %1 @@ -2965,53 +3336,53 @@ Already installed containers were found on the server. All installed containers تخلیق کی تاریخ: - + Rename نام تبدیل - + Client name کلائنٹ کا نام - + Save محفوظ - + Revoke واپس لین - + Revoke the config for a user - %1? کیا آپ مستعمل کے لئے کنفیگ کو واپس لینا چاہتے ہیں - %1؟ - + The user will no longer be able to connect to your server. صارف آپ کے سرور سے متصل ہونے کا اختیار نہیں رہے گا. - + Continue جاری رکھیں - + Cancel منسوخ - + Connection کنکشن + - Server سرور @@ -3021,8 +3392,8 @@ Already installed containers were found on the server. All installed containers کنکشن کی ترتیبات کی فائل + - Protocol پروٹوکول @@ -3037,24 +3408,24 @@ Already installed containers were found on the server. All installed containers کنفیگ منسوخ - + OpenVPN native format - + User name صارف کا نام + - Connection format کنکشن فارمیٹ - - + + Share شیئر @@ -3118,17 +3489,17 @@ Already installed containers were found on the server. All installed containers PageStart - + Logging was disabled after 14 days, log files were deleted لاگنگ کو 14 دنوں کے بعد غیر فعال کر دیا گیا، لاگ فائلوں کو حذف کر دیا گیا - + Settings restored from backup file ترتیبات بیک اپ فائل سے بحال کردی گئی ہیں - + Logging is enabled. Note that logs will be automaticallydisabled after 14 days, and all log files will be deleted. @@ -3386,7 +3757,7 @@ Already installed containers were found on the server. All installed containers - + SOCKS5 proxy server @@ -3407,87 +3778,88 @@ Already installed containers were found on the server. All installed containers فنکشن نافذ نہیں ہوا - + Server check failed سرور کی جانچ ناکام ہوگئی - + Server port already used. Check for another software سرور پورٹ پہلے ہی استعمال ہو چکا ہے۔ دوسرا سافٹ ویئر چیک کریں - + Server error: Docker container missing سرور کی خرابی: ڈوکر کنٹینر غائب ہے - + Server error: Docker failed سرور کی خرابی: ڈوکر ناکام ہو گیا - + Installation canceled by user صارف کے ذریعے انسٹالیشن منسوخ کر دی گئی - + The user is not a member of the sudo group صارف sudo گروپ کا رکن نہیں ہے - + SSH request was denied SSH درخواست مسترد کر دی گئی - + SSH request was interrupted SSH درخواست میں خلل پڑ - + SSH internal error SSH اندرونی خرابی - + Invalid private key or invalid passphrase entered غلط نجی کلید یا غلط پاسفریز درج کیا گیا - + The selected private key format is not supported, use openssh ED25519 key types or PEM key types منتخب کردہ پرائیویٹ کلیدی فارمیٹ تعاون یافتہ نہیں ہے، openssh ED25519 کلیدی اقسام یا PEM کلیدی اقسام استعمال کریں - + Timeout connecting to server سرور سے منسلک ہونے کا ٹائم آؤٹ - + VPN connection error VPN کنکشن کی خرابی - + + Error when retrieving configuration from API آپی سے کنفیگریشن بازیافت کرتے وقت خرابی - + This config has already been added to the application یہ تشکیل پہلے ہی ایپلی کیشن میں شامل کی جا چکی ہے - + ErrorCode: %1. ایرر کوڈ: %1. - + OpenVPN config missing OpenVPN تشکیل غائب ہے @@ -3497,132 +3869,168 @@ Already installed containers were found on the server. All installed containers - + + The selected protocol is not supported on the current platform + منتخب کردہ پروٹوکول موجودہ پلیٹ فارم پر تعاون یافتہ نہیں ہے + + + Server error: Package manager error سرور خطا: پیکیج منیجر خطا - + + The sudo package is not pre-installed on the server + + + + + The server user's home directory is not accessible + + + + + Action not allowed in sudoers + + + + + The user's password is required + + + + SCP error: Generic failure ایس سی پی کی خرابی: عام ناکامی - + OpenVPN management server error OpenVPN مینجمنٹ سرور کی خرابی - + OpenVPN executable missing OpenVPN قابل عمل غائب ہے - + Shadowsocks (ss-local) executable missing شیڈو ساکس (ss-local) قابل عمل غائب - + Cloak (ck-client) executable missing Cloak (ck-client) قابل عمل غائب - + Amnezia helper service error ایمنیزیا مددگار سروس کی خرابی - + OpenSSL failed OpenSSL ناکام ہوگیا - + Can't connect: another VPN connection is active منسلک نہیں ہو سکتا: دوسرا VPN کنکشن فعال ہے - + Can't setup OpenVPN TAP network adapter OpenVPN TAP نیٹ ورک اڈاپٹر سیٹ اپ نہیں کر سکتے - + VPN pool error: no available addresses VPN پول کی خرابی: کوئی پتہ دستیاب نہیں ہے - + The config does not contain any containers and credentials for connecting to the server ترتیب میں سرور سے منسلک ہونے کے لیے کوئی کنٹینرز اور اسناد نہیں ہیں - + Unable to open config file - + + VPN Protocols is not installed. + Please install VPN container at first + وی پی این پروٹوکول انسٹال نہیں ہے,براہ کرم پہلےوی پی این کنٹینر انسٹال کریں + + + In the response from the server, an empty config was received - + SSL error occurred - + Server response timeout on api request - + Missing AGW public key - + Failed to decrypt response payload - + Missing list of available services - + + The limit of allowed configurations per subscription has been exceeded + + + + QFile error: The file could not be opened QFile کی خرابی: فائل کو نہیں کھولا جا سکا - + QFile error: An error occurred when reading from the file کیو فائل کی خرابی: فائل سے پڑھتے وقت ایک خرابی پیش آگئی - + QFile error: The file could not be accessed QFile کی خرابی: فائل تک رسائی نہیں ہو سکی - + QFile error: An unspecified error occurred کیو فائل میں خرابی: ایک غیر متعینہ خرابی پیش آگئی - + QFile error: A fatal error occurred کیو فائل میں خرابی: ایک مہلک خرابی پیش آگئی - + QFile error: The operation was aborted کیو فائل کی خرابی: آپریشن روک دیا گیا تھا - + Internal error داخلی خامی @@ -3633,7 +4041,7 @@ Already installed containers were found on the server. All installed containers - + Website in Tor network ٹور نیٹ ورک میں ویب سائٹ @@ -3654,31 +4062,118 @@ Already installed containers were found on the server. All installed containers - Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions. - شیڈو ساکس - VPN ٹریفک کو ماسک کرتا ہے، جو اسے عام ویب ٹریفک جیسا بناتا ہے، لیکن اسے کچھ انتہائی سنسر والے علاقوں میں تجزیہ کے نظام کے ذریعے پہچانا جا سکتا ہے. + Shadowsocks masks VPN traffic, making it resemble normal web traffic, but it may still be detected by certain analysis systems. + + + + + OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against active-probing detection. It is very resistant to detection, but offers low speed. + + + + + WireGuard - popular VPN protocol with high performance, high speed and low power consumption. + + + + + AmneziaWG is a special protocol from Amnezia based on WireGuard. It provides high connection speed and ensures stable operation even in the most challenging network conditions. + + + + + XRay with REALITY masks VPN traffic as web traffic and protects against active probing. It is highly resistant to detection and offers high speed. + + + + + OpenVPN stands as one of the most popular and time-tested VPN protocols available. +It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, catering to a wide range of devices and operating systems. Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, which continually reinforces its security. With a strong balance of performance, security, and compatibility, OpenVPN remains a top choice for privacy-conscious individuals and businesses alike. + +* Available in the AmneziaVPN across all platforms +* Normal power consumption on mobile devices +* Flexible customisation to suit user needs to work with different operating systems and devices +* Recognised by DPI systems and therefore susceptible to blocking +* Can operate over both TCP and UDP network protocols. + + + + + This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for protecting against detection. + +OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client and the server. + +Cloak protects OpenVPN from detection. + +Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, and also protects the VPN from detection by Active Probing. This makes it very resistant to being detected + +Immediately after receiving the first data packet, Cloak authenticates the incoming connection. If authentication fails, the plugin masks the server as a fake website and your VPN becomes invisible to analysis systems. + +* Available in the AmneziaVPN across all platforms +* High power consumption on mobile devices +* Flexible settings +* Not recognised by detection systems +* Works over TCP network protocol, 443 port. + + + + + + A relatively new popular VPN protocol with a simplified architecture. +WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. +WireGuard is very susceptible to detection and blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. + +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Easily recognised by DPI analysis systems, susceptible to blocking +* Works over UDP network protocol. + + + + + A modern iteration of the popular VPN protocol, AmneziaWG builds upon the foundation set by WireGuard, retaining its simplified architecture and high-performance capabilities across devices. +While WireGuard is known for its efficiency, it had issues with being easily detected due to its distinct packet signatures. AmneziaWG solves this problem by using better obfuscation methods, making its traffic blend in with regular internet traffic. +This means that AmneziaWG keeps the fast performance of the original while adding an extra layer of stealth, making it a great choice for those wanting a fast and discreet VPN connection. + +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Not recognised by traffic analysis systems +* Works over UDP network protocol. + + + + + The REALITY protocol, a pioneering development by the creators of XRay, is designed to provide the highest level of protection against detection through its innovative approach to security and privacy. +It uniquely identifies attackers during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting attackers to genuine websites, thus presenting an authentic TLS certificate and data. +This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. +Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security. This makes REALITY a robust solution for maintaining internet freedom. + + + + Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions. + شیڈو ساکس - VPN ٹریفک کو ماسک کرتا ہے، جو اسے عام ویب ٹریفک جیسا بناتا ہے، لیکن اسے کچھ انتہائی سنسر والے علاقوں میں تجزیہ کے نظام کے ذریعے پہچانا جا سکتا ہے. - OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against active-probing detection. Ideal for bypassing blocking in regions with the highest levels of censorship. - اوپن وی پی این اوور کلوک - اوپن وی پی این کے ساتھ وی پی این کو ویب ٹریفک کے طور پر چھپانا اور ایکٹیو پروبنگ ڈٹیکشن کے خلاف تحفظ۔ سنسرشپ کی اعلی ترین سطح والے خطوں میں بلاکنگ کو نظرانداز کرنے کے لیے مثالی. + اوپن وی پی این اوور کلوک - اوپن وی پی این کے ساتھ وی پی این کو ویب ٹریفک کے طور پر چھپانا اور ایکٹیو پروبنگ ڈٹیکشن کے خلاف تحفظ۔ سنسرشپ کی اعلی ترین سطح والے خطوں میں بلاکنگ کو نظرانداز کرنے کے لیے مثالی. + + + XRay with REALITY - Suitable for countries with the highest level of internet censorship. Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods. + حقیقت کے ساتھ ایکسرےواقعیت کے ساتھ ایکس رے - سب سے زیادہ انٹرنیٹ سینسرشپ والے ممالک کے لئے مناسب ہے۔ ٹریفک ویب ٹریفک کی سطح TLS پر ماسکنگ اور فعال پرابنگ کے طریقوں سے شناخت سے بچائے جانے کی حفاظت۔ - XRay with REALITY - Suitable for countries with the highest level of internet censorship. Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods. - حقیقت کے ساتھ ایکسرےواقعیت کے ساتھ ایکس رے - سب سے زیادہ انٹرنیٹ سینسرشپ والے ممالک کے لئے مناسب ہے۔ ٹریفک ویب ٹریفک کی سطح TLS پر ماسکنگ اور فعال پرابنگ کے طریقوں سے شناخت سے بچائے جانے کی حفاظت۔ - - - IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss. It has native support on the latest versions of Android and iOS. - + Create a file vault on your server to securely store and transfer files. فائلوں کو محفوظ طریقے سے اسٹور اور ٹرانسفر کرنے کے لیے اپنے سرور پر ایک فائل والٹ بنائیں. - This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for protecting against blocking. OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client and the server. @@ -3697,11 +4192,10 @@ If there is a extreme level of Internet censorship in your region, we advise you * Not recognised by DPI analysis systems * Works over TCP network protocol, 443 port. - یہ اوپن وی پی این پروٹوکول اور کلوک پلگ ان کا مجموعہ ہے جو خاص طور پر بلاکنگ سے تحفظ کے لیے ڈیزائن کیا گیا ہے۔ اوپن وی پی این کلائنٹ اور سرور کے درمیان تمام انٹرنیٹ ٹریفک کو انکرپٹ کرکے ایک محفوظ وی پی این کنکشن فراہم کرتا ہے۔ Cloak OpenVPN کو پتہ لگانے اور بلاک کرنے سے بچاتا ہے۔ کلوک پیکٹ میٹا ڈیٹا میں ترمیم کر سکتا ہے تاکہ یہ VPN ٹریفک کو عام ویب ٹریفک کے طور پر مکمل طور پر ماسک کر دے، اور VPN کو ایکٹیو پروبنگ کے ذریعے پتہ لگانے سے بھی محفوظ رکھتا ہے۔ یہ پہلا ڈیٹا پیکٹ حاصل کرنے کے فوراً بعد پتہ لگانے کے لیے بہت مزاحم بناتا ہے، کلوک آنے والے کنکشن کی تصدیق کرتا ہے۔ اگر تصدیق ناکام ہو جاتی ہے، تو پلگ ان سرور کو ایک جعلی ویب سائٹ کے طور پر ماسک کر دیتا ہے اور آپ کا VPN تجزیہ کے نظام کے لیے پوشیدہ ہو جاتا ہے۔ اگر آپ کے علاقے میں انتہائی درجے کی انٹرنیٹ سنسرشپ ہے، تو ہم آپ کو مشورہ دیتے ہیں کہ پہلے کنکشن سے صرف اوپن وی پی این اوور کلوک استعمال کریں * تمام پلیٹ فارمز پر ایمنیزیا وی پی این میں دستیاب ہے * موبائل ڈیوائسز پر زیادہ بجلی کی کھپت * لچکدار ترتیبات * ڈی پی آئی تجزیہ کے ذریعے تسلیم شدہ نہیں سسٹمز * TCP نیٹ ورک پروٹوکول، 443 پورٹ پر کام کرتا ہے. + یہ اوپن وی پی این پروٹوکول اور کلوک پلگ ان کا مجموعہ ہے جو خاص طور پر بلاکنگ سے تحفظ کے لیے ڈیزائن کیا گیا ہے۔ اوپن وی پی این کلائنٹ اور سرور کے درمیان تمام انٹرنیٹ ٹریفک کو انکرپٹ کرکے ایک محفوظ وی پی این کنکشن فراہم کرتا ہے۔ Cloak OpenVPN کو پتہ لگانے اور بلاک کرنے سے بچاتا ہے۔ کلوک پیکٹ میٹا ڈیٹا میں ترمیم کر سکتا ہے تاکہ یہ VPN ٹریفک کو عام ویب ٹریفک کے طور پر مکمل طور پر ماسک کر دے، اور VPN کو ایکٹیو پروبنگ کے ذریعے پتہ لگانے سے بھی محفوظ رکھتا ہے۔ یہ پہلا ڈیٹا پیکٹ حاصل کرنے کے فوراً بعد پتہ لگانے کے لیے بہت مزاحم بناتا ہے، کلوک آنے والے کنکشن کی تصدیق کرتا ہے۔ اگر تصدیق ناکام ہو جاتی ہے، تو پلگ ان سرور کو ایک جعلی ویب سائٹ کے طور پر ماسک کر دیتا ہے اور آپ کا VPN تجزیہ کے نظام کے لیے پوشیدہ ہو جاتا ہے۔ اگر آپ کے علاقے میں انتہائی درجے کی انٹرنیٹ سنسرشپ ہے، تو ہم آپ کو مشورہ دیتے ہیں کہ پہلے کنکشن سے صرف اوپن وی پی این اوور کلوک استعمال کریں * تمام پلیٹ فارمز پر ایمنیزیا وی پی این میں دستیاب ہے * موبائل ڈیوائسز پر زیادہ بجلی کی کھپت * لچکدار ترتیبات * ڈی پی آئی تجزیہ کے ذریعے تسلیم شدہ نہیں سسٹمز * TCP نیٹ ورک پروٹوکول، 443 پورٹ پر کام کرتا ہے. - A relatively new popular VPN protocol with a simplified architecture. WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. WireGuard is very susceptible to blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. @@ -3711,22 +4205,21 @@ WireGuard is very susceptible to blocking due to its distinct packet signatures. * Minimum number of settings * Easily recognised by DPI analysis systems, susceptible to blocking * Works over UDP network protocol. - ایک نسبتاً نیا مقبول وی پی این پروٹوکول جس میں سادہ معماری ہے۔ وائر گارڈ تمام آلات پر مضبوط وی پی این کنکشن اور اعلی کارکردگی فراہم کرتا ہے۔ اس میں ہارڈ کوڈ کردہ انکرپشن سیٹنگز استعمال کی جاتی ہیں۔ وائر گارڈ کو اوپن وی پی این سے موازنہ کرنے پر لیٹنسی میں کمی اور بہتر ڈیٹا ٹرانسفر تھروپٹ حاصل ہوتی ہے۔ وائر گارڈ کا مخصوص پیکٹ سائنیچرز کی وجہ سے بلاک کرنا زیادہ آسان ہوتا ہے۔ کچھ دوسرے وی پی این پروٹوکول کے مخالف، جو اوبفسکیشن ٹیکنیکس کا استعمال کرتے ہیں، وائر گارڈ کے پیکٹس کے مسلسل سائنیچر پیٹرنز کو زیادہ آسانی سے پہچانا جا سکتا ہے اور اس طرح معقد ڈیپ پیکٹ انسپیکشن (DPI) سسٹمز اور دیگر نیٹ ورک مانیٹرنگ ٹولز کے ذریعے بلاک کیا جا سکتا ہے۔ * تمام پلیٹ فارمز پر دستیاب ہے * کم بجلی کی استعمال * کم سیٹنگز کی تعداد * ڈی پی آئی تجزیہ سسٹمز کے ذریعے آسانی سے پہچانا جاتا ہے، بلاک کرنے کے لئے زیادہ متاثر ہے * یو ڈی پی نیٹ ورک پروٹوکول پر کام کرتا ہے. + ایک نسبتاً نیا مقبول وی پی این پروٹوکول جس میں سادہ معماری ہے۔ وائر گارڈ تمام آلات پر مضبوط وی پی این کنکشن اور اعلی کارکردگی فراہم کرتا ہے۔ اس میں ہارڈ کوڈ کردہ انکرپشن سیٹنگز استعمال کی جاتی ہیں۔ وائر گارڈ کو اوپن وی پی این سے موازنہ کرنے پر لیٹنسی میں کمی اور بہتر ڈیٹا ٹرانسفر تھروپٹ حاصل ہوتی ہے۔ وائر گارڈ کا مخصوص پیکٹ سائنیچرز کی وجہ سے بلاک کرنا زیادہ آسان ہوتا ہے۔ کچھ دوسرے وی پی این پروٹوکول کے مخالف، جو اوبفسکیشن ٹیکنیکس کا استعمال کرتے ہیں، وائر گارڈ کے پیکٹس کے مسلسل سائنیچر پیٹرنز کو زیادہ آسانی سے پہچانا جا سکتا ہے اور اس طرح معقد ڈیپ پیکٹ انسپیکشن (DPI) سسٹمز اور دیگر نیٹ ورک مانیٹرنگ ٹولز کے ذریعے بلاک کیا جا سکتا ہے۔ * تمام پلیٹ فارمز پر دستیاب ہے * کم بجلی کی استعمال * کم سیٹنگز کی تعداد * ڈی پی آئی تجزیہ سسٹمز کے ذریعے آسانی سے پہچانا جاتا ہے، بلاک کرنے کے لئے زیادہ متاثر ہے * یو ڈی پی نیٹ ورک پروٹوکول پر کام کرتا ہے. - The REALITY protocol, a pioneering development by the creators of XRay, is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion. It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, thus presenting an authentic TLS certificate and data. This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship. - REALITY پروٹوکول، جو ایکس رے کے تخلیق کاروں کی ایک نوعیتی پیشرفت ہے، انٹرنیٹ سینسرشپ کی بلند ترین سطحوں کو مقابلہ کرنے کے لئے مخصوص طریقہ کار بنایا گیا ہے۔ + REALITY پروٹوکول، جو ایکس رے کے تخلیق کاروں کی ایک نوعیتی پیشرفت ہے، انٹرنیٹ سینسرشپ کی بلند ترین سطحوں کو مقابلہ کرنے کے لئے مخصوص طریقہ کار بنایا گیا ہے۔ یہ فرد معین کو TLS ہینڈشیک فیز کے دوران سینسرز کو شناخت کرتا ہے، اصل کلائنٹس کے طور پر پراکسی کے طور پر بغیر رکاوٹ چلنے کے دوران سینسرز کو اصل ویب سائٹوں جیسے google.com پر منتقل کرتا ہے، اس طرح ایک مستند TLS سرٹیفکیٹ اور ڈیٹا کو پیش کرتا ہے۔ یہ بلند پذیرای کی صلاحیت کو مخصوص ترتیبات کی ضرورت کے بغیر ویب ٹریفک کو اصلی سائٹس سے آنے کی طرح بنانے کی بنیاد میں مختلف ہے۔ پرانے پروٹوکولوں جیسے VMess، VLESS، اور XTLS-Vision ٹرانسپورٹ کے برعکس، REALITY کا TLS ہینڈشیک کے دوران نئی "دوست یا دشمن" شناخت TLS پر سکیورٹی کو بڑھاتا ہے اور توانائی کے ساتھ DPI سسٹمز کی پیشہ ورانہ چھان بین تکنیکوں کے ذریعے شناخت سے بچتا ہے۔ یہ REALITY کو سخت سینسرشپ والے ماحولوں میں انٹرنیٹ کی آزادی کو برقرار رکھنے کے لئے ایک مضبوط حل بناتا ہے۔ - + After installation, Amnezia will create a file storage on your server. You will be able to access it using @@ -3738,31 +4231,28 @@ For more detailed information, you can انسٹالیشن کے بعد، ایمنیزیا آپ کے سرور پر ایک فائل اسٹوریج بنائے گا۔ آپ اس تک رسائی حاصل کر سکیں گے فائل زلا یا دیگر ایس ایف ٹی پی کلائنٹس کے ذریعے، اور اسکے علاوہ آپ اس ڈسک کو اپنے آلہ پر ماؤنٹ کر کے اس تک سیدھے راستے سے رسائی حاصل کر سکیں گے۔ مزید تفصیلات کے لئے، آپ سپورٹ سیکشن میں "ایس ایف ٹی پی فائل اسٹوریج بنانا" میں جا کر مزید معلومات حاصل کر سکتے ہیں." - WireGuard - New popular VPN protocol with high performance, high speed and low power consumption. Recommended for regions with low levels of censorship. - وائر گارڈ - اعلی کارکردگی، تیز رفتار اور کم بجلی کی کھپت کے ساتھ نیا مقبول VPN پروٹوکول۔ سنسرشپ کی کم سطح والے علاقوں کے لیے تجویز کردہ. + وائر گارڈ - اعلی کارکردگی، تیز رفتار اور کم بجلی کی کھپت کے ساتھ نیا مقبول VPN پروٹوکول۔ سنسرشپ کی کم سطح والے علاقوں کے لیے تجویز کردہ. - AmneziaWG - Special protocol from Amnezia, based on WireGuard. It's fast like WireGuard, but very resistant to blockages. Recommended for regions with high levels of censorship. - AmneziaWG - Amnezia سے خصوصی پروٹوکول، WireGuard پر مبنی۔ یہ وائر گارڈ کی طرح تیز ہے، لیکن رکاوٹوں کے خلاف بہت مزاحم ہے۔ اعلی درجے کی سنسر شپ والے خطوں کے لیے تجویز کردہ۔ + AmneziaWG - Amnezia سے خصوصی پروٹوکول، WireGuard پر مبنی۔ یہ وائر گارڈ کی طرح تیز ہے، لیکن رکاوٹوں کے خلاف بہت مزاحم ہے۔ اعلی درجے کی سنسر شپ والے خطوں کے لیے تجویز کردہ۔ IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss. IKEv2/IPsec - جدید مستحکم پروٹوکول، دوسروں کے مقابلے میں تھوڑا تیز، سگنل ضائع ہونے کے بعد کنکشن بحال کرتا ہے۔ - + Deploy a WordPress site on the Tor network in two clicks. ٹور نیٹ ورک پر ایک ورڈپریس سائٹ کو دو کلکس میں تعینات کریں. - + Replace the current DNS server with your own. This will increase your privacy level. موجودہ DNS سرور کو اپنے سے تبدیل کریں۔ اس سے آپ کی رازداری کی سطح میں اضافہ ہوگا. - OpenVPN stands as one of the most popular and time-tested VPN protocols available. It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, catering to a wide range of devices and operating systems. Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, which continually reinforces its security. With a strong balance of performance, security, and compatibility, OpenVPN remains a top choice for privacy-conscious individuals and businesses alike. @@ -3771,10 +4261,10 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for * Flexible customisation to suit user needs to work with different operating systems and devices * Recognised by DPI analysis systems and therefore susceptible to blocking * Can operate over both TCP and UDP network protocols. - OpenVPN دستیاب سب سے زیادہ مقبول اور وقتی آزمائشی VPN پروٹوکولز میں سے ایک ہے۔ یہ انکرپشن اور کلیدی تبادلے کے لیے SSL/TLS کی طاقت کا فائدہ اٹھاتے ہوئے اپنا منفرد سیکیورٹی پروٹوکول استعمال کرتا ہے۔ مزید برآں، توثیق کے بہت سے طریقوں کے لیے OpenVPN کی حمایت اسے ورسٹائل اور قابل موافق بناتی ہے، جو آلات اور آپریٹنگ سسٹم کی ایک وسیع رینج کو پورا کرتی ہے۔ اوپن سورس کی نوعیت کی وجہ سے، اوپن وی پی این کو عالمی برادری کی طرف سے وسیع جانچ سے فائدہ ہوتا ہے، جو اس کی سلامتی کو مسلسل تقویت دیتا ہے۔ کارکردگی، سیکورٹی اور مطابقت کے مضبوط توازن کے ساتھ، OpenVPN رازداری کے بارے میں شعور رکھنے والے افراد اور کاروباروں کے لیے یکساں انتخاب ہے۔ * تمام پلیٹ فارمز پر AmneziaVPN میں دستیاب ہے * موبائل آلات پر بجلی کی عام کھپت * صارف کو مختلف آپریٹنگ سسٹمز اور ڈیوائسز کے ساتھ کام کرنے کی ضرورت کے مطابق لچکدار تخصیص * DPI تجزیہ سسٹمز کے ذریعہ پہچانا جاتا ہے اور اس وجہ سے بلاک کرنے کا خطرہ ہوتا ہے * TCP اور UDP دونوں نیٹ ورک پر کام کر سکتا ہے۔ پروٹوکول + OpenVPN دستیاب سب سے زیادہ مقبول اور وقتی آزمائشی VPN پروٹوکولز میں سے ایک ہے۔ یہ انکرپشن اور کلیدی تبادلے کے لیے SSL/TLS کی طاقت کا فائدہ اٹھاتے ہوئے اپنا منفرد سیکیورٹی پروٹوکول استعمال کرتا ہے۔ مزید برآں، توثیق کے بہت سے طریقوں کے لیے OpenVPN کی حمایت اسے ورسٹائل اور قابل موافق بناتی ہے، جو آلات اور آپریٹنگ سسٹم کی ایک وسیع رینج کو پورا کرتی ہے۔ اوپن سورس کی نوعیت کی وجہ سے، اوپن وی پی این کو عالمی برادری کی طرف سے وسیع جانچ سے فائدہ ہوتا ہے، جو اس کی سلامتی کو مسلسل تقویت دیتا ہے۔ کارکردگی، سیکورٹی اور مطابقت کے مضبوط توازن کے ساتھ، OpenVPN رازداری کے بارے میں شعور رکھنے والے افراد اور کاروباروں کے لیے یکساں انتخاب ہے۔ * تمام پلیٹ فارمز پر AmneziaVPN میں دستیاب ہے * موبائل آلات پر بجلی کی عام کھپت * صارف کو مختلف آپریٹنگ سسٹمز اور ڈیوائسز کے ساتھ کام کرنے کی ضرورت کے مطابق لچکدار تخصیص * DPI تجزیہ سسٹمز کے ذریعہ پہچانا جاتا ہے اور اس وجہ سے بلاک کرنے کا خطرہ ہوتا ہے * TCP اور UDP دونوں نیٹ ورک پر کام کر سکتا ہے۔ پروٹوکول - + Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection.However, certain traffic analysis systems might still detect a Shadowsocks connection. Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol. * Available in the AmneziaVPN only on desktop platforms @@ -3784,7 +4274,6 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for شیڈو ساکس، SOCKS5 پروٹوکول سے متاثر، AEAD سائفر کا استعمال کرتے ہوئے کنکشن کی حفاظت کرتا ہے۔ اگرچہ شیڈو ساکس کو سمجھدار اور شناخت کرنے کے لیے چیلنج کرنے کے لیے ڈیزائن کیا گیا ہے، لیکن یہ معیاری HTTPS کنکشن سے مماثل نہیں ہے۔ تاہم، کچھ ٹریفک تجزیہ نظام اب بھی شیڈو ساکس کنکشن کا پتہ لگا سکتے ہیں۔ Amnezia میں محدود تعاون کی وجہ سے، AmneziaWG پروٹوکول استعمال کرنے کی سفارش کی جاتی ہے۔ * صرف ڈیسک ٹاپ پلیٹ فارمز پر AmneziaVPN میں دستیاب ہے * قابل ترتیب انکرپشن پروٹوکول * کچھ DPI سسٹمز کے ذریعے قابل شناخت * TCP نیٹ ورک پروٹوکول پر کام کرتا ہے. - A modern iteration of the popular VPN protocol, AmneziaWG builds upon the foundation set by WireGuard, retaining its simplified architecture and high-performance capabilities across devices. While WireGuard is known for its efficiency, it had issues with being easily detected due to its distinct packet signatures. AmneziaWG solves this problem by using better obfuscation methods, making its traffic blend in with regular internet traffic. This means that AmneziaWG keeps the fast performance of the original while adding an extra layer of stealth, making it a great choice for those wanting a fast and discreet VPN connection. @@ -3794,10 +4283,10 @@ This means that AmneziaWG keeps the fast performance of the original while addin * Minimum number of settings * Not recognised by DPI analysis systems, resistant to blocking * Works over UDP network protocol. - ایک معاصر اشارہ جاتا ہے مقبول وی پی این پروٹوکول کا امنیزیہ ڈبلیو جی۔ امنیزیہ ڈبلیو جی وائر گارڈ کے بنیادی ڈھانچے پر مبنی ہے، جس نے اس کی آسانی سے معماری اور ایکسیلنٹ کارکردگی کی خصوصیات کو برقرار رکھا۔ جبکہ وائر گارڈ کو اس کی کارآمدی کے لئے جانا جاتا ہے، اس میں اپنے ممتاز پیکٹ سائنیچرز کی وجہ سے آسانی سے پہچان میں مسائل پیش آتے تھے۔ امنیزیہ ڈبلیو جی اس مسئلے کا حل پیش کرتا ہے بہتر اوبفسکیشن میتھڈس کے ذریعے، جس سے اس کی ٹریفک عام انٹرنیٹ ٹریفک کے ساتھ مل جل کر رہتی ہے۔ اس سے مطلب یہ ہے کہ امنیزیہ ڈبلیو جی نے اصل وائر گارڈ کی تیزی کارکردگی کو برقرار رکھا جبکہ اس میں ایک اضافی پردہ شامل کیا، جو اسے ایک تیز اور پرانے طریقہ سے وی پی این کنکشن کی درخواست کرنے والوں کے لئے ایک عمدہ چوئس بناتا ہے۔ * تمام پلیٹ فارمز پر دستیاب ہے * کم بجلی کی استعمال * کم سیٹنگز کی تعداد * ڈی پی آئی تجزیہ سسٹمز سے پہچانا نہیں جاتا، بند کرنے کے لئے مزید مضبوط ہے * یو ڈی پی نیٹ ورک پروٹوکول پر کام کرتا ہے۔ + ایک معاصر اشارہ جاتا ہے مقبول وی پی این پروٹوکول کا امنیزیہ ڈبلیو جی۔ امنیزیہ ڈبلیو جی وائر گارڈ کے بنیادی ڈھانچے پر مبنی ہے، جس نے اس کی آسانی سے معماری اور ایکسیلنٹ کارکردگی کی خصوصیات کو برقرار رکھا۔ جبکہ وائر گارڈ کو اس کی کارآمدی کے لئے جانا جاتا ہے، اس میں اپنے ممتاز پیکٹ سائنیچرز کی وجہ سے آسانی سے پہچان میں مسائل پیش آتے تھے۔ امنیزیہ ڈبلیو جی اس مسئلے کا حل پیش کرتا ہے بہتر اوبفسکیشن میتھڈس کے ذریعے، جس سے اس کی ٹریفک عام انٹرنیٹ ٹریفک کے ساتھ مل جل کر رہتی ہے۔ اس سے مطلب یہ ہے کہ امنیزیہ ڈبلیو جی نے اصل وائر گارڈ کی تیزی کارکردگی کو برقرار رکھا جبکہ اس میں ایک اضافی پردہ شامل کیا، جو اسے ایک تیز اور پرانے طریقہ سے وی پی این کنکشن کی درخواست کرنے والوں کے لئے ایک عمدہ چوئس بناتا ہے۔ * تمام پلیٹ فارمز پر دستیاب ہے * کم بجلی کی استعمال * کم سیٹنگز کی تعداد * ڈی پی آئی تجزیہ سسٹمز سے پہچانا نہیں جاتا، بند کرنے کے لئے مزید مضبوط ہے * یو ڈی پی نیٹ ورک پروٹوکول پر کام کرتا ہے۔ - + IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol. One of its distinguishing features is its ability to swiftly switch between networks and devices, making it particularly adaptive in dynamic network environments. While it offers a blend of security, stability, and speed, it's essential to note that IKEv2 can be easily detected and is susceptible to blocking. @@ -3810,7 +4299,7 @@ While it offers a blend of security, stability, and speed, it's essential t IKEv2، IPSec انکرپشن پرت کے ساتھ جوڑا، ایک جدید اور مستحکم VPN پروٹوکول کے طور پر کھڑا ہے۔ اس کی امتیازی خصوصیات میں سے ایک نیٹ ورکس اور ڈیوائسز کے درمیان تیزی سے سوئچ کرنے کی صلاحیت ہے، جو اسے متحرک نیٹ ورک کے ماحول میں خاص طور پر موافق بناتی ہے۔ اگرچہ یہ سیکیورٹی، استحکام اور رفتار کا امتزاج پیش کرتا ہے، لیکن یہ نوٹ کرنا ضروری ہے کہ IKEv2 کا آسانی سے پتہ لگایا جا سکتا ہے اور یہ بلاک کرنے کے لیے حساس ہے۔ * صرف ونڈوز پر AmneziaVPN میں دستیاب ہے * کم بجلی کی کھپت، موبائل ڈیوائسز پر * کم سے کم کنفیگریشن * DPI تجزیہ سسٹمز کے ذریعے پہچانا جاتا ہے * UDP نیٹ ورک پروٹوکول، پورٹ 500 اور 4500 پر کام .کرتا ہے - + DNS Service DNS سروس @@ -4001,6 +4490,19 @@ While it offers a blend of security, stability, and speed, it's essential t + + RenameServerDrawer + + + Server name + سرور کا نام + + + + Save + + + SelectLanguageDrawer @@ -4047,24 +4549,24 @@ While it offers a blend of security, stability, and speed, it's essential t ShareConnectionDrawer - - + + Save AmneziaVPN config AmneziaVPN ترتیب کو محفوظ کریں - + Share بانٹیں - + Copy کاپی - - + + Copied کاپی @@ -4074,12 +4576,12 @@ While it offers a blend of security, stability, and speed, it's essential t تشکیل سٹرنگ کو کاپی کریں - + Show connection settings کنکشن کی ترتیبات دکھائیں - + To read the QR code in the Amnezia app, select "Add server" → "I have data to connect" → "QR code, key or settings file" ایمنیزیا ایپ میں QR کوڈ پڑھنے کے لیے، "سرور شامل کریں" → "میرے پاس جوڑنے کے لیے ڈیٹا ہے" → "QR کوڈ، کلید یا سیٹنگ فائل" کو منتخب کریں @@ -4092,37 +4594,37 @@ While it offers a blend of security, stability, and speed, it's essential t میزبان کا نام آئی پی ایڈریس یا ڈومین نام نظر نہیں آ رہا ہے - + New site added: %1 نیا سائٹ شامل ہوگئی ہے: %1 - + Site removed: %1 سائٹ ہٹا دی گئی ہے: %1 - + Can't open file: %1 فائل نہیں کھول سکتا: %1 - + Failed to parse JSON data from file: %1 فائل سے JSON ڈیٹا پارس کرنے میں ناکامی: %1 - + The JSON data is not an array in file: %1 فائل میں JSON ڈیٹا ایک ایرے نہیں ہے: %1 - + Import completed واردات مکمل ہوگئی ہے - + Export completed ایکسپورٹ مکمل ہوگیا @@ -4163,7 +4665,7 @@ While it offers a blend of security, stability, and speed, it's essential t TextFieldWithHeaderType - + The field can't be empty یہ فیلڈ خالی نہیں ہو سکتا @@ -4171,7 +4673,7 @@ While it offers a blend of security, stability, and speed, it's essential t VpnConnection - + Mbps ایم بی پی ایس @@ -4222,9 +4724,8 @@ While it offers a blend of security, stability, and speed, it's essential t amnezia::ContainerProps - Low - کم + کم Medium or High @@ -4235,24 +4736,27 @@ While it offers a blend of security, stability, and speed, it's essential t انتہائی - - High - - - - I just want to increase the level of my privacy. - میں صرف اپنی خصوصیت کا سطح بڑھانا چاہتا ہوں. + میں صرف اپنی خصوصیت کا سطح بڑھانا چاہتا ہوں. - I want to bypass censorship. This option recommended in most cases. - میں سانسر شدگی سے چھٹکارا حاصل کرنا چاہتا ہوں۔ یہ اختیار بیشتر صورتوں میں تجویز کیا جاتا ہے. + میں سانسر شدگی سے چھٹکارا حاصل کرنا چاہتا ہوں۔ یہ اختیار بیشتر صورتوں میں تجویز کیا جاتا ہے. Most VPN protocols are blocked. Recommended if other options are not working. زیادہ تر وی پی این پروٹوکولز بلاک ہوتے ہیں۔ اگر دوسرے اختیارات کام نہیں کر رہے ہیں تو یہ تجویز کی جاتی ہے. + + + Automatic + + + + + AmneziaWG protocol will be installed. It provides high connection speed and ensures stable operation even in the most challenging network conditions. + + main2 diff --git a/client/translations/amneziavpn_zh_CN.ts b/client/translations/amneziavpn_zh_CN.ts index fa6a87d1..2b0cf848 100644 --- a/client/translations/amneziavpn_zh_CN.ts +++ b/client/translations/amneziavpn_zh_CN.ts @@ -4,60 +4,113 @@ AdLabel - - Amnezia Premium - for access to any website + + Amnezia Premium - for access to all websites and online resources + + + + + ApiAccountInfoModel + + + + Active + + + + + Inactive + + + + + %1 out of %2 + + + + + Classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. Speeds up to 200 Mbps + + + + + Free unlimited access to a basic set of websites such as Facebook, Instagram, Twitter (X), Discord, Telegram and more. YouTube is not included in the free plan. + + + + + amnezia_free_support_bot + + + + + amnezia_premium_support_bot + + + + + ApiConfigsController + + + %1 installed successfully. + + + + + API config reloaded + + + + + Successfully changed the country of connection to %1 ApiServicesModel - - Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s - - - - - VPN to access blocked sites in regions with high levels of Internet censorship. - - - - + <p><a style="color: #EB5757;">Not available in your region. If you have VPN enabled, disable it, return to the previous screen, and try again.</a> - - Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship. + + Amnezia Premium is classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. Speeds up to %1 Mbps. - - Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship + + + AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan. - + + Amnezia Premium is classic VPN for for seamless work, downloading large files, and watching videos. Access all websites and online resources. + + + + %1 MBit/s - + %1 days - + VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, <a href="%1/free" style="color: #FBB26A;">more details on the website.</a> - + Free - + %1 $/month @@ -96,63 +149,56 @@ ConnectionController - - - - + + + + Connect 连接 - VPN Protocols is not installed. Please install VPN container at first - 请先安装VPN协议 + 请先安装VPN协议 - + Connecting... 连接中 - + Connected 已连接 - + Reconnecting... 重连中 - + Disconnecting... 断开中 - + Preparing... - + Settings updated successfully, reconnnection... 配置已更新, 重连中... - + Settings updated successfully 配置更新成功 - The selected protocol is not supported on the current platform - 当前平台不支持所选协议 - - - - unable to create configuration - + 当前平台不支持所选协议 @@ -265,13 +311,18 @@ Can't be disabled for current server ImportController - + Scanned %1 of %2. 扫描 %1 of %2. - - In the imported configuration, potentially dangerous lines were found: + + This configuration contains an OpenVPN setup. OpenVPN configurations can include malicious scripts, so only add it if you fully trust the provider of this config. + + + + + <br>In the imported configuration, potentially dangerous lines were found: @@ -286,78 +337,63 @@ Can't be disabled for current server 已安装在服务器上 - + %1 installed successfully. %1 安装成功。 - + %1 is already installed on the server. 服务器上已经安装 %1。 - + Added containers that were already installed on the server 添加已安装在服务器上的容器 - + Already installed containers were found on the server. All installed containers have been added to the application 在服务上发现已经安装协议并添加至应用 - + Settings updated successfully 配置更新成功 - + Server '%1' was rebooted 服务器 '%1' 已重新启动 - + Server '%1' was removed 已移除服务器 '%1' - + All containers from server '%1' have been removed 服务器 '%1' 的所有容器已移除 - + %1 has been removed from the server '%2' %1 已从服务器 '%2' 上移除 - + Api config removed - + %1 cached profile cleared - - - %1 installed successfully. - - - - - API config reloaded - - - - - Successfully changed the country of connection to %1 - - 1% has been removed from the server '%2' %1 已从服务器 '%2' 上移除 @@ -375,12 +411,12 @@ Already installed containers were found on the server. All installed containers 协议已从 - + Please login as the user 请以用户身份登录 - + Server added successfully 增加服务器成功 @@ -494,12 +530,12 @@ Already installed containers were found on the server. All installed containers 分隔隧道已禁用 - + VPN protocol VPN协议 - + Servers 服务器 @@ -1393,22 +1429,22 @@ Already installed containers were found on the server. All installed containers 应用 - + Backup 备份 - + About AmneziaVPN 关于 - + Dev console - + Close application 关闭应用 @@ -1422,17 +1458,17 @@ And if you don't like the app, all the more support it - the donation will 如果您不喜欢,请捐助支持我们改进它。 - + Support Amnezia 支持Amnezia - + Amnezia is a free and open-source application. You can support the developers if you like it. Amnezia 是一款免费的开源应用程序。 如果您喜欢的话可以支持开发者。 - + Contacts 联系方式 @@ -1466,139 +1502,483 @@ And if you don't like the app, all the more support it - the donation will 用于评论和提交软件的缺陷 - - Copied + + mailto:support@amnezia.org - + GitHub GitHub - + Discover the source code - + https://github.com/amnezia-vpn/amnezia-client https://github.com/amnezia-vpn/amnezia-client - + Website 官网 - + Visit official website - + Software version: %1 软件版本: %1 - + Check for updates 检查更新 - + Privacy Policy 隐私政策 - PageSettingsApiLanguageList + PageSettingsApiAvailableCountries - + + Location for connection + + + + Unable change server location while there is an active connection - PageSettingsApiServerInfo + PageSettingsApiDevices - - For the region + + Active Devices - - Price + + Manage currently connected devices - - Work period + + You can find the identifier on the Support tab or, for older versions of the app, by tapping '+' and then the three dots at the top of the page. - - Valid until + + (current device) - - Speed + + Support tag: - - Support tag + + Last updated: - - Copied + + Cannot unlink device during active connection - - Reload API config + + Are you sure you want to unlink this device? - - Reload API config? + + This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect. - - + Continue 继续 - - + + Cancel + 取消 + + + + PageSettingsApiInstructions + + + Windows + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#windows + + + + + macOS + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#macos + + + + + Android + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#android + + + + + AndroidTV + + + + + https://docs.amnezia.org/ru/documentation/instructions/android_tv_connect/ + + + + + iOS + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#ios + + + + + Linux + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#linux + + + + + Routers + + + + + https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#routers + + + + + How to connect on another device + + + + + Setup guides on the Amnezia website + + + + + PageSettingsApiNativeConfigs + + + Save AmneziaVPN config + 保存配置 + + + + Configuration Files + + + + + For router setup or the AmneziaWG app + + + + + The configuration needs to be reissued + + + + + configuration file + + + + + Generate a new configuration file + + + + + The previously created one will stop working + + + + + Revoke the current configuration file + + + + + Config file saved + + + + + The config has been revoked + + + + + Generate a new %1 configuration file? + + + + + Revoke the current %1 configuration file? + + + + + Your previous configuration file will no longer work, and it will not be possible to connect using it + + + + + Download + + + + + Continue + 继续 + + + + Cancel + 取消 + + + + PageSettingsApiServerInfo + + + Subscription Status + + + + + Valid Until + + + + + Active Connections + + + + + Configurations have been updated for some countries. Download and install the updated configuration files + + + + + Subscription Key + + + + + Amnezia Premium subscription key + + + + + Save VPN key as a file + + + + + Copy VPN key + + + + + Configuration Files + + + + + Manage configuration files + + + + + Active Devices + + + + + Manage currently connected devices + + + + + Support + + + + + How to connect on another device + + + + + Reload API config + + + + + Reload API config? + + + + + + + Continue + 继续 + + + + + Cancel 取消 - + Cannot reload API config during active connection - + + Unlink this device + + + + + Are you sure you want to unlink this device? + + + + + This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect. + + + + + Cannot unlink device during active connection + + + + Remove from application - + Remove from application? - + Cannot remove server during active connection + + PageSettingsApiSupport + + + Telegram + + + + + Email + + + + + support@amnezia.org + + + + + Email Billing & Orders + + + + + help@vpnpay.io + + + + + Website + 官网 + + + + amnezia.org + + + + + Support + + + + + Our technical support specialists are available to assist you at any time + + + + + Support tag + + + + + Copied + + + PageSettingsAppSplitTunneling @@ -1880,7 +2260,7 @@ And if you don't like the app, all the more support it - the donation will - Cannot change killSwitch settings during active connection + Cannot change KillSwitch settings during active connection @@ -2022,20 +2402,20 @@ And if you don't like the app, all the more support it - the donation will 打开日志文件夹 - - + + Save 保存 - - + + Logs files (*.log) - - + + Logs file saved 日志文件已保存 @@ -2069,32 +2449,32 @@ And if you don't like the app, all the more support it - the donation will 日志已清理 - + Client logs - + AmneziaVPN logs - + Open logs folder - + Export logs - + Service logs - + AmneziaVPN-service logs @@ -2129,12 +2509,12 @@ And if you don't like the app, all the more support it - the donation will 清除缓存? - + Do you want to reboot the server? 您想重新启动服务器吗? - + Do you want to clear server from Amnezia software? 您要清除服务器上的Amnezia软件吗? @@ -2144,83 +2524,83 @@ And if you don't like the app, all the more support it - the donation will - - - - + + + + Continue 继续 - - - - + + + + Cancel 取消 - + Check the server for previously installed Amnezia services 检查服务器上,是否存在之前安装的 Amnezia 服务 - + Add them to the application if they were not displayed 如果存在且未显示,则添加到应用软件 - + Reboot server 重新启动服务器 - + The reboot process may take approximately 30 seconds. Are you sure you wish to proceed? 重新启动过程可能需要大约30秒。您确定要继续吗? - + Cannot reboot server during active connection - + Remove server from application 移除本地服务器信息 - + Do you want to remove the server from application? 您想要从应用程序中移除服务器吗? - + Cannot remove server during active connection - + All users whom you shared a connection with will no longer be able to connect to it. 与您共享连接的所有用户将无法再连接到该连接。 - + Cannot clear server from Amnezia software during active connection - + Reset API config 重置 API 配置 - + Do you want to reset API config? 您想重置 API 配置吗? - + Cannot reset API config during active connection @@ -2229,12 +2609,12 @@ And if you don't like the app, all the more support it - the donation will 移除本地服务器信息? - + All installed AmneziaVPN services will still remain on the server. 所有已安装的 AmneziaVPN 服务仍将保留在服务器上。 - + Clear server from Amnezia software 清理Amnezia中服务器信息 @@ -2250,32 +2630,25 @@ And if you don't like the app, all the more support it - the donation will PageSettingsServerInfo - - Subscription is valid until - - - - Server name - 服务器名 + 服务器名 - Save - 保存 + 保存 - + Protocols 协议 - + Services 服务 - + Management 管理 @@ -2423,17 +2796,17 @@ And if you don't like the app, all the more support it - the donation will 规则 - + Remove 移除 - + Continue 继续 - + Cancel 取消 @@ -2456,17 +2829,17 @@ And if you don't like the app, all the more support it - the donation will 只有这里列出的网站将通过VPN访问 - + website or IP 网站或IP - + Import / Export Sites 导入/导出网站 - + Import 导入 @@ -2482,29 +2855,29 @@ And if you don't like the app, all the more support it - the donation will - - + + Sites files (*.json) - + Import a list of sites 导入网址列表 - + Replace site list 替换网址列表 - - + + Open sites file 打开网址文件 - + Add imported sites to existing ones 将导入的网址添加到现有网址中 @@ -2631,85 +3004,70 @@ It's okay as long as it's from someone you trust. - + Site Amnezia - + VPN by Amnezia - + Connect to classic paid and free VPN services from Amnezia - + Self-hosted VPN - + Configure Amnezia VPN on your own server - + Restore from backup 从备份还原 - + - + Open backup file 打开备份文件 - + Backup files (*.backup) - + File with connection settings 包含连接配置的文件 - - - - - - + Open config file 打开配置文件 - + QR code 二维码 - - - - - - + I have nothing 我没有 - - - - - Key as text 授权码文本 @@ -2727,7 +3085,7 @@ It's okay as long as it's from someone you trust. 配置服务器 - + Server IP address [:port] 服务器IP [:端口] @@ -2740,12 +3098,12 @@ It's okay as long as it's from someone you trust. 密码 或 私钥 - + Continue 继续 - + All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties 您输入的所有数据将严格保密,不会与 Amnezia 或任何第三方共享或披露 @@ -2756,47 +3114,47 @@ and will not be shared or disclosed to the Amnezia or any third parties 不会向 Amnezia 或任何第三方分享或披露 - + 255.255.255.255:22 - + SSH Username SSH 用户名 - + Password or SSH private key 密码或 SSH 私钥 - + How to run your VPN server - + Where to get connection data, step-by-step instructions for buying a VPS - + Ip address cannot be empty IP不能为空 - + Enter the address in the format 255.255.255.255:88 按照这种格式输入 255.255.255.255:88 - + Login cannot be empty 账号不能为空 - + Password/private key cannot be empty 密码或私钥不能为空 @@ -2804,17 +3162,26 @@ and will not be shared or disclosed to the Amnezia or any third parties PageSetupWizardEasy - What is the level of internet control in your region? - 您所在地区的互联网管控力度如何? + 您所在地区的互联网管控力度如何? + + + + Choose Installation Type + + Manual + + + + Choose a VPN protocol 选择 VPN 协议 - + Skip setup 跳过设置 @@ -2827,7 +3194,7 @@ and will not be shared or disclosed to the Amnezia or any third parties 我想选择VPN协议 - + Continue 继续 @@ -3038,123 +3405,123 @@ and will not be shared or disclosed to the Amnezia or any third parties PageShare - + Save OpenVPN config 保存OpenVPN配置 - + Save WireGuard config 保存WireGuard配置 - + Save AmneziaWG config 保存 AmneziaWG 配置 - + Save Shadowsocks config 保存 Shadowsocks 配置 - + Save Cloak config 保存斗篷配置 - + Save XRay config - + For the AmneziaVPN app AmneziaVPN 应用 - + OpenVPN native format OpenVPN原生格式 - + WireGuard native format WireGuard原生格式 - + AmneziaWG native format AmneziaWG 本地格式 - + Shadowsocks native format Shadowsocks原生格式 - + Cloak native format Cloak原生格式 - + XRay native format - + Share VPN Access 共享 VPN 访问 - + Share full access to the server and VPN 共享服务器和VPN的完全访问权限 - + Use for your own devices, or share with those you trust to manage the server. 用于您自己的设备,或与您信任的人共享以管理服务器. - - + + Users 用户 - + Share VPN access without the ability to manage the server 共享 VPN 访问,无需管理服务器 - + Search 搜索 - + Creation date: %1 - + Latest handshake: %1 - + Data received: %1 - + Data sent: %1 - + Allowed IPs: %1 @@ -3163,42 +3530,42 @@ and will not be shared or disclosed to the Amnezia or any third parties 创建日期: - + Rename 重新命名 - + Client name 客户名称 - + Save 保存 - + Revoke 撤销 - + Revoke the config for a user - %1? 撤销用户的配置- %1? - + The user will no longer be able to connect to your server. 该用户将无法再连接到您的服务器. - + Continue 继续 - + Cancel 取消 @@ -3211,7 +3578,7 @@ and will not be shared or disclosed to the Amnezia or any third parties 访问VPN - + Connection 连接 @@ -3240,8 +3607,8 @@ and will not be shared or disclosed to the Amnezia or any third parties 服务器 + - Server 服务器 @@ -3263,8 +3630,8 @@ and will not be shared or disclosed to the Amnezia or any third parties 协议 + - Protocol 协议 @@ -3279,19 +3646,19 @@ and will not be shared or disclosed to the Amnezia or any third parties 配置已撤销 - + User name 用户名 + - Connection format 连接格式 - - + + Share 共享 @@ -3355,17 +3722,17 @@ and will not be shared or disclosed to the Amnezia or any third parties PageStart - + Logging was disabled after 14 days, log files were deleted - + Settings restored from backup file 从备份文件还原配置 - + Logging is enabled. Note that logs will be automaticallydisabled after 14 days, and all log files will be deleted. @@ -3623,7 +3990,7 @@ and will not be shared or disclosed to the Amnezia or any third parties - + SOCKS5 proxy server @@ -3649,80 +4016,111 @@ and will not be shared or disclosed to the Amnezia or any third parties - + + The selected protocol is not supported on the current platform + 当前平台不支持所选协议 + + + Server check failed 服务器检测失败 - + Server port already used. Check for another software 检测服务器该端口是否被其他软件被占用 - + Server error: Docker container missing 服务器错误: Docker容器丢失 - + Server error: Docker failed 服务器错误: Docker失败 - + Installation canceled by user 用户取消安装 - + The user is not a member of the sudo group 用户不是 sudo 组的成员 - + Server error: Package manager error 服务器错误:包管理器错误 + + + The sudo package is not pre-installed on the server + + + The server user's home directory is not accessible + + + + + Action not allowed in sudoers + + + + + The user's password is required + + + + SSH request was denied SSH请求被拒绝 - + SSH request was interrupted SSH请求中断 - + SSH internal error SSH内部错误 - + Invalid private key or invalid passphrase entered 输入的私钥或密码无效 - + The selected private key format is not supported, use openssh ED25519 key types or PEM key types 不支持所选私钥格式,请使用 openssh ED25519 密钥类型或 PEM 密钥类型 - + Timeout connecting to server 连接服务器超时 - + SCP error: Generic failure - + Unable to open config file + + + VPN Protocols is not installed. + Please install VPN container at first + 请先安装VPN协议 + Sftp error: End-of-file encountered Sftp错误: End-of-file encountered @@ -3776,82 +4174,88 @@ and will not be shared or disclosed to the Amnezia or any third parties Sftp 错误: 远程驱动器中没有媒介 - + VPN connection error VPN 连接错误 - + + Error when retrieving configuration from API 从 API 检索配置时出错 - + This config has already been added to the application 该配置已添加到应用程序中 - + In the response from the server, an empty config was received - + SSL error occurred - + Server response timeout on api request - + Missing AGW public key - + Failed to decrypt response payload - + Missing list of available services - + + The limit of allowed configurations per subscription has been exceeded + + + + QFile error: The file could not be opened - + QFile error: An error occurred when reading from the file - + QFile error: The file could not be accessed - + QFile error: An unspecified error occurred - + QFile error: A fatal error occurred - + QFile error: The operation was aborted - + ErrorCode: %1. 错误代码: %1. @@ -3860,57 +4264,57 @@ and will not be shared or disclosed to the Amnezia or any third parties 配置保存到磁盘失败 - + OpenVPN config missing OpenVPN配置丢失 - + OpenVPN management server error OpenVPN 管理服务器错误 - + OpenVPN executable missing OpenVPN 可执行文件丢失 - + Shadowsocks (ss-local) executable missing Shadowsocks (ss-local) 执行文件丢失 - + Cloak (ck-client) executable missing Cloak (ck-client) 执行文件丢失 - + Amnezia helper service error Amnezia 服务连接失败 - + OpenSSL failed OpenSSL错误 - + Can't connect: another VPN connection is active 无法连接:另一个VPN连接处于活跃状态 - + Can't setup OpenVPN TAP network adapter 无法设置 OpenVPN TAP 网络适配器 - + VPN pool error: no available addresses VPN 池错误:没有可用地址 - + The config does not contain any containers and credentials for connecting to the server 配置不包含任何用于连接服务器的容器和凭据 @@ -3919,7 +4323,7 @@ and will not be shared or disclosed to the Amnezia or any third parties 该配置不包含任何用于连接到服务器的容器和凭据。 - + Internal error @@ -3930,7 +4334,7 @@ and will not be shared or disclosed to the Amnezia or any third parties - + Website in Tor network 在 Tor 网络中架设网站 @@ -3951,29 +4355,106 @@ and will not be shared or disclosed to the Amnezia or any third parties - Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions. - Shadowsocks - 掩盖VPN流量,使其类似于正常的网络流量,但在一些高度审查的地区可能会被分析系统识别. - - - - XRay with REALITY - Suitable for countries with the highest level of internet censorship. Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods. + Shadowsocks masks VPN traffic, making it resemble normal web traffic, but it may still be detected by certain analysis systems. - + + OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against active-probing detection. It is very resistant to detection, but offers low speed. + + + + + WireGuard - popular VPN protocol with high performance, high speed and low power consumption. + + + + + AmneziaWG is a special protocol from Amnezia based on WireGuard. It provides high connection speed and ensures stable operation even in the most challenging network conditions. + + + + + XRay with REALITY masks VPN traffic as web traffic and protects against active probing. It is highly resistant to detection and offers high speed. + + + + + OpenVPN stands as one of the most popular and time-tested VPN protocols available. +It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, catering to a wide range of devices and operating systems. Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, which continually reinforces its security. With a strong balance of performance, security, and compatibility, OpenVPN remains a top choice for privacy-conscious individuals and businesses alike. + +* Available in the AmneziaVPN across all platforms +* Normal power consumption on mobile devices +* Flexible customisation to suit user needs to work with different operating systems and devices +* Recognised by DPI systems and therefore susceptible to blocking +* Can operate over both TCP and UDP network protocols. + + + + + This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for protecting against detection. + +OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client and the server. + +Cloak protects OpenVPN from detection. + +Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, and also protects the VPN from detection by Active Probing. This makes it very resistant to being detected + +Immediately after receiving the first data packet, Cloak authenticates the incoming connection. If authentication fails, the plugin masks the server as a fake website and your VPN becomes invisible to analysis systems. + +* Available in the AmneziaVPN across all platforms +* High power consumption on mobile devices +* Flexible settings +* Not recognised by detection systems +* Works over TCP network protocol, 443 port. + + + + + + A relatively new popular VPN protocol with a simplified architecture. +WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. +WireGuard is very susceptible to detection and blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. + +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Easily recognised by DPI analysis systems, susceptible to blocking +* Works over UDP network protocol. + + + + + A modern iteration of the popular VPN protocol, AmneziaWG builds upon the foundation set by WireGuard, retaining its simplified architecture and high-performance capabilities across devices. +While WireGuard is known for its efficiency, it had issues with being easily detected due to its distinct packet signatures. AmneziaWG solves this problem by using better obfuscation methods, making its traffic blend in with regular internet traffic. +This means that AmneziaWG keeps the fast performance of the original while adding an extra layer of stealth, making it a great choice for those wanting a fast and discreet VPN connection. + +* Available in the AmneziaVPN across all platforms +* Low power consumption +* Minimum number of settings +* Not recognised by traffic analysis systems +* Works over UDP network protocol. + + + + + The REALITY protocol, a pioneering development by the creators of XRay, is designed to provide the highest level of protection against detection through its innovative approach to security and privacy. +It uniquely identifies attackers during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting attackers to genuine websites, thus presenting an authentic TLS certificate and data. +This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. +Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security. This makes REALITY a robust solution for maintaining internet freedom. + + + + Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions. + Shadowsocks - 掩盖VPN流量,使其类似于正常的网络流量,但在一些高度审查的地区可能会被分析系统识别. + + + IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss. It has native support on the latest versions of Android and iOS. - - The REALITY protocol, a pioneering development by the creators of XRay, is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion. -It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, thus presenting an authentic TLS certificate and data. -This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. -Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship. - - - - + After installation, Amnezia will create a file storage on your server. You will be able to access it using @@ -3996,34 +4477,11 @@ For more detailed information, you can OpenVPN over Cloak - OpenVPN与VPN结合,伪装成Web流量,并保护免受主动探测的侦测。非常适合在具有最高审查水平的地区绕过封锁 - + Create a file vault on your server to securely store and transfer files. 在您的服务器上创建一个文件保险库,用于安全存储和传输文件。 - - This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for protecting against blocking. - -OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client and the server. - -Cloak protects OpenVPN from detection and blocking. - -Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, and also protects the VPN from detection by Active Probing. This makes it very resistant to being detected - -Immediately after receiving the first data packet, Cloak authenticates the incoming connection. If authentication fails, the plugin masks the server as a fake website and your VPN becomes invisible to analysis systems. - -If there is a extreme level of Internet censorship in your region, we advise you to use only OpenVPN over Cloak from the first connection - -* Available in the AmneziaVPN across all platforms -* High power consumption on mobile devices -* Flexible settings -* Not recognised by DPI analysis systems -* Works over TCP network protocol, 443 port. - - - - - A relatively new popular VPN protocol with a simplified architecture. WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. WireGuard is very susceptible to blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. @@ -4033,7 +4491,7 @@ WireGuard is very susceptible to blocking due to its distinct packet signatures. * Minimum number of settings * Easily recognised by DPI analysis systems, susceptible to blocking * Works over UDP network protocol. - 一个相对较新的流行VPN协议,具有简化的架构。 + 一个相对较新的流行VPN协议,具有简化的架构。 WireGuard提供稳定的VPN连接,并在所有设备上具有高性能。它使用硬编码的加密设置。与OpenVPN相比,WireGuard具有较低的延迟和更好的数据传输吞吐量。 WireGuard非常容易被阻挡,因为其独特的数据包签名。与一些其他VPN协议不同,这些协议使用混淆技术,WireGuard数据包的一致签名模式更容易被高级深度数据包检测(DPI)系统和其他网络监控工具识别和阻挡。 @@ -4048,31 +4506,28 @@ WireGuard非常容易被阻挡,因为其独特的数据包签名。与一些 Shadowsocks - 混淆 VPN 流量,使其与正常的 Web 流量相似,但在一些审查力度高的地区可以被分析系统识别。 - OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against active-probing detection. Ideal for bypassing blocking in regions with the highest levels of censorship. - OpenVPN over Cloak - OpenVPN与VPN结合,伪装成Web流量,并保护免受主动探测的侦测。非常适合在具有最高审查水平的地区绕过封锁. + OpenVPN over Cloak - OpenVPN与VPN结合,伪装成Web流量,并保护免受主动探测的侦测。非常适合在具有最高审查水平的地区绕过封锁. - WireGuard - New popular VPN protocol with high performance, high speed and low power consumption. Recommended for regions with low levels of censorship. - WireGuard - 新型流行的VPN协议,具有高性能、高速度和低功耗。建议用于审查力度较低的地区. + WireGuard - 新型流行的VPN协议,具有高性能、高速度和低功耗。建议用于审查力度较低的地区. - AmneziaWG - Special protocol from Amnezia, based on WireGuard. It's fast like WireGuard, but very resistant to blockages. Recommended for regions with high levels of censorship. - AmneziaWG - Amnezia 的特殊协议,基于 WireGuard。它的速度像 WireGuard 一样快,但非常抗堵塞。推荐用于审查较严的地区。 + AmneziaWG - Amnezia 的特殊协议,基于 WireGuard。它的速度像 WireGuard 一样快,但非常抗堵塞。推荐用于审查较严的地区。 IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss. IKEv2/IPsec - 现代稳定协议,相比其他协议较快一些,在信号丢失后恢复连接。 - + Deploy a WordPress site on the Tor network in two clicks. 只需点击两次即可架设 WordPress 网站到 Tor 网络. - + Replace the current DNS server with your own. This will increase your privacy level. 将当前的 DNS 服务器替换为您自己的。这将提高您的隐私保护级别。 @@ -4081,7 +4536,6 @@ WireGuard非常容易被阻挡,因为其独特的数据包签名。与一些 在您的服务器上创建文件仓库,以便安全地存储和传输文件 - OpenVPN stands as one of the most popular and time-tested VPN protocols available. It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, catering to a wide range of devices and operating systems. Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, which continually reinforces its security. With a strong balance of performance, security, and compatibility, OpenVPN remains a top choice for privacy-conscious individuals and businesses alike. @@ -4090,7 +4544,7 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for * Flexible customisation to suit user needs to work with different operating systems and devices * Recognised by DPI analysis systems and therefore susceptible to blocking * Can operate over both TCP and UDP network protocols. - OpenVPN 是最流行且经过时间考验的 VPN 协议之一。 + OpenVPN 是最流行且经过时间考验的 VPN 协议之一。 它采用其独特的安全协议,利用 SSL/TLS 的优势进行加密和密钥交换。此外,OpenVPN 支持多种身份验证方法,使其具有多功能性和适应性,可适应各种设备和操作系统。由于其开源性质,OpenVPN 受益于全球社区的广泛审查,这不断增强了其安全性。凭借性能、安全性和兼容性的强大平衡,OpenVPN 仍然是注重隐私的个人和企业的首选。 * 可在所有平台的 AmneziaVPN 中使用 @@ -4100,7 +4554,7 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for * 可以通过 TCP 和 UDP 网络协议运行. - + Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection.However, certain traffic analysis systems might still detect a Shadowsocks connection. Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol. * Available in the AmneziaVPN only on desktop platforms @@ -4172,7 +4626,6 @@ WireGuard is very susceptible to blocking due to its distinct packet signatures. * 通过 UDP 网络协议工作。 - A modern iteration of the popular VPN protocol, AmneziaWG builds upon the foundation set by WireGuard, retaining its simplified architecture and high-performance capabilities across devices. While WireGuard is known for its efficiency, it had issues with being easily detected due to its distinct packet signatures. AmneziaWG solves this problem by using better obfuscation methods, making its traffic blend in with regular internet traffic. This means that AmneziaWG keeps the fast performance of the original while adding an extra layer of stealth, making it a great choice for those wanting a fast and discreet VPN connection. @@ -4182,7 +4635,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin * Minimum number of settings * Not recognised by DPI analysis systems, resistant to blocking * Works over UDP network protocol. - AmneziaWG 是流行 VPN 协议的现代迭代,它建立在 WireGuard 的基础上,保留了其简化的架构和跨设备的高性能功能。 + AmneziaWG 是流行 VPN 协议的现代迭代,它建立在 WireGuard 的基础上,保留了其简化的架构和跨设备的高性能功能。 虽然 WireGuard 以其高效而闻名,但由于其独特的数据包签名,它存在容易被检测到的问题。 AmneziaWG 通过使用更好的混淆方法解决了这个问题,使其流量与常规互联网流量融合在一起。 这意味着 AmneziaWG 保留了原始版本的快速性能,同时添加了额外的隐秘层,使其成为那些想要快速且谨慎的 VPN 连接的人的绝佳选择。 @@ -4193,7 +4646,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin * 通过 UDP 网络协议工作。 - + IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol. One of its distinguishing features is its ability to swiftly switch between networks and devices, making it particularly adaptive in dynamic network environments. While it offers a blend of security, stability, and speed, it's essential to note that IKEv2 can be easily detected and is susceptible to blocking. @@ -4234,7 +4687,7 @@ While it offers a blend of security, stability, and speed, it's essential t IPsec 容器 - + DNS Service DNS 服务 @@ -4429,6 +4882,19 @@ While it offers a blend of security, stability, and speed, it's essential t + + RenameServerDrawer + + + Server name + 服务器名 + + + + Save + 保存 + + SelectLanguageDrawer @@ -4479,24 +4945,24 @@ While it offers a blend of security, stability, and speed, it's essential t ShareConnectionDrawer - - + + Save AmneziaVPN config 保存配置 - + Share 共享 - + Copy 拷贝 - - + + Copied 已拷贝 @@ -4506,7 +4972,7 @@ While it offers a blend of security, stability, and speed, it's essential t 复制配置字符串 - + Show connection settings 显示连接配置 @@ -4515,7 +4981,7 @@ While it offers a blend of security, stability, and speed, it's essential t 展示内容 - + To read the QR code in the Amnezia app, select "Add server" → "I have data to connect" → "QR code, key or settings file" 要应用二维码到 Amnezia,请底部工具栏点击“+”→“连接方式”→“二维码、授权码或配置文件” @@ -4528,37 +4994,37 @@ While it offers a blend of security, stability, and speed, it's essential t 请输入有效的域名或IP地址 - + New site added: %1 已经添加新网站: %1 - + Site removed: %1 已移除网站: %1 - + Can't open file: %1 无法打开文件: %1 - + Failed to parse JSON data from file: %1 JSON解析失败,文件: %1 - + The JSON data is not an array in file: %1 文件中的JSON数据不是一个数组,文件: %1 - + Import completed 完成导入 - + Export completed 完成导出 @@ -4599,7 +5065,7 @@ While it offers a blend of security, stability, and speed, it's essential t TextFieldWithHeaderType - + The field can't be empty 输入不能为空 @@ -4607,7 +5073,7 @@ While it offers a blend of security, stability, and speed, it's essential t VpnConnection - + Mbps @@ -4658,28 +5124,24 @@ While it offers a blend of security, stability, and speed, it's essential t amnezia::ContainerProps - Low - + - High - 中或高 + 中或高 Extreme 极度 - I just want to increase the level of my privacy. - 只是想提高隐私保护级别。 + 只是想提高隐私保护级别。 - I want to bypass censorship. This option recommended in most cases. - 想要绕过审查制度。大多数情况下推荐使用此选项。 + 想要绕过审查制度。大多数情况下推荐使用此选项。 Most VPN protocols are blocked. Recommended if other options are not working. @@ -4701,6 +5163,16 @@ While it offers a blend of security, stability, and speed, it's essential t Some foreign sites are blocked, but VPN providers are not blocked 一些国外网站被屏蔽,但VPN提供商未被屏蔽 + + + Automatic + + + + + AmneziaWG protocol will be installed. It provides high connection speed and ensures stable operation even in the most challenging network conditions. + + main2 From f3a4a1b1beae0e8e32466e3e379360602454c6be Mon Sep 17 00:00:00 2001 From: Yaroslav Date: Fri, 11 Apr 2025 19:09:12 +0300 Subject: [PATCH 16/65] feat: improve post uninstall script for macos to properly remove application and its components (#1521) --- deploy/data/macos/post_uninstall.sh | 34 +++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/deploy/data/macos/post_uninstall.sh b/deploy/data/macos/post_uninstall.sh index 6e8e9fa8..de7846db 100755 --- a/deploy/data/macos/post_uninstall.sh +++ b/deploy/data/macos/post_uninstall.sh @@ -2,13 +2,33 @@ APP_NAME=AmneziaVPN PLIST_NAME=$APP_NAME.plist -LAUNCH_DAEMONS_PLIST_NAME=/Library/LaunchDaemons/$PLIST_NAME +LAUNCH_DAEMONS_PLIST_NAME="/Library/LaunchDaemons/$PLIST_NAME" +APP_PATH="/Applications/$APP_NAME.app" +USER_APP_SUPPORT="$HOME/Library/Application Support/$APP_NAME" +SYSTEM_APP_SUPPORT="/Library/Application Support/$APP_NAME" +LOG_FOLDER="/var/log/$APP_NAME" +CACHES_FOLDER="$HOME/Library/Caches/$APP_NAME" -if launchctl list "$APP_NAME-service" &> /dev/null; then - launchctl unload $LAUNCH_DAEMONS_PLIST_NAME - rm -f $LAUNCH_DAEMONS_PLIST_NAME +# Stop the running service if it exists +if pgrep -x "${APP_NAME}-service" > /dev/null; then + sudo killall -9 "${APP_NAME}-service" fi -rm -rf "$HOME/Library/Application Support/$APP_NAME" -rm -rf /var/log/$APP_NAME -rm -rf /Applications/$APP_NAME.app/Contents +# Unload the service if loaded and remove its plist file regardless +if launchctl list "${APP_NAME}-service" &> /dev/null; then + sudo launchctl unload "$LAUNCH_DAEMONS_PLIST_NAME" +fi +sudo rm -f "$LAUNCH_DAEMONS_PLIST_NAME" + +# Remove the entire application bundle +sudo rm -rf "$APP_PATH" + +# Remove Application Support folders (user and system, if they exist) +rm -rf "$USER_APP_SUPPORT" +sudo rm -rf "$SYSTEM_APP_SUPPORT" + +# Remove the log folder +sudo rm -rf "$LOG_FOLDER" + +# Remove any caches left behind +rm -rf "$CACHES_FOLDER" From a180e12bdf4ddaa88440546517f3e8b522ef5fb5 Mon Sep 17 00:00:00 2001 From: MrMirDan <58086007+MrMirDan@users.noreply.github.com> Date: Sat, 12 Apr 2025 18:04:34 +0300 Subject: [PATCH 17/65] chore: updated ru translation (#1531) --- client/translations/amneziavpn_ru_RU.ts | 80 ++++++++++++------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/client/translations/amneziavpn_ru_RU.ts b/client/translations/amneziavpn_ru_RU.ts index 9c332f17..c740a560 100644 --- a/client/translations/amneziavpn_ru_RU.ts +++ b/client/translations/amneziavpn_ru_RU.ts @@ -10,7 +10,7 @@ Amnezia Premium - for access to all websites and online resources - + Amnezia Premium - доступ ко всем сайтам и онлайн ресурсам @@ -110,12 +110,12 @@ Amnezia Premium is classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. Speeds up to %1 Mbps. - + Amnezia Premium - это классический VPN для комфортной работы, загрузки больших файлов и просмотра видео. Доступ ко всем сайтам и онлайн ресурсам. Скорость - до %1 Мбит/с. Amnezia Premium is classic VPN for for seamless work, downloading large files, and watching videos. Access all websites and online resources. - + Amnezia Premium - это классический VPN для комфортной работы, загрузки больших файлов и просмотра видео. Доступ ко всем сайтам и онлайн ресурсам. @@ -359,12 +359,12 @@ Can't be disabled for current server This configuration contains an OpenVPN setup. OpenVPN configurations can include malicious scripts, so only add it if you fully trust the provider of this config. - + Эта конфигурация содержит настройки OpenVPN. Конфигурации OpenVPN могут содержать вредоносные скрипты, поэтому добавляйте их только в том случае, если полностью доверяете источнику этого файла. <br>In the imported configuration, potentially dangerous lines were found: - + <br>В импортированной конфигурации обнаружены потенциально опасные строки: In the imported configuration, potentially dangerous lines were found: @@ -538,12 +538,12 @@ Already installed containers were found on the server. All installed containers Gateway endpoint - + Gateway endpoint Dev gateway environment - + Dev gateway environment @@ -673,47 +673,47 @@ Already installed containers were found on the server. All installed containers Jc - Junk packet count - + Jc - Junk packet count Jmin - Junk packet minimum size - + Jmin - Junk packet minimum size Jmax - Junk packet maximum size - + Jmax - Junk packet maximum size S1 - Init packet junk size - + S1 - Init packet junk size S2 - Response packet junk size - + S2 - Response packet junk size H1 - Init packet magic header - + H1 - Init packet magic header H2 - Response packet magic header - + H2 - Response packet magic header H4 - Transport packet magic header - + H4 - Transport packet magic header H3 - Underload packet magic header - + H3 - Underload packet magic header @@ -1482,7 +1482,7 @@ Already installed containers were found on the server. All installed containers Dev console - + Dev console @@ -1616,7 +1616,7 @@ Already installed containers were found on the server. All installed containers Active Devices - + Активные устройства @@ -1773,7 +1773,7 @@ Already installed containers were found on the server. All installed containers Configuration Files - + Файлы конфигурации @@ -1919,37 +1919,37 @@ Already installed containers were found on the server. All installed containers Subscription Status - + Статус подписки Valid Until - + Действительна до Active Connections - + Активные соединения Subscription Key - + Ключ для подключения Save VPN key as a file - + Сохранить VPN-ключ в файл Configuration Files - + Файлы конфигурации Active Devices - + Активные устройства @@ -2045,7 +2045,7 @@ Already installed containers were found on the server. All installed containers Email - + Email @@ -2394,7 +2394,7 @@ Already installed containers were found on the server. All installed containers Cannot change KillSwitch settings during active connection - + Невозможно изменить настройки KillSwitch во время активного подключения Cannot change killSwitch settings during active connection @@ -2561,12 +2561,12 @@ Already installed containers were found on the server. All installed containers Client logs - + Логи приложения AmneziaVPN logs - + AmneziaVPN logs @@ -2581,12 +2581,12 @@ Already installed containers were found on the server. All installed containers Service logs - + Логи службы AmneziaVPN-service logs - + AmneziaVPN-service logs @@ -3071,7 +3071,7 @@ It's okay as long as it's from someone you trust. Support tag - + Support tag @@ -3659,7 +3659,7 @@ and will not be shared or disclosed to the Amnezia or any third parties Allowed IPs: %1 - + Разрешенные подсети: %1 Creation date: @@ -4114,22 +4114,22 @@ and will not be shared or disclosed to the Amnezia or any third parties The sudo package is not pre-installed on the server - + Пакет sudo не установлен на сервере по умолчанию The server user's home directory is not accessible - + Домашний каталог пользователя сервера недоступен Action not allowed in sudoers - + Действие не разрешено в sudoers The user's password is required - + Требуется пароль пользователя @@ -4328,7 +4328,7 @@ and will not be shared or disclosed to the Amnezia or any third parties Missing AGW public key - + Отсутствует публичный ключ AGW @@ -4338,7 +4338,7 @@ and will not be shared or disclosed to the Amnezia or any third parties Missing list of available services - + Отсутствует список доступных сервисов From 68db72108978a81bca3efd6beb0a93588066c8e9 Mon Sep 17 00:00:00 2001 From: DarthSidious007 Date: Wed, 16 Apr 2025 05:35:53 +0300 Subject: [PATCH 18/65] add S3 deploy (#1530) --- .github/workflows/tag-upload.yml | 61 ++++++++++---------------------- deploy/deploy_s3.sh | 38 ++++++++++++++++++++ 2 files changed, 57 insertions(+), 42 deletions(-) create mode 100755 deploy/deploy_s3.sh diff --git a/.github/workflows/tag-upload.yml b/.github/workflows/tag-upload.yml index 22629ed3..9ac2da58 100644 --- a/.github/workflows/tag-upload.yml +++ b/.github/workflows/tag-upload.yml @@ -1,64 +1,41 @@ name: 'Upload a new version' on: - push: - tags: - - '[0-9]+.[0-9]+.[0-9]+.[0-9]+' + workflow_dispatch: + inputs: + RELEASE_VERSION: + description: 'Release version (e.g. 1.2.3.4)' + required: true + type: string jobs: - upload: + Upload-S3: runs-on: ubuntu-latest - name: upload steps: - - name: Checkout CMakeLists.txt + - name: Checkout uses: actions/checkout@v4 with: - ref: ${{ github.ref_name }} + ref: ${{ inputs.RELEASE_VERSION }} sparse-checkout: | CMakeLists.txt + deploy/deploy_s3.sh sparse-checkout-cone-mode: false - name: Verify git tag run: | - GIT_TAG=${{ github.ref_name }} + TAG_NAME=${{ inputs.RELEASE_VERSION }} CMAKE_TAG=$(grep 'project.*VERSION' CMakeLists.txt | sed -E 's/.* ([0-9]+.[0-9]+.[0-9]+.[0-9]+)$/\1/') - - if [[ "$GIT_TAG" == "$CMAKE_TAG" ]]; then - echo "Git tag ($GIT_TAG) and version in CMakeLists.txt ($CMAKE_TAG) are the same. Continuing..." + if [[ "$TAG_NAME" == "$CMAKE_TAG" ]]; then + echo "Git tag ($TAG_NAME) matches CMakeLists.txt version ($CMAKE_TAG)." else - echo "Git tag ($GIT_TAG) and version in CMakeLists.txt ($CMAKE_TAG) are not the same! Cancelling..." + echo "::error::Mismatch: Git tag ($TAG_NAME) != CMakeLists.txt version ($CMAKE_TAG). Exiting with error..." exit 1 fi - - name: Download artifacts from the "${{ github.ref_name }}" tag - uses: robinraju/release-downloader@v1.8 + - name: Setup Rclone + uses: AnimMouse/setup-rclone@v1 with: - tag: ${{ github.ref_name }} - fileName: "AmneziaVPN_(Linux_|)${{ github.ref_name }}*" - out-file-path: ${{ github.ref_name }} + rclone_config: ${{ secrets.RCLONE_CONFIG }} - - name: Upload beta version - uses: jakejarvis/s3-sync-action@master - if: contains(github.event.base_ref, 'dev') - with: - args: --include "AmneziaVPN*" --delete - env: - AWS_S3_BUCKET: updates - AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }} - AWS_S3_ENDPOINT: https://${{ vars.CF_ACCOUNT_ID }}.r2.cloudflarestorage.com - SOURCE_DIR: ${{ github.ref_name }} - DEST_DIR: beta/${{ github.ref_name }} - - - name: Upload stable version - uses: jakejarvis/s3-sync-action@master - if: contains(github.event.base_ref, 'master') - with: - args: --include "AmneziaVPN*" --delete - env: - AWS_S3_BUCKET: updates - AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }} - AWS_S3_ENDPOINT: https://${{ vars.CF_ACCOUNT_ID }}.r2.cloudflarestorage.com - SOURCE_DIR: ${{ github.ref_name }} - DEST_DIR: stable/${{ github.ref_name }} + - name: Send dist to S3 + run: bash deploy/deploy_s3.sh ${{ inputs.RELEASE_VERSION }} diff --git a/deploy/deploy_s3.sh b/deploy/deploy_s3.sh new file mode 100755 index 00000000..c109a286 --- /dev/null +++ b/deploy/deploy_s3.sh @@ -0,0 +1,38 @@ +#!/bin/sh +set -e + +VERSION=$1 + +if [[ $VERSION = '' ]]; then + echo '::error::VERSION does not set. Exiting with error...' + exit 1 +fi + +mkdir -p dist + +cd dist + +echo $VERSION >> VERSION +curl -s https://api.github.com/repos/amnezia-vpn/amnezia-client/releases/tags/$VERSION | jq -r .body | tr -d '\r' > CHANGELOG + +if [[ $(cat CHANGELOG) = null ]]; then + echo '::error::Release does not exists. Exiting with error...' + exit 1 +fi + +wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android8+_arm64-v8a.apk +wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android8+_armeabi-v7a.apk +wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android8+_x86.apk +wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android8+_x86_64.apk +wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_arm64-v8a.apk +wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_armeabi-v7a.apk +wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_x86.apk +wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_x86_64.apk +wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_linux.tar.zip +wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_macos.dmg +wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_macos_old.dmg +wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_x64.exe + +cd ../ + +rclone sync ./dist/ r2:/updates/ From 7fd71a8408c881fc50d5d39a318378ab25097098 Mon Sep 17 00:00:00 2001 From: Nethius Date: Wed, 16 Apr 2025 09:58:44 +0700 Subject: [PATCH 19/65] feature: retrieving support info from gateway (#1483) * feature: retrieving support info from gateway * feature: added "external-premium" service-type * chore: fixed external premium visability --- client/core/api/apiDefs.h | 10 ++++- client/core/api/apiUtils.cpp | 15 ++++++-- client/core/api/apiUtils.h | 2 + .../controllers/api/apiConfigsController.cpp | 4 +- .../controllers/api/apiSettingsController.cpp | 2 +- client/ui/models/api/apiAccountInfoModel.cpp | 37 +++++++++++++++---- client/ui/models/api/apiAccountInfoModel.h | 6 +++ .../ui/qml/Pages2/PageSettingsApiSupport.qml | 12 +++--- 8 files changed, 67 insertions(+), 21 deletions(-) diff --git a/client/core/api/apiDefs.h b/client/core/api/apiDefs.h index 41dd80ba..d1a92d9d 100644 --- a/client/core/api/apiDefs.h +++ b/client/core/api/apiDefs.h @@ -10,7 +10,8 @@ namespace apiDefs AmneziaFreeV3, AmneziaPremiumV1, AmneziaPremiumV2, - SelfHosted + SelfHosted, + ExternalPremium }; enum ConfigSource { @@ -43,6 +44,13 @@ namespace apiDefs constexpr QLatin1String maxDeviceCount("max_device_count"); constexpr QLatin1String subscriptionEndDate("subscription_end_date"); constexpr QLatin1String issuedConfigs("issued_configs"); + + constexpr QLatin1String supportInfo("support_info"); + constexpr QLatin1String email("email"); + constexpr QLatin1String billingEmail("billing_email"); + constexpr QLatin1String website("website"); + constexpr QLatin1String websiteName("website_name"); + constexpr QLatin1String telegram("telegram"); } const int requestTimeoutMsecs = 12 * 1000; // 12 secs diff --git a/client/core/api/apiUtils.cpp b/client/core/api/apiUtils.cpp index be19a166..f5f575c5 100644 --- a/client/core/api/apiUtils.cpp +++ b/client/core/api/apiUtils.cpp @@ -32,15 +32,17 @@ apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObjec constexpr QLatin1String servicePremium("amnezia-premium"); constexpr QLatin1String serviceFree("amnezia-free"); + constexpr QLatin1String serviceExternalPremium("external-premium"); auto apiConfigObject = serverConfigObject.value(apiDefs::key::apiConfig).toObject(); - auto stackType = apiConfigObject.value(apiDefs::key::stackType).toString(); auto serviceType = apiConfigObject.value(apiDefs::key::serviceType).toString(); - if (serviceType == servicePremium || stackType == stackPremium) { + if (serviceType == servicePremium) { return apiDefs::ConfigType::AmneziaPremiumV2; - } else if (serviceType == serviceFree || stackType == stackFree) { + } else if (serviceType == serviceFree) { return apiDefs::ConfigType::AmneziaFreeV3; + } else if (serviceType == serviceExternalPremium) { + return apiDefs::ConfigType::ExternalPremium; } } default: { @@ -86,3 +88,10 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList &ssl qDebug() << "something went wrong"; return amnezia::ErrorCode::InternalError; } + +bool apiUtils::isPremiumServer(const QJsonObject &serverConfigObject) +{ + static const QSet premiumTypes = { apiDefs::ConfigType::AmneziaPremiumV1, apiDefs::ConfigType::AmneziaPremiumV2, + apiDefs::ConfigType::ExternalPremium }; + return premiumTypes.contains(getConfigType(serverConfigObject)); +} diff --git a/client/core/api/apiUtils.h b/client/core/api/apiUtils.h index 82ac315b..47006e80 100644 --- a/client/core/api/apiUtils.h +++ b/client/core/api/apiUtils.h @@ -13,6 +13,8 @@ namespace apiUtils bool isSubscriptionExpired(const QString &subscriptionEndDate); + bool isPremiumServer(const QJsonObject &serverConfigObject); + apiDefs::ConfigType getConfigType(const QJsonObject &serverConfigObject); apiDefs::ConfigSource getConfigSource(const QJsonObject &serverConfigObject); diff --git a/client/ui/controllers/api/apiConfigsController.cpp b/client/ui/controllers/api/apiConfigsController.cpp index 00e6ae3d..74e22a85 100644 --- a/client/ui/controllers/api/apiConfigsController.cpp +++ b/client/ui/controllers/api/apiConfigsController.cpp @@ -310,7 +310,7 @@ bool ApiConfigsController::deactivateDevice() auto serverConfigObject = m_serversModel->getServerConfig(serverIndex); auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject(); - if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV2) { + if (!apiUtils::isPremiumServer(serverConfigObject)) { return true; } @@ -345,7 +345,7 @@ bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const Q auto serverConfigObject = m_serversModel->getServerConfig(serverIndex); auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject(); - if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV2) { + if (!apiUtils::isPremiumServer(serverConfigObject)) { return true; } diff --git a/client/ui/controllers/api/apiSettingsController.cpp b/client/ui/controllers/api/apiSettingsController.cpp index 737bfd1a..8927312d 100644 --- a/client/ui/controllers/api/apiSettingsController.cpp +++ b/client/ui/controllers/api/apiSettingsController.cpp @@ -62,7 +62,7 @@ bool ApiSettingsController::getAccountInfo(bool reload) QByteArray responseBody; - if (apiUtils::getConfigType(serverConfig) == apiDefs::ConfigType::AmneziaPremiumV2) { + if (apiUtils::isPremiumServer(serverConfig)) { ErrorCode errorCode = gatewayController.post(QString("%1v1/account_info"), apiPayload, responseBody); if (errorCode != ErrorCode::NoError) { emit errorOccurred(errorCode); diff --git a/client/ui/models/api/apiAccountInfoModel.cpp b/client/ui/models/api/apiAccountInfoModel.cpp index 191582a5..fdd4e2ca 100644 --- a/client/ui/models/api/apiAccountInfoModel.cpp +++ b/client/ui/models/api/apiAccountInfoModel.cpp @@ -48,15 +48,19 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const } case ServiceDescriptionRole: { if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2) { - return tr("Classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. " + return tr("Classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online " + "resources. " "Speeds up to 200 Mbps"); } else if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) { return tr("Free unlimited access to a basic set of websites such as Facebook, Instagram, Twitter (X), Discord, Telegram and " "more. YouTube is not included in the free plan."); + } else { + return ""; } } case IsComponentVisibleRole: { - return m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2; + return m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2 + || m_accountInfoData.configType == apiDefs::ConfigType::ExternalPremium; } case HasExpiredWorkerRole: { for (int i = 0; i < m_issuedConfigsInfo.size(); i++) { @@ -93,6 +97,8 @@ void ApiAccountInfoModel::updateModel(const QJsonObject &accountInfoObject, cons m_accountInfoData = accountInfoData; + m_supportInfo = accountInfoObject.value(apiDefs::key::supportInfo).toObject(); + endResetModel(); } @@ -121,12 +127,27 @@ QJsonArray ApiAccountInfoModel::getIssuedConfigsInfo() QString ApiAccountInfoModel::getTelegramBotLink() { - if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) { - return tr("amnezia_free_support_bot"); - } else if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2) { - return tr("amnezia_premium_support_bot"); - } - return ""; + return m_supportInfo.value(apiDefs::key::telegram).toString(); +} + +QString ApiAccountInfoModel::getEmailLink() +{ + return m_supportInfo.value(apiDefs::key::email).toString(); +} + +QString ApiAccountInfoModel::getBillingEmailLink() +{ + return m_supportInfo.value(apiDefs::key::billingEmail).toString(); +} + +QString ApiAccountInfoModel::getSiteLink() +{ + return m_supportInfo.value(apiDefs::key::websiteName).toString(); +} + +QString ApiAccountInfoModel::getFullSiteLink() +{ + return m_supportInfo.value(apiDefs::key::website).toString(); } QHash ApiAccountInfoModel::roleNames() const diff --git a/client/ui/models/api/apiAccountInfoModel.h b/client/ui/models/api/apiAccountInfoModel.h index 44eb7ee6..ead92488 100644 --- a/client/ui/models/api/apiAccountInfoModel.h +++ b/client/ui/models/api/apiAccountInfoModel.h @@ -33,7 +33,12 @@ public slots: QJsonArray getAvailableCountries(); QJsonArray getIssuedConfigsInfo(); + QString getTelegramBotLink(); + QString getEmailLink(); + QString getBillingEmailLink(); + QString getSiteLink(); + QString getFullSiteLink(); protected: QHash roleNames() const override; @@ -51,6 +56,7 @@ private: AccountInfoData m_accountInfoData; QJsonArray m_availableCountries; QJsonArray m_issuedConfigsInfo; + QJsonObject m_supportInfo; }; #endif // APIACCOUNTINFOMODEL_H diff --git a/client/ui/qml/Pages2/PageSettingsApiSupport.qml b/client/ui/qml/Pages2/PageSettingsApiSupport.qml index 2ca13151..0ea8ec84 100644 --- a/client/ui/qml/Pages2/PageSettingsApiSupport.qml +++ b/client/ui/qml/Pages2/PageSettingsApiSupport.qml @@ -28,24 +28,24 @@ PageType { id: techSupport readonly property string title: qsTr("Email") - readonly property string description: qsTr("support@amnezia.org") - readonly property string link: "mailto:support@amnezia.org" + readonly property string description: ApiAccountInfoModel.getEmailLink() + readonly property string link: "mailto:" + ApiAccountInfoModel.getEmailLink() } QtObject { id: paymentSupport readonly property string title: qsTr("Email Billing & Orders") - readonly property string description: qsTr("help@vpnpay.io") - readonly property string link: "mailto:help@vpnpay.io" + readonly property string description: ApiAccountInfoModel.getBillingEmailLink() + readonly property string link: "mailto:" + ApiAccountInfoModel.getBillingEmailLink() } QtObject { id: site readonly property string title: qsTr("Website") - readonly property string description: qsTr("amnezia.org") - readonly property string link: LanguageModel.getCurrentSiteUrl() + readonly property string description: ApiAccountInfoModel.getSiteLink() + readonly property string link: ApiAccountInfoModel.getFullSiteLink() } property list supportModel: [ From c44ce0d77cc3acdf1de48a12459a1a821d404a1c Mon Sep 17 00:00:00 2001 From: Mikhail Kiselev <73298492+sund3RRR@users.noreply.github.com> Date: Sat, 19 Apr 2025 19:21:10 +0300 Subject: [PATCH 20/65] fix: add missing include (#1541) --- client/daemon/interfaceconfig.h | 1 + 1 file changed, 1 insertion(+) diff --git a/client/daemon/interfaceconfig.h b/client/daemon/interfaceconfig.h index 6a816f87..4c93e740 100644 --- a/client/daemon/interfaceconfig.h +++ b/client/daemon/interfaceconfig.h @@ -6,6 +6,7 @@ #define INTERFACECONFIG_H #include +#include #include #include "ipaddress.h" From 7169480999a9d16074047f6755b8f7cba689bcd0 Mon Sep 17 00:00:00 2001 From: lunardunno <126363523+lunardunno@users.noreply.github.com> Date: Wed, 23 Apr 2025 09:12:23 +0400 Subject: [PATCH 21/65] feature: error handling for cgroup (#1486) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Error for cgroup mountpoint Added handling of message: cgroup mountpoint does not exist. * Case for error cgroup Added case and case description for: Cgroup Mountpoint Does Not Exist * Case for Runc Added error handling for Runc, which does not work in cgroup v2. Changed numbering of new errors. * stdErr handling fot run_container Enabling stdErr handling fot run_container.sh * change for stdErr handling * Another place to handle the error 211 Another place to handle the error: ServerRuncNotWorkOnCgroupsV2 * test_1 * test 2 * test 3 * Moving error handling Moving error handling to the right place in the controller. * Polishing * Еext correction Сorrection of description text. --- client/core/controllers/serverController.cpp | 17 ++++++++++++----- client/core/defs.h | 2 ++ client/core/errorstrings.cpp | 2 ++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/client/core/controllers/serverController.cpp b/client/core/controllers/serverController.cpp index d8c94f4d..9ac62759 100644 --- a/client/core/controllers/serverController.cpp +++ b/client/core/controllers/serverController.cpp @@ -439,15 +439,22 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden stdOut += data + "\n"; return ErrorCode::NoError; }; + auto cbReadStdErr = [&](const QString &data, libssh::Client &) { + stdOut += data + "\n"; + return ErrorCode::NoError; + }; - errorCode = + ErrorCode error = runScript(credentials, replaceVars(amnezia::scriptData(SharedScriptType::build_container), genVarsForScript(credentials, container, config)), - cbReadStdOut); - if (errorCode) - return errorCode; + 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; - return errorCode; + return error; } ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config) diff --git a/client/core/defs.h b/client/core/defs.h index 2e683314..eff3df3b 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -58,6 +58,8 @@ namespace amnezia ServerUserDirectoryNotAccessible = 208, ServerUserNotAllowedInSudoers = 209, ServerUserPasswordRequired = 210, + ServerDockerOnCgroupsV2 = 211, + ServerCgroupMountpoint = 212, // Ssh connection errors SshRequestDeniedError = 300, diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp index 9dcd8065..6abab0e0 100644 --- a/client/core/errorstrings.cpp +++ b/client/core/errorstrings.cpp @@ -26,6 +26,8 @@ QString errorString(ErrorCode code) { case(ErrorCode::ServerUserDirectoryNotAccessible): errorMessage = QObject::tr("The server user's home directory is not accessible"); break; case(ErrorCode::ServerUserNotAllowedInSudoers): errorMessage = QObject::tr("Action not allowed in sudoers"); break; 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; // Libssh errors case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break; From 94fa5b59f3fadb20ea30eeb20f5982362467c414 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Fri, 2 May 2025 23:51:49 -0700 Subject: [PATCH 22/65] bugfix: awg/wg protocol with system disabled IPv6 (#1536) * fix: AWG/WG protocol with system disabled IPv6 * add check for route prefix type * fix: ignore IPv6 setup error for Linux This error can be cased by system disabled IPv6 --- client/platforms/linux/daemon/iputilslinux.cpp | 4 +++- .../platforms/windows/daemon/wireguardutilswindows.cpp | 9 +++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/client/platforms/linux/daemon/iputilslinux.cpp b/client/platforms/linux/daemon/iputilslinux.cpp index f0f2fbab..63bd92f9 100644 --- a/client/platforms/linux/daemon/iputilslinux.cpp +++ b/client/platforms/linux/daemon/iputilslinux.cpp @@ -31,7 +31,9 @@ IPUtilsLinux::~IPUtilsLinux() { } bool IPUtilsLinux::addInterfaceIPs(const InterfaceConfig& config) { - return addIP4AddressToDevice(config) && addIP6AddressToDevice(config); + bool ret = addIP4AddressToDevice(config); + addIP6AddressToDevice(config); + return ret; } bool IPUtilsLinux::setMTUAndUp(const InterfaceConfig& config) { diff --git a/client/platforms/windows/daemon/wireguardutilswindows.cpp b/client/platforms/windows/daemon/wireguardutilswindows.cpp index 0823b9d7..d01ef54a 100644 --- a/client/platforms/windows/daemon/wireguardutilswindows.cpp +++ b/client/platforms/windows/daemon/wireguardutilswindows.cpp @@ -14,8 +14,6 @@ #include "leakdetector.h" #include "logger.h" -#include "platforms/windows/windowscommons.h" -#include "windowsdaemon.h" #include "windowsfirewall.h" #pragma comment(lib, "iphlpapi.lib") @@ -269,6 +267,13 @@ bool WireguardUtilsWindows::updateRoutePrefix(const IPAddress& prefix) { if (result == ERROR_OBJECT_ALREADY_EXISTS) { return true; } + + // Case for ipv6 route with disabled ipv6 + if (prefix.address().protocol() == QAbstractSocket::IPv6Protocol + && result == ERROR_NOT_FOUND) { + return true; + } + if (result != NO_ERROR) { logger.error() << "Failed to create route to" << prefix.toString() From 5bd88ac2e909c3c850d5ee76af0d84622ce47d0f Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Fri, 2 May 2025 23:52:59 -0700 Subject: [PATCH 23/65] bugfix: check IPv6 support before IPv6 setup for OpenVPN (#1552) --- client/configurators/openvpn_configurator.cpp | 9 ++++++--- client/core/networkUtilities.cpp | 12 ++++++++++++ client/core/networkUtilities.h | 2 +- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/client/configurators/openvpn_configurator.cpp b/client/configurators/openvpn_configurator.cpp index fafb7c2b..eca81afd 100644 --- a/client/configurators/openvpn_configurator.cpp +++ b/client/configurators/openvpn_configurator.cpp @@ -13,10 +13,10 @@ #include #endif +#include "core/networkUtilities.h" #include "containers/containers_defs.h" #include "core/controllers/serverController.h" #include "core/scripts_registry.h" -#include "core/server_defs.h" #include "settings.h" #include "utilities.h" @@ -24,6 +24,7 @@ #include #include + OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr settings, const QSharedPointer &serverController, QObject *parent) : ConfiguratorBase(settings, serverController, parent) @@ -122,12 +123,14 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPairrouteMode() == Settings::VpnOnlyForwardSites) { - // no redirect-gateway + // no redirect-gateway } else if (m_settings->routeMode() == Settings::VpnAllExceptSites) { #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n"); diff --git a/client/core/networkUtilities.cpp b/client/core/networkUtilities.cpp index a5825f0d..cf33fa55 100644 --- a/client/core/networkUtilities.cpp +++ b/client/core/networkUtilities.cpp @@ -12,6 +12,7 @@ #include #include #include "qendian.h" + #include #endif #ifdef Q_OS_LINUX #include @@ -185,6 +186,17 @@ int NetworkUtilities::AdapterIndexTo(const QHostAddress& dst) { return 0; } +bool NetworkUtilities::checkIpv6Enabled() { +#ifdef Q_OS_WIN + QSettings RegHLM("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters", + QSettings::NativeFormat); + int ret = RegHLM.value("DisabledComponents", 0).toInt(); + qDebug() << "Check for Windows disabled IPv6 return " << ret; + return (ret != 255); +#endif + return true; +} + #ifdef Q_OS_WIN DWORD GetAdaptersAddressesWrapper(const ULONG Family, const ULONG Flags, diff --git a/client/core/networkUtilities.h b/client/core/networkUtilities.h index 3b64b547..1bd1114c 100644 --- a/client/core/networkUtilities.h +++ b/client/core/networkUtilities.h @@ -16,6 +16,7 @@ public: static QString getStringBetween(const QString &s, const QString &a, const QString &b); static bool checkIPv4Format(const QString &ip); static bool checkIpSubnetFormat(const QString &ip); + static bool checkIpv6Enabled(); static QString getGatewayAndIface(); // Returns the Interface Index that could Route to dst static int AdapterIndexTo(const QHostAddress& dst); @@ -29,7 +30,6 @@ public: static QString netMaskFromIpWithSubnet(const QString ip); static QString ipAddressFromIpWithSubnet(const QString ip); - static QStringList summarizeRoutes(const QStringList &ips, const QString cidr); }; From f6d7552b589cb2ad7d6116a952b2d1bd060fe5bb Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Fri, 2 May 2025 23:54:36 -0700 Subject: [PATCH 24/65] feature: fillswitch strict mode (#1333) * Add allowed DNS list for killswitch * Windows killswitch strict mode backend part * Killswitch strict mode for Linux and MacOS * Windows fixes * feature: Add Kill Switch settings page with strict mode option * fix windows build after merge * Refresh killswitch mode when it toggled * Use HLM to store strictMode flag * Some Linux updates * feat: Enhance VerticalRadioButton with improved styling and disabled states * Refresh killSwitch state update * Fix build * refactor: Modularize header components * Change kill switch radio button styling * Fix strict kill switch mode handling * Refactor: Replace HeaderType with new Types for headers in QML pages * Remove deprecated HeaderType QML component * Refresh strict mode killswitch after global toggle change * Implement model, controller and UI for killswitch dns exceptions * Connect backend part and UI * Change label text to DNS exceptions * Remove HeaderType from PageSettingsApiDevices * Some pretty fixes * Fix problem with definition sequence of PageSettingsKillSwitchExceptions.pml elements * Add exclusion method for Windows firewall * Change ubuntu version in deploy script * Update ubuntu version in GH actions * Add confirmation popup for strict killswitch mode * Add qt standard path for build script * Add method to killswitch for expanding strickt mode exceptions list and fix allowTrafficTo() for Windows. Also Added cache in KillSwitch class for exceptions * Add insertion of gateway address to strict killswitch exceptions * Review fixes * buildfix and naming --------- Co-authored-by: aiamnezia --- client/core/controllers/coreController.cpp | 13 + client/core/controllers/coreController.h | 5 + client/core/controllers/gatewayController.cpp | 28 ++ client/daemon/daemon.cpp | 3 + client/daemon/interfaceconfig.cpp | 7 + client/daemon/interfaceconfig.h | 1 + client/mozilla/localsocketcontroller.cpp | 3 + .../platforms/linux/daemon/linuxfirewall.cpp | 3 - .../linux/daemon/wireguardutilslinux.cpp | 4 +- .../macos/daemon/wireguardutilsmacos.cpp | 4 +- .../windows/daemon/windowsfirewall.cpp | 122 ++++-- .../windows/daemon/windowsfirewall.h | 2 + client/protocols/openvpnprotocol.cpp | 5 + client/protocols/protocols_defs.h | 2 + client/resources.qrc | 7 +- client/secure_qsettings.cpp | 4 +- client/secure_qsettings.h | 2 +- client/settings.cpp | 20 + client/settings.h | 7 + .../ui/controllers/allowedDnsController.cpp | 101 +++++ client/ui/controllers/allowedDnsController.h | 35 ++ client/ui/controllers/pageController.h | 4 +- client/ui/controllers/settingsController.cpp | 17 + client/ui/controllers/settingsController.h | 7 + client/ui/models/allowed_dns_model.cpp | 86 +++++ client/ui/models/allowed_dns_model.h | 37 ++ client/ui/qml/Components/AddSitePanel.qml | 73 ++++ client/ui/qml/Controls2/BaseHeaderType.qml | 45 +++ client/ui/qml/Controls2/HeaderType.qml | 86 ----- .../ui/qml/Controls2/HeaderTypeWithButton.qml | 44 +++ .../qml/Controls2/HeaderTypeWithSwitcher.qml | 28 ++ .../ui/qml/Controls2/VerticalRadioButton.qml | 29 +- client/ui/qml/Pages2/PageDeinstalling.qml | 2 +- client/ui/qml/Pages2/PageDevMenu.qml | 2 +- .../Pages2/PageProtocolAwgClientSettings.qml | 2 +- .../ui/qml/Pages2/PageProtocolAwgSettings.qml | 2 +- .../qml/Pages2/PageProtocolCloakSettings.qml | 2 +- .../Pages2/PageProtocolOpenVpnSettings.qml | 2 +- client/ui/qml/Pages2/PageProtocolRaw.qml | 2 +- .../PageProtocolShadowSocksSettings.qml | 2 +- .../PageProtocolWireGuardClientSettings.qml | 2 +- .../Pages2/PageProtocolWireGuardSettings.qml | 2 +- .../qml/Pages2/PageProtocolXraySettings.qml | 2 +- .../ui/qml/Pages2/PageServiceDnsSettings.qml | 2 +- .../ui/qml/Pages2/PageServiceSftpSettings.qml | 2 +- .../Pages2/PageServiceSocksProxySettings.qml | 4 +- .../Pages2/PageServiceTorWebsiteSettings.qml | 2 +- client/ui/qml/Pages2/PageSettings.qml | 2 +- .../PageSettingsApiAvailableCountries.qml | 2 +- .../ui/qml/Pages2/PageSettingsApiDevices.qml | 2 +- .../Pages2/PageSettingsApiInstructions.qml | 2 +- .../Pages2/PageSettingsApiNativeConfigs.qml | 2 +- .../qml/Pages2/PageSettingsApiServerInfo.qml | 2 +- .../ui/qml/Pages2/PageSettingsApiSupport.qml | 2 +- .../Pages2/PageSettingsAppSplitTunneling.qml | 31 +- .../ui/qml/Pages2/PageSettingsApplication.qml | 2 +- client/ui/qml/Pages2/PageSettingsBackup.qml | 2 +- .../ui/qml/Pages2/PageSettingsConnection.qml | 27 +- client/ui/qml/Pages2/PageSettingsDns.qml | 2 +- .../ui/qml/Pages2/PageSettingsKillSwitch.qml | 123 ++++++ .../PageSettingsKillSwitchExceptions.qml | 302 +++++++++++++++ client/ui/qml/Pages2/PageSettingsLogging.qml | 2 +- .../ui/qml/Pages2/PageSettingsServerInfo.qml | 2 +- .../qml/Pages2/PageSettingsServerProtocol.qml | 2 +- .../ui/qml/Pages2/PageSettingsServersList.qml | 2 +- .../qml/Pages2/PageSettingsSplitTunneling.qml | 37 +- .../Pages2/PageSetupWizardApiServiceInfo.qml | 2 +- .../Pages2/PageSetupWizardApiServicesList.qml | 2 +- .../Pages2/PageSetupWizardConfigSource.qml | 4 +- .../qml/Pages2/PageSetupWizardCredentials.qml | 2 +- client/ui/qml/Pages2/PageSetupWizardEasy.qml | 2 +- .../qml/Pages2/PageSetupWizardInstalling.qml | 2 +- .../PageSetupWizardProtocolSettings.qml | 2 +- .../qml/Pages2/PageSetupWizardProtocols.qml | 2 +- .../ui/qml/Pages2/PageSetupWizardTextKey.qml | 2 +- .../qml/Pages2/PageSetupWizardViewConfig.qml | 2 +- client/ui/qml/Pages2/PageShare.qml | 2 +- client/ui/qml/Pages2/PageShareFullAccess.qml | 2 +- client/vpnconnection.cpp | 23 ++ client/vpnconnection.h | 4 +- deploy/build_linux.sh | 6 +- ipc/ipc_interface.rep | 4 + ipc/ipcserver.cpp | 196 ++-------- ipc/ipcserver.h | 4 + service/server/CMakeLists.txt | 63 +++ service/server/killswitch.cpp | 358 ++++++++++++++++++ service/server/killswitch.h | 31 ++ service/server/localserver.cpp | 7 +- 88 files changed, 1718 insertions(+), 418 deletions(-) create mode 100644 client/ui/controllers/allowedDnsController.cpp create mode 100644 client/ui/controllers/allowedDnsController.h create mode 100644 client/ui/models/allowed_dns_model.cpp create mode 100644 client/ui/models/allowed_dns_model.h create mode 100644 client/ui/qml/Components/AddSitePanel.qml create mode 100644 client/ui/qml/Controls2/BaseHeaderType.qml delete mode 100644 client/ui/qml/Controls2/HeaderType.qml create mode 100644 client/ui/qml/Controls2/HeaderTypeWithButton.qml create mode 100644 client/ui/qml/Controls2/HeaderTypeWithSwitcher.qml create mode 100644 client/ui/qml/Pages2/PageSettingsKillSwitch.qml create mode 100644 client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml create mode 100644 service/server/killswitch.cpp create mode 100644 service/server/killswitch.h diff --git a/client/core/controllers/coreController.cpp b/client/core/controllers/coreController.cpp index d8f199e9..f2c09d45 100644 --- a/client/core/controllers/coreController.cpp +++ b/client/core/controllers/coreController.cpp @@ -48,6 +48,9 @@ void CoreController::initModels() m_sitesModel.reset(new SitesModel(m_settings, this)); m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get()); + m_allowedDnsModel.reset(new AllowedDnsModel(m_settings, this)); + m_engine->rootContext()->setContextProperty("AllowedDnsModel", m_allowedDnsModel.get()); + m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this)); m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get()); @@ -130,6 +133,9 @@ void CoreController::initControllers() m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel)); m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get()); + m_allowedDnsController.reset(new AllowedDnsController(m_settings, m_allowedDnsModel)); + m_engine->rootContext()->setContextProperty("AllowedDnsController", m_allowedDnsController.get()); + m_appSplitTunnelingController.reset(new AppSplitTunnelingController(m_settings, m_appSplitTunnelingModel)); m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get()); @@ -214,6 +220,7 @@ void CoreController::initSignalHandlers() initAutoConnectHandler(); initAmneziaDnsToggledHandler(); initPrepareConfigHandler(); + initStrictKillSwitchHandler(); } void CoreController::initNotificationHandler() @@ -356,6 +363,12 @@ void CoreController::initPrepareConfigHandler() }); } +void CoreController::initStrictKillSwitchHandler() +{ + connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged, + m_vpnConnection.get(), &VpnConnection::onKillSwitchModeChanged); +} + QSharedPointer CoreController::pageController() const { return m_pageController; diff --git a/client/core/controllers/coreController.h b/client/core/controllers/coreController.h index 700504af..6342d738 100644 --- a/client/core/controllers/coreController.h +++ b/client/core/controllers/coreController.h @@ -8,6 +8,7 @@ #include "ui/controllers/api/apiConfigsController.h" #include "ui/controllers/api/apiSettingsController.h" #include "ui/controllers/appSplitTunnelingController.h" +#include "ui/controllers/allowedDnsController.h" #include "ui/controllers/connectionController.h" #include "ui/controllers/exportController.h" #include "ui/controllers/focusController.h" @@ -18,6 +19,7 @@ #include "ui/controllers/sitesController.h" #include "ui/controllers/systemController.h" +#include "ui/models/allowed_dns_model.h" #include "ui/models/containers_model.h" #include "ui/models/languageModel.h" #include "ui/models/protocols/cloakConfigModel.h" @@ -80,6 +82,7 @@ private: void initAutoConnectHandler(); void initAmneziaDnsToggledHandler(); void initPrepareConfigHandler(); + void initStrictKillSwitchHandler(); QQmlApplicationEngine *m_engine {}; // TODO use parent child system here? std::shared_ptr m_settings; @@ -102,6 +105,7 @@ private: QScopedPointer m_sitesController; QScopedPointer m_systemController; QScopedPointer m_appSplitTunnelingController; + QScopedPointer m_allowedDnsController; QScopedPointer m_apiSettingsController; QScopedPointer m_apiConfigsController; @@ -112,6 +116,7 @@ private: QSharedPointer m_languageModel; QSharedPointer m_protocolsModel; QSharedPointer m_sitesModel; + QSharedPointer m_allowedDnsModel; QSharedPointer m_appSplitTunnelingModel; QSharedPointer m_clientManagementModel; diff --git a/client/core/controllers/gatewayController.cpp b/client/core/controllers/gatewayController.cpp index f8c23c1a..0d86b9d5 100644 --- a/client/core/controllers/gatewayController.cpp +++ b/client/core/controllers/gatewayController.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "QBlockCipher.h" #include "QRsa.h" @@ -14,6 +15,11 @@ #include "amnezia_application.h" #include "core/api/apiUtils.h" #include "utilities.h" +#include "core/networkUtilities.h" + +#ifdef AMNEZIA_DESKTOP + #include "core/ipcclient.h" +#endif namespace { @@ -50,6 +56,17 @@ ErrorCode GatewayController::get(const QString &endpoint, QByteArray &responseBo request.setUrl(QString(endpoint).arg(m_gatewayEndpoint)); + // bypass killSwitch exceptions for API-gateway +#ifdef AMNEZIA_DESKTOP + { + QString host = QUrl(request.url()).host(); + QString ip = NetworkUtilities::getIPAddress(host); + if (!ip.isEmpty()) { + IpcClient::Interface()->addKillSwitchAllowedRange(QStringList{ip}); + } + } +#endif + QNetworkReply *reply; reply = amnApp->networkManager()->get(request); @@ -101,6 +118,17 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api request.setUrl(endpoint.arg(m_gatewayEndpoint)); + // bypass killSwitch exceptions for API-gateway +#ifdef AMNEZIA_DESKTOP + { + QString host = QUrl(request.url()).host(); + QString ip = NetworkUtilities::getIPAddress(host); + if (!ip.isEmpty()) { + IpcClient::Interface()->addKillSwitchAllowedRange(QStringList{ip}); + } + } +#endif + QSimpleCrypto::QBlockCipher blockCipher; QByteArray key = blockCipher.generatePrivateSalt(32); QByteArray iv = blockCipher.generatePrivateSalt(32); diff --git a/client/daemon/daemon.cpp b/client/daemon/daemon.cpp index 081a7a90..e4b0ab3d 100644 --- a/client/daemon/daemon.cpp +++ b/client/daemon/daemon.cpp @@ -371,6 +371,9 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) { if (!parseStringList(obj, "vpnDisabledApps", config.m_vpnDisabledApps)) { return false; } + if (!parseStringList(obj, "allowedDnsServers", config.m_allowedDnsServers)) { + return false; + } config.m_killSwitchEnabled = QVariant(obj.value("killSwitchOption").toString()).toBool(); diff --git a/client/daemon/interfaceconfig.cpp b/client/daemon/interfaceconfig.cpp index b2ad31c6..f0adcc92 100644 --- a/client/daemon/interfaceconfig.cpp +++ b/client/daemon/interfaceconfig.cpp @@ -48,6 +48,13 @@ QJsonObject InterfaceConfig::toJson() const { } json.insert("excludedAddresses", jsExcludedAddresses); + + QJsonArray jsAllowedDnsServers; + for (const QString& i : m_allowedDnsServers) { + jsAllowedDnsServers.append(QJsonValue(i)); + } + json.insert("allowedDnsServers", jsAllowedDnsServers); + QJsonArray disabledApps; for (const QString& i : m_vpnDisabledApps) { disabledApps.append(QJsonValue(i)); diff --git a/client/daemon/interfaceconfig.h b/client/daemon/interfaceconfig.h index 4c93e740..ee43a253 100644 --- a/client/daemon/interfaceconfig.h +++ b/client/daemon/interfaceconfig.h @@ -38,6 +38,7 @@ class InterfaceConfig { QList m_allowedIPAddressRanges; QStringList m_excludedAddresses; QStringList m_vpnDisabledApps; + QStringList m_allowedDnsServers; bool m_killSwitchEnabled; #if defined(MZ_ANDROID) || defined(MZ_IOS) QString m_installationId; diff --git a/client/mozilla/localsocketcontroller.cpp b/client/mozilla/localsocketcontroller.cpp index 1081bcae..afa29c47 100644 --- a/client/mozilla/localsocketcontroller.cpp +++ b/client/mozilla/localsocketcontroller.cpp @@ -123,6 +123,7 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) { int appSplitTunnelType = rawConfig.value(amnezia::config_key::appSplitTunnelType).toInt(); QJsonArray splitTunnelApps = rawConfig.value(amnezia::config_key::splitTunnelApps).toArray(); + QJsonArray allowedDns = rawConfig.value(amnezia::config_key::allowedDnsServers).toArray(); QJsonObject wgConfig = rawConfig.value(protocolName + "_config_data").toObject(); @@ -226,6 +227,8 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) { json.insert("vpnDisabledApps", splitTunnelApps); + json.insert("allowedDnsServers", allowedDns); + json.insert(amnezia::config_key::killSwitchOption, rawConfig.value(amnezia::config_key::killSwitchOption)); if (protocolName == amnezia::config_key::awg) { diff --git a/client/platforms/linux/daemon/linuxfirewall.cpp b/client/platforms/linux/daemon/linuxfirewall.cpp index 96194bc7..de88c962 100644 --- a/client/platforms/linux/daemon/linuxfirewall.cpp +++ b/client/platforms/linux/daemon/linuxfirewall.cpp @@ -455,9 +455,6 @@ void LinuxFirewall::updateDNSServers(const QStringList& servers) void LinuxFirewall::updateAllowNets(const QStringList& servers) { - static QStringList existingServers {}; - - existingServers = servers; execute(QStringLiteral("iptables -F %1.110.allowNets").arg(kAnchorName)); for (const QString& rule : getAllowRule(servers)) execute(QStringLiteral("iptables -A %1.110.allowNets %2").arg(kAnchorName, rule)); diff --git a/client/platforms/linux/daemon/wireguardutilslinux.cpp b/client/platforms/linux/daemon/wireguardutilslinux.cpp index 1528d901..0fbb65a8 100644 --- a/client/platforms/linux/daemon/wireguardutilslinux.cpp +++ b/client/platforms/linux/daemon/wireguardutilslinux.cpp @@ -17,6 +17,8 @@ #include "leakdetector.h" #include "logger.h" +#include "killswitch.h" + constexpr const int WG_TUN_PROC_TIMEOUT = 5000; constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg"; @@ -182,7 +184,7 @@ bool WireguardUtilsLinux::deleteInterface() { QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name")); // double-check + ensure our firewall is installed and enabled - LinuxFirewall::uninstall(); + KillSwitch::instance()->disableKillSwitch(); return true; } diff --git a/client/platforms/macos/daemon/wireguardutilsmacos.cpp b/client/platforms/macos/daemon/wireguardutilsmacos.cpp index eae22837..1d8aa6e0 100644 --- a/client/platforms/macos/daemon/wireguardutilsmacos.cpp +++ b/client/platforms/macos/daemon/wireguardutilsmacos.cpp @@ -16,6 +16,8 @@ #include "leakdetector.h" #include "logger.h" +#include "killswitch.h" + constexpr const int WG_TUN_PROC_TIMEOUT = 5000; constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg"; @@ -180,7 +182,7 @@ bool WireguardUtilsMacos::deleteInterface() { QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name")); // double-check + ensure our firewall is installed and enabled - MacOSFirewall::uninstall(); + KillSwitch::instance()->disableKillSwitch(); return true; } diff --git a/client/platforms/windows/daemon/windowsfirewall.cpp b/client/platforms/windows/daemon/windowsfirewall.cpp index 03525387..85a2a155 100644 --- a/client/platforms/windows/daemon/windowsfirewall.cpp +++ b/client/platforms/windows/daemon/windowsfirewall.cpp @@ -29,6 +29,8 @@ #include "logger.h" #include "platforms/windows/windowsutils.h" +#include "killswitch.h" + #define IPV6_ADDRESS_SIZE 16 // ID for the Firewall Sublayer @@ -180,16 +182,29 @@ bool WindowsFirewall::enableInterface(int vpnAdapterIndex) { } \ } - logger.info() << "Enabling firewall Using Adapter:" << vpnAdapterIndex; + logger.info() << "Enabling Killswitch Using Adapter:" << vpnAdapterIndex; + if (vpnAdapterIndex < 0) + { + IPAddress allv4("0.0.0.0/0"); + if (!blockTrafficTo(allv4, MED_WEIGHT, + "Block Internet", "killswitch")) { + return false; + } + IPAddress allv6("::/0"); + if (!blockTrafficTo(allv6, MED_WEIGHT, + "Block Internet", "killswitch")) { + return false; + } + } else FW_OK(allowTrafficOfAdapter(vpnAdapterIndex, MED_WEIGHT, - "Allow usage of VPN Adapter")); + "Allow usage of VPN Adapter")); FW_OK(allowDHCPTraffic(MED_WEIGHT, "Allow DHCP Traffic")); - FW_OK(allowHyperVTraffic(MED_WEIGHT, "Allow Hyper-V Traffic")); + FW_OK(allowHyperVTraffic(MAX_WEIGHT, "Allow Hyper-V Traffic")); FW_OK(allowTrafficForAppOnAll(getCurrentPath(), MAX_WEIGHT, "Allow all for AmneziaVPN.exe")); FW_OK(blockTrafficOnPort(53, MED_WEIGHT, "Block all DNS")); - FW_OK( - allowLoopbackTraffic(MED_WEIGHT, "Allow Loopback traffic on device %1")); + FW_OK(allowLoopbackTraffic(MED_WEIGHT, + "Allow Loopback traffic on device %1")); logger.debug() << "Killswitch on! Rules:" << m_activeRules.length(); return true; @@ -226,6 +241,37 @@ bool WindowsFirewall::enableLanBypass(const QList& ranges) { return true; } +// Allow unprotected traffic sent to the following address ranges. +bool WindowsFirewall::allowTrafficRange(const QStringList& ranges) { + // Start the firewall transaction + auto result = FwpmTransactionBegin(m_sessionHandle, NULL); + if (result != ERROR_SUCCESS) { + disableKillSwitch(); + return false; + } + auto cleanup = qScopeGuard([&] { + FwpmTransactionAbort0(m_sessionHandle); + disableKillSwitch(); + }); + + for (const QString& addr : ranges) { + logger.debug() << "Allow killswitch exclude: " << addr; + if (!allowTrafficTo(QHostAddress(addr), LOW_WEIGHT + 1, "Allow killswitch bypass traffic")) { + return false; + } + } + + result = FwpmTransactionCommit0(m_sessionHandle); + if (result != ERROR_SUCCESS) { + logger.error() << "FwpmTransactionCommit0 failed with error:" << result; + return false; + } + + cleanup.dismiss(); + return true; +} + + bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) { // Start the firewall transaction auto result = FwpmTransactionBegin(m_sessionHandle, NULL); @@ -262,12 +308,20 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) { } } + for (const QString& dns : config.m_allowedDnsServers) { + logger.debug() << "Allow DNS: " << dns; + if (!allowTrafficTo(QHostAddress(dns), 53, HIGH_WEIGHT, + "Allow DNS-Server", config.m_serverPublicKey)) { + return false; + } + } + if (!config.m_excludedAddresses.empty()) { for (const QString& i : config.m_excludedAddresses) { logger.debug() << "excludedAddresses range: " << i; if (!allowTrafficTo(i, HIGH_WEIGHT, - "Allow Ecxlude route", config.m_serverPublicKey)) { + "Allow Ecxlude route", config.m_serverPublicKey)) { return false; } } @@ -313,37 +367,41 @@ bool WindowsFirewall::disablePeerTraffic(const QString& pubkey) { } bool WindowsFirewall::disableKillSwitch() { - auto result = FwpmTransactionBegin(m_sessionHandle, NULL); - auto cleanup = qScopeGuard([&] { + return KillSwitch::instance()->disableKillSwitch(); +} + +bool WindowsFirewall::allowAllTraffic() { + auto result = FwpmTransactionBegin(m_sessionHandle, NULL); + auto cleanup = qScopeGuard([&] { + if (result != ERROR_SUCCESS) { + FwpmTransactionAbort0(m_sessionHandle); + } + }); if (result != ERROR_SUCCESS) { - FwpmTransactionAbort0(m_sessionHandle); + logger.error() << "FwpmTransactionBegin0 failed. Return value:.\n" + << result; + return false; } - }); - if (result != ERROR_SUCCESS) { - logger.error() << "FwpmTransactionBegin0 failed. Return value:.\n" - << result; - return false; - } - for (const auto& filterID : m_peerRules.values()) { - FwpmFilterDeleteById0(m_sessionHandle, filterID); - } + for (const auto& filterID : m_peerRules.values()) { + FwpmFilterDeleteById0(m_sessionHandle, filterID); + } - for (const auto& filterID : qAsConst(m_activeRules)) { - FwpmFilterDeleteById0(m_sessionHandle, filterID); - } + for (const auto& filterID : qAsConst(m_activeRules)) { + FwpmFilterDeleteById0(m_sessionHandle, filterID); + } - // Commit! - result = FwpmTransactionCommit0(m_sessionHandle); - if (result != ERROR_SUCCESS) { - logger.error() << "FwpmTransactionCommit0 failed. Return value:.\n" - << result; - return false; - } - m_peerRules.clear(); - m_activeRules.clear(); - logger.debug() << "Firewall Disabled!"; - return true; + // Commit! + result = FwpmTransactionCommit0(m_sessionHandle); + if (result != ERROR_SUCCESS) { + logger.error() << "FwpmTransactionCommit0 failed. Return value:.\n" + << result; + return false; + } + m_peerRules.clear(); + m_activeRules.clear(); + logger.debug() << "Firewall Disabled!"; + return true; } bool WindowsFirewall::allowTrafficForAppOnAll(const QString& exePath, diff --git a/client/platforms/windows/daemon/windowsfirewall.h b/client/platforms/windows/daemon/windowsfirewall.h index 55ee9417..9a0062da 100644 --- a/client/platforms/windows/daemon/windowsfirewall.h +++ b/client/platforms/windows/daemon/windowsfirewall.h @@ -43,6 +43,8 @@ class WindowsFirewall final : public QObject { bool enablePeerTraffic(const InterfaceConfig& config); bool disablePeerTraffic(const QString& pubkey); bool disableKillSwitch(); + bool allowAllTraffic(); + bool allowTrafficRange(const QStringList& ranges); private: static bool initSublayer(); diff --git a/client/protocols/openvpnprotocol.cpp b/client/protocols/openvpnprotocol.cpp index 4c2feb52..0c8f5907 100644 --- a/client/protocols/openvpnprotocol.cpp +++ b/client/protocols/openvpnprotocol.cpp @@ -171,6 +171,11 @@ ErrorCode OpenVpnProtocol::start() return lastError(); } +#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) + IpcClient::Interface()->addKillSwitchAllowedRange(QStringList(NetworkUtilities::getIPAddress( + m_configData.value(amnezia::config_key::hostName).toString()))); +#endif + // Detect default gateway #ifdef Q_OS_MAC QProcess p; diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index 865edae4..feeabb2f 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -95,6 +95,8 @@ namespace amnezia constexpr char splitTunnelApps[] = "splitTunnelApps"; constexpr char appSplitTunnelType[] = "appSplitTunnelType"; + constexpr char allowedDnsServers[] = "allowedDnsServers"; + constexpr char killSwitchOption[] = "killSwitchOption"; constexpr char crc[] = "crc"; diff --git a/client/resources.qrc b/client/resources.qrc index 16071da0..a36b60d1 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -129,6 +129,7 @@ ui/qml/Components/SettingsContainersListView.qml ui/qml/Components/ShareConnectionDrawer.qml ui/qml/Components/TransportProtoSelector.qml + ui/qml/Components/AddSitePanel.qml ui/qml/Config/GlobalConfig.qml ui/qml/Config/qmldir ui/qml/Controls2/BackButtonType.qml @@ -143,7 +144,9 @@ ui/qml/Controls2/DropDownType.qml ui/qml/Controls2/FlickableType.qml ui/qml/Controls2/Header2Type.qml - ui/qml/Controls2/HeaderType.qml + ui/qml/Controls2/BaseHeaderType.qml + ui/qml/Controls2/HeaderTypeWithButton.qml + ui/qml/Controls2/HeaderTypeWithSwitcher.qml ui/qml/Controls2/HorizontalRadioButton.qml ui/qml/Controls2/ImageButtonType.qml ui/qml/Controls2/LabelWithButtonType.qml @@ -199,6 +202,8 @@ ui/qml/Pages2/PageSettingsBackup.qml ui/qml/Pages2/PageSettingsConnection.qml ui/qml/Pages2/PageSettingsDns.qml + ui/qml/Pages2/PageSettingsKillSwitch.qml + ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml ui/qml/Pages2/PageSettingsLogging.qml ui/qml/Pages2/PageSettingsServerData.qml ui/qml/Pages2/PageSettingsServerInfo.qml diff --git a/client/secure_qsettings.cpp b/client/secure_qsettings.cpp index 4fd199db..8df24e74 100644 --- a/client/secure_qsettings.cpp +++ b/client/secure_qsettings.cpp @@ -1,7 +1,7 @@ #include "secure_qsettings.h" -#include "QAead.h" -#include "QBlockCipher.h" +#include "../client/3rd/QSimpleCrypto/src/include/QAead.h" +#include "../client/3rd/QSimpleCrypto/src/include/QBlockCipher.h" #include "utilities.h" #include #include diff --git a/client/secure_qsettings.h b/client/secure_qsettings.h index 3f04096e..8878e1d5 100644 --- a/client/secure_qsettings.h +++ b/client/secure_qsettings.h @@ -6,7 +6,7 @@ #include #include -#include "keychain.h" +#include "../client/3rd/qtkeychain/qtkeychain/keychain.h" class SecureQSettings : public QObject { diff --git a/client/settings.cpp b/client/settings.cpp index 94b11d00..9a0a32e5 100644 --- a/client/settings.cpp +++ b/client/settings.cpp @@ -443,6 +443,16 @@ void Settings::setKillSwitchEnabled(bool enabled) setValue("Conf/killSwitchEnabled", enabled); } +bool Settings::isStrictKillSwitchEnabled() const +{ + return value("Conf/strictKillSwitchEnabled", false).toBool(); +} + +void Settings::setStrictKillSwitchEnabled(bool enabled) +{ + setValue("Conf/strictKillSwitchEnabled", enabled); +} + QString Settings::getInstallationUuid(const bool needCreate) { auto uuid = value("Conf/installationUuid", "").toString(); @@ -548,3 +558,13 @@ void Settings::disableHomeAdLabel() { setValue("Conf/homeAdLabelVisible", false); } + +QStringList Settings::allowedDnsServers() const +{ + return value("Conf/allowedDnsServers").toStringList(); +} + +void Settings::setAllowedDnsServers(const QStringList &servers) +{ + setValue("Conf/allowedDnsServers", servers); +} diff --git a/client/settings.h b/client/settings.h index b383d3da..01155c0c 100644 --- a/client/settings.h +++ b/client/settings.h @@ -213,6 +213,10 @@ public: bool isKillSwitchEnabled() const; void setKillSwitchEnabled(bool enabled); + + bool isStrictKillSwitchEnabled() const; + void setStrictKillSwitchEnabled(bool enabled); + QString getInstallationUuid(const bool needCreate); void resetGatewayEndpoint(); @@ -225,6 +229,9 @@ public: bool isHomeAdLabelVisible(); void disableHomeAdLabel(); + QStringList allowedDnsServers() const; + void setAllowedDnsServers(const QStringList &servers); + signals: void saveLogsChanged(bool enabled); void screenshotsEnabledChanged(bool enabled); diff --git a/client/ui/controllers/allowedDnsController.cpp b/client/ui/controllers/allowedDnsController.cpp new file mode 100644 index 00000000..12e8b599 --- /dev/null +++ b/client/ui/controllers/allowedDnsController.cpp @@ -0,0 +1,101 @@ +#include "allowedDnsController.h" + +#include +#include +#include +#include +#include + +#include "systemController.h" +#include "core/networkUtilities.h" +#include "core/defs.h" + +AllowedDnsController::AllowedDnsController(const std::shared_ptr &settings, + const QSharedPointer &allowedDnsModel, + QObject *parent) + : QObject(parent), m_settings(settings), m_allowedDnsModel(allowedDnsModel) +{ +} + +void AllowedDnsController::addDns(QString ip) +{ + if (ip.isEmpty()) { + return; + } + + if (!NetworkUtilities::ipAddressRegExp().match(ip).hasMatch()) { + emit errorOccurred(tr("The address does not look like a valid IP address")); + return; + } + + if (m_allowedDnsModel->addDns(ip)) { + emit finished(tr("New DNS server added: %1").arg(ip)); + } else { + emit errorOccurred(tr("DNS server already exists: %1").arg(ip)); + } +} + +void AllowedDnsController::removeDns(int index) +{ + auto modelIndex = m_allowedDnsModel->index(index); + auto ip = m_allowedDnsModel->data(modelIndex, AllowedDnsModel::Roles::IpRole).toString(); + m_allowedDnsModel->removeDns(modelIndex); + + emit finished(tr("DNS server removed: %1").arg(ip)); +} + +void AllowedDnsController::importDns(const QString &fileName, bool replaceExisting) +{ + QByteArray jsonData; + if (!SystemController::readFile(fileName, jsonData)) { + emit errorOccurred(tr("Can't open file: %1").arg(fileName)); + return; + } + + QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonData); + if (jsonDocument.isNull()) { + emit errorOccurred(tr("Failed to parse JSON data from file: %1").arg(fileName)); + return; + } + + if (!jsonDocument.isArray()) { + emit errorOccurred(tr("The JSON data is not an array in file: %1").arg(fileName)); + return; + } + + auto jsonArray = jsonDocument.array(); + QStringList dnsServers; + + for (auto jsonValue : jsonArray) { + auto ip = jsonValue.toString(); + + if (!NetworkUtilities::ipAddressRegExp().match(ip).hasMatch()) { + qDebug() << ip << " is not a valid IP address"; + continue; + } + + dnsServers.append(ip); + } + + m_allowedDnsModel->addDnsList(dnsServers, replaceExisting); + + emit finished(tr("Import completed")); +} + +void AllowedDnsController::exportDns(const QString &fileName) +{ + auto dnsServers = m_allowedDnsModel->getCurrentDnsServers(); + + QJsonArray jsonArray; + + for (const auto &ip : dnsServers) { + jsonArray.append(ip); + } + + QJsonDocument jsonDocument(jsonArray); + QByteArray jsonData = jsonDocument.toJson(); + + SystemController::saveFile(fileName, jsonData); + + emit finished(tr("Export completed")); +} diff --git a/client/ui/controllers/allowedDnsController.h b/client/ui/controllers/allowedDnsController.h new file mode 100644 index 00000000..5509a036 --- /dev/null +++ b/client/ui/controllers/allowedDnsController.h @@ -0,0 +1,35 @@ +#ifndef ALLOWEDDNSCONTROLLER_H +#define ALLOWEDDNSCONTROLLER_H + +#include + +#include "settings.h" +#include "ui/models/allowed_dns_model.h" + +class AllowedDnsController : public QObject +{ + Q_OBJECT +public: + explicit AllowedDnsController(const std::shared_ptr &settings, + const QSharedPointer &allowedDnsModel, + QObject *parent = nullptr); + +public slots: + void addDns(QString ip); + void removeDns(int index); + + void importDns(const QString &fileName, bool replaceExisting); + void exportDns(const QString &fileName); + +signals: + void errorOccurred(const QString &errorMessage); + void finished(const QString &message); + + void saveFile(const QString &fileName, const QString &data); + +private: + std::shared_ptr m_settings; + QSharedPointer m_allowedDnsModel; +}; + +#endif // ALLOWEDDNSCONTROLLER_H diff --git a/client/ui/controllers/pageController.h b/client/ui/controllers/pageController.h index 60621414..fc981091 100644 --- a/client/ui/controllers/pageController.h +++ b/client/ui/controllers/pageController.h @@ -31,13 +31,15 @@ namespace PageLoader PageSettingsLogging, PageSettingsSplitTunneling, PageSettingsAppSplitTunneling, + PageSettingsKillSwitch, PageSettingsApiServerInfo, PageSettingsApiAvailableCountries, PageSettingsApiSupport, PageSettingsApiInstructions, PageSettingsApiNativeConfigs, PageSettingsApiDevices, - + PageSettingsKillSwitchExceptions, + PageServiceSftpSettings, PageServiceTorWebsiteSettings, PageServiceDnsSettings, diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index f4e3d83d..c5c569db 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -245,6 +245,23 @@ bool SettingsController::isKillSwitchEnabled() void SettingsController::toggleKillSwitch(bool enable) { m_settings->setKillSwitchEnabled(enable); + emit killSwitchEnabledChanged(); + if (enable == false) { + emit strictKillSwitchEnabledChanged(false); + } else { + emit strictKillSwitchEnabledChanged(isStrictKillSwitchEnabled()); + } +} + +bool SettingsController::isStrictKillSwitchEnabled() +{ + return m_settings->isStrictKillSwitchEnabled(); +} + +void SettingsController::toggleStrictKillSwitch(bool enable) +{ + m_settings->setStrictKillSwitchEnabled(enable); + emit strictKillSwitchEnabledChanged(enable); } bool SettingsController::isNotificationPermissionGranted() diff --git a/client/ui/controllers/settingsController.h b/client/ui/controllers/settingsController.h index 7781f6c7..1485e1a0 100644 --- a/client/ui/controllers/settingsController.h +++ b/client/ui/controllers/settingsController.h @@ -24,6 +24,8 @@ public: Q_PROPERTY(QString secondaryDns READ getSecondaryDns WRITE setSecondaryDns NOTIFY secondaryDnsChanged) Q_PROPERTY(bool isLoggingEnabled READ isLoggingEnabled WRITE toggleLogging NOTIFY loggingStateChanged) Q_PROPERTY(bool isNotificationPermissionGranted READ isNotificationPermissionGranted NOTIFY onNotificationStateChanged) + Q_PROPERTY(bool isKillSwitchEnabled READ isKillSwitchEnabled WRITE toggleKillSwitch NOTIFY killSwitchEnabledChanged) + Q_PROPERTY(bool strictKillSwitchEnabled READ isStrictKillSwitchEnabled WRITE toggleStrictKillSwitch NOTIFY strictKillSwitchEnabledChanged) Q_PROPERTY(bool isDevModeEnabled READ isDevModeEnabled NOTIFY devModeEnabled) Q_PROPERTY(QString gatewayEndpoint READ getGatewayEndpoint WRITE setGatewayEndpoint NOTIFY gatewayEndpointChanged) @@ -75,6 +77,9 @@ public slots: bool isKillSwitchEnabled(); void toggleKillSwitch(bool enable); + bool isStrictKillSwitchEnabled(); + void toggleStrictKillSwitch(bool enable); + bool isNotificationPermissionGranted(); void requestNotificationPermission(); @@ -98,6 +103,8 @@ signals: void primaryDnsChanged(); void secondaryDnsChanged(); void loggingStateChanged(); + void killSwitchEnabledChanged(); + void strictKillSwitchEnabledChanged(bool enabled); void restoreBackupFinished(); void changeSettingsFinished(const QString &finishedMessage); diff --git a/client/ui/models/allowed_dns_model.cpp b/client/ui/models/allowed_dns_model.cpp new file mode 100644 index 00000000..e3c59945 --- /dev/null +++ b/client/ui/models/allowed_dns_model.cpp @@ -0,0 +1,86 @@ +#include "allowed_dns_model.h" + +AllowedDnsModel::AllowedDnsModel(std::shared_ptr settings, QObject *parent) + : QAbstractListModel(parent), m_settings(settings) +{ + fillDnsServers(); +} + +int AllowedDnsModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return m_dnsServers.size(); +} + +QVariant AllowedDnsModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() < 0 || index.row() >= static_cast(rowCount())) + return QVariant(); + + switch (role) { + case IpRole: + return m_dnsServers.at(index.row()); + default: + return QVariant(); + } +} + +bool AllowedDnsModel::addDns(const QString &ip) +{ + if (m_dnsServers.contains(ip)) { + return false; + } + + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + m_dnsServers.append(ip); + m_settings->setAllowedDnsServers(m_dnsServers); + endInsertRows(); + return true; +} + +void AllowedDnsModel::addDnsList(const QStringList &dnsServers, bool replaceExisting) +{ + beginResetModel(); + + if (replaceExisting) { + m_dnsServers.clear(); + } + + for (const QString &ip : dnsServers) { + if (!m_dnsServers.contains(ip)) { + m_dnsServers.append(ip); + } + } + + m_settings->setAllowedDnsServers(m_dnsServers); + endResetModel(); +} + +void AllowedDnsModel::removeDns(QModelIndex index) +{ + if (!index.isValid() || index.row() >= m_dnsServers.size()) { + return; + } + + beginRemoveRows(QModelIndex(), index.row(), index.row()); + m_dnsServers.removeAt(index.row()); + m_settings->setAllowedDnsServers(m_dnsServers); + endRemoveRows(); +} + +QStringList AllowedDnsModel::getCurrentDnsServers() +{ + return m_dnsServers; +} + +QHash AllowedDnsModel::roleNames() const +{ + QHash roles; + roles[IpRole] = "ip"; + return roles; +} + +void AllowedDnsModel::fillDnsServers() +{ + m_dnsServers = m_settings->allowedDnsServers(); +} diff --git a/client/ui/models/allowed_dns_model.h b/client/ui/models/allowed_dns_model.h new file mode 100644 index 00000000..fdefcc0e --- /dev/null +++ b/client/ui/models/allowed_dns_model.h @@ -0,0 +1,37 @@ +#ifndef ALLOWEDDNSMODEL_H +#define ALLOWEDDNSMODEL_H + +#include +#include "settings.h" + +class AllowedDnsModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Roles { + IpRole = Qt::UserRole + 1 + }; + + explicit AllowedDnsModel(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: + bool addDns(const QString &ip); + void addDnsList(const QStringList &dnsServers, bool replaceExisting); + void removeDns(QModelIndex index); + QStringList getCurrentDnsServers(); + +protected: + QHash roleNames() const override; + +private: + void fillDnsServers(); + + std::shared_ptr m_settings; + QStringList m_dnsServers; +}; + +#endif // ALLOWEDDNSMODEL_H diff --git a/client/ui/qml/Components/AddSitePanel.qml b/client/ui/qml/Components/AddSitePanel.qml new file mode 100644 index 00000000..18fdfa57 --- /dev/null +++ b/client/ui/qml/Components/AddSitePanel.qml @@ -0,0 +1,73 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import Style 1.0 +import "../Controls2" +import "../Controls2/TextTypes" + +Item { + id: root + + property bool enabled: true + property string placeholderText: "" + property alias textField: searchField.textField + + signal addClicked(string text) + signal moreClicked() + + implicitWidth: 360 + implicitHeight: 96 + + Rectangle { + id: background + anchors.fill: parent + color: "#0E0F12" + opacity: 0.85 + z: -1 + } + + RowLayout { + id: addSiteButton + + enabled: root.enabled + spacing: 2 + + anchors { + fill: parent + topMargin: 16 + leftMargin: 16 + rightMargin: 16 + bottomMargin: 24 + } + + TextFieldWithHeaderType { + id: searchField + + Layout.fillWidth: true + rightButtonClickedOnEnter: true + + textField.placeholderText: root.placeholderText + buttonImageSource: "qrc:/images/controls/plus.svg" + + clickedFunc: function() { + root.addClicked(textField.text) + textField.text = "" + } + } + + ImageButtonType { + id: addSiteButtonImage + implicitWidth: 56 + implicitHeight: 56 + + image: "qrc:/images/controls/more-vertical.svg" + imageColor: AmneziaStyle.color.paleGray + + onClicked: root.moreClicked() + + Keys.onReturnPressed: addSiteButtonImage.clicked() + Keys.onEnterPressed: addSiteButtonImage.clicked() + } + } +} diff --git a/client/ui/qml/Controls2/BaseHeaderType.qml b/client/ui/qml/Controls2/BaseHeaderType.qml new file mode 100644 index 00000000..eb7fe36f --- /dev/null +++ b/client/ui/qml/Controls2/BaseHeaderType.qml @@ -0,0 +1,45 @@ +import QtQuick +import QtQuick.Layouts + +import Style 1.0 + +import "TextTypes" + +Item { + id: root + + property string headerText + property int headerTextMaximumLineCount: 2 + property int headerTextElide: Qt.ElideRight + property string descriptionText + property alias headerRow: headerRow + + implicitWidth: content.implicitWidth + implicitHeight: content.implicitHeight + + ColumnLayout { + id: content + anchors.fill: parent + + RowLayout { + id: headerRow + + Header1TextType { + id: header + Layout.fillWidth: true + text: root.headerText + maximumLineCount: root.headerTextMaximumLineCount + elide: root.headerTextElide + } + } + + ParagraphTextType { + id: description + Layout.topMargin: 16 + Layout.fillWidth: true + text: root.descriptionText + color: AmneziaStyle.color.mutedGray + visible: root.descriptionText !== "" + } + } +} diff --git a/client/ui/qml/Controls2/HeaderType.qml b/client/ui/qml/Controls2/HeaderType.qml deleted file mode 100644 index 1366148d..00000000 --- a/client/ui/qml/Controls2/HeaderType.qml +++ /dev/null @@ -1,86 +0,0 @@ -import QtQuick -import QtQuick.Layouts - -import Style 1.0 - -import "TextTypes" - -Item { - id: root - - property string actionButtonImage - property var actionButtonFunction - - property alias actionButton: headerActionButton - - property string headerText - property int headerTextMaximumLineCount: 2 - property int headerTextElide: Qt.ElideRight - - property string descriptionText - - implicitWidth: content.implicitWidth - implicitHeight: content.implicitHeight - - ColumnLayout { - id: content - anchors.fill: parent - - RowLayout { - Header1TextType { - id: header - - Layout.fillWidth: true - - text: root.headerText - maximumLineCount: root.headerTextMaximumLineCount - elide: root.headerTextElide - } - - ImageButtonType { - id: headerActionButton - - implicitWidth: 40 - implicitHeight: 40 - - Layout.alignment: Qt.AlignRight - - image: root.actionButtonImage - imageColor: AmneziaStyle.color.paleGray - - visible: image ? true : false - - onClicked: { - if (actionButtonFunction && typeof actionButtonFunction === "function") { - actionButtonFunction() - } - } - } - } - - ParagraphTextType { - id: description - - Layout.topMargin: 16 - Layout.fillWidth: true - - text: root.descriptionText - - color: AmneziaStyle.color.mutedGray - - visible: root.descriptionText !== "" - } - } - - Keys.onEnterPressed: { - if (actionButtonFunction && typeof actionButtonFunction === "function") { - actionButtonFunction() - } - } - - Keys.onReturnPressed: { - if (actionButtonFunction && typeof actionButtonFunction === "function") { - actionButtonFunction() - } - } -} diff --git a/client/ui/qml/Controls2/HeaderTypeWithButton.qml b/client/ui/qml/Controls2/HeaderTypeWithButton.qml new file mode 100644 index 00000000..7feff3ce --- /dev/null +++ b/client/ui/qml/Controls2/HeaderTypeWithButton.qml @@ -0,0 +1,44 @@ +import QtQuick +import QtQuick.Layouts + +import Style 1.0 + +BaseHeaderType { + id: root + + property string actionButtonImage + property var actionButtonFunction + property alias actionButton: headerActionButton + + Component.onCompleted: { + headerRow.children.push(headerActionButton) + } + + ImageButtonType { + id: headerActionButton + implicitWidth: 40 + implicitHeight: 40 + Layout.alignment: Qt.AlignRight + image: root.actionButtonImage + imageColor: AmneziaStyle.color.paleGray + visible: image ? true : false + + onClicked: { + if (actionButtonFunction && typeof actionButtonFunction === "function") { + actionButtonFunction() + } + } + } + + Keys.onEnterPressed: { + if (actionButtonFunction && typeof actionButtonFunction === "function") { + actionButtonFunction() + } + } + + Keys.onReturnPressed: { + if (actionButtonFunction && typeof actionButtonFunction === "function") { + actionButtonFunction() + } + } +} diff --git a/client/ui/qml/Controls2/HeaderTypeWithSwitcher.qml b/client/ui/qml/Controls2/HeaderTypeWithSwitcher.qml new file mode 100644 index 00000000..2fa4e735 --- /dev/null +++ b/client/ui/qml/Controls2/HeaderTypeWithSwitcher.qml @@ -0,0 +1,28 @@ +import QtQuick +import QtQuick.Layouts + +import Style 1.0 + +BaseHeaderType { + id: root + + property var switcherFunction + property bool showSwitcher: false + property alias switcher: headerSwitcher + + Component.onCompleted: { + headerRow.children.push(headerSwitcher) + } + + SwitcherType { + id: headerSwitcher + Layout.alignment: Qt.AlignRight + visible: root.showSwitcher + + onToggled: { + if (switcherFunction && typeof switcherFunction === "function") { + switcherFunction(checked) + } + } + } +} diff --git a/client/ui/qml/Controls2/VerticalRadioButton.qml b/client/ui/qml/Controls2/VerticalRadioButton.qml index bee8ef7b..1c878f15 100644 --- a/client/ui/qml/Controls2/VerticalRadioButton.qml +++ b/client/ui/qml/Controls2/VerticalRadioButton.qml @@ -20,7 +20,11 @@ RadioButton { property string selectedColor: AmneziaStyle.color.transparent property string textColor: AmneziaStyle.color.paleGray + property string textDisabledColor: AmneziaStyle.color.mutedGray property string selectedTextColor: AmneziaStyle.color.goldenApricot + property string selectedTextDisabledColor: AmneziaStyle.color.burntOrange + property string descriptionColor: AmneziaStyle.color.mutedGray + property string descriptionDisabledColor: AmneziaStyle.color.charcoalGray property string borderFocusedColor: AmneziaStyle.color.paleGray property int borderFocusedWidth: 1 @@ -30,6 +34,12 @@ RadioButton { property bool isFocusable: true + + property string radioButtonInnerCirclePressedSource: "qrc:/images/controls/radio-button-inner-circle-pressed.png" + property string radioButtonInnerCircleSource: "qrc:/images/controls/radio-button-inner-circle.png" + property string radioButtonPressedSource: "qrc:/images/controls/radio-button-pressed.svg" + property string radioButtonDefaultSource: "qrc:/images/controls/radio-button.svg" + Keys.onTabPressed: { FocusController.nextKeyTabItem() } @@ -94,14 +104,15 @@ RadioButton { if (showImage) { return imageSource } else if (root.pressed) { - return "qrc:/images/controls/radio-button-inner-circle-pressed.png" + return root.radioButtonInnerCirclePressedSource } else if (root.checked) { - return "qrc:/images/controls/radio-button-inner-circle.png" + return root.radioButtonInnerCircleSource } return "" } + opacity: root.enabled ? 1.0 : 0.3 anchors.centerIn: parent width: 24 @@ -113,12 +124,13 @@ RadioButton { if (showImage) { return "" } else if (root.pressed || root.checked) { - return "qrc:/images/controls/radio-button-pressed.svg" + return root.radioButtonPressedSource } else { - return "qrc:/images/controls/radio-button.svg" + return root.radioButtonDefaultSource } } + opacity: root.enabled ? 1.0 : 0.3 anchors.centerIn: parent width: 24 @@ -148,10 +160,11 @@ RadioButton { elide: root.textElide color: { - if (root.checked) { - return selectedTextColor + if (root.enabled) { + return root.checked ? selectedTextColor : textColor + } else { + return root.checked ? selectedTextDisabledColor : textDisabledColor } - return textColor } Layout.fillWidth: true @@ -164,7 +177,7 @@ RadioButton { CaptionTextType { id: description - color: AmneziaStyle.color.mutedGray + color: root.enabled ? root.descriptionColor : root.descriptionDisabledColor text: root.descriptionText visible: root.descriptionText !== "" diff --git a/client/ui/qml/Pages2/PageDeinstalling.qml b/client/ui/qml/Pages2/PageDeinstalling.qml index f5fdb29a..69b1f319 100644 --- a/client/ui/qml/Pages2/PageDeinstalling.qml +++ b/client/ui/qml/Pages2/PageDeinstalling.qml @@ -56,7 +56,7 @@ PageType { anchors.rightMargin: 16 anchors.leftMargin: 16 - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.topMargin: 20 diff --git a/client/ui/qml/Pages2/PageDevMenu.qml b/client/ui/qml/Pages2/PageDevMenu.qml index d7afde1d..5fccb43a 100644 --- a/client/ui/qml/Pages2/PageDevMenu.qml +++ b/client/ui/qml/Pages2/PageDevMenu.qml @@ -39,7 +39,7 @@ PageType { header: ColumnLayout { width: listView.width - HeaderType { + BaseHeaderType { id: header Layout.fillWidth: true diff --git a/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml b/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml index c22fdf0c..b8cf5f93 100644 --- a/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml @@ -91,7 +91,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true headerText: qsTr("AmneziaWG settings") diff --git a/client/ui/qml/Pages2/PageProtocolAwgSettings.qml b/client/ui/qml/Pages2/PageProtocolAwgSettings.qml index 8c629b68..e8fd2b94 100644 --- a/client/ui/qml/Pages2/PageProtocolAwgSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolAwgSettings.qml @@ -91,7 +91,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true headerText: qsTr("AmneziaWG settings") diff --git a/client/ui/qml/Pages2/PageProtocolCloakSettings.qml b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml index 686ccd7b..7a0fafbd 100644 --- a/client/ui/qml/Pages2/PageProtocolCloakSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml @@ -76,7 +76,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true headerText: qsTr("Cloak settings") diff --git a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml index 9cc628b7..2e00d54a 100644 --- a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml @@ -75,7 +75,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true headerText: qsTr("OpenVPN settings") diff --git a/client/ui/qml/Pages2/PageProtocolRaw.qml b/client/ui/qml/Pages2/PageProtocolRaw.qml index 03b4e297..bba3eafe 100644 --- a/client/ui/qml/Pages2/PageProtocolRaw.qml +++ b/client/ui/qml/Pages2/PageProtocolRaw.qml @@ -32,7 +32,7 @@ PageType { id: backButton } - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 diff --git a/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml index 5786012b..63e60dcb 100644 --- a/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml @@ -78,7 +78,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true headerText: qsTr("Shadowsocks settings") diff --git a/client/ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml b/client/ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml index a30c17e7..96ec1dc6 100644 --- a/client/ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml @@ -85,7 +85,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true headerText: qsTr("WG settings") diff --git a/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml b/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml index 10523b74..7b5180f3 100644 --- a/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml @@ -77,7 +77,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true headerText: qsTr("WG settings") } diff --git a/client/ui/qml/Pages2/PageProtocolXraySettings.qml b/client/ui/qml/Pages2/PageProtocolXraySettings.qml index 90705d3e..ca30e0a9 100644 --- a/client/ui/qml/Pages2/PageProtocolXraySettings.qml +++ b/client/ui/qml/Pages2/PageProtocolXraySettings.qml @@ -75,7 +75,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true headerText: qsTr("XRay settings") } diff --git a/client/ui/qml/Pages2/PageServiceDnsSettings.qml b/client/ui/qml/Pages2/PageServiceDnsSettings.qml index cef29813..d534f991 100644 --- a/client/ui/qml/Pages2/PageServiceDnsSettings.qml +++ b/client/ui/qml/Pages2/PageServiceDnsSettings.qml @@ -43,7 +43,7 @@ PageType { anchors.left: parent.left anchors.right: parent.right - HeaderType { + BaseHeaderType { id: header Layout.fillWidth: true diff --git a/client/ui/qml/Pages2/PageServiceSftpSettings.qml b/client/ui/qml/Pages2/PageServiceSftpSettings.qml index 2deb315c..b58cb2e0 100644 --- a/client/ui/qml/Pages2/PageServiceSftpSettings.qml +++ b/client/ui/qml/Pages2/PageServiceSftpSettings.qml @@ -85,7 +85,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 diff --git a/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml b/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml index 1b77267a..b1daa0fb 100644 --- a/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml +++ b/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml @@ -77,7 +77,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 @@ -217,7 +217,7 @@ PageType { } } - HeaderType { + BaseHeaderType { Layout.fillWidth: true headerText: qsTr("SOCKS5 settings") diff --git a/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml b/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml index 249c70c7..200beeb8 100644 --- a/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml +++ b/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml @@ -54,7 +54,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 diff --git a/client/ui/qml/Pages2/PageSettings.qml b/client/ui/qml/Pages2/PageSettings.qml index a47bb535..bb83ec92 100644 --- a/client/ui/qml/Pages2/PageSettings.qml +++ b/client/ui/qml/Pages2/PageSettings.qml @@ -29,7 +29,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { id: header Layout.fillWidth: true Layout.topMargin: 24 diff --git a/client/ui/qml/Pages2/PageSettingsApiAvailableCountries.qml b/client/ui/qml/Pages2/PageSettingsApiAvailableCountries.qml index 6e67ef1f..3e999cff 100644 --- a/client/ui/qml/Pages2/PageSettingsApiAvailableCountries.qml +++ b/client/ui/qml/Pages2/PageSettingsApiAvailableCountries.qml @@ -69,7 +69,7 @@ PageType { Layout.topMargin: 20 } - HeaderType { + HeaderTypeWithButton { id: headerContent objectName: "headerContent" diff --git a/client/ui/qml/Pages2/PageSettingsApiDevices.qml b/client/ui/qml/Pages2/PageSettingsApiDevices.qml index c6a2f98c..d8e24d42 100644 --- a/client/ui/qml/Pages2/PageSettingsApiDevices.qml +++ b/client/ui/qml/Pages2/PageSettingsApiDevices.qml @@ -35,7 +35,7 @@ PageType { id: backButton } - HeaderType { + BaseHeaderType { id: header Layout.fillWidth: true diff --git a/client/ui/qml/Pages2/PageSettingsApiInstructions.qml b/client/ui/qml/Pages2/PageSettingsApiInstructions.qml index 7961594b..9b325b82 100644 --- a/client/ui/qml/Pages2/PageSettingsApiInstructions.qml +++ b/client/ui/qml/Pages2/PageSettingsApiInstructions.qml @@ -91,7 +91,7 @@ PageType { id: backButton } - HeaderType { + BaseHeaderType { id: header Layout.fillWidth: true diff --git a/client/ui/qml/Pages2/PageSettingsApiNativeConfigs.qml b/client/ui/qml/Pages2/PageSettingsApiNativeConfigs.qml index 44b2d2fa..d2042a76 100644 --- a/client/ui/qml/Pages2/PageSettingsApiNativeConfigs.qml +++ b/client/ui/qml/Pages2/PageSettingsApiNativeConfigs.qml @@ -38,7 +38,7 @@ PageType { id: backButton } - HeaderType { + BaseHeaderType { id: header Layout.fillWidth: true diff --git a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml index 689502c1..48080c3a 100644 --- a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml @@ -93,7 +93,7 @@ PageType { Layout.topMargin: 20 } - HeaderType { + HeaderTypeWithButton { id: headerContent objectName: "headerContent" diff --git a/client/ui/qml/Pages2/PageSettingsApiSupport.qml b/client/ui/qml/Pages2/PageSettingsApiSupport.qml index 0ea8ec84..af629ebe 100644 --- a/client/ui/qml/Pages2/PageSettingsApiSupport.qml +++ b/client/ui/qml/Pages2/PageSettingsApiSupport.qml @@ -71,7 +71,7 @@ PageType { id: backButton } - HeaderType { + BaseHeaderType { id: header Layout.fillWidth: true diff --git a/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml b/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml index b6920a8f..e31c92db 100644 --- a/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml +++ b/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml @@ -79,29 +79,22 @@ PageType { id: backButton } - RowLayout { - HeaderType { - Layout.fillWidth: true - Layout.leftMargin: 16 + HeaderTypeWithSwitcher { + Layout.fillWidth: true + Layout.leftMargin: 16 + Layout.rightMargin: 16 - headerText: qsTr("App split tunneling") + headerText: qsTr("App split tunneling") + enabled: root.pageEnabled + showSwitcher: true + switcher { + checked: AppSplitTunnelingModel.isTunnelingEnabled enabled: root.pageEnabled } - - SwitcherType { - id: switcher - - Layout.fillWidth: true - Layout.rightMargin: 16 - - enabled: root.pageEnabled - - checked: AppSplitTunnelingModel.isTunnelingEnabled - onToggled: { - AppSplitTunnelingModel.toggleSplitTunneling(checked) - selector.text = root.routeModesModel[getRouteModesModelIndex()].name - } + switcherFunction: function(checked) { + AppSplitTunnelingModel.toggleSplitTunneling(checked) + selector.text = root.routeModesModel[getRouteModesModelIndex()].name } } diff --git a/client/ui/qml/Pages2/PageSettingsApplication.qml b/client/ui/qml/Pages2/PageSettingsApplication.qml index 6f77a521..cbc04075 100644 --- a/client/ui/qml/Pages2/PageSettingsApplication.qml +++ b/client/ui/qml/Pages2/PageSettingsApplication.qml @@ -38,7 +38,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 diff --git a/client/ui/qml/Pages2/PageSettingsBackup.qml b/client/ui/qml/Pages2/PageSettingsBackup.qml index d2dd4f2a..83e0f567 100644 --- a/client/ui/qml/Pages2/PageSettingsBackup.qml +++ b/client/ui/qml/Pages2/PageSettingsBackup.qml @@ -60,7 +60,7 @@ PageType { spacing: 16 - HeaderType { + BaseHeaderType { Layout.fillWidth: true headerText: qsTr("Back up your configuration") diff --git a/client/ui/qml/Pages2/PageSettingsConnection.qml b/client/ui/qml/Pages2/PageSettingsConnection.qml index 69671f27..84b98230 100644 --- a/client/ui/qml/Pages2/PageSettingsConnection.qml +++ b/client/ui/qml/Pages2/PageSettingsConnection.qml @@ -36,7 +36,7 @@ PageType { anchors.left: parent.left anchors.right: parent.right - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 @@ -94,9 +94,7 @@ PageType { } } - DividerType { - visible: root.isAppSplitTinnelingEnabled - } + DividerType {} LabelWithButtonType { id: splitTunnelingButton2 @@ -119,29 +117,20 @@ PageType { visible: root.isAppSplitTinnelingEnabled } - SwitcherType { - id: killSwitchSwitcher + LabelWithButtonType { + id: killSwitchButton visible: !GC.isMobile() Layout.fillWidth: true - Layout.margins: 16 text: qsTr("KillSwitch") - descriptionText: qsTr("Disables your internet if your encrypted VPN connection drops out for any reason.") + descriptionText: qsTr("Blocks network connections without VPN") + rightImageSource: "qrc:/images/controls/chevron-right.svg" parentFlickable: fl - checked: SettingsController.isKillSwitchEnabled() - checkable: !ConnectionController.isConnected - onCheckedChanged: { - if (checked !== SettingsController.isKillSwitchEnabled()) { - SettingsController.toggleKillSwitch(checked) - } - } - onClicked: { - if (!checkable) { - PageController.showNotificationMessage(qsTr("Cannot change KillSwitch settings during active connection")) - } + clickedFunction: function() { + PageController.goToPage(PageEnum.PageSettingsKillSwitch) } } diff --git a/client/ui/qml/Pages2/PageSettingsDns.qml b/client/ui/qml/Pages2/PageSettingsDns.qml index d78c5aa8..d5e2c52b 100644 --- a/client/ui/qml/Pages2/PageSettingsDns.qml +++ b/client/ui/qml/Pages2/PageSettingsDns.qml @@ -50,7 +50,7 @@ PageType { spacing: 16 - HeaderType { + BaseHeaderType { Layout.fillWidth: true headerText: qsTr("DNS servers") diff --git a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml new file mode 100644 index 00000000..f6e0f5d7 --- /dev/null +++ b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml @@ -0,0 +1,123 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import PageEnum 1.0 +import Style 1.0 + +import "./" +import "../Controls2" +import "../Config" + +PageType { + id: root + + BackButtonType { + id: backButton + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: 20 + } + + FlickableType { + id: fl + anchors.top: backButton.bottom + anchors.bottom: parent.bottom + contentHeight: content.height + + ColumnLayout { + id: content + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + HeaderTypeWithSwitcher { + Layout.fillWidth: true + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + headerText: qsTr("KillSwitch") + descriptionText: qsTr("Enable to ensure network traffic goes through a secure VPN tunnel, preventing accidental exposure of your IP and DNS queries if the connection drops") + + showSwitcher: true + switcher { + checked: SettingsController.isKillSwitchEnabled + enabled: !ConnectionController.isConnected + } + switcherFunction: function(checked) { + if (!ConnectionController.isConnected) { + SettingsController.isKillSwitchEnabled = checked + } else { + PageController.showNotificationMessage(qsTr("Cannot change killSwitch settings during active connection")) + switcher.checked = SettingsController.isKillSwitchEnabled + } + } + } + + VerticalRadioButton { + id: softKillSwitch + Layout.fillWidth: true + Layout.topMargin: 32 + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected + checked: !SettingsController.strictKillSwitchEnabled + + text: qsTr("Soft KillSwitch") + descriptionText: qsTr("Internet connection is blocked if VPN connection drops accidentally") + + onClicked: function() { + SettingsController.strictKillSwitchEnabled = false + } + } + + DividerType {} + + VerticalRadioButton { + id: strictKillSwitch + Layout.fillWidth: true + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected + checked: SettingsController.strictKillSwitchEnabled + + text: qsTr("Strict KillSwitch") + descriptionText: qsTr("Internet connection is blocked even if VPN was turned off manually or not started") + + onClicked: function() { + var headerText = qsTr("Just a little heads-up") + var descriptionText = qsTr("If you disconnect from VPN or the VPN connection drops while the Strict Kill Switch is turned on, your internet access will be disabled. To restore it, connect to VPN, change the Kill Switch mode or turn the Kill Switch off.") + var yesButtonText = qsTr("Continue") + var noButtonText = qsTr("Cancel") + + var yesButtonFunction = function() { + SettingsController.strictKillSwitchEnabled = true + } + var noButtonFunction = function() { + } + + showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) + } + } + + DividerType {} + + LabelWithButtonType { + Layout.topMargin: 32 + Layout.fillWidth: true + + enabled: true + text: qsTr("DNS Exceptions") + descriptionText: qsTr("DNS servers from the list will remain accessible when Kill Switch is triggered") + rightImageSource: "qrc:/images/controls/chevron-right.svg" + + clickedFunction: function() { + PageController.goToPage(PageEnum.PageSettingsKillSwitchExceptions) + } + } + } + } +} diff --git a/client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml b/client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml new file mode 100644 index 00000000..d442b60c --- /dev/null +++ b/client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml @@ -0,0 +1,302 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Dialogs + +import QtCore + +import SortFilterProxyModel 0.2 + +import PageEnum 1.0 +import ProtocolEnum 1.0 +import ContainerProps 1.0 +import Style 1.0 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" +import "../Components" + +PageType { + id: root + + property bool pageEnabled: true + + ColumnLayout { + id: header + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + anchors.topMargin: 20 + + BackButtonType { + id: backButton + } + + BaseHeaderType { + enabled: root.pageEnabled + + Layout.fillWidth: true + Layout.leftMargin: 16 + + headerText: qsTr("DNS Exceptions") + descriptionText: qsTr("DNS servers from the list will remain accessible when Kill Switch is triggered") + } + } + + ListView { + id: listView + + anchors.top: header.bottom + anchors.topMargin: 16 + anchors.bottom: parent.bottom + + width: parent.width + + enabled: root.pageEnabled + + property bool isFocusable: true + + cacheBuffer: 200 + displayMarginBeginning: 40 + displayMarginEnd: 40 + + ScrollBar.vertical: ScrollBarType { } + + footer: Item { + width: listView.width + height: addSitePanel.height + } + + footerPositioning: ListView.InlineFooter + + model: SortFilterProxyModel { + id: dnsFilterModel + sourceModel: AllowedDnsModel + filters: [ + RegExpFilter { + roleName: "ip" + pattern: ".*" + addSitePanel.textField.text + ".*" + caseSensitivity: Qt.CaseInsensitive + } + ] + } + + clip: true + + reuseItems: true + + delegate: ColumnLayout { + id: delegateContent + + width: listView.width + + LabelWithButtonType { + id: site + Layout.fillWidth: true + + text: ip + rightImageSource: "qrc:/images/controls/trash.svg" + rightImageColor: AmneziaStyle.color.paleGray + + clickedFunction: function() { + var headerText = qsTr("Delete ") + ip + "?" + var yesButtonText = qsTr("Continue") + var noButtonText = qsTr("Cancel") + + var yesButtonFunction = function() { + AllowedDnsController.removeDns(dnsFilterModel.mapToSource(index)) + if (!GC.isMobile()) { + site.rightButton.forceActiveFocus() + } + } + var noButtonFunction = function() { + if (!GC.isMobile()) { + site.rightButton.forceActiveFocus() + } + } + + showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) + } + } + + DividerType {} + } + } + + AddSitePanel { + id: addSitePanel + + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + + enabled: root.pageEnabled + placeholderText: qsTr("IPv4 address") + + onAddClicked: function(text) { + PageController.showBusyIndicator(true) + AllowedDnsController.addDns(text) + PageController.showBusyIndicator(false) + } + + onMoreClicked: { + moreActionsDrawer.openTriggered() + } + } + + DrawerType2 { + id: moreActionsDrawer + + anchors.fill: parent + expandedHeight: parent.height * 0.4375 + + expandedStateContent: ColumnLayout { + id: moreActionsDrawerContent + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + Header2Type { + Layout.fillWidth: true + Layout.margins: 16 + + headerText: qsTr("Import / Export addresses") + } + + LabelWithButtonType { + id: importSitesButton + Layout.fillWidth: true + + text: qsTr("Import") + rightImageSource: "qrc:/images/controls/chevron-right.svg" + + clickedFunction: function() { + importSitesDrawer.openTriggered() + } + } + + DividerType {} + + LabelWithButtonType { + id: exportSitesButton + Layout.fillWidth: true + text: qsTr("Save address list") + + clickedFunction: function() { + var fileName = "" + if (GC.isMobile()) { + fileName = "amnezia_killswitch_exceptions.json" + } else { + fileName = SystemController.getFileName(qsTr("Save addresses"), + qsTr("Address files (*.json)"), + StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/amnezia_killswitch_exceptions", + true, + ".json") + } + if (fileName !== "") { + PageController.showBusyIndicator(true) + AllowedDnsController.exportDns(fileName) + moreActionsDrawer.closeTriggered() + PageController.showBusyIndicator(false) + } + } + } + + DividerType {} + } + } + + DrawerType2 { + id: importSitesDrawer + + anchors.fill: parent + expandedHeight: parent.height * 0.4375 + + expandedStateContent: Item { + implicitHeight: importSitesDrawer.expandedHeight + + BackButtonType { + id: importSitesDrawerBackButton + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: 16 + + backButtonFunction: function() { + importSitesDrawer.closeTriggered() + } + } + + FlickableType { + anchors.top: importSitesDrawerBackButton.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + + contentHeight: importSitesDrawerContent.height + + ColumnLayout { + id: importSitesDrawerContent + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + Header2Type { + Layout.fillWidth: true + Layout.margins: 16 + + headerText: qsTr("Import address list") + } + + LabelWithButtonType { + id: importSitesButton2 + Layout.fillWidth: true + + text: qsTr("Replace address list") + + clickedFunction: function() { + var fileName = SystemController.getFileName(qsTr("Open address file"), + qsTr("Address files (*.json)")) + if (fileName !== "") { + importSitesDrawerContent.importSites(fileName, true) + } + } + } + + DividerType {} + + LabelWithButtonType { + id: importSitesButton3 + Layout.fillWidth: true + text: qsTr("Add imported addresses to existing ones") + + clickedFunction: function() { + var fileName = SystemController.getFileName(qsTr("Open address file"), + qsTr("Address files (*.json)")) + if (fileName !== "") { + importSitesDrawerContent.importSites(fileName, false) + } + } + } + + function importSites(fileName, replaceExistingSites) { + PageController.showBusyIndicator(true) + AllowedDnsController.importDns(fileName, replaceExistingSites) + PageController.showBusyIndicator(false) + importSitesDrawer.closeTriggered() + moreActionsDrawer.closeTriggered() + } + + DividerType {} + } + } + } + } +} diff --git a/client/ui/qml/Pages2/PageSettingsLogging.qml b/client/ui/qml/Pages2/PageSettingsLogging.qml index 2c760e37..5b20936c 100644 --- a/client/ui/qml/Pages2/PageSettingsLogging.qml +++ b/client/ui/qml/Pages2/PageSettingsLogging.qml @@ -40,7 +40,7 @@ PageType { header: ColumnLayout { width: listView.width - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 diff --git a/client/ui/qml/Pages2/PageSettingsServerInfo.qml b/client/ui/qml/Pages2/PageSettingsServerInfo.qml index d350ebef..6ac81764 100644 --- a/client/ui/qml/Pages2/PageSettingsServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsServerInfo.qml @@ -71,7 +71,7 @@ PageType { objectName: "backButton" } - HeaderType { + HeaderTypeWithButton { id: headerContent objectName: "headerContent" diff --git a/client/ui/qml/Pages2/PageSettingsServerProtocol.qml b/client/ui/qml/Pages2/PageSettingsServerProtocol.qml index ade94ebb..fce9b2a3 100644 --- a/client/ui/qml/Pages2/PageSettingsServerProtocol.qml +++ b/client/ui/qml/Pages2/PageSettingsServerProtocol.qml @@ -34,7 +34,7 @@ PageType { id: backButton } - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 diff --git a/client/ui/qml/Pages2/PageSettingsServersList.qml b/client/ui/qml/Pages2/PageSettingsServersList.qml index 554b6cbb..57e39ae8 100644 --- a/client/ui/qml/Pages2/PageSettingsServersList.qml +++ b/client/ui/qml/Pages2/PageSettingsServersList.qml @@ -31,7 +31,7 @@ PageType { id: backButton } - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 diff --git a/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml b/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml index f5978687..292f903a 100644 --- a/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml +++ b/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml @@ -94,33 +94,22 @@ PageType { id: backButton } - RowLayout { - HeaderType { - enabled: root.pageEnabled + HeaderTypeWithSwitcher { + Layout.fillWidth: true + Layout.leftMargin: 16 + Layout.rightMargin: 16 - Layout.fillWidth: true - Layout.leftMargin: 16 - - headerText: qsTr("Split tunneling") - } - - SwitcherType { - id: switcher - - enabled: root.pageEnabled - - Layout.fillWidth: true - Layout.rightMargin: 16 - - function onToggledFunc() { - SitesModel.toggleSplitTunneling(this.checked) - selector.text = root.routeModesModel[getRouteModesModelIndex()].name - } + headerText: qsTr("Split tunneling") + enabled: root.pageEnabled + showSwitcher: true + switcher { checked: SitesModel.isTunnelingEnabled - onToggled: { onToggledFunc() } - Keys.onEnterPressed: { onToggledFunc() } - Keys.onReturnPressed: { onToggledFunc() } + enabled: root.pageEnabled + } + switcherFunction: function(checked) { + SitesModel.toggleSplitTunneling(checked) + selector.text = root.routeModesModel[getRouteModesModelIndex()].name } } diff --git a/client/ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml b/client/ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml index 134e73b6..30128de6 100644 --- a/client/ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml +++ b/client/ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml @@ -35,7 +35,7 @@ PageType { Layout.topMargin: 20 } - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.topMargin: 8 Layout.rightMargin: 16 diff --git a/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml b/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml index c3e3edbc..549eb381 100644 --- a/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml +++ b/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml @@ -28,7 +28,7 @@ PageType { Layout.topMargin: 20 } - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.topMargin: 8 Layout.rightMargin: 16 diff --git a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml index bcbb7fb2..7159ab59 100644 --- a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml +++ b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml @@ -45,7 +45,7 @@ PageType { header: ColumnLayout { width: listView.width - HeaderType { + HeaderTypeWithButton { id: moreButton property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible() @@ -76,7 +76,7 @@ PageType { anchors.right: parent.right spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.topMargin: 32 Layout.leftMargin: 16 diff --git a/client/ui/qml/Pages2/PageSetupWizardCredentials.qml b/client/ui/qml/Pages2/PageSetupWizardCredentials.qml index cdafa47f..63d4d5f6 100644 --- a/client/ui/qml/Pages2/PageSetupWizardCredentials.qml +++ b/client/ui/qml/Pages2/PageSetupWizardCredentials.qml @@ -66,7 +66,7 @@ PageType { header: ColumnLayout { width: listView.width - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 diff --git a/client/ui/qml/Pages2/PageSetupWizardEasy.qml b/client/ui/qml/Pages2/PageSetupWizardEasy.qml index ae04f635..096406e9 100644 --- a/client/ui/qml/Pages2/PageSetupWizardEasy.qml +++ b/client/ui/qml/Pages2/PageSetupWizardEasy.qml @@ -59,7 +59,7 @@ PageType { spacing: 16 - HeaderType { + BaseHeaderType { id: header implicitWidth: parent.width diff --git a/client/ui/qml/Pages2/PageSetupWizardInstalling.qml b/client/ui/qml/Pages2/PageSetupWizardInstalling.qml index 1128761d..822931b8 100644 --- a/client/ui/qml/Pages2/PageSetupWizardInstalling.qml +++ b/client/ui/qml/Pages2/PageSetupWizardInstalling.qml @@ -118,7 +118,7 @@ PageType { anchors.rightMargin: 16 anchors.leftMargin: 16 - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.topMargin: 20 diff --git a/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml b/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml index 50d1ea81..ac7fc4b2 100644 --- a/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml +++ b/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml @@ -96,7 +96,7 @@ PageType { Layout.leftMargin: -16 } - HeaderType { + BaseHeaderType { id: header Layout.fillWidth: true diff --git a/client/ui/qml/Pages2/PageSetupWizardProtocols.qml b/client/ui/qml/Pages2/PageSetupWizardProtocols.qml index 6b6b6038..7afab630 100644 --- a/client/ui/qml/Pages2/PageSetupWizardProtocols.qml +++ b/client/ui/qml/Pages2/PageSetupWizardProtocols.qml @@ -58,7 +58,7 @@ PageType { header: ColumnLayout { width: listView.width - HeaderType { + BaseHeaderType { id: header Layout.fillWidth: true diff --git a/client/ui/qml/Pages2/PageSetupWizardTextKey.qml b/client/ui/qml/Pages2/PageSetupWizardTextKey.qml index 3cf154e4..930efb57 100644 --- a/client/ui/qml/Pages2/PageSetupWizardTextKey.qml +++ b/client/ui/qml/Pages2/PageSetupWizardTextKey.qml @@ -33,7 +33,7 @@ PageType { Layout.topMargin: 20 } - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.rightMargin: 16 Layout.leftMargin: 16 diff --git a/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml b/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml index 14096742..cfa9c90f 100644 --- a/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml +++ b/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml @@ -61,7 +61,7 @@ PageType { anchors.rightMargin: 16 anchors.leftMargin: 16 - HeaderType { + BaseHeaderType { headerText: qsTr("New connection") } diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index af208544..48f74acf 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -169,7 +169,7 @@ PageType { spacing: 0 - HeaderType { + HeaderTypeWithButton { id: header Layout.fillWidth: true Layout.topMargin: 24 diff --git a/client/ui/qml/Pages2/PageShareFullAccess.qml b/client/ui/qml/Pages2/PageShareFullAccess.qml index 70fd6292..82effb57 100644 --- a/client/ui/qml/Pages2/PageShareFullAccess.qml +++ b/client/ui/qml/Pages2/PageShareFullAccess.qml @@ -44,7 +44,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.topMargin: 24 diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index ff875b39..3de0f035 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -52,6 +52,28 @@ void VpnConnection::onBytesChanged(quint64 receivedBytes, quint64 sentBytes) emit bytesChanged(receivedBytes, sentBytes); } +void VpnConnection::onKillSwitchModeChanged(bool enabled) +{ +#ifdef AMNEZIA_DESKTOP + if (!m_IpcClient) { + m_IpcClient = new IpcClient(this); + } + + if (!m_IpcClient->isSocketConnected()) { + if (!IpcClient::init(m_IpcClient)) { + qWarning() << "Error occurred when init IPC client"; + emit serviceIsNotReady(); + return; + } + } + + if (IpcClient::Interface()) { + qDebug() << "Set KillSwitch Strict mode enabled " << enabled; + IpcClient::Interface()->refreshKillSwitch(enabled); + } +#endif +} + void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state) { @@ -286,6 +308,7 @@ void VpnConnection::createProtocolConnections() void VpnConnection::appendKillSwitchConfig() { m_vpnConfiguration.insert(config_key::killSwitchOption, QVariant(m_settings->isKillSwitchEnabled()).toString()); + m_vpnConfiguration.insert(config_key::allowedDnsServers, QVariant(m_settings->allowedDnsServers()).toJsonValue()); } void VpnConnection::appendSplitTunnelingConfig() diff --git a/client/vpnconnection.h b/client/vpnconnection.h index 0160edce..cb5aaaf9 100644 --- a/client/vpnconnection.h +++ b/client/vpnconnection.h @@ -48,14 +48,14 @@ public: public slots: void connectToVpn(int serverIndex, - const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration); + const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration); void disconnectFromVpn(); - void addRoutes(const QStringList &ips); void deleteRoutes(const QStringList &ips); void flushDns(); + void onKillSwitchModeChanged(bool enabled); signals: void bytesChanged(quint64 receivedBytes, quint64 sentBytes); diff --git a/deploy/build_linux.sh b/deploy/build_linux.sh index 57217a1e..aaf59eb5 100755 --- a/deploy/build_linux.sh +++ b/deploy/build_linux.sh @@ -41,6 +41,10 @@ if [ -z "${QT_VERSION+x}" ]; then QT_BIN_DIR=/opt/Qt/$QT_VERSION/gcc_64/bin elif [ -f $HOME/Qt/$QT_VERSION/gcc_64/bin/qmake ]; then QT_BIN_DIR=$HOME/Qt/$QT_VERSION/gcc_64/bin + elif [ -f /usr/lib/qt6/bin/qmake ]; then + QT_BIN_DIR=/usr/lib/qt6/bin + elif [ -f /usr/lib/x86_64-linux-gnu/qt6/bin/qmake ]; then + QT_BIN_DIR=/usr/lib/x86_64-linux-gnu/qt6/bin fi fi @@ -56,7 +60,7 @@ echo "Building App..." cd $BUILD_DIR $QT_BIN_DIR/qt-cmake -S $PROJECT_DIR -cmake --build . --config release +cmake --build . -j --config release # Build and run tests here diff --git a/ipc/ipc_interface.rep b/ipc/ipc_interface.rep index c0f031fe..4ecae9bc 100644 --- a/ipc/ipc_interface.rep +++ b/ipc/ipc_interface.rep @@ -29,6 +29,10 @@ class IpcInterface SLOT( void StopRoutingIpv6() ); SLOT( bool disableKillSwitch() ); + SLOT( bool disableAllTraffic() ); + SLOT( bool refreshKillSwitch( bool enabled ) ); + SLOT( bool addKillSwitchAllowedRange( const QStringList ranges ) ); + SLOT( bool resetKillSwitchAllowedRange( const QStringList ranges ) ); SLOT( bool enablePeerTraffic( const QJsonObject &configStr) ); SLOT( bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex) ); SLOT( bool updateResolvers(const QString& ifname, const QList& resolvers) ); diff --git a/ipc/ipcserver.cpp b/ipc/ipcserver.cpp index 17f34499..0c7f5295 100644 --- a/ipc/ipcserver.cpp +++ b/ipc/ipcserver.cpp @@ -8,21 +8,12 @@ #include "logger.h" #include "router.h" -#include "../core/networkUtilities.h" -#include "../client/protocols/protocols_defs.h" +#include "killswitch.h" + #ifdef Q_OS_WIN - #include "../client/platforms/windows/daemon/windowsdaemon.h" - #include "../client/platforms/windows/daemon/windowsfirewall.h" #include "tapcontroller_win.h" #endif -#ifdef Q_OS_LINUX - #include "../client/platforms/linux/daemon/linuxfirewall.h" -#endif - -#ifdef Q_OS_MACOS - #include "../client/platforms/macos/daemon/macosfirewall.h" -#endif IpcServer::IpcServer(QObject *parent) : IpcInterfaceSource(parent) @@ -188,174 +179,37 @@ void IpcServer::setLogsEnabled(bool enabled) } } +bool IpcServer::resetKillSwitchAllowedRange(QStringList ranges) +{ + return KillSwitch::instance()->resetAllowedRange(ranges); +} + +bool IpcServer::addKillSwitchAllowedRange(QStringList ranges) +{ + return KillSwitch::instance()->addAllowedRange(ranges); +} + +bool IpcServer::disableAllTraffic() +{ + return KillSwitch::instance()->disableAllTraffic(); +} + bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex) { -#ifdef Q_OS_WIN - auto firewallManager = WindowsFirewall::create(this); - Q_ASSERT(firewallManager != nullptr); - return firewallManager->enableInterface(vpnAdapterIndex); -#endif - -#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) - int splitTunnelType = configStr.value("splitTunnelType").toInt(); - QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray(); - bool blockAll = 0; - bool allowNets = 0; - bool blockNets = 0; - QStringList allownets; - QStringList blocknets; - - if (splitTunnelType == 0) { - blockAll = true; - allowNets = true; - allownets.append(configStr.value("vpnServer").toString()); - } else if (splitTunnelType == 1) { - blockNets = true; - for (auto v : splitTunnelSites) { - blocknets.append(v.toString()); - } - } else if (splitTunnelType == 2) { - blockAll = true; - allowNets = true; - allownets.append(configStr.value("vpnServer").toString()); - for (auto v : splitTunnelSites) { - allownets.append(v.toString()); - } - } -#endif - -#ifdef Q_OS_LINUX - // double-check + ensure our firewall is installed and enabled - if (!LinuxFirewall::isInstalled()) - LinuxFirewall::install(); - LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true); - LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), blockAll); - LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), allowNets); - LinuxFirewall::updateAllowNets(allownets); - LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), blockAll); - LinuxFirewall::updateBlockNets(blocknets); - LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), true); - LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true); - LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true); - 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()); - dnsServers.append("127.0.0.1"); - dnsServers.append("127.0.0.53"); - LinuxFirewall::updateDNSServers(dnsServers); - LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true); - LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true); -#endif - -#ifdef Q_OS_MACOS - - // double-check + ensure our firewall is installed and enabled. This is necessary as - // other software may disable pfctl before re-enabling with their own rules (e.g other VPNs) - if (!MacOSFirewall::isInstalled()) - MacOSFirewall::install(); - - MacOSFirewall::ensureRootAnchorPriority(); - MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true); - MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), blockAll); - MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), allowNets); - MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), allowNets, QStringLiteral("allownets"), allownets); - - MacOSFirewall::setAnchorEnabled(QStringLiteral("120.blockNets"), blockNets); - MacOSFirewall::setAnchorTable(QStringLiteral("120.blockNets"), blockNets, QStringLiteral("blocknets"), blocknets); - MacOSFirewall::setAnchorEnabled(QStringLiteral("200.allowVPN"), true); - MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true); - MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), true); - MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), true); - - QStringList dnsServers; - dnsServers.append(configStr.value(amnezia::config_key::dns1).toString()); - dnsServers.append(configStr.value(amnezia::config_key::dns2).toString()); - MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true); - MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), dnsServers); -#endif - - return true; + return KillSwitch::instance()->enableKillSwitch(configStr, vpnAdapterIndex); } bool IpcServer::disableKillSwitch() { -#ifdef Q_OS_WIN - auto firewallManager = WindowsFirewall::create(this); - Q_ASSERT(firewallManager != nullptr); - return firewallManager->disableKillSwitch(); -#endif - -#ifdef Q_OS_LINUX - LinuxFirewall::uninstall(); -#endif - -#ifdef Q_OS_MACOS - MacOSFirewall::uninstall(); -#endif - - return true; + return KillSwitch::instance()->disableKillSwitch(); } bool IpcServer::enablePeerTraffic(const QJsonObject &configStr) { -#ifdef Q_OS_WIN - InterfaceConfig config; - config.m_dnsServer = configStr.value(amnezia::config_key::dns1).toString(); - config.m_serverPublicKey = "openvpn"; - config.m_serverIpv4Gateway = configStr.value("vpnGateway").toString(); - config.m_serverIpv4AddrIn = configStr.value("vpnServer").toString(); - int vpnAdapterIndex = configStr.value("vpnAdapterIndex").toInt(); - int inetAdapterIndex = configStr.value("inetAdapterIndex").toInt(); - - int splitTunnelType = configStr.value("splitTunnelType").toInt(); - QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray(); - - QStringList AllowedIPAddesses; - - // Use APP split tunnel - if (splitTunnelType == 0 || splitTunnelType == 2) { - config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("0.0.0.0"), 0)); - config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("::"), 0)); - } - - if (splitTunnelType == 1) { - for (auto v : splitTunnelSites) { - QString ipRange = v.toString(); - if (ipRange.split('/').size() > 1) { - config.m_allowedIPAddressRanges.append( - IPAddress(QHostAddress(ipRange.split('/')[0]), atoi(ipRange.split('/')[1].toLocal8Bit()))); - } else { - config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress(ipRange), 32)); - } - } - } - - config.m_excludedAddresses.append(configStr.value("vpnServer").toString()); - if (splitTunnelType == 2) { - for (auto v : splitTunnelSites) { - QString ipRange = v.toString(); - config.m_excludedAddresses.append(ipRange); - } - } - - for (const QJsonValue &i : configStr.value(amnezia::config_key::splitTunnelApps).toArray()) { - if (!i.isString()) { - break; - } - config.m_vpnDisabledApps.append(i.toString()); - } - - // killSwitch toggle - if (QVariant(configStr.value(amnezia::config_key::killSwitchOption).toString()).toBool()) { - auto firewallManager = WindowsFirewall::create(this); - Q_ASSERT(firewallManager != nullptr); - firewallManager->enablePeerTraffic(config); - } - - WindowsDaemon::instance()->prepareActivation(config, inetAdapterIndex); - WindowsDaemon::instance()->activateSplitTunnel(config, vpnAdapterIndex); -#endif - return true; + return KillSwitch::instance()->enablePeerTraffic(configStr); +} + +bool IpcServer::refreshKillSwitch(bool enabled) +{ + return KillSwitch::instance()->refresh(enabled); } diff --git a/ipc/ipcserver.h b/ipc/ipcserver.h index 9810046b..00d36354 100644 --- a/ipc/ipcserver.h +++ b/ipc/ipcserver.h @@ -34,9 +34,13 @@ public: virtual bool deleteTun(const QString &dev) override; virtual void StartRoutingIpv6() override; virtual void StopRoutingIpv6() override; + virtual bool disableAllTraffic() override; + virtual bool addKillSwitchAllowedRange(QStringList ranges) override; + virtual bool resetKillSwitchAllowedRange(QStringList ranges) override; virtual bool enablePeerTraffic(const QJsonObject &configStr) override; virtual bool enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) override; virtual bool disableKillSwitch() override; + virtual bool refreshKillSwitch( bool enabled ) override; virtual bool updateResolvers(const QString& ifname, const QList& resolvers) override; private: diff --git a/service/server/CMakeLists.txt b/service/server/CMakeLists.txt index 28174774..aa7661fa 100644 --- a/service/server/CMakeLists.txt +++ b/service/server/CMakeLists.txt @@ -12,8 +12,47 @@ qt_standard_project_setup() configure_file(${CMAKE_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h) +set(QSIMPLECRYPTO_DIR ${CMAKE_CURRENT_LIST_DIR}/../../client/3rd/QSimpleCrypto/src) + + +set(OPENSSL_ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/../../client/3rd-prebuilt/3rd-prebuilt/openssl/") +set(OPENSSL_LIBRARIES_DIR "${OPENSSL_ROOT_DIR}/lib") + +set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/windows/include") +if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8") + set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/windows/win64/libssl.lib") + set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win64/libcrypto.lib") +else() + set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libssl.lib") + set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libcrypto.lib") +endif() + + +if(WIN32) + set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/windows/include") + if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8") + set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win64/libcrypto.lib") + else() + set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libcrypto.lib") + endif() +elseif(APPLE AND NOT IOS) + set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/macos/include") + set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libcrypto.a") +elseif(LINUX) + set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/linux/include") + set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/linux/x86_64/libcrypto.a") +endif() + +set(OPENSSL_USE_STATIC_LIBS TRUE) + +include_directories( + ${OPENSSL_INCLUDE_DIR} + ${QSIMPLECRYPTO_DIR} +) + set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/../../client/utilities.h + ${CMAKE_CURRENT_LIST_DIR}/../../client/secure_qsettings.h ${CMAKE_CURRENT_LIST_DIR}/../../client/core/networkUtilities.h ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipc.h ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.h @@ -22,12 +61,20 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/localserver.h ${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.h ${CMAKE_CURRENT_LIST_DIR}/router.h + ${CMAKE_CURRENT_LIST_DIR}/killswitch.h ${CMAKE_CURRENT_LIST_DIR}/systemservice.h ${CMAKE_CURRENT_BINARY_DIR}/version.h + ${QSIMPLECRYPTO_DIR}/include/QAead.h + ${QSIMPLECRYPTO_DIR}/include/QBlockCipher.h + ${QSIMPLECRYPTO_DIR}/include/QRsa.h + ${QSIMPLECRYPTO_DIR}/include/QSimpleCrypto_global.h + ${QSIMPLECRYPTO_DIR}/include/QX509.h + ${QSIMPLECRYPTO_DIR}/include/QX509Store.h ) set(SOURCES ${CMAKE_CURRENT_LIST_DIR}/../../client/utilities.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../client/secure_qsettings.cpp ${CMAKE_CURRENT_LIST_DIR}/../../client/core/networkUtilities.cpp ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.cpp ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserverprocess.cpp @@ -36,7 +83,13 @@ set(SOURCES ${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.cpp ${CMAKE_CURRENT_LIST_DIR}/main.cpp ${CMAKE_CURRENT_LIST_DIR}/router.cpp + ${CMAKE_CURRENT_LIST_DIR}/killswitch.cpp ${CMAKE_CURRENT_LIST_DIR}/systemservice.cpp + ${QSIMPLECRYPTO_DIR}/sources/QAead.cpp + ${QSIMPLECRYPTO_DIR}/sources/QBlockCipher.cpp + ${QSIMPLECRYPTO_DIR}/sources/QRsa.cpp + ${QSIMPLECRYPTO_DIR}/sources/QX509.cpp + ${QSIMPLECRYPTO_DIR}/sources/QX509Store.cpp ) # Mozilla headres @@ -133,6 +186,7 @@ if(WIN32) set(SOURCES ${SOURCES} ${CMAKE_CURRENT_LIST_DIR}/tapcontroller_win.cpp ${CMAKE_CURRENT_LIST_DIR}/router_win.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/daemon/windowsdaemon.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/daemon/windowsdaemontunnel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/daemon/windowsfirewall.cpp @@ -159,6 +213,8 @@ if(WIN32) gdi32 Advapi32 Kernel32 + ${OPENSSL_LIB_CRYPTO_PATH} + qt6keychain ) add_compile_definitions(_WINSOCKAPI_) @@ -203,6 +259,9 @@ if(APPLE) ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/macos/daemon/wireguardutilsmacos.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/macos/daemon/macosfirewall.cpp ) + + set(LIBS ${OPENSSL_LIB_CRYPTO_PATH} qt6keychain) + endif() if(LINUX) @@ -233,6 +292,9 @@ if(LINUX) ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxroutemonitor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxfirewall.cpp ) + + set(LIBS ${OPENSSL_LIB_CRYPTO_PATH} qt6keychain -static-libstdc++ -static-libgcc -ldl) + endif() include(${CMAKE_CURRENT_LIST_DIR}/../src/qtservice.cmake) @@ -245,6 +307,7 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) + add_executable(${PROJECT} ${SOURCES} ${HEADERS}) target_link_libraries(${PROJECT} PRIVATE Qt6::Core Qt6::Widgets Qt6::Network Qt6::RemoteObjects Qt6::Core5Compat Qt6::DBus ${LIBS}) target_compile_definitions(${PROJECT} PRIVATE "MZ_$") diff --git a/service/server/killswitch.cpp b/service/server/killswitch.cpp new file mode 100644 index 00000000..c44bd6a2 --- /dev/null +++ b/service/server/killswitch.cpp @@ -0,0 +1,358 @@ +#include "killswitch.h" + + +#include +#include + +#include "../client/protocols/protocols_defs.h" +#include "qjsonarray.h" +#include "version.h" + +#ifdef Q_OS_WIN + #include "../client/platforms/windows/daemon/windowsfirewall.h" + #include "../client/platforms/windows/daemon/windowsdaemon.h" +#endif + +#ifdef Q_OS_LINUX + #include "../client/platforms/linux/daemon/linuxfirewall.h" +#endif + +#ifdef Q_OS_MACOS + #include "../client/platforms/macos/daemon/macosfirewall.h" +#endif + +KillSwitch* s_instance = nullptr; + +KillSwitch* KillSwitch::instance() +{ + if (s_instance == nullptr) { + s_instance = new KillSwitch(qApp); + } + return s_instance; +} + +bool KillSwitch::init() +{ +#ifdef Q_OS_LINUX + if (!LinuxFirewall::isInstalled()) { + LinuxFirewall::install(); + } + m_appSettigns = QSharedPointer(new SecureQSettings(ORGANIZATION_NAME, APPLICATION_NAME, nullptr)); +#endif +#ifdef Q_OS_MACOS + if (!MacOSFirewall::isInstalled()) { + MacOSFirewall::install(); + } + m_appSettigns = QSharedPointer(new SecureQSettings(ORGANIZATION_NAME, APPLICATION_NAME, nullptr)); +#endif + if (isStrictKillSwitchEnabled()) { + return disableAllTraffic(); + } + + return true; +} + +bool KillSwitch::refresh(bool enabled) +{ +#ifdef Q_OS_WIN + QSettings RegHLM("HKEY_LOCAL_MACHINE\\Software\\" + QString(ORGANIZATION_NAME) + + "\\" + QString(APPLICATION_NAME), QSettings::NativeFormat); + RegHLM.setValue("strictKillSwitchEnabled", enabled); +#endif + +#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) + m_appSettigns->setValue("Conf/strictKillSwitchEnabled", enabled); +#endif + + if (isStrictKillSwitchEnabled()) { + return disableAllTraffic(); + } else { + return disableKillSwitch(); + } + +} + +bool KillSwitch::isStrictKillSwitchEnabled() +{ +#ifdef Q_OS_WIN + QSettings RegHLM("HKEY_LOCAL_MACHINE\\Software\\" + QString(ORGANIZATION_NAME) + + "\\" + QString(APPLICATION_NAME), QSettings::NativeFormat); + return RegHLM.value("strictKillSwitchEnabled", false).toBool(); +#endif + m_appSettigns->sync(); + return m_appSettigns->value("Conf/strictKillSwitchEnabled", false).toBool(); +} + +bool KillSwitch::disableKillSwitch() { +#ifdef Q_OS_LINUX + if (isStrictKillSwitchEnabled()) { + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), true); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), false); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), false); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), false); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), false); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), false); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), false); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), false); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), false); + } else { + LinuxFirewall::uninstall(); + } +#endif + +#ifdef Q_OS_MACOS + if (isStrictKillSwitchEnabled()) { + MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true); + MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), true); + MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), false); + MacOSFirewall::setAnchorEnabled(QStringLiteral("120.blockNets"), false); + MacOSFirewall::setAnchorEnabled(QStringLiteral("200.allowVPN"), false); + MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true); + MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), false); + MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), false); + MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), false); + } else { + MacOSFirewall::uninstall(); + } +#endif + +#ifdef Q_OS_WIN + if (isStrictKillSwitchEnabled()) { + return disableAllTraffic(); + } + return WindowsFirewall::create(this)->allowAllTraffic(); +#endif + + m_allowedRanges.clear(); + return true; +} + +bool KillSwitch::disableAllTraffic() { +#ifdef Q_OS_WIN + WindowsFirewall::create(this)->enableInterface(-1); +#endif +#ifdef Q_OS_LINUX + if (!LinuxFirewall::isInstalled()) { + LinuxFirewall::install(); + } + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), true); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true); +#endif +#ifdef Q_OS_MACOS + // double-check + ensure our firewall is installed and enabled. This is necessary as + // other software may disable pfctl before re-enabling with their own rules (e.g other VPNs) + if (!MacOSFirewall::isInstalled()) + MacOSFirewall::install(); + MacOSFirewall::ensureRootAnchorPriority(); + MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), true); + MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true); + MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true); +#endif + m_allowedRanges.clear(); + return true; +} + +bool KillSwitch::resetAllowedRange(const QStringList &ranges) { + + m_allowedRanges = ranges; + +#ifdef Q_OS_LINUX + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), true); + LinuxFirewall::updateAllowNets(m_allowedRanges); +#endif + +#ifdef Q_OS_MACOS + MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), true); + MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), true, QStringLiteral("allownets"), m_allowedRanges); +#endif + +#ifdef Q_OS_WIN + if (isStrictKillSwitchEnabled()) { + WindowsFirewall::create(this)->enableInterface(-1); + } + WindowsFirewall::create(this)->allowTrafficRange(m_allowedRanges); +#endif + + return true; +} + +bool KillSwitch::addAllowedRange(const QStringList &ranges) { + for (const QString &range : ranges) { + if (!range.isEmpty() && !m_allowedRanges.contains(range)) { + m_allowedRanges.append(range); + } + } + + return resetAllowedRange(m_allowedRanges); +} + +bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) { +#ifdef Q_OS_WIN + InterfaceConfig config; + config.m_dnsServer = configStr.value(amnezia::config_key::dns1).toString(); + config.m_serverPublicKey = "openvpn"; + config.m_serverIpv4Gateway = configStr.value("vpnGateway").toString(); + config.m_serverIpv4AddrIn = configStr.value("vpnServer").toString(); + int vpnAdapterIndex = configStr.value("vpnAdapterIndex").toInt(); + int inetAdapterIndex = configStr.value("inetAdapterIndex").toInt(); + + int splitTunnelType = configStr.value("splitTunnelType").toInt(); + QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray(); + + // Use APP split tunnel + if (splitTunnelType == 0 || splitTunnelType == 2) { + config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("0.0.0.0"), 0)); + config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("::"), 0)); + } + + if (splitTunnelType == 1) { + for (auto v : splitTunnelSites) { + QString ipRange = v.toString(); + if (ipRange.split('/').size() > 1) { + config.m_allowedIPAddressRanges.append( + IPAddress(QHostAddress(ipRange.split('/')[0]), atoi(ipRange.split('/')[1].toLocal8Bit()))); + } else { + config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress(ipRange), 32)); + } + } + } + + config.m_excludedAddresses.append(configStr.value("vpnServer").toString()); + if (splitTunnelType == 2) { + for (auto v : splitTunnelSites) { + QString ipRange = v.toString(); + config.m_excludedAddresses.append(ipRange); + } + } + + for (const QJsonValue &i : configStr.value(amnezia::config_key::splitTunnelApps).toArray()) { + if (!i.isString()) { + break; + } + config.m_vpnDisabledApps.append(i.toString()); + } + + for (auto dns : configStr.value(amnezia::config_key::allowedDnsServers).toArray()) { + if (!dns.isString()) { + break; + } + config.m_allowedDnsServers.append(dns.toString()); + } + + // killSwitch toggle + if (QVariant(configStr.value(amnezia::config_key::killSwitchOption).toString()).toBool()) { + WindowsFirewall::create(this)->enablePeerTraffic(config); + } + + WindowsDaemon::instance()->prepareActivation(config, inetAdapterIndex); + WindowsDaemon::instance()->activateSplitTunnel(config, vpnAdapterIndex); +#endif + return true; +} + +bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex) { +#ifdef Q_OS_WIN + return WindowsFirewall::create(this)->enableInterface(vpnAdapterIndex); +#endif + +#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) + int splitTunnelType = configStr.value("splitTunnelType").toInt(); + QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray(); + bool blockAll = 0; + bool allowNets = 0; + bool blockNets = 0; + QStringList allownets; + QStringList blocknets; + + if (splitTunnelType == 0) { + blockAll = true; + allowNets = true; + allownets.append(configStr.value("vpnServer").toString()); + } else if (splitTunnelType == 1) { + blockNets = true; + for (auto v : splitTunnelSites) { + blocknets.append(v.toString()); + } + } else if (splitTunnelType == 2) { + blockAll = true; + allowNets = true; + allownets.append(configStr.value("vpnServer").toString()); + for (auto v : splitTunnelSites) { + allownets.append(v.toString()); + } + } +#endif + +#ifdef Q_OS_LINUX + if (!LinuxFirewall::isInstalled()) { + LinuxFirewall::install(); + } + + // double-check + ensure our firewall is installed and enabled + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), blockAll); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), allowNets); + LinuxFirewall::updateAllowNets(allownets); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), blockAll); + LinuxFirewall::updateBlockNets(blocknets); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), true); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true); + 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()); + dnsServers.append("127.0.0.1"); + dnsServers.append("127.0.0.53"); + + for (auto dns : configStr.value(amnezia::config_key::allowedDnsServers).toArray()) { + if (!dns.isString()) { + break; + } + dnsServers.append(dns.toString()); + } + + LinuxFirewall::updateDNSServers(dnsServers); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true); +#endif + +#ifdef Q_OS_MACOS + // double-check + ensure our firewall is installed and enabled. This is necessary as + // other software may disable pfctl before re-enabling with their own rules (e.g other VPNs) + if (!MacOSFirewall::isInstalled()) + MacOSFirewall::install(); + + MacOSFirewall::ensureRootAnchorPriority(); + MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true); + MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), blockAll); + MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), allowNets); + MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), allowNets, QStringLiteral("allownets"), allownets); + + MacOSFirewall::setAnchorEnabled(QStringLiteral("120.blockNets"), blockNets); + MacOSFirewall::setAnchorTable(QStringLiteral("120.blockNets"), blockNets, QStringLiteral("blocknets"), blocknets); + MacOSFirewall::setAnchorEnabled(QStringLiteral("200.allowVPN"), true); + MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true); + MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), true); + MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), true); + + QStringList dnsServers; + dnsServers.append(configStr.value(amnezia::config_key::dns1).toString()); + dnsServers.append(configStr.value(amnezia::config_key::dns2).toString()); + + for (auto dns : configStr.value(amnezia::config_key::allowedDnsServers).toArray()) { + if (!dns.isString()) { + break; + } + dnsServers.append(dns.toString()); + } + + MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true); + MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), dnsServers); +#endif + return true; +} diff --git a/service/server/killswitch.h b/service/server/killswitch.h new file mode 100644 index 00000000..519e2ed2 --- /dev/null +++ b/service/server/killswitch.h @@ -0,0 +1,31 @@ +#ifndef KILLSWITCH_H +#define KILLSWITCH_H + +#include +#include + +#include "secure_qsettings.h" + +class KillSwitch : public QObject +{ + Q_OBJECT +public: + static KillSwitch *instance(); + bool init(); + bool refresh(bool enabled); + bool disableKillSwitch(); + bool disableAllTraffic(); + bool enablePeerTraffic(const QJsonObject &configStr); + bool enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex); + bool resetAllowedRange(const QStringList &ranges); + bool addAllowedRange(const QStringList &ranges); + bool isStrictKillSwitchEnabled(); + +private: + KillSwitch(QObject* parent) {}; + QStringList m_allowedRanges; + QSharedPointer m_appSettigns; + +}; + +#endif // KILLSWITCH_H diff --git a/service/server/localserver.cpp b/service/server/localserver.cpp index 8a5079cb..4f005a59 100644 --- a/service/server/localserver.cpp +++ b/service/server/localserver.cpp @@ -5,13 +5,12 @@ #include "ipc.h" #include "localserver.h" -#include "utilities.h" -#include "router.h" +#include "killswitch.h" #include "logger.h" #ifdef Q_OS_WIN -#include "tapcontroller_win.h" + #include "tapcontroller_win.h" #endif namespace { @@ -47,6 +46,8 @@ LocalServer::LocalServer(QObject *parent) : QObject(parent), return; } + KillSwitch::instance()->init(); + #ifdef Q_OS_LINUX // Signal handling for a proper shutdown. QObject::connect(qApp, &QCoreApplication::aboutToQuit, From b86356b0cc3b42b7452fab9f2ae43a0595e30153 Mon Sep 17 00:00:00 2001 From: aiamnezia Date: Sat, 3 May 2025 10:56:50 +0400 Subject: [PATCH 25/65] bugfix: fix ListViewType scrolling (#1550) * Fix ListViewType scrolling on country selection page * Disable highlightFollowsCurrentItem for country selection page * Fix scrolling on container DropDown * Fix ListView height * Fix listview layout in DropDownType * Remove unnecessary MouseArea from country selection page --- client/ui/qml/Components/HomeContainersListView.qml | 3 ++- client/ui/qml/Controls2/DropDownType.qml | 1 + client/ui/qml/Controls2/VerticalRadioButton.qml | 1 + client/ui/qml/Pages2/PageSettingsApiAvailableCountries.qml | 6 ------ 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/client/ui/qml/Components/HomeContainersListView.qml b/client/ui/qml/Components/HomeContainersListView.qml index 337918f1..1cbdd82d 100644 --- a/client/ui/qml/Components/HomeContainersListView.qml +++ b/client/ui/qml/Components/HomeContainersListView.qml @@ -18,7 +18,8 @@ ListView { property var selectedText width: rootWidth - height: contentItem.height + anchors.top: parent.top + anchors.bottom: parent.bottom clip: true snapMode: ListView.SnapToItem diff --git a/client/ui/qml/Controls2/DropDownType.qml b/client/ui/qml/Controls2/DropDownType.qml index ae6dac85..633b11cf 100644 --- a/client/ui/qml/Controls2/DropDownType.qml +++ b/client/ui/qml/Controls2/DropDownType.qml @@ -239,6 +239,7 @@ Item { sourceComponent: root.listView Layout.fillHeight: true + Layout.fillWidth: true } } } diff --git a/client/ui/qml/Controls2/VerticalRadioButton.qml b/client/ui/qml/Controls2/VerticalRadioButton.qml index 1c878f15..7ad6afc8 100644 --- a/client/ui/qml/Controls2/VerticalRadioButton.qml +++ b/client/ui/qml/Controls2/VerticalRadioButton.qml @@ -190,6 +190,7 @@ RadioButton { MouseArea { anchors.fill: root cursorShape: Qt.PointingHandCursor + preventStealing: false enabled: false } } diff --git a/client/ui/qml/Pages2/PageSettingsApiAvailableCountries.qml b/client/ui/qml/Pages2/PageSettingsApiAvailableCountries.qml index 3e999cff..ce57e97e 100644 --- a/client/ui/qml/Pages2/PageSettingsApiAvailableCountries.qml +++ b/client/ui/qml/Pages2/PageSettingsApiAvailableCountries.qml @@ -135,12 +135,6 @@ PageType { } } - MouseArea { - anchors.fill: containerRadioButton - cursorShape: Qt.PointingHandCursor - enabled: false - } - Keys.onEnterPressed: { if (checkable) { checked = true From e59a48f9f4f3abc84f630f21661718bf29a6db16 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Tue, 6 May 2025 08:11:58 -0700 Subject: [PATCH 26/65] Fixes for Windows killswitch (#1565) * fix: Win OpenVPN with strict mode killswitch * Fixes for Windows killswitch --- client/platforms/windows/daemon/windowsfirewall.cpp | 2 +- client/protocols/openvpnprotocol.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/platforms/windows/daemon/windowsfirewall.cpp b/client/platforms/windows/daemon/windowsfirewall.cpp index 85a2a155..1834452e 100644 --- a/client/platforms/windows/daemon/windowsfirewall.cpp +++ b/client/platforms/windows/daemon/windowsfirewall.cpp @@ -256,7 +256,7 @@ bool WindowsFirewall::allowTrafficRange(const QStringList& ranges) { for (const QString& addr : ranges) { logger.debug() << "Allow killswitch exclude: " << addr; - if (!allowTrafficTo(QHostAddress(addr), LOW_WEIGHT + 1, "Allow killswitch bypass traffic")) { + if (!allowTrafficTo(QHostAddress(addr), HIGH_WEIGHT, "Allow killswitch bypass traffic")) { return false; } } diff --git a/client/protocols/openvpnprotocol.cpp b/client/protocols/openvpnprotocol.cpp index 0c8f5907..429b85a6 100644 --- a/client/protocols/openvpnprotocol.cpp +++ b/client/protocols/openvpnprotocol.cpp @@ -171,7 +171,7 @@ ErrorCode OpenVpnProtocol::start() return lastError(); } -#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) +#ifdef AMNEZIA_DESKTOP IpcClient::Interface()->addKillSwitchAllowedRange(QStringList(NetworkUtilities::getIPAddress( m_configData.value(amnezia::config_key::hostName).toString()))); #endif From 2c44999a31fc0ad5b3e10a03ec5e251111bf81a8 Mon Sep 17 00:00:00 2001 From: Mitternacht822 Date: Wed, 7 May 2025 17:17:42 +0400 Subject: [PATCH 27/65] Fixed bug with not applying changes to subnet address when reinstalling server (#1546) * fixed bug with not applying changes to subnet address when reinstalling server * fixed wireguard empty 'subnet address' field after reinstalling and removed showing mask for AWG and wireguard in UI --- client/ui/controllers/installController.cpp | 68 ++++++++++++++++++- .../qml/Pages2/PageProtocolXraySettings.qml | 4 +- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/client/ui/controllers/installController.cpp b/client/ui/controllers/installController.cpp index 7a6d8d40..eab8979a 100755 --- a/client/ui/controllers/installController.cpp +++ b/client/ui/controllers/installController.cpp @@ -363,7 +363,8 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia QJsonObject config; Proto mainProto = ContainerProps::defaultProtocol(container); - for (auto protocol : ContainerProps::protocolsForContainer(container)) { + const auto &protocols = ContainerProps::protocolsForContainer(container); + for (const auto &protocol : protocols) { QJsonObject containerConfig; if (protocol == mainProto) { containerConfig.insert(config_key::port, port); @@ -387,6 +388,7 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia } } + containerConfig[config_key::subnet_address] = serverConfigMap.value("Address").remove("/24"); containerConfig[config_key::junkPacketCount] = serverConfigMap.value(config_key::junkPacketCount); containerConfig[config_key::junkPacketMinSize] = serverConfigMap.value(config_key::junkPacketMinSize); containerConfig[config_key::junkPacketMaxSize] = serverConfigMap.value(config_key::junkPacketMaxSize); @@ -398,6 +400,25 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia serverConfigMap.value(config_key::underloadPacketMagicHeader); containerConfig[config_key::transportPacketMagicHeader] = serverConfigMap.value(config_key::transportPacketMagicHeader); + + } else if (protocol == Proto::WireGuard) { + QString serverConfig = serverController->getTextFileFromContainer(container, credentials, + protocols::wireguard::serverConfigPath, errorCode); + + QMap serverConfigMap; + auto serverConfigLines = serverConfig.split("\n"); + for (auto &line : serverConfigLines) { + auto trimmedLine = line.trimmed(); + if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) { + continue; + } else { + QStringList parts = trimmedLine.split(" = "); + if (parts.count() == 2) { + serverConfigMap.insert(parts[0].trimmed(), parts[1].trimmed()); + } + } + } + containerConfig[config_key::subnet_address] = serverConfigMap.value("Address").remove("/24"); } else if (protocol == Proto::Sftp) { stdOut.clear(); script = QString("sudo docker inspect --format '{{.Config.Cmd}}' %1").arg(name); @@ -432,6 +453,51 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia containerConfig.insert(config_key::userName, userName); containerConfig.insert(config_key::password, password); } + } else if (protocol == Proto::Xray) { + QString currentConfig = serverController->getTextFileFromContainer( + container, credentials, amnezia::protocols::xray::serverConfigPath, errorCode); + + QJsonDocument doc = QJsonDocument::fromJson(currentConfig.toUtf8()); + qDebug() << doc; + if (doc.isNull() || !doc.isObject()) { + logger.error() << "Failed to parse server config JSON"; + errorCode = ErrorCode::InternalError; + return errorCode; + } + QJsonObject serverConfig = doc.object(); + + if (!serverConfig.contains("inbounds")) { + logger.error() << "Server config missing 'inbounds' field"; + errorCode = ErrorCode::InternalError; + return errorCode; + } + + QJsonArray inbounds = serverConfig["inbounds"].toArray(); + if (inbounds.isEmpty()) { + logger.error() << "Server config has empty 'inbounds' array"; + errorCode = ErrorCode::InternalError; + return errorCode; + } + + QJsonObject inbound = inbounds[0].toObject(); + if (!inbound.contains("streamSettings")) { + logger.error() << "Inbound missing 'streamSettings' field"; + errorCode = ErrorCode::InternalError; + return errorCode; + } + + QJsonObject streamSettings = inbound["streamSettings"].toObject(); + QJsonObject realitySettings = streamSettings["realitySettings"].toObject(); + if (!realitySettings.contains("serverNames")) { + logger.error() << "Settings missing 'clients' field"; + errorCode = ErrorCode::InternalError; + return errorCode; + } + + QString siteName = realitySettings["serverNames"][0].toString(); + qDebug() << siteName; + + containerConfig.insert(config_key::site, siteName); } config.insert(config_key::container, ContainerProps::containerToString(container)); diff --git a/client/ui/qml/Pages2/PageProtocolXraySettings.qml b/client/ui/qml/Pages2/PageProtocolXraySettings.qml index ca30e0a9..bef84af7 100644 --- a/client/ui/qml/Pages2/PageProtocolXraySettings.qml +++ b/client/ui/qml/Pages2/PageProtocolXraySettings.qml @@ -93,9 +93,9 @@ PageType { var tmpText = textField.text tmpText = tmpText.toLocaleLowerCase() - var indexHttps = tmpText.indexOf("https://") - if (indexHttps === 0) { + if (tmpText.startsWith("https://")) { tmpText = textField.text.substring(8) + site = tmpText } else { site = textField.text } From acc4485e8190471c2e58bd7632aba62788ffba85 Mon Sep 17 00:00:00 2001 From: Nethius Date: Wed, 7 May 2025 21:18:11 +0800 Subject: [PATCH 28/65] bugfix: improve malicious string detection for openvpn configs (#1571) * bugfix: improve malicious string detection for openvpn configs --- client/ui/controllers/importController.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/client/ui/controllers/importController.cpp b/client/ui/controllers/importController.cpp index be66d8f3..fdc06120 100644 --- a/client/ui/controllers/importController.cpp +++ b/client/ui/controllers/importController.cpp @@ -665,27 +665,27 @@ void ImportController::checkForMaliciousStrings(const QJsonObject &serverConfig) containerConfig[ProtocolProps::protoToString(Proto::OpenVpn)].toObject()[config_key::last_config].toString(); QString protocolConfigJson = QJsonDocument::fromJson(protocolConfig.toUtf8()).object()[config_key::config].toString(); - const QRegularExpression regExp { "(\\w+-\\w+|\\w+)" }; - const size_t dangerousTagsMaxCount = 3; - // https://github.com/OpenVPN/openvpn/blob/master/doc/man-sections/script-options.rst QStringList dangerousTags { "up", "tls-verify", "ipchange", "client-connect", "route-up", "route-pre-down", "client-disconnect", "down", "learn-address", "auth-user-pass-verify" }; QStringList maliciousStrings; - QStringList lines = protocolConfigJson.replace("\r", "").split("\n"); - for (const QString &l : lines) { - QRegularExpressionMatch match = regExp.match(l); - if (dangerousTags.contains(match.captured(0))) { - maliciousStrings << l; + QStringList lines = protocolConfigJson.split('\n', Qt::SkipEmptyParts); + + for (const QString &rawLine : lines) { + QString line = rawLine.trimmed(); + + QString command = line.section(' ', 0, 0, QString::SectionSkipEmpty); + if (dangerousTags.contains(command, Qt::CaseInsensitive)) { + maliciousStrings << rawLine; } } m_maliciousWarningText = tr("This configuration contains an OpenVPN setup. OpenVPN configurations can include malicious " "scripts, so only add it if you fully trust the provider of this config. "); - if (maliciousStrings.size() >= dangerousTagsMaxCount) { + if (!maliciousStrings.isEmpty()) { m_maliciousWarningText.push_back(tr("
In the imported configuration, potentially dangerous lines were found:")); for (const auto &string : maliciousStrings) { m_maliciousWarningText.push_back(QString("
%1").arg(string)); From 63b5257986baa9dc30af0e73bd3a3bb88fe5d6b2 Mon Sep 17 00:00:00 2001 From: MrMirDan <58086007+MrMirDan@users.noreply.github.com> Date: Mon, 12 May 2025 10:31:41 +0300 Subject: [PATCH 29/65] chore: update text translations and text (#1573) --- client/containers/containers_defs.cpp | 153 +++-- client/translations/amneziavpn_ru_RU.ts | 537 +++++++----------- client/ui/models/api/apiServicesModel.cpp | 4 +- .../ui/qml/Pages2/PageSettingsApiDevices.qml | 2 +- .../qml/Pages2/PageSettingsApiServerInfo.qml | 2 +- 5 files changed, 290 insertions(+), 408 deletions(-) diff --git a/client/containers/containers_defs.cpp b/client/containers/containers_defs.cpp index 52b148c0..214e2a51 100644 --- a/client/containers/containers_defs.cpp +++ b/client/containers/containers_defs.cpp @@ -140,98 +140,83 @@ QMap ContainerProps::containerDetailedDescriptions() { return { { DockerContainer::OpenVpn, - QObject::tr( - "OpenVPN stands as one of the most popular and time-tested VPN protocols available.\n" - "It employs its unique security protocol, " - "leveraging the strength of SSL/TLS for encryption and key exchange. " - "Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, " - "catering to a wide range of devices and operating systems. " - "Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, " - "which continually reinforces its security. " - "With a strong balance of performance, security, and compatibility, " - "OpenVPN remains a top choice for privacy-conscious individuals and businesses alike.\n\n" - "* Available in the AmneziaVPN across all platforms\n" - "* Normal power consumption on mobile devices\n" - "* Flexible customisation to suit user needs to work with different operating systems and devices\n" - "* Recognised by DPI systems and therefore susceptible to blocking\n" - "* Can operate over both TCP and UDP network protocols.") }, + QObject::tr("OpenVPN is one of the most popular and reliable VPN protocols. " + "It uses SSL/TLS encryption, supports a wide variety of devices and operating systems, " + "and is continuously improved by the community due to its open-source nature. " + "It provides a good balance between speed and security but is easily recognized by DPI systems, " + "making it susceptible to blocking.\n" + "\nFeatures:\n" + "* Available on all AmneziaVPN platforms\n" + "* Normal battery consumption on mobile devices\n" + "* Flexible customization for various devices and OS\n" + "* Operates over both TCP and UDP protocols") }, { DockerContainer::ShadowSocks, - QObject::tr("Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. " - "Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection." - "However, certain traffic analysis systems might still detect a Shadowsocks connection. " - "Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol.\n\n" - "* Available in the AmneziaVPN only on desktop platforms\n" - "* Configurable encryption protocol\n" + QObject::tr("Shadowsocks is based on the SOCKS5 protocol and encrypts connections using AEAD cipher. " + "Although designed to be discreet, it doesn't mimic a standard HTTPS connection and can be detected by some DPI systems. " + "Due to limited support in Amnezia, we recommend using the AmneziaWG protocol.\n" + "\nFeatures:\n" + "* Available in AmneziaVPN only on desktop platforms\n" + "* Customizable encryption protocol\n" "* Detectable by some DPI systems\n" - "* Works over TCP network protocol.") }, + "* Operates over TCP protocol\n") }, { DockerContainer::Cloak, - QObject::tr("This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for " - "protecting against detection.\n\n" - "OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client " - "and the server.\n\n" - "Cloak protects OpenVPN from detection. \n\n" - "Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, " - "and also protects the VPN from detection by Active Probing. This makes it very resistant to " - "being detected\n\n" - "Immediately after receiving the first data packet, Cloak authenticates the incoming connection. " - "If authentication fails, the plugin masks the server as a fake website and your VPN becomes " - "invisible to analysis systems.\n\n" - "* Available in the AmneziaVPN across all platforms\n" + QObject::tr("This combination includes the OpenVPN protocol and the Cloak plugin, specifically designed to protect against blocking.\n" + "\nOpenVPN securely encrypts all internet traffic between your device and the server.\n" + "\nThe Cloak plugin further protects the connection from DPI detection. " + "It modifies traffic metadata to disguise VPN traffic as regular web traffic and prevents detection through active probing. " + "If an incoming connection fails authentication, Cloak serves a fake website, making your VPN invisible to traffic analysis systems.\n" + "\nIn regions with heavy internet censorship, we strongly recommend using OpenVPN with Cloak from your first connection.\n" + "\nFeatures:\n" + "* Available on all AmneziaVPN platforms\n" "* High power consumption on mobile devices\n" - "* Flexible settings\n" - "* Not recognised by detection systems\n" - "* Works over TCP network protocol, 443 port.\n") }, + "* Flexible configuration options\n" + "* Undetectable by DPI systems\n" + "* Operates over TCP protocol on port 443") }, { DockerContainer::WireGuard, - QObject::tr("A relatively new popular VPN protocol with a simplified architecture.\n" - "WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption " - "settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput.\n" - "WireGuard is very susceptible to detection and blocking due to its distinct packet signatures. " - "Unlike some other VPN protocols that employ obfuscation techniques, " - "the consistent signature patterns of WireGuard packets can be more easily identified and " - "thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools.\n\n" - "* Available in the AmneziaVPN across all platforms\n" - "* Low power consumption\n" - "* Minimum number of settings\n" - "* Easily recognised by DPI analysis systems, susceptible to blocking\n" - "* Works over UDP network protocol.") }, + QObject::tr("WireGuard is a modern, streamlined VPN protocol offering stable connectivity and excellent performance across all devices. " + "It uses fixed encryption settings, delivering lower latency and higher data transfer speeds compared to OpenVPN. " + "However, WireGuard is easily identifiable by DPI systems due to its distinctive packet signatures, making it susceptible to blocking.\n" + "\nFeatures:\n" + "* Available on all AmneziaVPN platforms\n" + "* Low power consumption on mobile devices\n" + "* Minimal configuration required\n" + "* Easily detected by DPI systems (susceptible to blocking)\n" + "* Operates over UDP protocol") }, { DockerContainer::Awg, - QObject::tr("A modern iteration of the popular VPN protocol, " - "AmneziaWG builds upon the foundation set by WireGuard, " - "retaining its simplified architecture and high-performance capabilities across devices.\n" - "While WireGuard is known for its efficiency, " - "it had issues with being easily detected due to its distinct packet signatures. " - "AmneziaWG solves this problem by using better obfuscation methods, " - "making its traffic blend in with regular internet traffic.\n" - "This means that AmneziaWG keeps the fast performance of the original " - "while adding an extra layer of stealth, " - "making it a great choice for those wanting a fast and discreet VPN connection.\n\n" - "* Available in the AmneziaVPN across all platforms\n" - "* Low power consumption\n" - "* Minimum number of settings\n" - "* Not recognised by traffic analysis systems\n" - "* Works over UDP network protocol.") }, + QObject::tr("AmneziaWG is a modern VPN protocol based on WireGuard, " + "combining simplified architecture with high performance across all devices. " + "It addresses WireGuard's main vulnerability (easy detection by DPI systems) through advanced obfuscation techniques, " + "making VPN traffic indistinguishable from regular internet traffic.\n" + "\nAmneziaWG is an excellent choice for those seeking a fast, stealthy VPN connection.\n" + "\nFeatures:\n" + "* Available on all AmneziaVPN platforms\n" + "* Low battery consumption on mobile devices\n" + "* Minimal settings required\n" + "* Undetectable by traffic analysis systems (DPI)\n" + "* Operates over UDP protocol") }, { DockerContainer::Xray, - QObject::tr("The REALITY protocol, a pioneering development by the creators of XRay, " - "is designed to provide the highest level of protection against detection through its innovative approach to security and privacy.\n" - "It uniquely identifies attackers during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting attackers to genuine websites, " - "thus presenting an authentic TLS certificate and data. \n" - "This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, " - "legitimate sites without the need for specific configurations. \n" - "Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, " - "REALITY's innovative \"friend or foe\" recognition at the TLS handshake enhances security. " - "This makes REALITY a robust solution for maintaining internet freedom.") - }, + QObject::tr("REALITY is an innovative protocol developed by the creators of XRay, designed specifically to combat high levels of internet censorship. " + "REALITY identifies censorship systems during the TLS handshake, " + "redirecting suspicious traffic seamlessly to legitimate websites like google.com while providing genuine TLS certificates. " + "This allows VPN traffic to blend indistinguishably with regular web traffic without special configuration." + "\nUnlike older protocols such as VMess, VLESS, and XTLS-Vision, REALITY incorporates an advanced built-in \"friend-or-foe\" detection mechanism, " + "effectively protecting against DPI and other traffic analysis methods.\n" + "\nFeatures:\n" + "* Resistant to active probing and DPI detection\n" + "* No special configuration required to disguise traffic\n" + "* Highly effective in heavily censored regions\n" + "* Minimal battery consumption on devices\n" + "* Operates over TCP protocol") }, { DockerContainer::Ipsec, - QObject::tr("IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol.\n" - "One of its distinguishing features is its ability to swiftly switch between networks and devices, " - "making it particularly adaptive in dynamic network environments. \n" - "While it offers a blend of security, stability, and speed, " - "it's essential to note that IKEv2 can be easily detected and is susceptible to blocking.\n\n" - "* Available in the AmneziaVPN only on Windows\n" - "* Low power consumption, on mobile devices\n" - "* Minimal configuration\n" - "* Recognised by DPI analysis systems\n" - "* Works over UDP network protocol, ports 500 and 4500.") }, + QObject::tr("IKEv2, combined with IPSec encryption, is a modern and reliable VPN protocol. " + "It reconnects quickly when switching networks or devices, making it ideal for dynamic network environments. " + "While it provides good security and speed, it's easily recognized by DPI systems and susceptible to blocking.\n" + "\nFeatures:\n" + "* Available in AmneziaVPN only on Windows\n" + "* Low battery consumption on mobile devices\n" + "* Minimal configuration required\n" + "* Detectable by DPI analysis systems(easily blocked)\n" + "* Operates over UDP protocol(ports 500 and 4500)") }, { DockerContainer::TorWebSite, QObject::tr("Website in Tor network") }, { DockerContainer::Dns, QObject::tr("DNS Service") }, diff --git a/client/translations/amneziavpn_ru_RU.ts b/client/translations/amneziavpn_ru_RU.ts index c740a560..e653b32b 100644 --- a/client/translations/amneziavpn_ru_RU.ts +++ b/client/translations/amneziavpn_ru_RU.ts @@ -100,8 +100,8 @@ - AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan. - AmneziaFree предоставляет бесплатный неограниченный доступ к базовому набору сайтов и приложений, таким как Facebook, Instagram, Twitter (X), Discord, Telegram и другим. YouTube не включен в бесплатный тариф. + Amnezia Free provides unlimited, free access to a basic set of websites and apps, including Facebook, Instagram, Twitter (X), Discord, Telegram, and more. YouTube is not included in the free plan. + Amnezia Free позволяет бесплатно и без ограничений пользоваться базовым набором сайтов и приложений, включая Facebook, Instagram, Twitter (X), Discord, Telegram и другие. YouTube не входит в бесплатный тариф. Amnezia Premium is VPN for comfortable work, downloading large files and watching videos in 8K resolution. Works for any sites with no restrictions. @@ -127,6 +127,11 @@ %1 days %1 дней + + + + + VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. Other sites will be opened from your real IP address, <a href="%1/free" style="color: #FBB26A;">more details on the website.</a> @@ -1655,8 +1660,12 @@ Already installed containers were found on the server. All installed containers + This will unlink the device from your subscription. You can reconnect it anytime by pressing "Reload API config" in subscription settings on device. + Это отключит устройство от вашей подписки. Вы можете повторно подключить его в любое время, нажав "Перезагрузить конфигурацию API" в настройках подписки на устройстве. + + This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect. - Это устройство будет отвязано от вашей подписки. Вы можете подключить его снова в любой момент, нажав кнопку "Подключиться". + Это устройство будет отвязано от вашей подписки. Вы можете подключить его снова в любой момент, нажав кнопку "Подключиться". @@ -2007,8 +2016,12 @@ Already installed containers were found on the server. All installed containers + This will unlink the device from your subscription. You can reconnect it anytime by pressing "Reload API config" in subscription settings on device. + Это отключит устройство от вашей подписки. Вы можете повторно подключить его в любое время, нажав "Перезагрузить конфигурацию API" в настройках подписки на устройстве. + + This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect. - Это устройство будет отвязано от вашей подписки. Вы можете подключить его снова в любой момент, нажав кнопку Подключиться. + Это устройство будет отвязано от вашей подписки. Вы можете подключить его снова в любой момент, нажав кнопку Подключиться. @@ -2618,11 +2631,6 @@ Already installed containers were found on the server. All installed containers No new installed containers found Новые установленные протоколы и сервисы не обнаружены - - - - - @@ -2787,11 +2795,6 @@ Already installed containers were found on the server. All installed containers Clear %1 profile? Очистить профиль %1? - - - - - connection settings @@ -3045,7 +3048,7 @@ It's okay as long as it's from someone you trust. Что у вас есть? - + File with connection settings Файл с настройками подключения @@ -3054,112 +3057,135 @@ It's okay as long as it's from someone you trust. Файл с настройками подключения или резервной копией
- + Connection Соединение - + Settings Настройки - + Enable logs Включить запись логов + Export client logs + + + + + Save + Сохранить + + + + Logs files (*.log) + Файлы логов (*.log) + + + + Logs file saved + Файл с логами сохранен + + + Support tag Support tag - + Copied Скопировано - + Insert the key, add a configuration file or scan the QR-code Вставьте ключ, добавьте файл конфигурации или отсканируйте QR-код - + Insert key Вставьте ключ - + Insert Вставить - + Continue Продолжить - + Other connection options Другие варианты подключения - + Site Amnezia Сайт Amnezia - + VPN by Amnezia VPN от Amnezia - + Connect to classic paid and free VPN services from Amnezia Подключайтесь к классическим платным и бесплатным VPN-сервисам от Amnezia - + Self-hosted VPN Self-hosted VPN - + Configure Amnezia VPN on your own server Настроить VPN на собственном сервере - + Restore from backup Восстановить из резервной копии - + + + + - + Open backup file Открыть резервную копию - + Backup files (*.backup) Файлы резервных копий (*.backup) - + Open config file Открыть файл с конфигурацией - + QR code QR-код - + I have nothing У меня ничего нет @@ -4404,95 +4430,56 @@ and will not be shared or disclosed to the Amnezia or any third parties Создайте на сервере файловое хранилище для безопасного хранения и передачи файлов.
- This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for protecting against blocking. + + AmneziaWG is a modern VPN protocol based on WireGuard, combining simplified architecture with high performance across all devices. It addresses WireGuard's main vulnerability (easy detection by DPI systems) through advanced obfuscation techniques, making VPN traffic indistinguishable from regular internet traffic. -OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client and the server. +AmneziaWG is an excellent choice for those seeking a fast, stealthy VPN connection. -Cloak protects OpenVPN from detection and blocking. +Features: +* Available on all AmneziaVPN platforms +* Low battery consumption on mobile devices +* Minimal settings required +* Undetectable by traffic analysis systems (DPI) +* Operates over UDP protocol + AmneziaWG — современный VPN-протокол на основе WireGuard, сочетающий простую архитектуру и высокую производительность на всех устройствах. Он устраняет основной недостаток WireGuard (лёгкое обнаружение трафика системами DPI) за счёт эффективного маскирования VPN-трафика под обычный интернет-трафик. -Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, and also protects the VPN from detection by Active Probing. This makes it very resistant to being detected +Таким образом, AmneziaWG идеально подойдёт тем, кто ищет быстрое и незаметное VPN-соединение. -Immediately after receiving the first data packet, Cloak authenticates the incoming connection. If authentication fails, the plugin masks the server as a fake website and your VPN becomes invisible to analysis systems. - -If there is a extreme level of Internet censorship in your region, we advise you to use only OpenVPN over Cloak from the first connection - -* Available in the AmneziaVPN across all platforms -* High power consumption on mobile devices -* Flexible settings -* Not recognised by DPI analysis systems -* Works over TCP network protocol, 443 port. - - Это связка протокола OpenVPN и плагина Cloak, разработанная специально для защиты от блокировки. - -OpenVPN обеспечивает безопасное VPN-соединение, шифруя весь интернет-трафик между клиентом и сервером. - -Cloak защищает OpenVPN от обнаружения и блокировки. - -Cloak изменяет метаданные пакетов таким образом, что полностью маскирует VPN-трафик под обычный веб-трафик, а также защищает VPN от обнаружения с помощью активного зондирования. Это делает его очень защищенным от обнаружения. - -Сразу после получения первого пакета данных Cloak устанавливает подлинность входящего соединения. Если аутентификация не проходит, плагин маскирует сервер под фальшивый веб-сайт, и ваш VPN становится невидимым для систем анализа трафика. - -Если в вашем регионе наблюдается жесткая интернет-цензура, мы советуем вам уже при первом подключении использовать только OpenVPN over Cloak. - -* Доступен в AmneziaVPN на всех платформах -* Высокое энергопотребление на мобильных устройствах -* Гибкие настройки -* Не распознается системами DPI-анализа -* Работает по сетевому протоколу TCP, использует порт 443 - - - A relatively new popular VPN protocol with a simplified architecture. -WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. -WireGuard is very susceptible to blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. - -* Available in the AmneziaVPN across all platforms -* Low power consumption -* Minimum number of settings -* Easily recognised by DPI analysis systems, susceptible to blocking -* Works over UDP network protocol. - Относительно новый и популярный VPN-протокол с простой архитектурой. -WireGuard обеспечивает стабильное VPN-соединение и высокую производительность на всех устройствах. Он использует строго заданные настройки шифрования. WireGuard по сравнению с OpenVPN имеет меньшую задержку и лучшую пропускную способность при передаче данных. -WireGuard очень уязвим для блокировки из-за характерных сигнатур пакетов. В отличие от некоторых других VPN-протоколов, использующих методы обфускации, последовательные сигнатуры пакетов WireGuard легче идентифицируются и, следовательно, могут блокироваться современными Deep Packet Inspection (DPI) системами и другими инструментами для сетевого мониторинга. - -* Доступен в AmneziaVPN на всех платформах +Особенности: +* Доступен во всех версиях AmneziaVPN * Низкое энергопотребление на мобильных устройствах -* Минимальная конфигурация -* Легко распознается системами DPI-анализа, поддается блокировке -* Работает по сетевому протоколу UDP +* Минимум настроек +* Незаметен для систем анализа трафика (DPI) +* Работает по протоколу UDP + - The REALITY protocol, a pioneering development by the creators of XRay, is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion. -It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, thus presenting an authentic TLS certificate and data. -This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. -Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship. - Протокол REALITY, новаторская разработка создателей XRay, специально спроектирован для противодействия самой строгой цензуре с помощью нового способа обхода блокировок. -Он уникальным образом идентифицирует цензоров на этапе TLS-рукопожатия, беспрепятственно работая в качестве прокси для реальных клиентов и перенаправляя цензоров на реальные сайты, такие как google.com, тем самым предъявляя подлинный TLS-сертификат и данные. -REALITY отличается от аналогичных технологий благодаря способности без специальной настройки маскировать веб-трафик так, как будто он поступает со случайных легитимных сайтов. -В отличие от более старых протоколов, таких как VMess, VLESS и транспорт XTLS-Vision, технология распознавания "друг или враг" на этапе TLS-рукопожатия повышает безопасность и обходит обнаружение сложными системами DPI-анализа, которые используют методы активного зондирования. Это делает REALITY эффективным решением для поддержания свободы интернета в регионах с жесткой цензурой. - - - - IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol. -One of its distinguishing features is its ability to swiftly switch between networks and devices, making it particularly adaptive in dynamic network environments. -While it offers a blend of security, stability, and speed, it's essential to note that IKEv2 can be easily detected and is susceptible to blocking. + + REALITY is an innovative protocol developed by the creators of XRay, designed specifically to combat high levels of internet censorship. REALITY identifies censorship systems during the TLS handshake, redirecting suspicious traffic seamlessly to legitimate websites like google.com while providing genuine TLS certificates. This allows VPN traffic to blend indistinguishably with regular web traffic without special configuration. +Unlike older protocols such as VMess, VLESS, and XTLS-Vision, REALITY incorporates an advanced built-in "friend-or-foe" detection mechanism, effectively protecting against DPI and other traffic analysis methods. -* Available in the AmneziaVPN only on Windows -* Low power consumption, on mobile devices -* Minimal configuration -* Recognised by DPI analysis systems -* Works over UDP network protocol, ports 500 and 4500. - IKEv2 в сочетании с уровнем шифрования IPSec представляет собой современный и стабильный VPN-протокол. -Он может быстро переключаться между сетями и устройствами, что делает его особенно адаптивным в динамичных сетевых средах. -Несмотря на сочетание безопасности, стабильности и скорости, необходимо отметить, что IKEv2 легко обнаруживается и подвержен блокировке. +Features: +* Resistant to active probing and DPI detection +* No special configuration required to disguise traffic +* Highly effective in heavily censored regions +* Minimal battery consumption on devices +* Operates over TCP protocol + REALITY — это инновационный протокол от разработчиков XRay, специально созданный для эффективного противодействия жесткой интернет-цензуре. -* Доступен в AmneziaVPN только для Windows -* Низкое энергопотребление на мобильных устройствах -* Минимальная конфигурация -* Распознается системами DPI-анализа -* Работает по сетевому протоколу UDP, использует порты 500 и 4500 +REALITY распознаёт системы блокировки во время TLS-рукопожатия и незаметно перенаправляет подозрительные запросы на реальные сайты, такие как google.com, предъявляя подлинные TLS-сертификаты. Это позволяет маскировать VPN-трафик под обычный веб-трафик без дополнительных настроек. + +В отличие от протоколов старого поколения (VMess, VLESS и XTLS-Vision), REALITY использует встроенную технологию распознавания «свой-чужой», надёжно защищая от DPI и других методов сетевого анализа. + +Особенности: +* Устойчив к активному зондированию и DPI-системам +* Не требует специальной настройки для маскировки трафика +* Эффективен в регионах с жесткой цензурой +* Минимальное энергопотребление на устройствах +* Работает по протоколу TCP + - + DNS Service Сервис DNS @@ -4503,7 +4490,7 @@ While it offers a blend of security, stability, and speed, it's essential t
- + Website in Tor network Веб-сайт в сети Tor @@ -4543,109 +4530,140 @@ While it offers a blend of security, stability, and speed, it's essential t XRay с REALITY маскирует VPN-трафик под веб-трафик. Обладает высокой устойчивостью к обнаружению и обеспечивает высокую скорость соединения.
- - OpenVPN stands as one of the most popular and time-tested VPN protocols available. -It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, catering to a wide range of devices and operating systems. Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, which continually reinforces its security. With a strong balance of performance, security, and compatibility, OpenVPN remains a top choice for privacy-conscious individuals and businesses alike. - -* Available in the AmneziaVPN across all platforms -* Normal power consumption on mobile devices -* Flexible customisation to suit user needs to work with different operating systems and devices -* Recognised by DPI systems and therefore susceptible to blocking -* Can operate over both TCP and UDP network protocols. - OpenVPN является одним из самых популярных и проверенных временем VPN-протоколов. Он использует собственный протокол безопасности, и криптографические протоколы SSL/TLS для шифрования и обмена ключами. Более того, поддержка множества методов аутентификации делает OpenVPN универсальным, адаптируемым и подходящим для широкого спектра устройств и операционных систем. Благодаря своему открытому коду, OpenVPN подвергается тщательной проверке со стороны мирового сообщества, что постоянно укрепляет его безопасность. Имея отличный баланс между производительностью, безопасностью и совместимостью OpenVPN остается лучшим выбором для людей и компаний, заботящихся о конфиденциальности, однако OpenVPN легко распознается современными системами анализа трафика. -Доступен в AmneziaVPN на всех платформах -Нормальное энергопотребление на мобильных устройствах -Гибкая настройка полезная при работе с различными операционными системами и устройствами -Распознается системами DPI и, следовательно, уязвим к блокировкам -Может работать как по TCP, так и по UDP протоколу. + + + - - This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for protecting against detection. + + OpenVPN is one of the most popular and reliable VPN protocols. It uses SSL/TLS encryption, supports a wide variety of devices and operating systems, and is continuously improved by the community due to its open-source nature. It provides a good balance between speed and security but is easily recognized by DPI systems, making it susceptible to blocking. -OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client and the server. +Features: +* Available on all AmneziaVPN platforms +* Normal battery consumption on mobile devices +* Flexible customization for various devices and OS +* Operates over both TCP and UDP protocols + OpenVPN — один из самых популярных и надежных VPN-протоколов. Он использует шифрование SSL/TLS, совместим со множеством устройств и ОС, а благодаря открытому коду постоянно совершенствуется сообществом. Имеет хороший баланс скорости и безопасности, но легко распознаётся системами DPI, что делает его уязвимым к блокировкам. -Cloak protects OpenVPN from detection. +Особенности: +* Доступен во всех приложениях AmneziaVPN +* Нормальное энергопотребление на мобильных устройствах +* Гибкие настройки под разные устройства и ОС +* Работает по TCP и UDP + + + + Shadowsocks is based on the SOCKS5 protocol and encrypts connections using AEAD cipher. Although designed to be discreet, it doesn't mimic a standard HTTPS connection and can be detected by some DPI systems. Due to limited support in Amnezia, we recommend using the AmneziaWG protocol. -Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, and also protects the VPN from detection by Active Probing. This makes it very resistant to being detected - -Immediately after receiving the first data packet, Cloak authenticates the incoming connection. If authentication fails, the plugin masks the server as a fake website and your VPN becomes invisible to analysis systems. - -* Available in the AmneziaVPN across all platforms -* High power consumption on mobile devices -* Flexible settings -* Not recognised by detection systems -* Works over TCP network protocol, 443 port. +Features: +* Available in AmneziaVPN only on desktop platforms +* Customizable encryption protocol +* Detectable by some DPI systems +* Operates over TCP protocol - Это связка протокола OpenVPN и плагина Cloak, созданная специально для защиты от обнаружения. + Shadowsocks основан на протоколе SOCKS5 и шифрует соединение алгоритмом AEAD. Он разработан так, чтобы быть малозаметным, однако не идентичен HTTPS, поэтому может распознаваться некоторыми системами DPI. В связи с ограниченной поддержкой в Amnezia, рекомендуем использовать протокол AmneziaWG. -OpenVPN обеспечивает безопасное VPN-соединение, шифруя весь интернет-трафик между клиентом и сервером. +Особенности: +* Доступен только на ПК в AmneziaVPN +* Настраиваемое шифрование +* Может обнаруживаться некоторыми DPI-системами +* Работает по протоколу TCP + + + + This combination includes the OpenVPN protocol and the Cloak plugin, specifically designed to protect against blocking. -Плагин Cloak защищает OpenVPN от обнаружения. +OpenVPN securely encrypts all internet traffic between your device and the server. -Cloak может изменять метаданные пакета, чтобы полностью замаскировать VPN-трафик под обычный веб-трафик, а также защищает VPN от обнаружения с помощью метода Active Probing. Это делает его очень устойчивым к обнаружению. +The Cloak plugin further protects the connection from DPI detection. It modifies traffic metadata to disguise VPN traffic as regular web traffic and prevents detection through active probing. If an incoming connection fails authentication, Cloak serves a fake website, making your VPN invisible to traffic analysis systems. -Сразу после получения первого пакета данных Cloak аутентифицирует входящее соединение, если аутентификация не удалась, плагин маскирует сервер под настоящий веб-сайт, и ваш VPN становится невидимым для систем анализа. Имеет низкую скорость работы в сравнении с другими похожими протоколами. +In regions with heavy internet censorship, we strongly recommend using OpenVPN with Cloak from your first connection. -* Доступно в AmneziaVPN на всех платформах. +Features: +* Available on all AmneziaVPN platforms +* High power consumption on mobile devices +* Flexible configuration options +* Undetectable by DPI systems +* Operates over TCP protocol on port 443 + Эта комбинация состоит из протокола OpenVPN и плагина Cloak, специально разработанных для защиты от блокировок. + +OpenVPN надёжно шифрует весь интернет-трафик между вами и сервером. + +Плагин Cloak дополнительно защищает соединение от распознавания системами DPI. Он изменяет метаданные трафика, маскируя VPN-подключение под обычный веб-трафик, и предотвращает обнаружение с помощью активного зондирования. Если попытка подключения не прошла аутентификацию, Cloak выдаёт поддельный веб-сайт, делая VPN невидимым для анализирующих систем. + +Если в вашем регионе сильная интернет-цензура, мы рекомендуем сразу использовать OpenVPN с плагином Cloak. + +Особенности: +* Доступен на всех платформах AmneziaVPN * Высокое энергопотребление на мобильных устройствах * Гибкие настройки -* Не распознается системами обнаружения. -* Работает по сетевому протоколу TCP, порт 443. - +* Незаметен для систем DPI-анализа +* Использует протокол TCP на порту 443 - - A relatively new popular VPN protocol with a simplified architecture. -WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. -WireGuard is very susceptible to detection and blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. + + WireGuard is a modern, streamlined VPN protocol offering stable connectivity and excellent performance across all devices. It uses fixed encryption settings, delivering lower latency and higher data transfer speeds compared to OpenVPN. However, WireGuard is easily identifiable by DPI systems due to its distinctive packet signatures, making it susceptible to blocking. -* Available in the AmneziaVPN across all platforms -* Low power consumption -* Minimum number of settings -* Easily recognised by DPI analysis systems, susceptible to blocking -* Works over UDP network protocol. - Популярный VPN-протокол с упрощенной архитектурой. -WireGuard обеспечивает стабильное VPN-соединение и высокую производительность на всех устройствах. Он использует закодированные настройки шифрования. WireGuard по сравнению с OpenVPN имеет меньшую задержку и лучшую пропускную способность передачи данных. -WireGuard очень чувствителен к обнаружению и блокировке из-за различных сигнатур пакетов. В отличие от некоторых других VPN протоколов, использующих методы запутывания, последовательные шаблоны сигнатур пакетов WireGuard легко идентифицируются системами анализа трафика. +Features: +* Available on all AmneziaVPN platforms +* Low power consumption on mobile devices +* Minimal configuration required +* Easily detected by DPI systems (susceptible to blocking) +* Operates over UDP protocol + WireGuard — современный и простой VPN-протокол, который обеспечивает стабильное соединение и высокую скорость передачи данных на любых устройствах. Он использует фиксированные настройки шифрования, имеет меньшую задержку и выше пропускную способность по сравнению с OpenVPN. -* Доступно в AmneziaVPN на всех платформах. -* Низкое энергопотребление -* Минимальное количество настроек -* Легко распознается системами анализа DPI, подвержен блокировке. -* Работает по сетевому протоколу UDP. +Однако WireGuard легко распознаётся системами DPI из-за характерных сигнатур трафика, что делает его уязвимым к блокировкам. + +Особенности: +* Доступен на всех платформах AmneziaVPN +* Низкое энергопотребление на мобильных устройствах +* Минимум настроек +* Легко определяется DPI-системами (подвержен блокировкам) +* Работает по протоколу UDP - - A modern iteration of the popular VPN protocol, AmneziaWG builds upon the foundation set by WireGuard, retaining its simplified architecture and high-performance capabilities across devices. -While WireGuard is known for its efficiency, it had issues with being easily detected due to its distinct packet signatures. AmneziaWG solves this problem by using better obfuscation methods, making its traffic blend in with regular internet traffic. -This means that AmneziaWG keeps the fast performance of the original while adding an extra layer of stealth, making it a great choice for those wanting a fast and discreet VPN connection. + + IKEv2, combined with IPSec encryption, is a modern and reliable VPN protocol. It reconnects quickly when switching networks or devices, making it ideal for dynamic network environments. While it provides good security and speed, it's easily recognized by DPI systems and susceptible to blocking. -* Available in the AmneziaVPN across all platforms -* Low power consumption -* Minimum number of settings -* Not recognised by traffic analysis systems -* Works over UDP network protocol. - AmneziaWG — это современная версия популярного VPN протокола, основанная на базе WireGuard, сохранившая упрощенную архитектуру и высокопроизводительные возможности на всех устройствах. -Хотя WireGuard известен своей эффективностью, обнаружить его довольно легко из-за различных сигнатур пакетов. AmneziaWG решает эту проблему, используя более совершенные методы работы, смешивая свой трафик с обычным интернет-трафиком. -Это означает, что AmneziaWG сохраняет высокую производительность оригинала, добавляя при этом дополнительный уровень скрытности, что делает его отличным выбором для тех, кому нужно быстрое и незаметное VPN-соединение. +Features: +* Available in AmneziaVPN only on Windows +* Low battery consumption on mobile devices +* Minimal configuration required +* Detectable by DPI analysis systems(easily blocked) +* Operates over UDP protocol(ports 500 and 4500) + IKEv2 — современный и стабильный VPN-протокол, работающий совместно с шифрованием IPSec. Он обеспечивает быстрое переподключение при смене сети или устройства, отлично подходит для динамичных сетевых условий. Несмотря на хорошую скорость и безопасность, легко распознаётся системами DPI и подвержен блокировкам. -* Доступно в AmneziaVPN на всех платформах. -* Низкое энергопотребление -* Минимальное количество настроек -* Не распознается системами анализа трафика. -* Работает по сетевому протоколу UDP. +Особенности: +* Доступен в AmneziaVPN только на Windows +* Низкое энергопотребление на мобильных устройствах +* Минимум настроек +* Распознаётся DPI-системами (легко блокируется) +* Работает по UDP (порты 500 и 4500) - - The REALITY protocol, a pioneering development by the creators of XRay, is designed to provide the highest level of protection against detection through its innovative approach to security and privacy. -It uniquely identifies attackers during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting attackers to genuine websites, thus presenting an authentic TLS certificate and data. -This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. -Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security. This makes REALITY a robust solution for maintaining internet freedom. - Протокол REALITY, современная разработка от создателей XRay. Призван обеспечить высочайший уровень защиты от обнаружения благодаря инновационному подходу к безопасности и конфиденциальности. -Он безошибочно идентифицирует злоумышленников на этапе установления связи TLS, беспрепятственно работая в качестве прокси-сервера для оригинального клиента и перенаправляя злоумышленников на подлинные веб-сайты, предоставляя тем самым подлинный сертификат TLS и данные. -Эта расширенная возможность отличает REALITY от аналогичных технологий тем, что способна маскироваться под случайный веб-трафик без использования специальных настроек. -В отличие от старых протоколов, таких как VMess, VLESS и транспорт XTLS-Vision, REALITY имеет инновационную технологию распознавания «свой-чужой».Это делает REALITY надежным решением для обеспечения доступа к свободному интернету. + + AmneziaWG is a modern VPN protocol based on WireGuard, combining simplified architecture with high performance across all devices. It addresses WireGuard’s main vulnerability (easy detection by DPI systems) through advanced obfuscation techniques, making VPN traffic indistinguishable from regular internet traffic. + + AmneziaWG is an excellent choice for those seeking a fast, stealthy VPN connection. + + Features: + + * Available on all AmneziaVPN platforms + * Low battery consumption on mobile devices + * Minimal settings required + * Undetectable by traffic analysis systems (DPI) + * Operates over UDP protocol + + AmneziaWG — современный VPN-протокол на основе WireGuard, сочетающий простую архитектуру и высокую производительность на всех устройствах. Он устраняет основной недостаток WireGuard (лёгкое обнаружение трафика системами DPI) за счёт эффективного маскирования VPN-трафика под обычный интернет-трафик. + +Таким образом, AmneziaWG идеально подойдёт тем, кто ищет быстрое и незаметное VPN-соединение. + +Особенности: +* Доступен во всех версиях AmneziaVPN +* Низкое энергопотребление на мобильных устройствах +* Минимум настроек +* Незаметен для систем анализа трафика (DPI) +* Работает по протоколу UDP WireGuard - New popular VPN protocol with high performance, high speed and low power consumption. Recommended for regions with low levels of censorship. @@ -4674,50 +4692,7 @@ Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REAL Замените текущий DNS-сервер на свой собственный. Это повысит уровень вашей конфиденциальности. - OpenVPN stands as one of the most popular and time-tested VPN protocols available. -It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, catering to a wide range of devices and operating systems. Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, which continually reinforces its security. With a strong balance of performance, security, and compatibility, OpenVPN remains a top choice for privacy-conscious individuals and businesses alike. - -* Available in the AmneziaVPN across all platforms -* Normal power consumption on mobile devices -* Flexible customisation to suit user needs to work with different operating systems and devices -* Recognised by DPI analysis systems and therefore susceptible to blocking -* Can operate over both TCP and UDP network protocols. - OpenVPN — один из самых популярных и проверенных временем VPN-протоколов. -В нем используется уникальный протокол безопасности, опирающийся на SSL/TLS для шифрования и обмена ключами. Кроме того, OpenVPN поддерживает множество методов аутентификации, что делает его универсальным и адаптируемым к широкому спектру устройств и операционных систем. Благодаря открытому исходному коду OpenVPN подвергается тщательному анализу со стороны мирового сообщества, что постоянно повышает его безопасность. Оптимальное соотношение производительности, безопасности и совместимости делает OpenVPN лучшим выбором как для частных лиц, так и для компаний, заботящихся о конфиденциальности. - -* Доступен в AmneziaVPN на всех платформах -* Нормальное энергопотребление на мобильных устройствах -* Гибкая настройка под нужды пользователя для работы с различными операционными системами и устройствами -* Распознается системами DPI-анализа и поэтому подвержен блокировке -* Может работать по сетевым протоколам TCP и UDP - - - - Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection.However, certain traffic analysis systems might still detect a Shadowsocks connection. Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol. - -* Available in the AmneziaVPN only on desktop platforms -* Configurable encryption protocol -* Detectable by some DPI systems -* Works over TCP network protocol. - Shadowsocks создан на основе протокола SOCKS5, защищает соединение с помощью шифра AEAD. Несмотря на то, что протокол Shadowsocks разработан таким образом, чтобы быть незаметным и сложным для идентификации, он не идентичен стандартному HTTPS-соединению, поэтому некоторые системы анализа трафика всё же могут обнаружить соединение Shadowsocks. В связи с ограниченной поддержкой в Amnezia рекомендуется использовать протокол AmneziaWG. - -* Доступен в AmneziaVPN только для ПК и ноутбуков -* Настраиваемый протокол шифрования -* Распознается некоторыми системами DPI-анализа -* Работает по сетевому протоколу TCP. - - - The REALITY protocol, a pioneering development by the creators of XRay, is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion. - It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, thus presenting an authentic TLS certificate and data. - This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations. - Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship. - Протокол REALITY, новаторская разработка создателей XRay, специально спроектирован для противодействия самой строгой цензуре с помощью нового способа обхода блокировок. - Он уникальным образом идентифицирует цензоров на этапе TLS-рукопожатия, беспрепятственно работая в качестве прокси для реальных клиентов и перенаправляя цензоров на реальные сайты, такие как google.com, тем самым предъявляя подлинный TLS-сертификат и данные. - REALITY отличается от аналогичных технологий благодаря способности без специальной настройки маскировать веб-трафик так, как будто он поступает со случайных легитимных сайтов. - В отличие от более старых протоколов, таких как VMess, VLESS и XTLS-Vision, технология распознавания "друг или враг" на этапе TLS-рукопожатия повышает безопасность и обходит обнаружение сложными системами DPI-анализа, которые используют методы активного зондирования. Это делает REALITY эффективным решением для поддержания свободы интернета в регионах с жесткой цензурой. - - - + After installation, Amnezia will create a file storage on your server. You will be able to access it using @@ -4734,84 +4709,6 @@ For more detailed information, you can Более подробную информацию вы можете найти в разделе поддержки "Создание файлового хранилища SFTP." - - - This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for blocking protection. - -OpenVPN provides a secure VPN connection by encrypting all Internet traffic between the client and the server. - -Cloak protects OpenVPN from detection and blocking. - -Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, and also protects the VPN from detection by Active Probing. This makes it very resistant to being detected - -Immediately after receiving the first data packet, Cloak authenticates the incoming connection. If authentication fails, the plugin masks the server as a fake website and your VPN becomes invisible to analysis systems. - -If there is a extreme level of Internet censorship in your region, we advise you to use only OpenVPN over Cloak from the first connection - -* Available in the AmneziaVPN across all platforms -* High power consumption on mobile devices -* Flexible settings -* Not recognised by DPI analysis systems -* Works over TCP network protocol, 443 port. - - OpenVPN over Cloak - это комбинация протокола OpenVPN и плагина Cloak, разработанного специально для защиты от обнаружения и блокировок. - -Протокол OpenVPN обеспечивает безопасное VPN-соединение за счет шифрования всего интернет-трафика между клиентом и сервером. - -Плагин Cloak защищает OpenVPN от обнаружения и блокировок. - -Cloak может изменять метаданные пакетов. Он полностью маскирует VPN-трафик под обычный веб-трафик, а также защищает VPN от обнаружения с помощью Active Probing. Это делает его очень устойчивым к обнаружению - -Сразу же после получения первого пакета данных Cloak проверяет подлинность входящего соединения. Если аутентификация не проходит, плагин маскирует сервер под поддельный сайт, и ваш VPN становится невидимым для аналитических систем. - -Если в вашем регионе экстремальный уровень цензуры в Интернете, мы советуем вам с первого подключения использовать только OpenVPN over Cloak - -* Доступен в AmneziaVPN для всех платформ -* Высокое энергопотребление на мобильных устройствах -* Гибкие настройки -* Не распознается системами DPI-анализа -* Работает по сетевому протоколу TCP, 443 порт. - - - - A relatively new popular VPN protocol with a simplified architecture. -Provides stable VPN connection, high performance on all devices. Uses hard-coded encryption settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput. -WireGuard is very susceptible to blocking due to its distinct packet signatures. Unlike some other VPN protocols that employ obfuscation techniques, the consistent signature patterns of WireGuard packets can be more easily identified and thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools. - -* Available in the AmneziaVPN across all platforms -* Low power consumption -* Minimum number of settings -* Easily recognised by DPI analysis systems, susceptible to blocking -* Works over UDP network protocol. - WireGuard - относительно новый популярный VPN-протокол с упрощенной архитектурой. -Обеспечивает стабильное VPN-соединение, высокую производительность на всех устройствах. Использует жестко заданные настройки шифрования. WireGuard по сравнению с OpenVPN имеет меньшую задержку и лучшую пропускную способность при передаче данных. -WireGuard очень восприимчив к блокированию из-за особенностей сигнатур пакетов. В отличие от некоторых других VPN-протоколов, использующих методы обфускации, последовательные сигнатуры пакетов WireGuard легче выявляются и, соответственно, блокируются современными системами глубокой проверки пакетов (DPI) и другими средствами сетевого мониторинга. - -* Доступен в AmneziaVPN для всех платформ -* Низкое энергопотребление -* Минимальное количество настроек -* Легко распознается системами DPI-анализа, подвержен блокировке -* Работает по сетевому протоколу UDP. - - - A modern iteration of the popular VPN protocol, AmneziaWG builds upon the foundation set by WireGuard, retaining its simplified architecture and high-performance capabilities across devices. -While WireGuard is known for its efficiency, it had issues with being easily detected due to its distinct packet signatures. AmneziaWG solves this problem by using better obfuscation methods, making its traffic blend in with regular internet traffic. -This means that AmneziaWG keeps the fast performance of the original while adding an extra layer of stealth, making it a great choice for those wanting a fast and discreet VPN connection. - -* Available in the AmneziaVPN across all platforms -* Low power consumption -* Minimum number of settings -* Not recognised by DPI analysis systems, resistant to blocking -* Works over UDP network protocol. - AmneziaWG — усовершенствованная версия популярного VPN-протокола WireGuard. AmneziaWG опирается на фундамент, заложенный WireGuard, сохраняя упрощенную архитектуру и высокую производительность на различных устройствах. -Хотя WireGuard известен своей эффективностью, у него были проблемы с обнаружением из-за характерных сигнатур пакетов. AmneziaWG решает эту проблему за счет использования более совершенных методов обфускации, благодаря чему его трафик сливается с обычным интернет-трафиком. -Таким образом, AmneziaWG сохраняет высокую производительность оригинального протокола, добавляя при этом дополнительный уровень скрытности, что делает его отличным выбором для тех, кому нужно быстрое и незаметное VPN-соединение. - -* Доступен в AmneziaVPN на всех платформах -* Низкое энергопотребление на мобильных устройствах -* Минимальное количество настроек -* Не распознается системами DPI-анализа, устойчив к блокировке -* Работает по сетевому протоколу UDP AmneziaWG container @@ -4894,7 +4791,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin - + SOCKS5 proxy server Прокси-сервер SOCKS5 @@ -5308,12 +5205,12 @@ This means that AmneziaWG keeps the fast performance of the original while addin Хочу просто повысить уровень приватности - + Automatic Автоматическая - + AmneziaWG protocol will be installed. It provides high connection speed and ensures stable operation even in the most challenging network conditions. Будет установлен протокол AmneziaWG. Он обеспечивает высокую скорость соединения и гарантирует стабильную работу даже в самых сложных условиях. diff --git a/client/ui/models/api/apiServicesModel.cpp b/client/ui/models/api/apiServicesModel.cpp index 65f17758..b893096a 100644 --- a/client/ui/models/api/apiServicesModel.cpp +++ b/client/ui/models/api/apiServicesModel.cpp @@ -69,7 +69,7 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const "Access all websites and online resources. Speeds up to %1 Mbps.") .arg(speed); } else if (serviceType == serviceType::amneziaFree) { - QString description = tr("AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan."); + QString description = tr("Amnezia Free provides unlimited, free access to a basic set of websites and apps, including Facebook, Instagram, Twitter (X), Discord, Telegram, and more. YouTube is not included in the free plan."); if (!isServiceAvailable) { description += tr("

Not available in your region. If you have VPN enabled, disable it, " "return to the previous screen, and try again."); @@ -82,7 +82,7 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const return tr("Amnezia Premium is classic VPN for for seamless work, downloading large files, and watching videos. " "Access all websites and online resources."); } else { - return tr("AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan."); + return tr("Amnezia Free provides unlimited, free access to a basic set of websites and apps, including Facebook, Instagram, Twitter (X), Discord, Telegram, and more. YouTube is not included in the free plan."); } } case IsServiceAvailableRole: { diff --git a/client/ui/qml/Pages2/PageSettingsApiDevices.qml b/client/ui/qml/Pages2/PageSettingsApiDevices.qml index d8e24d42..231b58a0 100644 --- a/client/ui/qml/Pages2/PageSettingsApiDevices.qml +++ b/client/ui/qml/Pages2/PageSettingsApiDevices.qml @@ -77,7 +77,7 @@ PageType { } var headerText = qsTr("Are you sure you want to unlink this device?") - var descriptionText = qsTr("This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect.") + var descriptionText = qsTr("This will unlink the device from your subscription. You can reconnect it anytime by pressing \"Reload API config\" in subscription settings on device.") var yesButtonText = qsTr("Continue") var noButtonText = qsTr("Cancel") diff --git a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml index 48080c3a..93118755 100644 --- a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml @@ -333,7 +333,7 @@ PageType { clickedFunc: function() { var headerText = qsTr("Are you sure you want to unlink this device?") - var descriptionText = qsTr("This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect.") + var descriptionText = qsTr("This will unlink the device from your subscription. You can reconnect it anytime by pressing \"Reload API config\" in subscription settings on device.") var yesButtonText = qsTr("Continue") var noButtonText = qsTr("Cancel") From 7e380b6cfb46ae23ab3682e5a676f8585e6d22e3 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Mon, 12 May 2025 05:36:25 -0700 Subject: [PATCH 30/65] OpenVPN with system disabled IPv6 (#1563) * Fix for Win OpenVPN with disabled IPv6 and AllExceptSites Splittunnel mode * Remove unneeded stuff for ipv6 openvpn --- client/configurators/openvpn_configurator.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/client/configurators/openvpn_configurator.cpp b/client/configurators/openvpn_configurator.cpp index eca81afd..6d6603da 100644 --- a/client/configurators/openvpn_configurator.cpp +++ b/client/configurators/openvpn_configurator.cpp @@ -120,13 +120,6 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPairisSitesSplitTunnelingEnabled()) { config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n"); - -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) - // Prevent ipv6 leak - if (NetworkUtilities::checkIpv6Enabled()) { - config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n"); - } -#endif config.append("block-ipv6\n"); } else if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) { @@ -135,7 +128,6 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair Date: Mon, 12 May 2025 20:37:35 +0800 Subject: [PATCH 31/65] chore: update link to submodule (#1544) * chore: update link to submodule --- client/3rd-prebuilt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/3rd-prebuilt b/client/3rd-prebuilt index efad1a5b..0f3748ef 160000 --- a/client/3rd-prebuilt +++ b/client/3rd-prebuilt @@ -1 +1 @@ -Subproject commit efad1a5b5cb8e8ab61e49ccdca18c9090a0da8d3 +Subproject commit 0f3748efd7cc04e0c914304b68931f925bed1259 From a28ed6a977c6104004061665b9be9ad99dc6525c Mon Sep 17 00:00:00 2001 From: Mitternacht822 Date: Mon, 12 May 2025 18:14:59 +0400 Subject: [PATCH 32/65] feature: added the ability to change port after installing xray (#1556) * added the ability to change port after installing xray * fixed issue with not updating server config for xray on windows platform * fixed some warning in exportcontroller.cpp --- client/core/controllers/serverController.cpp | 15 ++++++++---- client/ui/controllers/exportController.cpp | 12 +++++----- .../ui/models/protocols/xrayConfigModel.cpp | 3 +++ client/ui/models/protocols/xrayConfigModel.h | 3 ++- .../qml/Pages2/PageProtocolXraySettings.qml | 23 ++++++++++++++++++- 5 files changed, 44 insertions(+), 12 deletions(-) diff --git a/client/core/controllers/serverController.cpp b/client/core/controllers/serverController.cpp index 9ac62759..8ff6b6c8 100644 --- a/client/core/controllers/serverController.cpp +++ b/client/core/controllers/serverController.cpp @@ -138,7 +138,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, if (overwriteMode == libssh::ScpOverwriteMode::ScpOverwriteExisting) { e = runScript(credentials, - replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path), + replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, path), genVarsForScript(credentials, container)), cbReadStd, cbReadStd); @@ -146,7 +146,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, return e; } else if (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting) { e = runScript(credentials, - replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(tmpFileName), + replaceVars(QStringLiteral("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName, tmpFileName), genVarsForScript(credentials, container)), cbReadStd, cbReadStd); @@ -154,7 +154,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, return e; e = runScript(credentials, - replaceVars(QString("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName).arg(path), + replaceVars(QStringLiteral("sudo docker exec -i $CONTAINER_NAME sh -c \"cat %1 >> %2\"").arg(tmpFileName, path), genVarsForScript(credentials, container)), cbReadStd, cbReadStd); @@ -177,7 +177,7 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container, errorCode = ErrorCode::NoError; - QString script = QString("sudo docker exec -i %1 sh -c \"xxd -p \'%2\'\"").arg(ContainerProps::containerToString(container)).arg(path); + QString script = QStringLiteral("sudo docker exec -i %1 sh -c \"xxd -p '%2'\"").arg(ContainerProps::containerToString(container), path); QString stdOut; auto cbReadStdOut = [&](const QString &data, libssh::Client &) { @@ -383,6 +383,13 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c return true; } + if (container == DockerContainer::Xray) { + if (oldProtoConfig.value(config_key::port).toString(protocols::xray::defaultPort) + != newProtoConfig.value(config_key::port).toString(protocols::xray::defaultPort)) { + return true; + } + } + return false; } diff --git a/client/ui/controllers/exportController.cpp b/client/ui/controllers/exportController.cpp index b47111ae..c487e3b2 100644 --- a/client/ui/controllers/exportController.cpp +++ b/client/ui/controllers/exportController.cpp @@ -145,7 +145,7 @@ void ExportController::generateOpenVpnConfig(const QString &clientName) } QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n"); - for (const QString &line : lines) { + for (const QString &line : std::as_const(lines)) { m_config.append(line + "\n"); } @@ -163,7 +163,7 @@ void ExportController::generateWireGuardConfig(const QString &clientName) } QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n"); - for (const QString &line : lines) { + for (const QString &line : std::as_const(lines)) { m_config.append(line + "\n"); } @@ -183,7 +183,7 @@ void ExportController::generateAwgConfig(const QString &clientName) } QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n"); - for (const QString &line : lines) { + for (const QString &line : std::as_const(lines)) { m_config.append(line + "\n"); } @@ -211,7 +211,7 @@ void ExportController::generateShadowSocksConfig() } QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n"); - for (const QString &line : lines) { + for (const QString &line : std::as_const(lines)) { m_config.append(line + "\n"); } @@ -240,7 +240,7 @@ void ExportController::generateCloakConfig() nativeConfig.insert("ProxyMethod", "shadowsocks"); QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n"); - for (const QString &line : lines) { + for (const QString &line : std::as_const(lines)) { m_config.append(line + "\n"); } @@ -257,7 +257,7 @@ void ExportController::generateXrayConfig(const QString &clientName) } QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n"); - for (const QString &line : lines) { + for (const QString &line : std::as_const(lines)) { m_config.append(line + "\n"); } diff --git a/client/ui/models/protocols/xrayConfigModel.cpp b/client/ui/models/protocols/xrayConfigModel.cpp index 84bbb2f7..3917b544 100644 --- a/client/ui/models/protocols/xrayConfigModel.cpp +++ b/client/ui/models/protocols/xrayConfigModel.cpp @@ -20,6 +20,7 @@ bool XrayConfigModel::setData(const QModelIndex &index, const QVariant &value, i switch (role) { case Roles::SiteRole: m_protocolConfig.insert(config_key::site, value.toString()); break; + case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break; } emit dataChanged(index, index, QList { role }); @@ -34,6 +35,7 @@ QVariant XrayConfigModel::data(const QModelIndex &index, int role) const switch (role) { case Roles::SiteRole: return m_protocolConfig.value(config_key::site).toString(protocols::xray::defaultSite); + case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(protocols::xray::defaultPort); } return QVariant(); @@ -67,6 +69,7 @@ QHash XrayConfigModel::roleNames() const QHash roles; roles[SiteRole] = "site"; + roles[PortRole] = "port"; return roles; } diff --git a/client/ui/models/protocols/xrayConfigModel.h b/client/ui/models/protocols/xrayConfigModel.h index cb57f858..41aac589 100644 --- a/client/ui/models/protocols/xrayConfigModel.h +++ b/client/ui/models/protocols/xrayConfigModel.h @@ -12,7 +12,8 @@ class XrayConfigModel : public QAbstractListModel public: enum Roles { - SiteRole + SiteRole, + PortRole }; explicit XrayConfigModel(QObject *parent = nullptr); diff --git a/client/ui/qml/Pages2/PageProtocolXraySettings.qml b/client/ui/qml/Pages2/PageProtocolXraySettings.qml index bef84af7..d22e31a2 100644 --- a/client/ui/qml/Pages2/PageProtocolXraySettings.qml +++ b/client/ui/qml/Pages2/PageProtocolXraySettings.qml @@ -103,8 +103,29 @@ PageType { } } + TextFieldWithHeaderType { + id: portTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + enabled: delegateItem.isEnabled + + headerText: qsTr("Port") + textField.text: port + textField.maximumLength: 5 + textField.validator: IntValidator { bottom: 1; top: 65535 } + + textField.onEditingFinished: { + if (textField.text !== port) { + port = textField.text + } + } + + checkEmptyText: true + } + BasicButtonType { - id: basicButton + id: saveButton Layout.fillWidth: true Layout.topMargin: 24 Layout.bottomMargin: 24 From b457ef9a3f7f1c74f4898a3acd9e1955f80110b3 Mon Sep 17 00:00:00 2001 From: Nethius Date: Tue, 13 May 2025 12:29:33 +0800 Subject: [PATCH 33/65] feature/premium v1 migration (#1569) * feature: premium v1 migration * chore: added stage for macos with new qt version * chore: downgrade qif version * chore: minor ui fixes --- .github/workflows/deploy.yml | 82 +++++++- .github/workflows/tag-deploy.yml | 2 + client/CMakeLists.txt | 3 + client/core/api/apiDefs.h | 11 + client/core/api/apiUtils.cpp | 68 +++++- client/core/api/apiUtils.h | 2 + client/core/controllers/coreController.cpp | 28 ++- client/core/controllers/coreController.h | 4 + client/core/defs.h | 1 + client/core/errorstrings.cpp | 1 + client/resources.qrc | 3 + client/settings.cpp | 10 + client/settings.h | 3 + .../api/apiPremV1MigrationController.cpp | 127 ++++++++++++ .../api/apiPremV1MigrationController.h | 50 +++++ client/ui/models/servers_model.cpp | 19 ++ client/ui/models/servers_model.h | 1 + .../Components/ApiPremV1MigrationDrawer.qml | 194 ++++++++++++++++++ .../qml/Components/ApiPremV1SubListDrawer.qml | 89 ++++++++ client/ui/qml/Components/OtpCodeDrawer.qml | 77 +++++++ client/ui/qml/Components/QuestionDrawer.qml | 20 +- client/ui/qml/Pages2/PageHome.qml | 30 +++ .../ui/qml/Pages2/PageSettingsServerData.qml | 18 ++ 23 files changed, 829 insertions(+), 14 deletions(-) create mode 100644 client/ui/controllers/api/apiPremV1MigrationController.cpp create mode 100644 client/ui/controllers/api/apiPremV1MigrationController.h create mode 100644 client/ui/qml/Components/ApiPremV1MigrationDrawer.qml create mode 100644 client/ui/qml/Components/ApiPremV1SubListDrawer.qml create mode 100644 client/ui/qml/Components/OtpCodeDrawer.qml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ae8768a6..86779f33 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -20,6 +20,8 @@ jobs: DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} + FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} + PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Install Qt' @@ -90,6 +92,8 @@ jobs: DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} + FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} + PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Get sources' @@ -156,6 +160,8 @@ jobs: DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} + FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} + PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Setup xcode' @@ -243,7 +249,7 @@ jobs: # ------------------------------------------------------ - Build-MacOS: + Build-MacOS-old: runs-on: macos-latest env: @@ -255,6 +261,78 @@ jobs: DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} + FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} + PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} + + steps: + - name: 'Setup xcode' + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '15.4.0' + + - name: 'Install Qt' + uses: jurplel/install-qt-action@v3 + with: + version: ${{ env.QT_VERSION }} + host: 'mac' + target: 'desktop' + arch: 'clang_64' + modules: 'qtremoteobjects qt5compat qtshadertools' + dir: ${{ runner.temp }} + setup-python: 'true' + set-env: 'true' + extra: '--external 7z --base ${{ env.QT_MIRROR }}' + + - name: '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 + with: + submodules: 'true' + fetch-depth: 10 + + - name: 'Setup ccache' + uses: hendrikmuhs/ccache-action@v1.2 + + - name: 'Build project' + run: | + export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin" + 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 + retention-days: 7 + + - name: 'Upload unpacked artifact' + uses: actions/upload-artifact@v4 + with: + name: AmneziaVPN_MacOS_old_unpacked + path: deploy/build/client/AmneziaVPN.app + retention-days: 7 + +# ------------------------------------------------------ + + Build-MacOS: + runs-on: macos-latest + + env: + QT_VERSION: 6.8.0 + 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 }} + DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} + DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} + FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} + PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Setup xcode' @@ -324,6 +402,8 @@ jobs: DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} + FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} + PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Install desktop Qt' diff --git a/.github/workflows/tag-deploy.yml b/.github/workflows/tag-deploy.yml index 2bcbd8c6..31c334bf 100644 --- a/.github/workflows/tag-deploy.yml +++ b/.github/workflows/tag-deploy.yml @@ -20,6 +20,8 @@ jobs: DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} + FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} + PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Install desktop Qt' diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index b3f775a0..a454142d 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -31,6 +31,9 @@ add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}") add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}") add_definitions(-DDEV_S3_ENDPOINT="$ENV{DEV_S3_ENDPOINT}") +add_definitions(-DFREE_V2_ENDPOINT="$ENV{FREE_V2_ENDPOINT}") +add_definitions(-DPREM_V1_ENDPOINT="$ENV{PREM_V1_ENDPOINT}") + if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID)) set(PACKAGES ${PACKAGES} Widgets) endif() diff --git a/client/core/api/apiDefs.h b/client/core/api/apiDefs.h index d1a92d9d..6d1a27fa 100644 --- a/client/core/api/apiDefs.h +++ b/client/core/api/apiDefs.h @@ -22,12 +22,19 @@ namespace apiDefs namespace key { constexpr QLatin1String configVersion("config_version"); + constexpr QLatin1String apiEndpoint("api_endpoint"); + constexpr QLatin1String apiKey("api_key"); + constexpr QLatin1String description("description"); + constexpr QLatin1String name("name"); + constexpr QLatin1String protocol("protocol"); constexpr QLatin1String apiConfig("api_config"); constexpr QLatin1String stackType("stack_type"); constexpr QLatin1String serviceType("service_type"); constexpr QLatin1String vpnKey("vpn_key"); + constexpr QLatin1String config("config"); + constexpr QLatin1String configs("configs"); constexpr QLatin1String installationUuid("installation_uuid"); constexpr QLatin1String workerLastUpdated("worker_last_updated"); @@ -51,6 +58,10 @@ namespace apiDefs constexpr QLatin1String website("website"); constexpr QLatin1String websiteName("website_name"); constexpr QLatin1String telegram("telegram"); + + constexpr QLatin1String id("id"); + constexpr QLatin1String orderId("order_id"); + constexpr QLatin1String migrationCode("migration_code"); } const int requestTimeoutMsecs = 12 * 1000; // 12 secs diff --git a/client/core/api/apiUtils.cpp b/client/core/api/apiUtils.cpp index f5f575c5..f85d2207 100644 --- a/client/core/api/apiUtils.cpp +++ b/client/core/api/apiUtils.cpp @@ -3,6 +3,24 @@ #include #include +namespace +{ + const QByteArray AMNEZIA_CONFIG_SIGNATURE = QByteArray::fromHex("000000ff"); + + QString escapeUnicode(const QString &input) + { + QString output; + for (QChar c : input) { + if (c.unicode() < 0x20 || c.unicode() > 0x7E) { + output += QString("\\u%1").arg(QString::number(c.unicode(), 16).rightJustified(4, '0')); + } else { + output += c; + } + } + return output; + } +} + bool apiUtils::isSubscriptionExpired(const QString &subscriptionEndDate) { QDateTime now = QDateTime::currentDateTime(); @@ -27,22 +45,28 @@ apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObjec case apiDefs::ConfigSource::Telegram: { }; case apiDefs::ConfigSource::AmneziaGateway: { - constexpr QLatin1String stackPremium("prem"); - constexpr QLatin1String stackFree("free"); - constexpr QLatin1String servicePremium("amnezia-premium"); constexpr QLatin1String serviceFree("amnezia-free"); constexpr QLatin1String serviceExternalPremium("external-premium"); + constexpr QLatin1String freeV2Endpoint(FREE_V2_ENDPOINT); + constexpr QLatin1String premiumV1Endpoint(PREM_V1_ENDPOINT); + auto apiConfigObject = serverConfigObject.value(apiDefs::key::apiConfig).toObject(); auto serviceType = apiConfigObject.value(apiDefs::key::serviceType).toString(); + auto apiEndpoint = serverConfigObject.value(apiDefs::key::apiEndpoint).toString(); + if (serviceType == servicePremium) { return apiDefs::ConfigType::AmneziaPremiumV2; } else if (serviceType == serviceFree) { return apiDefs::ConfigType::AmneziaFreeV3; } else if (serviceType == serviceExternalPremium) { return apiDefs::ConfigType::ExternalPremium; + } else if (apiEndpoint.contains(premiumV1Endpoint)) { + return apiDefs::ConfigType::AmneziaPremiumV1; + } else if (apiEndpoint.contains(freeV2Endpoint)) { + return apiDefs::ConfigType::AmneziaFreeV2; } } default: { @@ -95,3 +119,41 @@ bool apiUtils::isPremiumServer(const QJsonObject &serverConfigObject) apiDefs::ConfigType::ExternalPremium }; return premiumTypes.contains(getConfigType(serverConfigObject)); } + +QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject) +{ + if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV1) { + return {}; + } + + QList> orderedFields; + orderedFields.append(qMakePair(apiDefs::key::name, serverConfigObject[apiDefs::key::name].toString())); + orderedFields.append(qMakePair(apiDefs::key::description, serverConfigObject[apiDefs::key::description].toString())); + orderedFields.append(qMakePair(apiDefs::key::configVersion, serverConfigObject[apiDefs::key::configVersion].toDouble())); + orderedFields.append(qMakePair(apiDefs::key::protocol, serverConfigObject[apiDefs::key::protocol].toString())); + orderedFields.append(qMakePair(apiDefs::key::apiEndpoint, serverConfigObject[apiDefs::key::apiEndpoint].toString())); + orderedFields.append(qMakePair(apiDefs::key::apiKey, serverConfigObject[apiDefs::key::apiKey].toString())); + + QString vpnKeyStr = "{"; + for (int i = 0; i < orderedFields.size(); ++i) { + const auto &pair = orderedFields[i]; + if (pair.second.typeId() == QMetaType::Type::QString) { + vpnKeyStr += "\"" + pair.first + "\": \"" + pair.second.toString() + "\""; + } else if (pair.second.typeId() == QMetaType::Type::Double || pair.second.typeId() == QMetaType::Type::Int) { + vpnKeyStr += "\"" + pair.first + "\": " + QString::number(pair.second.toDouble(), 'f', 1); + } + + if (i < orderedFields.size() - 1) { + vpnKeyStr += ", "; + } + } + vpnKeyStr += "}"; + + QByteArray vpnKeyCompressed = escapeUnicode(vpnKeyStr).toUtf8(); + vpnKeyCompressed = qCompress(vpnKeyCompressed, 6); + vpnKeyCompressed = vpnKeyCompressed.mid(4); + + QByteArray signedData = AMNEZIA_CONFIG_SIGNATURE + vpnKeyCompressed; + + return QString("vpn://%1").arg(QString(signedData.toBase64(QByteArray::Base64UrlEncoding))); +} diff --git a/client/core/api/apiUtils.h b/client/core/api/apiUtils.h index 47006e80..45eaf2de 100644 --- a/client/core/api/apiUtils.h +++ b/client/core/api/apiUtils.h @@ -19,6 +19,8 @@ namespace apiUtils apiDefs::ConfigSource getConfigSource(const QJsonObject &serverConfigObject); amnezia::ErrorCode checkNetworkReplyErrors(const QList &sslErrors, QNetworkReply *reply); + + QString getPremiumV1VpnKey(const QJsonObject &serverConfigObject); } #endif // APIUTILS_H diff --git a/client/core/controllers/coreController.cpp b/client/core/controllers/coreController.cpp index f2c09d45..0e72ef1a 100644 --- a/client/core/controllers/coreController.cpp +++ b/client/core/controllers/coreController.cpp @@ -148,6 +148,9 @@ void CoreController::initControllers() m_apiConfigsController.reset(new ApiConfigsController(m_serversModel, m_apiServicesModel, m_settings)); m_engine->rootContext()->setContextProperty("ApiConfigsController", m_apiConfigsController.get()); + + m_apiPremV1MigrationController.reset(new ApiPremV1MigrationController(m_serversModel, m_settings, this)); + m_engine->rootContext()->setContextProperty("ApiPremV1MigrationController", m_apiPremV1MigrationController.get()); } void CoreController::initAndroidController() @@ -220,6 +223,8 @@ void CoreController::initSignalHandlers() initAutoConnectHandler(); initAmneziaDnsToggledHandler(); initPrepareConfigHandler(); + initImportPremiumV2VpnKeyHandler(); + initShowMigrationDrawerHandler(); initStrictKillSwitchHandler(); } @@ -363,10 +368,29 @@ void CoreController::initPrepareConfigHandler() }); } +void CoreController::initImportPremiumV2VpnKeyHandler() +{ + connect(m_apiPremV1MigrationController.get(), &ApiPremV1MigrationController::importPremiumV2VpnKey, this, [this](const QString &vpnKey) { + m_importController->extractConfigFromData(vpnKey); + m_importController->importConfig(); + + emit m_apiPremV1MigrationController->migrationFinished(); + }); +} + +void CoreController::initShowMigrationDrawerHandler() +{ + QTimer::singleShot(1000, this, [this]() { + if (m_apiPremV1MigrationController->isPremV1MigrationReminderActive() && m_apiPremV1MigrationController->hasConfigsToMigration()) { + m_apiPremV1MigrationController->showMigrationDrawer(); + } + }); +} + void CoreController::initStrictKillSwitchHandler() { - connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged, - m_vpnConnection.get(), &VpnConnection::onKillSwitchModeChanged); + connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged, m_vpnConnection.get(), + &VpnConnection::onKillSwitchModeChanged); } QSharedPointer CoreController::pageController() const diff --git a/client/core/controllers/coreController.h b/client/core/controllers/coreController.h index 6342d738..9ae53562 100644 --- a/client/core/controllers/coreController.h +++ b/client/core/controllers/coreController.h @@ -7,6 +7,7 @@ #include "ui/controllers/api/apiConfigsController.h" #include "ui/controllers/api/apiSettingsController.h" +#include "ui/controllers/api/apiPremV1MigrationController.h" #include "ui/controllers/appSplitTunnelingController.h" #include "ui/controllers/allowedDnsController.h" #include "ui/controllers/connectionController.h" @@ -82,6 +83,8 @@ private: void initAutoConnectHandler(); void initAmneziaDnsToggledHandler(); void initPrepareConfigHandler(); + void initImportPremiumV2VpnKeyHandler(); + void initShowMigrationDrawerHandler(); void initStrictKillSwitchHandler(); QQmlApplicationEngine *m_engine {}; // TODO use parent child system here? @@ -109,6 +112,7 @@ private: QScopedPointer m_apiSettingsController; QScopedPointer m_apiConfigsController; + QScopedPointer m_apiPremV1MigrationController; QSharedPointer m_containersModel; QSharedPointer m_defaultServerContainersModel; diff --git a/client/core/defs.h b/client/core/defs.h index eff3df3b..674d1add 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -117,6 +117,7 @@ namespace amnezia ApiServicesMissingError = 1107, ApiConfigLimitError = 1108, ApiNotFoundError = 1109, + ApiMigrationError = 1110, // QFile errors OpenError = 1200, diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp index 6abab0e0..f330bc34 100644 --- a/client/core/errorstrings.cpp +++ b/client/core/errorstrings.cpp @@ -74,6 +74,7 @@ QString errorString(ErrorCode code) { case (ErrorCode::ApiServicesMissingError): errorMessage = QObject::tr("Missing list of available services"); break; case (ErrorCode::ApiConfigLimitError): errorMessage = QObject::tr("The limit of allowed configurations per subscription has been exceeded"); break; case (ErrorCode::ApiNotFoundError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break; + case (ErrorCode::ApiMigrationError): errorMessage = QObject::tr("A migration error occurred. Please contact our technical support"); break; // QFile errors case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break; diff --git a/client/resources.qrc b/client/resources.qrc index a36b60d1..72eb15c7 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -236,6 +236,9 @@ ui/qml/Pages2/PageSettingsApiNativeConfigs.qml ui/qml/Pages2/PageSettingsApiDevices.qml images/controls/monitor.svg + ui/qml/Components/ApiPremV1MigrationDrawer.qml + ui/qml/Components/ApiPremV1SubListDrawer.qml + ui/qml/Components/OtpCodeDrawer.qml images/flagKit/ZW.svg diff --git a/client/settings.cpp b/client/settings.cpp index 9a0a32e5..fb9c72c1 100644 --- a/client/settings.cpp +++ b/client/settings.cpp @@ -559,6 +559,16 @@ void Settings::disableHomeAdLabel() setValue("Conf/homeAdLabelVisible", false); } +bool Settings::isPremV1MigrationReminderActive() +{ + return value("Conf/premV1MigrationReminderActive", true).toBool(); +} + +void Settings::disablePremV1MigrationReminder() +{ + setValue("Conf/premV1MigrationReminderActive", false); +} + QStringList Settings::allowedDnsServers() const { return value("Conf/allowedDnsServers").toStringList(); diff --git a/client/settings.h b/client/settings.h index 01155c0c..eec6cc44 100644 --- a/client/settings.h +++ b/client/settings.h @@ -229,6 +229,9 @@ public: bool isHomeAdLabelVisible(); void disableHomeAdLabel(); + bool isPremV1MigrationReminderActive(); + void disablePremV1MigrationReminder(); + QStringList allowedDnsServers() const; void setAllowedDnsServers(const QStringList &servers); diff --git a/client/ui/controllers/api/apiPremV1MigrationController.cpp b/client/ui/controllers/api/apiPremV1MigrationController.cpp new file mode 100644 index 00000000..0a9b6139 --- /dev/null +++ b/client/ui/controllers/api/apiPremV1MigrationController.cpp @@ -0,0 +1,127 @@ +#include "apiPremV1MigrationController.h" + +#include +#include + +#include "core/api/apiDefs.h" +#include "core/api/apiUtils.h" +#include "core/controllers/gatewayController.h" + +ApiPremV1MigrationController::ApiPremV1MigrationController(const QSharedPointer &serversModel, + const std::shared_ptr &settings, QObject *parent) + : QObject(parent), m_serversModel(serversModel), m_settings(settings) +{ +} + +bool ApiPremV1MigrationController::hasConfigsToMigration() +{ + QJsonArray vpnKeys; + + auto serversCount = m_serversModel->getServersCount(); + for (size_t i = 0; i < serversCount; i++) { + auto serverConfigObject = m_serversModel->getServerConfig(i); + + if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV1) { + continue; + } + + QString vpnKey = apiUtils::getPremiumV1VpnKey(serverConfigObject); + vpnKeys.append(vpnKey); + } + + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + QJsonObject apiPayload; + + apiPayload["configs"] = vpnKeys; + QByteArray responseBody; + ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/is-active-subscription"), apiPayload, responseBody); + + auto migrationsStatus = QJsonDocument::fromJson(responseBody).object(); + for (const auto &migrationStatus : migrationsStatus) { + if (migrationStatus == "not_found") { + return true; + } + } + + return false; +} + +void ApiPremV1MigrationController::getSubscriptionList(const QString &email) +{ + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + QJsonObject apiPayload; + + apiPayload[apiDefs::key::email] = email; + QByteArray responseBody; + ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/subscription-list"), apiPayload, responseBody); + + if (errorCode == ErrorCode::NoError) { + m_email = email; + m_subscriptionsModel = QJsonDocument::fromJson(responseBody).array(); + if (m_subscriptionsModel.isEmpty()) { + emit noSubscriptionToMigrate(); + return; + } + + emit subscriptionsModelChanged(); + } else { + emit errorOccurred(ErrorCode::ApiMigrationError); + } +} + +QJsonArray ApiPremV1MigrationController::getSubscriptionModel() +{ + return m_subscriptionsModel; +} + +void ApiPremV1MigrationController::sendMigrationCode(const int subscriptionIndex) +{ + QEventLoop wait; + QTimer::singleShot(1000, &wait, &QEventLoop::quit); + wait.exec(); + + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + QJsonObject apiPayload; + + apiPayload[apiDefs::key::email] = m_email; + QByteArray responseBody; + ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/migration-code"), apiPayload, responseBody); + + if (errorCode == ErrorCode::NoError) { + m_subscriptionIndex = subscriptionIndex; + emit otpSuccessfullySent(); + } else { + emit errorOccurred(ErrorCode::ApiMigrationError); + } +} + +void ApiPremV1MigrationController::migrate(const QString &migrationCode) +{ + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + QJsonObject apiPayload; + + apiPayload[apiDefs::key::email] = m_email; + apiPayload[apiDefs::key::orderId] = m_subscriptionsModel.at(m_subscriptionIndex).toObject().value(apiDefs::key::id).toString(); + apiPayload[apiDefs::key::migrationCode] = migrationCode; + QByteArray responseBody; + ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/migrate"), apiPayload, responseBody); + + if (errorCode == ErrorCode::NoError) { + auto responseObject = QJsonDocument::fromJson(responseBody).object(); + QString premiumV2VpnKey = responseObject.value(apiDefs::key::config).toString(); + + emit importPremiumV2VpnKey(premiumV2VpnKey); + } else { + emit errorOccurred(ErrorCode::ApiMigrationError); + } +} + +bool ApiPremV1MigrationController::isPremV1MigrationReminderActive() +{ + return m_settings->isPremV1MigrationReminderActive(); +} + +void ApiPremV1MigrationController::disablePremV1MigrationReminder() +{ + m_settings->disablePremV1MigrationReminder(); +} diff --git a/client/ui/controllers/api/apiPremV1MigrationController.h b/client/ui/controllers/api/apiPremV1MigrationController.h new file mode 100644 index 00000000..d7c10460 --- /dev/null +++ b/client/ui/controllers/api/apiPremV1MigrationController.h @@ -0,0 +1,50 @@ +#ifndef APIPREMV1MIGRATIONCONTROLLER_H +#define APIPREMV1MIGRATIONCONTROLLER_H + +#include + +#include "ui/models/servers_model.h" + +class ApiPremV1MigrationController : public QObject +{ + Q_OBJECT +public: + ApiPremV1MigrationController(const QSharedPointer &serversModel, const std::shared_ptr &settings, + QObject *parent = nullptr); + + Q_PROPERTY(QJsonArray subscriptionsModel READ getSubscriptionModel NOTIFY subscriptionsModelChanged) + +public slots: + bool hasConfigsToMigration(); + void getSubscriptionList(const QString &email); + QJsonArray getSubscriptionModel(); + void sendMigrationCode(const int subscriptionIndex); + void migrate(const QString &migrationCode); + + bool isPremV1MigrationReminderActive(); + void disablePremV1MigrationReminder(); + +signals: + void subscriptionsModelChanged(); + + void otpSuccessfullySent(); + + void importPremiumV2VpnKey(const QString &vpnKey); + + void errorOccurred(ErrorCode errorCode); + + void showMigrationDrawer(); + void migrationFinished(); + + void noSubscriptionToMigrate(); + +private: + QSharedPointer m_serversModel; + std::shared_ptr m_settings; + + QJsonArray m_subscriptionsModel; + int m_subscriptionIndex; + QString m_email; +}; + +#endif // APIPREMV1MIGRATIONCONTROLLER_H diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index 7cde28b4..0a7b2526 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -348,6 +348,25 @@ void ServersModel::removeServer() endResetModel(); } +void ServersModel::removeServer(const int serverIndex) +{ + beginResetModel(); + m_settings->removeServer(serverIndex); + m_servers = m_settings->serversArray(); + + if (m_settings->defaultServerIndex() == serverIndex) { + setDefaultServerIndex(0); + } else if (m_settings->defaultServerIndex() > serverIndex) { + setDefaultServerIndex(m_settings->defaultServerIndex() - 1); + } + + if (m_settings->serversCount() == 0) { + setDefaultServerIndex(-1); + } + setProcessedServerIndex(m_defaultServerIndex); + endResetModel(); +} + QHash ServersModel::roleNames() const { QHash roles; diff --git a/client/ui/models/servers_model.h b/client/ui/models/servers_model.h index 4b790c7a..c4803708 100644 --- a/client/ui/models/servers_model.h +++ b/client/ui/models/servers_model.h @@ -90,6 +90,7 @@ public slots: void addServer(const QJsonObject &server); void editServer(const QJsonObject &server, const int serverIndex); void removeServer(); + void removeServer(const int serverIndex); QJsonObject getServerConfig(const int serverIndex); diff --git a/client/ui/qml/Components/ApiPremV1MigrationDrawer.qml b/client/ui/qml/Components/ApiPremV1MigrationDrawer.qml new file mode 100644 index 00000000..113ec1f6 --- /dev/null +++ b/client/ui/qml/Components/ApiPremV1MigrationDrawer.qml @@ -0,0 +1,194 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import QtCore + +import PageEnum 1.0 +import Style 1.0 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" +import "../Components" + +DrawerType2 { + id: root + + expandedHeight: parent.height * 0.9 + + Connections { + target: ApiPremV1MigrationController + + function onErrorOccurred(error, goToPageHome) { + PageController.showErrorMessage(error) + root.closeTriggered() + } + } + + expandedStateContent: Item { + implicitHeight: root.expandedHeight + + ListViewType { + id: listView + + anchors.fill: parent + + model: 1 // fake model to force the ListView to be created without a model + snapMode: ListView.NoSnap + + header: ColumnLayout { + width: listView.width + + Header2Type { + id: header + Layout.fillWidth: true + Layout.topMargin: 20 + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + headerText: qsTr("Switch to the new Amnezia Premium subscription") + } + } + + delegate: ColumnLayout { + width: listView.width + + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: 16 + anchors.rightMargin: 16 + + ParagraphTextType { + Layout.fillWidth: true + Layout.topMargin: 24 + Layout.bottomMargin: 24 + + horizontalAlignment: Text.AlignLeft + textFormat: Text.RichText + text: { + var str = qsTr("We'll preserve all remaining days of your current subscription and give you an extra month as a thank you. ") + str += qsTr("This new subscription type will be actively developed with more locations and features added regularly. Currently available:") + str += "

    " + str += qsTr("
  • 9 locations (with more coming soon)
  • ") + str += qsTr("
  • Easier switching between countries in the app
  • ") + str += qsTr("
  • Personal dashboard to manage your subscription
  • ") + str += "
" + str += qsTr("Old keys will be deactivated after switching.") + } + } + + TextFieldWithHeaderType { + id: emailLabel + Layout.fillWidth: true + + borderColor: AmneziaStyle.color.mutedGray + headerTextColor: AmneziaStyle.color.paleGray + + headerText: qsTr("Email") + textField.placeholderText: qsTr("mail@example.com") + + + textField.onFocusChanged: { + textField.text = textField.text.replace(/^\s+|\s+$/g, '') + } + + Connections { + target: ApiPremV1MigrationController + + function onNoSubscriptionToMigrate() { + emailLabel.errorText = qsTr("No old format subscriptions for a given email") + } + } + } + + CaptionTextType { + Layout.fillWidth: true + Layout.topMargin: 16 + + color: AmneziaStyle.color.mutedGray + + text: qsTr("Enter the email you used for your current subscription") + } + + ApiPremV1SubListDrawer { + id: apiPremV1SubListDrawer + parent: root + + anchors.fill: parent + } + + OtpCodeDrawer { + id: otpCodeDrawer + parent: root + + anchors.fill: parent + } + + BasicButtonType { + id: yesButton + Layout.fillWidth: true + Layout.topMargin: 32 + + text: qsTr("Continue") + + clickedFunc: function() { + PageController.showBusyIndicator(true) + ApiPremV1MigrationController.getSubscriptionList(emailLabel.textField.text) + PageController.showBusyIndicator(false) + } + } + + BasicButtonType { + id: noButton + Layout.fillWidth: true + + defaultColor: AmneziaStyle.color.transparent + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + disabledColor: AmneziaStyle.color.mutedGray + textColor: AmneziaStyle.color.paleGray + borderWidth: 1 + + text: qsTr("Remind me later") + + clickedFunc: function() { + root.closeTriggered() + } + } + + BasicButtonType { + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: 32 + Layout.bottomMargin: 32 + implicitHeight: 32 + + defaultColor: "transparent" + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + textColor: AmneziaStyle.color.vibrantRed + + text: qsTr("Don't remind me again") + + clickedFunc: function() { + var headerText = qsTr("No more reminders? You can always switch to the new format in the server settings") + var yesButtonText = qsTr("Continue") + var noButtonText = qsTr("Cancel") + + var yesButtonFunction = function() { + ApiPremV1MigrationController.disablePremV1MigrationReminder() + root.closeTriggered() + } + var noButtonFunction = function() { + } + + showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) + } + } + } + } + } +} diff --git a/client/ui/qml/Components/ApiPremV1SubListDrawer.qml b/client/ui/qml/Components/ApiPremV1SubListDrawer.qml new file mode 100644 index 00000000..221b7a89 --- /dev/null +++ b/client/ui/qml/Components/ApiPremV1SubListDrawer.qml @@ -0,0 +1,89 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import Style 1.0 + +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" + +DrawerType2 { + id: root + + Connections { + target: ApiPremV1MigrationController + + function onSubscriptionsModelChanged() { + if (ApiPremV1MigrationController.subscriptionsModel.length > 1) { + root.openTriggered() + } else { + sendMigrationCode(0) + } + } + } + + function sendMigrationCode(index) { + PageController.showBusyIndicator(true) + ApiPremV1MigrationController.sendMigrationCode(index) + root.closeTriggered() + PageController.showBusyIndicator(false) + } + + expandedHeight: parent.height * 0.9 + + expandedStateContent: Item { + implicitHeight: root.expandedHeight + + ListViewType { + id: listView + + anchors.fill: parent + + model: ApiPremV1MigrationController.subscriptionsModel + + header: ColumnLayout { + width: listView.width + + Header2Type { + id: header + Layout.fillWidth: true + Layout.topMargin: 20 + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + headerText: qsTr("Choose Subscription") + } + } + + delegate: Item { + implicitWidth: listView.width + implicitHeight: delegateContent.implicitHeight + + ColumnLayout { + id: delegateContent + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + LabelWithButtonType { + id: server + Layout.fillWidth: true + + text: qsTr("Order ID: ") + modelData.id + + descriptionText: qsTr("Purchase Date: ") + Qt.formatDateTime(new Date(modelData.created_at), "dd.MM.yyyy hh:mm") + rightImageSource: "qrc:/images/controls/chevron-right.svg" + + clickedFunction: function() { + sendMigrationCode(index) + } + } + + DividerType {} + } + } + } + } +} diff --git a/client/ui/qml/Components/OtpCodeDrawer.qml b/client/ui/qml/Components/OtpCodeDrawer.qml new file mode 100644 index 00000000..e26982b9 --- /dev/null +++ b/client/ui/qml/Components/OtpCodeDrawer.qml @@ -0,0 +1,77 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import Style 1.0 + +import "../Controls2" +import "../Controls2/TextTypes" + +import "../Config" + +DrawerType2 { + id: root + + Connections { + target: ApiPremV1MigrationController + + function onOtpSuccessfullySent() { + root.openTriggered() + } + } + + expandedHeight: parent.height * 0.6 + + expandedStateContent: Item { + implicitHeight: root.expandedHeight + + ColumnLayout { + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: 16 + anchors.rightMargin: 16 + spacing: 0 + + Header2Type { + id: header + Layout.fillWidth: true + Layout.topMargin: 20 + + headerText: qsTr("OTP code was sent to your email") + } + + TextFieldWithHeaderType { + id: otpFiled + + borderColor: AmneziaStyle.color.mutedGray + headerTextColor: AmneziaStyle.color.paleGray + + Layout.fillWidth: true + Layout.topMargin: 16 + headerText: qsTr("OTP Code") + textField.maximumLength: 30 + checkEmptyText: true + } + + BasicButtonType { + id: saveButton + + Layout.fillWidth: true + Layout.topMargin: 16 + + text: qsTr("Continue") + + clickedFunc: function() { + PageController.showBusyIndicator(true) + ApiPremV1MigrationController.migrate(otpFiled.textField.text) + PageController.showBusyIndicator(false) + root.closeTriggered() + } + } + } + } +} diff --git a/client/ui/qml/Components/QuestionDrawer.qml b/client/ui/qml/Components/QuestionDrawer.qml index 0c14e52d..ddf5cda4 100644 --- a/client/ui/qml/Components/QuestionDrawer.qml +++ b/client/ui/qml/Components/QuestionDrawer.qml @@ -1,3 +1,5 @@ +pragma ComponentBehavior: Bound + import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -39,7 +41,7 @@ DrawerType2 { Layout.rightMargin: 16 Layout.leftMargin: 16 - text: headerText + text: root.headerText } ParagraphTextType { @@ -48,7 +50,7 @@ DrawerType2 { Layout.rightMargin: 16 Layout.leftMargin: 16 - text: descriptionText + text: root.descriptionText } BasicButtonType { @@ -58,11 +60,11 @@ DrawerType2 { Layout.rightMargin: 16 Layout.leftMargin: 16 - text: yesButtonText + text: root.yesButtonText clickedFunc: function() { - if (yesButtonFunction && typeof yesButtonFunction === "function") { - yesButtonFunction() + if (root.yesButtonFunction && typeof root.yesButtonFunction === "function") { + root.yesButtonFunction() } } } @@ -80,11 +82,13 @@ DrawerType2 { textColor: AmneziaStyle.color.paleGray borderWidth: 1 - text: noButtonText + visible: root.noButtonText !== "" + + text: root.noButtonText clickedFunc: function() { - if (noButtonFunction && typeof noButtonFunction === "function") { - noButtonFunction() + if (root.noButtonFunction && typeof root.noButtonFunction === "function") { + root.noButtonFunction() } } } diff --git a/client/ui/qml/Pages2/PageHome.qml b/client/ui/qml/Pages2/PageHome.qml index f7233a89..7934e5fb 100644 --- a/client/ui/qml/Pages2/PageHome.qml +++ b/client/ui/qml/Pages2/PageHome.qml @@ -33,6 +33,31 @@ PageType { } } + Connections { + + target: ApiPremV1MigrationController + + function onMigrationFinished() { + apiPremV1MigrationDrawer.closeTriggered() + + var headerText = qsTr("You've successfully switched to the new Amnezia Premium subscription!") + var descriptionText = qsTr("Old keys will no longer work. Please use your new subscription key to connect. \nThank you for staying with us!") + var yesButtonText = qsTr("Continue") + var noButtonText = "" + + var yesButtonFunction = function() { + } + var noButtonFunction = function() { + } + + showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) + } + + function onShowMigrationDrawer() { + apiPremV1MigrationDrawer.openTriggered() + } + } + Item { objectName: "homeColumnItem" @@ -429,4 +454,9 @@ PageType { } } } + + ApiPremV1MigrationDrawer { + id: apiPremV1MigrationDrawer + anchors.fill: parent + } } diff --git a/client/ui/qml/Pages2/PageSettingsServerData.qml b/client/ui/qml/Pages2/PageSettingsServerData.qml index 977e669e..995ca74b 100644 --- a/client/ui/qml/Pages2/PageSettingsServerData.qml +++ b/client/ui/qml/Pages2/PageSettingsServerData.qml @@ -257,6 +257,24 @@ PageType { DividerType { visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") } + + LabelWithButtonType { + id: labelWithButton6 + visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") + Layout.fillWidth: true + + text: qsTr("Switch to the new Amnezia Premium subscription") + textColor: AmneziaStyle.color.vibrantRed + + clickedFunction: function() { + PageController.goToPageHome() + ApiPremV1MigrationController.showMigrationDrawer() + } + } + + DividerType { + visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") + } } } } From 7702f2f74cc4a48362083693ba893938140bbfc6 Mon Sep 17 00:00:00 2001 From: Nethius Date: Thu, 15 May 2025 21:34:48 +0800 Subject: [PATCH 34/65] bugfix: adding gateway to exceptions only if strict killswitch is enabled (#1585) --- client/core/controllers/gatewayController.cpp | 19 +++++++++------ client/core/controllers/gatewayController.h | 4 +++- .../controllers/api/apiConfigsController.cpp | 24 ++++++++++++------- .../api/apiPremV1MigrationController.cpp | 12 ++++++---- .../controllers/api/apiSettingsController.cpp | 3 ++- 5 files changed, 41 insertions(+), 21 deletions(-) diff --git a/client/core/controllers/gatewayController.cpp b/client/core/controllers/gatewayController.cpp index 0d86b9d5..9a7ee6e5 100644 --- a/client/core/controllers/gatewayController.cpp +++ b/client/core/controllers/gatewayController.cpp @@ -14,8 +14,8 @@ #include "amnezia_application.h" #include "core/api/apiUtils.h" -#include "utilities.h" #include "core/networkUtilities.h" +#include "utilities.h" #ifdef AMNEZIA_DESKTOP #include "core/ipcclient.h" @@ -38,8 +38,13 @@ namespace constexpr QLatin1String errorResponsePattern3("Account not found."); } -GatewayController::GatewayController(const QString &gatewayEndpoint, bool isDevEnvironment, int requestTimeoutMsecs, QObject *parent) - : QObject(parent), m_gatewayEndpoint(gatewayEndpoint), m_isDevEnvironment(isDevEnvironment), m_requestTimeoutMsecs(requestTimeoutMsecs) +GatewayController::GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs, + const bool isStrictKillSwitchEnabled, QObject *parent) + : QObject(parent), + m_gatewayEndpoint(gatewayEndpoint), + m_isDevEnvironment(isDevEnvironment), + m_requestTimeoutMsecs(requestTimeoutMsecs), + m_isStrictKillSwitchEnabled(isStrictKillSwitchEnabled) { } @@ -58,11 +63,11 @@ ErrorCode GatewayController::get(const QString &endpoint, QByteArray &responseBo // bypass killSwitch exceptions for API-gateway #ifdef AMNEZIA_DESKTOP - { + if (m_isStrictKillSwitchEnabled) { QString host = QUrl(request.url()).host(); QString ip = NetworkUtilities::getIPAddress(host); if (!ip.isEmpty()) { - IpcClient::Interface()->addKillSwitchAllowedRange(QStringList{ip}); + IpcClient::Interface()->addKillSwitchAllowedRange(QStringList { ip }); } } #endif @@ -120,11 +125,11 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api // bypass killSwitch exceptions for API-gateway #ifdef AMNEZIA_DESKTOP - { + if (m_isStrictKillSwitchEnabled) { QString host = QUrl(request.url()).host(); QString ip = NetworkUtilities::getIPAddress(host); if (!ip.isEmpty()) { - IpcClient::Interface()->addKillSwitchAllowedRange(QStringList{ip}); + IpcClient::Interface()->addKillSwitchAllowedRange(QStringList { ip }); } } #endif diff --git a/client/core/controllers/gatewayController.h b/client/core/controllers/gatewayController.h index 45d989f0..9f91df53 100644 --- a/client/core/controllers/gatewayController.h +++ b/client/core/controllers/gatewayController.h @@ -15,7 +15,8 @@ class GatewayController : public QObject Q_OBJECT public: - explicit GatewayController(const QString &gatewayEndpoint, bool isDevEnvironment, int requestTimeoutMsecs, QObject *parent = nullptr); + explicit GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs, + const bool isStrictKillSwitchEnabled, QObject *parent = nullptr); amnezia::ErrorCode get(const QString &endpoint, QByteArray &responseBody); amnezia::ErrorCode post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody); @@ -30,6 +31,7 @@ private: int m_requestTimeoutMsecs; QString m_gatewayEndpoint; bool m_isDevEnvironment = false; + bool m_isStrictKillSwitchEnabled = false; }; #endif // GATEWAYCONTROLLER_H diff --git a/client/ui/controllers/api/apiConfigsController.cpp b/client/ui/controllers/api/apiConfigsController.cpp index 74e22a85..21d371bb 100644 --- a/client/ui/controllers/api/apiConfigsController.cpp +++ b/client/ui/controllers/api/apiConfigsController.cpp @@ -63,7 +63,8 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode, return false; } - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + 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(); @@ -94,7 +95,8 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode, bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode) { - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + 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(); @@ -140,7 +142,8 @@ void ApiConfigsController::copyVpnKeyToClipboard() bool ApiConfigsController::fillAvailableServices() { - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, + m_settings->isStrictKillSwitchEnabled()); QJsonObject apiPayload; apiPayload[configKey::osVersion] = QSysInfo::productType(); @@ -171,7 +174,8 @@ bool ApiConfigsController::importServiceFromGateway() return false; } - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, + m_settings->isStrictKillSwitchEnabled()); auto installationUuid = m_settings->getInstallationUuid(true); auto userCountryCode = m_apiServicesModel->getCountryCode(); @@ -211,7 +215,8 @@ 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); + 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(); @@ -274,7 +279,8 @@ bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex) QThread::msleep(10); #endif - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, + m_settings->isStrictKillSwitchEnabled()); auto serverConfig = m_serversModel->getServerConfig(serverIndex); auto installationUuid = m_settings->getInstallationUuid(true); @@ -304,7 +310,8 @@ bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex) bool ApiConfigsController::deactivateDevice() { - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, + m_settings->isStrictKillSwitchEnabled()); auto serverIndex = m_serversModel->getProcessedServerIndex(); auto serverConfigObject = m_serversModel->getServerConfig(serverIndex); @@ -339,7 +346,8 @@ bool ApiConfigsController::deactivateDevice() bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const QString &serverCountryCode) { - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, + m_settings->isStrictKillSwitchEnabled()); auto serverIndex = m_serversModel->getProcessedServerIndex(); auto serverConfigObject = m_serversModel->getServerConfig(serverIndex); diff --git a/client/ui/controllers/api/apiPremV1MigrationController.cpp b/client/ui/controllers/api/apiPremV1MigrationController.cpp index 0a9b6139..7b0ff100 100644 --- a/client/ui/controllers/api/apiPremV1MigrationController.cpp +++ b/client/ui/controllers/api/apiPremV1MigrationController.cpp @@ -29,7 +29,8 @@ bool ApiPremV1MigrationController::hasConfigsToMigration() vpnKeys.append(vpnKey); } - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, + m_settings->isStrictKillSwitchEnabled()); QJsonObject apiPayload; apiPayload["configs"] = vpnKeys; @@ -48,7 +49,8 @@ bool ApiPremV1MigrationController::hasConfigsToMigration() void ApiPremV1MigrationController::getSubscriptionList(const QString &email) { - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, + m_settings->isStrictKillSwitchEnabled()); QJsonObject apiPayload; apiPayload[apiDefs::key::email] = email; @@ -80,7 +82,8 @@ void ApiPremV1MigrationController::sendMigrationCode(const int subscriptionIndex QTimer::singleShot(1000, &wait, &QEventLoop::quit); wait.exec(); - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, + m_settings->isStrictKillSwitchEnabled()); QJsonObject apiPayload; apiPayload[apiDefs::key::email] = m_email; @@ -97,7 +100,8 @@ void ApiPremV1MigrationController::sendMigrationCode(const int subscriptionIndex void ApiPremV1MigrationController::migrate(const QString &migrationCode) { - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, + m_settings->isStrictKillSwitchEnabled()); QJsonObject apiPayload; apiPayload[apiDefs::key::email] = m_email; diff --git a/client/ui/controllers/api/apiSettingsController.cpp b/client/ui/controllers/api/apiSettingsController.cpp index 8927312d..b5da751d 100644 --- a/client/ui/controllers/api/apiSettingsController.cpp +++ b/client/ui/controllers/api/apiSettingsController.cpp @@ -48,7 +48,8 @@ bool ApiSettingsController::getAccountInfo(bool reload) wait.exec(); } - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), requestTimeoutMsecs); + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), requestTimeoutMsecs, + m_settings->isStrictKillSwitchEnabled()); auto processedIndex = m_serversModel->getProcessedServerIndex(); auto serverConfig = m_serversModel->getServerConfig(processedIndex); From e23cbe67ad361e0216520df3cc8a7ee8e69e1a2a Mon Sep 17 00:00:00 2001 From: Nethius Date: Fri, 16 May 2025 14:34:56 +0800 Subject: [PATCH 35/65] chore: added account_info request for amfree (#1586) --- client/ui/controllers/api/apiSettingsController.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/client/ui/controllers/api/apiSettingsController.cpp b/client/ui/controllers/api/apiSettingsController.cpp index b5da751d..f20f92bf 100644 --- a/client/ui/controllers/api/apiSettingsController.cpp +++ b/client/ui/controllers/api/apiSettingsController.cpp @@ -63,12 +63,10 @@ bool ApiSettingsController::getAccountInfo(bool reload) QByteArray responseBody; - if (apiUtils::isPremiumServer(serverConfig)) { - ErrorCode errorCode = gatewayController.post(QString("%1v1/account_info"), apiPayload, responseBody); - if (errorCode != ErrorCode::NoError) { - emit errorOccurred(errorCode); - return false; - } + ErrorCode errorCode = gatewayController.post(QString("%1v1/account_info"), apiPayload, responseBody); + if (errorCode != ErrorCode::NoError) { + emit errorOccurred(errorCode); + return false; } QJsonObject accountInfo = QJsonDocument::fromJson(responseBody).object(); From e16243ff551d175f2eca8adb4bc2751fb666f031 Mon Sep 17 00:00:00 2001 From: MrMirDan <58086007+MrMirDan@users.noreply.github.com> Date: Tue, 20 May 2025 05:55:24 +0300 Subject: [PATCH 36/65] chore: text translations etc (#1590) --- client/core/errorstrings.cpp | 2 +- client/translations/amneziavpn_ru_RU.ts | 1168 +++++++---------- .../Components/ApiPremV1MigrationDrawer.qml | 2 +- .../ui/qml/Pages2/PageSettingsKillSwitch.qml | 10 +- 4 files changed, 460 insertions(+), 722 deletions(-) diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp index f330bc34..e141b3c7 100644 --- a/client/core/errorstrings.cpp +++ b/client/core/errorstrings.cpp @@ -74,7 +74,7 @@ QString errorString(ErrorCode code) { case (ErrorCode::ApiServicesMissingError): errorMessage = QObject::tr("Missing list of available services"); break; case (ErrorCode::ApiConfigLimitError): errorMessage = QObject::tr("The limit of allowed configurations per subscription has been exceeded"); break; case (ErrorCode::ApiNotFoundError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break; - case (ErrorCode::ApiMigrationError): errorMessage = QObject::tr("A migration error occurred. Please contact our technical support"); break; + case (ErrorCode::ApiMigrationError): errorMessage = QObject::tr("A migration error has occurred. Please contact our technical support"); break; // QFile errors case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break; diff --git a/client/translations/amneziavpn_ru_RU.ts b/client/translations/amneziavpn_ru_RU.ts index e653b32b..af47ffb3 100644 --- a/client/translations/amneziavpn_ru_RU.ts +++ b/client/translations/amneziavpn_ru_RU.ts @@ -3,16 +3,60 @@ AdLabel - - Amnezia Premium - for access to any website - Amnezia Premium - для доступа к любым сайтам - Amnezia Premium - for access to all websites and online resources Amnezia Premium - доступ ко всем сайтам и онлайн ресурсам + + AllowedDnsController + + + The address does not look like a valid IP address + Адрес не похож на корректный IP-адрес + + + + New DNS server added: %1 + Добавлен новый DNS сервер: %1 + + + + DNS server already exists: %1 + DNS сервер уже существует: %1 + + + + DNS server removed: %1 + DNS сервер удален: %1 + + + + Can't open file: %1 + Невозможно открыть файл: %1 + + + + Failed to parse JSON data from file: %1 + Не удалось разобрать JSON-данные из файла: %1 + + + + The JSON data is not an array in file: %1 + JSON-данные не являются массивом в файле: %1 + + + + Import completed + Импорт завершён + + + + Export completed + Экспорт завершён + + ApiAccountInfoModel @@ -37,19 +81,9 @@ Классический VPN для комфортной работы, загрузки больших файлов и просмотра видео. Доступ ко всем сайтам и онлайн-ресурсам. Скорость — до 200 Мбит/с - + Free unlimited access to a basic set of websites such as Facebook, Instagram, Twitter (X), Discord, Telegram and more. YouTube is not included in the free plan. - Бесплатный неограниченный доступ к базовому набору сайтов и приложений, таким как Facebook, Instagram, Twitter (X), Discord, Telegram и другим. YouTube не включен в бесплатный тариф. - - - - amnezia_free_support_bot - - - - - amnezia_premium_support_bot - + Бесплатный неограниченный доступ к базовому набору сайтов и приложений, таким как Facebook, Instagram, Twitter (X), Discord, Telegram и другим. YouTube не включён в бесплатный тариф. @@ -70,43 +104,121 @@ Страна подключения изменена на %1
+ + ApiPremV1MigrationDrawer + + + Switch to the new Amnezia Premium subscription + Перейдите на новый тип подписки Amnezia Premium + + + + We'll preserve all remaining days of your current subscription and give you an extra month as a thank you. + Мы сохраним все оставшиеся дни текущей подписки и подарим дополнительный месяц в благодарность за переход. + + + + This new subscription type will be actively developed with more locations and features added regularly. Currently available: + Именно новый тип подписки будет активно развиваться и пополняться новыми локациями и функциями. Уже доступны: + + + + <li>13 locations (with more coming soon)</li> + <li>13 локаций (их число будет расти)</li> + + + + <li>Easier switching between countries in the app</li> + <li>Удобное переключение между странами в приложении</li> + + + + <li>Personal dashboard to manage your subscription</li> + Личный кабинет для управления подпиской + + + + Old keys will be deactivated after switching. + После перехода старые ключи перестанут работать. + + + + Email + Email + + + + mail@example.com + + + + + No old format subscriptions for a given email + Для указанного адреса электронной почты нет подписок старого типа + + + + Enter the email you used for your current subscription + Укажите адрес почты, который использовали при заказе текущей подписки + + + + + Continue + Продолжить + + + + Remind me later + Напомнить позже + + + + Don't remind me again + Больше не напоминать + + + + No more reminders? You can always switch to the new format in the server settings + Отключить напоминания? Вы всегда сможете перейти на новый тип подписки в настройках сервера + + + + Cancel + Отменить + + + + ApiPremV1SubListDrawer + + + Choose Subscription + Выбрать подписку + + + + Order ID: + ID заказа: + + + + Purchase Date: + Дата покупки: + + ApiServicesModel - - Classic VPN for comfortable work, downloading large files and watching videos. Works for any sites. Speed up to %1 MBit/s - Классический VPN для комфортной работы, загрузки больших файлов и просмотра видео. Работает для любых сайтов. Скорость до %1 Мбит/с - - - VPN to access blocked sites in regions with high levels of Internet censorship. - VPN для доступа к заблокированным сайтам в регионах с высоким уровнем интернет-цензуры. - <p><a style="color: #EB5757;">Not available in your region. If you have VPN enabled, disable it, return to the previous screen, and try again.</a> <p><a style="color: #EB5757;">Недоступно в вашем регионе. Если у вас включен VPN, отключите его, вернитесь на предыдущий экран и попробуйте снова.</a> - - Amnezia Premium - A classic VPN for comfortable work, downloading large files, and watching videos in high resolution. It works for all websites, even in countries with the highest level of internet censorship. - Amnezia Premium — классический VPN для комфортной работы, загрузки больших файлов и просмотра видео в высоком разрешении. Работает на всех сайтах, даже в странах с самым высоким уровнем интернет-цензуры. - - - Amnezia Free is a free VPN to bypass blocking in countries with high levels of internet censorship - Amnezia Free - это бесплатный VPN для обхода блокировок в странах с высоким уровнем интернет-цензуры - - - Amnezia Premium is VPN for comfortable work, downloading large files and watching videos in 8K resolution. Works for any sites with no restrictions. Speed up to %1 MBit/s. Unlimited traffic. - Amnezia Premium — VPN для комфортной работы, скачивания больших файлов и просмотра видео в высоком разрешении. Скорость до %1 Мбит/с. Безлимитный трафик. - Amnezia Free provides unlimited, free access to a basic set of websites and apps, including Facebook, Instagram, Twitter (X), Discord, Telegram, and more. YouTube is not included in the free plan. Amnezia Free позволяет бесплатно и без ограничений пользоваться базовым набором сайтов и приложений, включая Facebook, Instagram, Twitter (X), Discord, Telegram и другие. YouTube не входит в бесплатный тариф. - - Amnezia Premium is VPN for comfortable work, downloading large files and watching videos in 8K resolution. Works for any sites with no restrictions. - Amnezia Premium — VPN для комфортной работы, скачивания больших файлов и просмотра видео в высоком разрешении. Работает для любых сайтов без ограничений. - Amnezia Premium is classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. Speeds up to %1 Mbps. @@ -120,7 +232,7 @@ %1 MBit/s - + %1 Мбит/с @@ -181,12 +293,6 @@ ConnectionController - - VPN Protocols is not installed. - Please install VPN container at first - VPN-протоколы не установлены. - Пожалуйста, установите протокол - Connecting... @@ -212,14 +318,6 @@ Settings updated successfully Настройки успешно обновлены - - The selected protocol is not supported on the current platform - Выбранный протокол не поддерживается на данном устройстве - - - unable to create configuration - не удалось создать конфигурацию - Reconnecting... @@ -282,22 +380,14 @@ ExportController - - Access error! - Ошибка доступа! - HomeContainersListView - + Unable change protocol while there is an active connection Невозможно изменить протокол во время активного соединения - - The selected protocol is not supported on the current platform - Выбранный протокол не поддерживается на данном устройстве - HomeSplitTunnelingDrawer @@ -348,14 +438,6 @@ Can't be disabled for current server ImportController - - Unable to open file - Невозможно открыть файл - - - Invalid configuration file - Неверный файл конфигурации - Scanned %1 of %2. @@ -371,10 +453,6 @@ Can't be disabled for current server <br>In the imported configuration, potentially dangerous lines were found: <br>В импортированной конфигурации обнаружены потенциально опасные строки: - - In the imported configuration, potentially dangerous lines were found: - В импортированной конфигурации были обнаружены потенциально опасные строки: - InstallController @@ -403,62 +481,50 @@ Already installed containers were found on the server. All installed containers На сервере обнаружены установленные протоколы и сервисы. Все они были добавлены в приложение - + Settings updated successfully Настройки успешно обновлены - + Server '%1' was rebooted Сервер '%1' был перезагружен - + Server '%1' was removed Сервер '%1' был удален - + All containers from server '%1' have been removed Все протоколы и сервисы были удалены с сервера '%1' - + %1 has been removed from the server '%2' %1 был удален с сервера '%2' - + Api config removed Конфигурация API удалена - + %1 cached profile cleared %1 закэшированный профиль очищен - + Please login as the user Пожалуйста, войдите в систему от имени пользователя - + Server added successfully Сервер успешно добавлен - - %1 installed successfully. - %1 успешно установлен. - - - API config reloaded - Конфигурация API перезагружена - - - Successfully changed the country of connection to %1 - Изменение страны подключения на %1 - InstalledAppsDrawer @@ -525,6 +591,24 @@ Already installed containers were found on the server. All installed containers Обнаружена незащищенная сеть: + + OtpCodeDrawer + + + OTP code was sent to your email + Одноразовый код был отправлен на ваш email + + + + OTP Code + Одноразовый код + + + + Continue + Продолжить + + PageDeinstalling @@ -554,34 +638,47 @@ Already installed containers were found on the server. All installed containers PageHome - + + You've successfully switched to the new Amnezia Premium subscription! + Вы успешно перешли на новый тип подписки Amnezia Premium! + + + + Old keys will no longer work. Please use your new subscription key to connect. +Thank you for staying with us! + Старые ключи перестанут работать. Пожалуйста, используйте новый ключ для подключения. +Спасибо, что остаетесь с нами! + + + + Continue + Продолжить + + + Logging enabled Логирование включено - + Split tunneling enabled Раздельное туннелирование включено - + Split tunneling disabled Раздельное туннелирование выключено - + VPN protocol VPN-протокол - + Servers Серверы - - Unable change server while there is an active connection - Невозможно изменить сервер во время активного соединения - PageProtocolAwgClientSettings @@ -648,18 +745,6 @@ Already installed containers were found on the server. All installed containers Port Порт - - MTU - MTU - - - Remove AmneziaWG - Удалить AmneziaWG - - - Remove AmneziaWG from server? - Удалить AmneziaWG с сервера? - All users with whom you shared a connection with will no longer be able to connect to it. @@ -955,26 +1040,6 @@ Already installed containers were found on the server. All installed containers Unable change settings while there is an active connection Невозможно изменить настройки во время активного соединения - - Remove OpenVPN - Удалить OpenVPN - - - Remove OpenVPN from server? - Удалить OpenVPN с сервера? - - - All users with whom you shared a connection with will no longer be able to connect to it. - Все пользователи, с которыми вы поделились конфигурацией вашего VPN, больше не смогут к нему подключаться. - - - Continue - Продолжить - - - Cancel - Отменить - Save @@ -1137,27 +1202,11 @@ Already installed containers were found on the server. All installed containers All users with whom you shared a connection with will no longer be able to connect to it. Все пользователи, с которыми вы поделились конфигурацией вашего VPN, больше не смогут к нему подключаться. - - MTU - MTU - Unable change settings while there is an active connection Невозможно изменить настройки во время активного соединения - - Remove WG - Удалить WG - - - Remove WG from server? - Удалить WG с сервера? - - - All users with whom you shared a connection will no longer be able to connect to it. - Все пользователи, с которыми вы поделились конфигурацией вашего VPN, больше не смогут к нему подключаться. - Continue @@ -1187,12 +1236,17 @@ Already installed containers were found on the server. All installed containers Замаскировать трафик под - + + Port + Порт + + + Save Сохранить - + Unable change settings while there is an active connection Невозможно изменить настройки во время активного соединения @@ -1311,22 +1365,6 @@ Already installed containers were found on the server. All installed containers Detailed instructions Подробные инструкции - - Remove SFTP and all data stored there - Удалить SFTP-хранилище со всеми данными - - - Remove SFTP and all data stored there? - Удалить SFTP-хранилище и все хранящиеся там данные? - - - Continue - Продолжить - - - Cancel - Отменить - PageServiceSocksProxySettings @@ -1435,22 +1473,6 @@ Already installed containers were found on the server. All installed containers When configuring WordPress set the this onion address as domain. При настройке WordPress укажите этот onion-адрес в качестве домена. - - Remove website - Удалить сайт - - - The site with all data will be removed from the tor network. - Сайт со всеми данными будет удален из сети Tor. - - - Continue - Продолжить - - - Cancel - Отменить - PageSettings @@ -1536,19 +1558,11 @@ Already installed containers were found on the server. All installed containers support@amnezia.org support@amnezia.org - - Mail - Почта - For reviews and bug reports Для отзывов и сообщений об ошибках - - Copied - Скопировано - mailto:support@amnezia.org @@ -1579,10 +1593,6 @@ Already installed containers were found on the server. All installed containers Visit official website Посетить официальный сайт - - https://amnezia.org - https://amnezia.org - Software version: %1 @@ -1614,10 +1624,6 @@ Already installed containers were found on the server. All installed containers PageSettingsApiDevices - - Active devices - Активные устройства - Active Devices @@ -1663,10 +1669,6 @@ Already installed containers were found on the server. All installed containers This will unlink the device from your subscription. You can reconnect it anytime by pressing "Reload API config" in subscription settings on device. Это отключит устройство от вашей подписки. Вы можете повторно подключить его в любое время, нажав "Перезагрузить конфигурацию API" в настройках подписки на устройстве. - - This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect. - Это устройство будет отвязано от вашей подписки. Вы можете подключить его снова в любой момент, нажав кнопку "Подключиться". - Continue @@ -1763,10 +1765,6 @@ Already installed containers were found on the server. All installed containers PageSettingsApiLanguageList - - Unable change server location while there is an active connection - Невозможно изменить локацию во время активного соединения - PageSettingsApiNativeConfigs @@ -1775,10 +1773,6 @@ Already installed containers were found on the server. All installed containers Save AmneziaVPN config Сохранить конфигурацию AmneziaVPN - - Configuration files - Файл конфигурации - Configuration Files @@ -1857,74 +1851,26 @@ Already installed containers were found on the server. All installed containers PageSettingsApiServerInfo - - For the region - Для региона - - - Price - Цена - - - Work period - Период работы - - - Valid until - Действует до - - - Speed - Скорость - - - Copied - Скопировано - - - Subscription status - Статус подписки - - - Active connections - Активные соединения - Configurations have been updated for some countries. Download and install the updated configuration files Сетевые адреса одного или нескольких серверов были обновлены. Пожалуйста, удалите старые конфигурацию и загрузите новые файлы - - Subscription key - Ключ для подключения - Amnezia Premium subscription key Ключ подписки Amnezia Premium - - Save VPN key to file - Сохранить VPN-ключ в файле - Copy VPN key Скопировать VPN ключ - - Configuration files - Файл конфигурации - Manage configuration files Управление файлами конфигурации - - Active devices - Активные устройства - Subscription Status @@ -2019,10 +1965,6 @@ Already installed containers were found on the server. All installed containers This will unlink the device from your subscription. You can reconnect it anytime by pressing "Reload API config" in subscription settings on device. Это отключит устройство от вашей подписки. Вы можете повторно подключить его в любое время, нажав "Перезагрузить конфигурацию API" в настройках подписки на устройстве. - - This will unlink the device from your subscription. You can reconnect it anytime by pressing Connect. - Это устройство будет отвязано от вашей подписки. Вы можете подключить его снова в любой момент, нажав кнопку Подключиться. - Cannot unlink device during active connection @@ -2051,40 +1993,21 @@ Already installed containers were found on the server. All installed containers Telegram - - Email Support - Email - Email Email - - - support@amnezia.org - - Email Billing & Orders По вопросам оплаты - - - help@vpnpay.io - - Website Сайт - - - amnezia.org - amnezia.org - Support @@ -2129,37 +2052,37 @@ Already installed containers were found on the server. All installed containers Раздельное туннелирование приложений - + Mode Режим - + Remove Удалить - + Continue Продолжить - + Cancel Отменить - + application name название приложения - + Open executable file Открыть исполняемый файл - + Executable files (*.*) Исполняемые файлы (*.*) @@ -2269,10 +2192,6 @@ Already installed containers were found on the server. All installed containers PageSettingsBackup - - Backup - Резервное копирование - Settings restored from backup file @@ -2283,10 +2202,6 @@ Already installed containers were found on the server. All installed containers Back up your configuration Создать резервную копию конфигурации - - Configuration backup - Резервная копия конфигурации - You can save your settings to a backup file to restore them the next time you install the application. @@ -2361,14 +2276,6 @@ Already installed containers were found on the server. All installed containers Connection Соединение - - Auto connect - Автоподключение - - - Connect to VPN on app start - Подключение к VPN при запуске приложения - Use AmneziaDNS @@ -2390,28 +2297,19 @@ Already installed containers were found on the server. All installed containers Когда AmneziaDNS не используется или не установлен - + Allows you to use the VPN only for certain Apps Позволяет использовать VPN только для определенных приложений - + KillSwitch KillSwitch - - Disables your internet if your encrypted VPN connection drops out for any reason. - Отключает ваше интернет-соединение, если ваше зашифрованное VPN-соединение по какой-либо причине прерывается. - - - - Cannot change KillSwitch settings during active connection - Невозможно изменить настройки KillSwitch во время активного подключения - - - Cannot change killSwitch settings during active connection - Невозможно изменить настройки аварийного выключателя во время активного соединения + + Blocks network connections without VPN + Блокирует интернет-соединение без VPN @@ -2424,14 +2322,10 @@ Already installed containers were found on the server. All installed containers Позволяет выбирать, к каким сайтам подключаться через VPN - + App-based split tunneling Раздельное туннелирование приложений - - Allows you to use the VPN only for certain applications - Позволяет использовать VPN только для определённых приложений - PageSettingsDns @@ -2445,10 +2339,6 @@ Already installed containers were found on the server. All installed containers DNS servers DNS-серверы - - When AmneziaDNS is not used or installed - Когда AmneziaDNS не используется или не установлен - If AmneziaDNS is not used or installed @@ -2487,7 +2377,7 @@ Already installed containers were found on the server. All installed containers Settings have been reset - Настройки сброшены + Настройки были сброшены @@ -2501,11 +2391,156 @@ Already installed containers were found on the server. All installed containers - PageSettingsLogging + PageSettingsKillSwitch - Logging is enabled. Note that logs will be automatically disabled after 14 days, and all log files will be deleted. - Логирование включено. Обратите внимание, что логирование будет автоматически отключено через 14 дней, и все логи будут удалены. + + KillSwitch + KillSwitch + + + Enable to ensure network traffic goes through a secure VPN tunnel, preventing accidental exposure of your IP and DNS queries if the connection drops + Включите, чтобы весь сетевой трафик проходил только через безопасный VPN-туннель. Это предотвратит случайное раскрытие вашего IP-адреса и DNS-запросов при разрыве соединения + + + + KillSwitch settings cannot be changed during an active connection + Настройки KillSwitch нельзя изменить во время активного подключения + + + + Soft KillSwitch + Soft KillSwitch + + + + Internet access is blocked if the VPN disconnects unexpectedly + Доступ в интернет блокируется при разрыве VPN-соединения + + + + Strict KillSwitch + Strict KillSwitch + + + + Internet connection is blocked even when VPN is turned off manually or hasn't started + Доступ в интернет блокируется, даже если VPN отключен вручную или не был запущен + + + + Just a little heads-up + Небольшое предупреждение + + + + If the VPN disconnects or drops while Strict KillSwitch is enabled, internet access will be blocked. To restore access, reconnect VPN or disable/change the KillSwitch. + Если VPN отключится или соединение прервётся при включённом Strict KillSwitch, доступ в интернет будет заблокирован. Чтобы восстановить доступ, снова подключитесь к VPN или отключите (измените) режим KillSwitch. + + + + Continue + Продолжить + + + + Cancel + Отменить + + + + DNS Exceptions + Исключения для DNS + + + + DNS servers listed here will remain accessible when KillSwitch is active. + DNS-серверы из этого списка останутся доступными при активном KillSwitch. + + + + PageSettingsKillSwitchExceptions + + + DNS Exceptions + Исключения для DNS + + + + DNS servers listed here will be excluded from KillSwitch restrictions and remain accessible when KillSwitch is active. + Перечисленные DNS-серверы будут исключены из ограничений KillSwitch и останутся доступными при активном KillSwitch. + + + + Delete + Удалить + + + + Continue + Продолжить + + + + Cancel + Отменить + + + + IPv4 address + IPv4 адрес + + + + Import / Export addresses + Импорт / Экспорт адресов + + + + Import + Импорт + + + + Save address list + Сохранить список адресов + + + + Save addresses + Сохранить адреса + + + + + + Address files (*.json) + Файлы адресов (*.json) + + + + Import address list + Импорт списка адресов + + + + Replace address list + Заменить список адресов + + + + + Open address file + Открыть файл адресов + + + + Add imported addresses to existing ones + Добавить импортированные адреса к существующим + + + + PageSettingsLogging Logging @@ -2516,14 +2551,6 @@ Already installed containers were found on the server. All installed containers Enabling this function will save application's logs automatically. By default, logging functionality is disabled. Enable log saving in case of application malfunction. Включение этой функции позволяет сохранять логи на вашем устройстве. По умолчанию она отключена. Включите сохранение логов в случае сбоев в работе приложения. - - Save logs - Сохранять логи - - - Open folder with logs - Открыть папку с логами - @@ -2542,10 +2569,6 @@ Already installed containers were found on the server. All installed containers Logs file saved Файл с логами сохранен - - Save logs to file - Сохранить логи в файл - Enable logs @@ -2614,18 +2637,6 @@ Already installed containers were found on the server. All installed containers All installed containers have been added to the application Все установленные протоколы и сервисы были добавлены в приложение - - Clear Amnezia cache - Очистить кэш Amnezia - - - May be needed when changing other settings - Может понадобиться при изменении других настроек - - - Clear cached profiles? - Удалить кэш Amnezia? - No new installed containers found @@ -2717,15 +2728,16 @@ Already installed containers were found on the server. All installed containers Cannot reset API config during active connection Невозможно сбросить конфигурацию API во время активного соединения + + + Switch to the new Amnezia Premium subscription + Перейти на новый тип подписки Amnezia Premium + Remove server from application Удалить сервер из приложения - - Remove server? - Удалить сервер? - All installed AmneziaVPN services will still remain on the server. @@ -2736,29 +2748,9 @@ Already installed containers were found on the server. All installed containers Clear server from Amnezia software Очистить сервер от протоколов и сервисов Amnezia - - Clear server from Amnezia software? - Удалить все сервисы и протоколы Amnezia с сервера? - - - All containers will be deleted on the server. This means that configuration files, keys and certificates will be deleted. - На сервере будут удалены все данные, связанные с Amnezia: протоколы, сервисы, конфигурационные файлы, ключи и сертификаты. - PageSettingsServerInfo - - Subscription is valid until - Подписка заканчивается через - - - Server name - Имя сервера - - - Save - Сохранить - Protocols @@ -2786,10 +2778,6 @@ Already installed containers were found on the server. All installed containers settings настройки - - Clear %1 profile - Очистить профиль %1 - Clear %1 profile? @@ -2845,10 +2833,6 @@ Already installed containers were found on the server. All installed containers Cannot remove active container Невозможно удалить активный контейнер - - All users who you shared a connection with will no longer be able to connect to it. - Все пользователи, с которыми вы поделились VPN, больше не смогут к нему подключаться. - @@ -2887,36 +2871,32 @@ Already installed containers were found on the server. All installed containers Адреса из списка не должны открываться через VPN - + Split tunneling Раздельное туннелирование сайтов - + Mode Режим - + Remove Удалить - + Continue Продолжить - + Cancel Отменить - Website or IP - Сайт или IP - - - + Import / Export Sites Импорт/экспорт сайтов @@ -2931,50 +2911,50 @@ Already installed containers were found on the server. All installed containers Невозможно изменить настройки раздельного туннелирования во время активного соединения - + website or IP веб-сайт или IP - + Import Импорт - + Save site list Сохранить список сайтов - + Save sites Сохранить сайты - - - + + + Sites files (*.json) Файлы сайтов (*.json) - + Import a list of sites Импортировать список с сайтами - + Replace site list Заменить список с сайтами - - + + Open sites file Открыть список с сайтами - + Add imported sites to existing ones Добавить импортированные сайты к существующим @@ -3027,35 +3007,11 @@ Already installed containers were found on the server. All installed containers PageSetupWizardConfigSource - - Server connection - Подключение к серверу - - - Do not use connection code from public sources. It may have been created to intercept your data. - -It's okay as long as it's from someone you trust. - Не используйте код подключения из публичных источников. Его могли создать, чтобы перехватить ваши данные. - -Всё в порядке, если кодом поделился пользователь, которому вы доверяете. - - - Do not use connection codes from untrusted sources, as they may be created to intercept your data. - Не используйте коды подключения из ненадежных источников, так как они могут быть созданы для перехвата ваших данных. - - - What do you have? - Что у вас есть? - File with connection settings Файл с настройками подключения - - File with connection settings or backup - Файл с настройками подключения или резервной копией - Connection @@ -3074,22 +3030,22 @@ It's okay as long as it's from someone you trust. Export client logs - + Экспорт логов клиента Save - Сохранить + Сохранить Logs files (*.log) - Файлы логов (*.log) + Файлы логов (*.log) Logs file saved - Файл с логами сохранен + Файл с логами сохранен @@ -3189,49 +3145,24 @@ It's okay as long as it's from someone you trust. I have nothing У меня ничего нет - - Key as text - Ключ в виде текста - PageSetupWizardCredentials - - Server connection - Подключение к серверу - Server IP address [:port] IP-адрес[:порт] сервера - - 255.255.255.255:88 - 255.255.255.255:88 - - - Password / SSH private key - Password / SSH private key - Continue Продолжить - - All data you enter will remain strictly confidential -and will not be shared or disclosed to the Amnezia or any third parties - Все данные, которые вы вводите, останутся строго конфиденциальными и не будут переданы или раскрыты Amnezia или каким-либо третьим лицам - Enter the address in the format 255.255.255.255:88 Введите адрес в формате 255.255.255.255:88 - - Login to connect via SSH - Login to connect via SSH - Configure your server @@ -3285,10 +3216,6 @@ and will not be shared or disclosed to the Amnezia or any third parties PageSetupWizardEasy - - What is the level of internet control in your region? - Какой уровень контроля над интернетом в вашем регионе? - Choose Installation Type @@ -3309,23 +3236,11 @@ and will not be shared or disclosed to the Amnezia or any third parties Skip setup Пропустить настройку - - Set up a VPN yourself - Настроить VPN самостоятельно - - - I want to choose a VPN protocol - Выбрать VPN-протокол - Continue Продолжить - - Set up later - Настроить позднее - PageSetupWizardInstalling @@ -3427,30 +3342,6 @@ and will not be shared or disclosed to the Amnezia or any third parties PageSetupWizardStart - - Settings restored from backup file - Настройки восстановлены из резервной копии - - - Free service for creating a personal VPN on your server. - Простое и бесплатное приложение для запуска собственного VPN на своем сервере. - - - Helps you access blocked content without revealing your privacy, even to VPN providers. - Помогает получить доступ к заблокированному контенту, не раскрывая вашу конфиденциальность даже провайдерам VPN. - - - I have the data to connect - У меня есть данные для подключения - - - I have nothing - У меня ничего нет - - - https://amnezia.org/instructions/0_starter-guide - https://amnezia.org/ru/starter-guide - Let's get started @@ -3492,10 +3383,6 @@ and will not be shared or disclosed to the Amnezia or any third parties New connection Новое соединение - - Do not use connection code from public sources. It could be created to intercept your data. - Не используйте код подключения из публичных источников. Его могли создать, чтобы перехватить ваши данные. - Collapse content @@ -3534,33 +3421,17 @@ and will not be shared or disclosed to the Amnezia or any third parties WireGuard native format Оригинальный формат WireGuard - - VPN Access - VPN-Доступ - Connection Соединение - - VPN access without the ability to manage the server - Доступ к VPN без возможности управления сервером - - - Access to server management. The user with whom you share full access to the connection will be able to add and remove your protocols and services to the server, as well as change settings. - Доступ к управлению сервером. Пользователь, с которым вы делитесь полным доступом к соединению, сможет добавлять и удалять ваши протоколы и службы на сервере, а также изменять настройки. - Server Сервер - - Accessing - Доступ - Config revoked @@ -3687,10 +3558,6 @@ and will not be shared or disclosed to the Amnezia or any third parties Allowed IPs: %1 Разрешенные подсети: %1 - - Creation date: - Дата создания: - Rename @@ -3731,10 +3598,6 @@ and will not be shared or disclosed to the Amnezia or any third parties Cancel Отменить - - Full access - Полный доступ - Share VPN access without the ability to manage the server @@ -4083,10 +3946,6 @@ and will not be shared or disclosed to the Amnezia or any third parties No error Нет ошибки - - Unknown Error - Неизвестная ошибка - Function not implemented @@ -4158,251 +4017,210 @@ and will not be shared or disclosed to the Amnezia or any third parties Требуется пароль пользователя - + + Docker error: runc doesn't work on cgroups v2 + Docker error: runc не работает на cgroups v2 + + + + Server error: cgroup mountpoint does not exist + Server error: cgroup mountpoint не существует + + + SSH request was denied SSH-запрос был отклонён - + SSH request was interrupted SSH-запрос был прерван - + SSH internal error Внутренняя ошибка SSH - + Invalid private key or invalid passphrase entered Введен неверный закрытый ключ или неверная парольная фраза - + The selected private key format is not supported, use openssh ED25519 key types or PEM key types Выбранный формат закрытого ключа не поддерживается, используйте типы ключей openssh ED25519 или PEM - + Timeout connecting to server Тайм-аут подключения к серверу - + SCP error: Generic failure Ошибка SCP: общий сбой - Sftp error: End-of-file encountered - Sftp error: End-of-file encountered - - - Sftp error: File does not exist - Sftp error: File does not exist - - - Sftp error: Permission denied - Sftp error: Permission denied - - - Sftp error: Generic failure - Sftp error: Generic failure - - - Sftp error: Garbage received from server - Sftp error: Garbage received from server - - - Sftp error: No connection has been set up - Sftp error: No connection has been set up - - - Sftp error: There was a connection, but we lost it - Sftp error: There was a connection, but we lost it - - - Sftp error: Operation not supported by libssh yet - Sftp error: Operation not supported by libssh yet - - - Sftp error: Invalid file handle - Sftp error: Invalid file handle - - - Sftp error: No such file or directory path exists - Sftp error: No such file or directory path exists - - - Sftp error: An attempt to create an already existing file or directory has been made - Sftp error: An attempt to create an already existing file or directory has been made - - - Sftp error: Write-protected filesystem - Sftp error: Write-protected filesystem - - - Sftp error: No media was in remote drive - Sftp error: No media was in remote drive - - - + The config does not contain any containers and credentials for connecting to the server Конфигурация не содержит каких-либо контейнеров и учетных данных для подключения к серверу - - + + Error when retrieving configuration from API Ошибка при получении конфигурации из API - + This config has already been added to the application Данная конфигурация уже была добавлена в приложение - + ErrorCode: %1. Код ошибки: %1. - Failed to save config to disk - Failed to save config to disk - - - + OpenVPN config missing Отсутствует конфигурация OpenVPN - + OpenVPN management server error Серверная ошибка управлением OpenVPN - + OpenVPN executable missing Отсутствует исполняемый файл OpenVPN - + Shadowsocks (ss-local) executable missing Отсутствует исполняемый файл Shadowsocks (ss-local) - + Cloak (ck-client) executable missing Отсутствует исполняемый файл Cloak (ck-client) - + Amnezia helper service error Ошибка вспомогательной службы Amnezia - + OpenSSL failed Ошибка OpenSSL - + Can't connect: another VPN connection is active Невозможно подключиться: активно другое VPN-соединение - + Can't setup OpenVPN TAP network adapter Невозможно настроить сетевой адаптер OpenVPN TAP - + VPN pool error: no available addresses Ошибка пула VPN: нет доступных адресов - + Unable to open config file Не удалось открыть файл конфигурации - + VPN Protocols is not installed. Please install VPN container at first VPN-протоколы не установлены. Пожалуйста, установите протокол - + VPN connection error Ошибка VPN-соединения - + In the response from the server, an empty config was received В ответе от сервера была получена пустая конфигурация - + SSL error occurred Произошла ошибка SSL - + Server response timeout on api request Тайм-аут ответа сервера на запрос API - + Missing AGW public key Отсутствует публичный ключ AGW - + Failed to decrypt response payload - + Missing list of available services Отсутствует список доступных сервисов - + The limit of allowed configurations per subscription has been exceeded Превышен лимит разрешенных конфигураций для одной подписки + A migration has error occurred. Please contact our technical support + Произошла ошибка миграции. Обратитесь в нашу техническую поддержку + + + QFile error: The file could not be opened Ошибка QFile: не удалось открыть файл - + QFile error: An error occurred when reading from the file Ошибка QFile: произошла ошибка при чтении из файла - + QFile error: The file could not be accessed Ошибка QFile: не удалось получить доступ к файлу - + QFile error: An unspecified error occurred Ошибка QFile: произошла неизвестная ошибка - + QFile error: A fatal error occurred Ошибка QFile: произошла фатальная ошибка - + QFile error: The operation was aborted Ошибка QFile: операция была прервана - + Internal error Внутренняя ошибка @@ -4411,14 +4229,6 @@ and will not be shared or disclosed to the Amnezia or any third parties IPsec IPsec - - Shadowsocks - masks VPN traffic, making it similar to normal web traffic, but it may be recognized by analysis systems in some highly censored regions. - Shadowsocks маскирует VPN-трафик под обычный веб-трафик, но распознается системами анализа в некоторых регионах с высоким уровнем цензуры. - - - OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against active-probing detection. Ideal for bypassing blocking in regions with the highest levels of censorship. - OpenVPN over Cloak — это OpenVPN с маскировкой VPN-трафика под обычный веб-трафик и защитой от обнаружения активным зондированием. Подходит для регионов с самым высоким уровнем цензуры. - IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss. It has native support on the latest versions of Android and iOS. @@ -4665,22 +4475,6 @@ Features: * Незаметен для систем анализа трафика (DPI) * Работает по протоколу UDP - - WireGuard - New popular VPN protocol with high performance, high speed and low power consumption. Recommended for regions with low levels of censorship. - WireGuard — новый популярный VPN-протокол с высокой производительностью, высокой скоростью и низким энергопотреблением. Рекомендуется для регионов с низким уровнем цензуры. - - - AmneziaWG - Special protocol from Amnezia, based on WireGuard. It's fast like WireGuard, but very resistant to blockages. Recommended for regions with high levels of censorship. - AmneziaWG — специальный протокол от Amnezia, основанный на протоколе WireGuard. Он такой же быстрый, как WireGuard, но очень устойчив к блокировкам. Рекомендуется для регионов с высоким уровнем цензуры. - - - XRay with REALITY - Suitable for countries with the highest level of internet censorship. Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods. - XRay with REALITY подойдет для стран с самым высоким уровнем цензуры. Маскировка трафика под веб-трафик на уровне TLS и защита от обнаружения методами активного зондирования. - - - IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after signal loss. - IKEv2/IPsec — современный стабильный протокол, немного быстрее других, восстанавливает соединение после потери сигнала. - Deploy a WordPress site on the Tor network in two clicks. @@ -4710,18 +4504,6 @@ For more detailed information, you can Более подробную информацию вы можете найти в разделе поддержки "Создание файлового хранилища SFTP." - - AmneziaWG container - AmneziaWG протокол - - - Sftp file sharing service - is secure FTP service - Файловое хранилище для безопасного хранения данных - - - Sftp service - SFTP-сервис - Entry not found @@ -4977,10 +4759,6 @@ For more detailed information, you can All settings have been reset to default values Все настройки сброшены до значений по умолчанию - - Cached profiles cleared - Закэшированные профили очищены - Backup file is corrupted @@ -5114,7 +4892,7 @@ For more detailed information, you can VpnConnection - + Mbps Мбит/с @@ -5164,46 +4942,6 @@ For more detailed information, you can amnezia::ContainerProps - - Low - Низкий - - - High - Высокий - - - Extreme - Экстремальный - - - I just want to increase the level of my privacy. - Я просто хочу повысить уровень своей приватности. - - - I want to bypass censorship. This option recommended in most cases. - Я хочу обойти блокировки. Этот вариант рекомендуется в большинстве случаев. - - - Most VPN protocols are blocked. Recommended if other options are not working. - Большинство VPN-протоколов заблокированы. Рекомендуется, если другие варианты не работают. - - - Medium - Средний - - - Many foreign websites and VPN providers are blocked - Многие иностранные сайты и VPN-провайдеры заблокированы - - - Some foreign sites are blocked, but VPN providers are not blocked - Некоторые иностранные сайты заблокированы, но VPN-провайдеры не блокируются - - - I just want to increase the level of privacy - Хочу просто повысить уровень приватности - Automatic diff --git a/client/ui/qml/Components/ApiPremV1MigrationDrawer.qml b/client/ui/qml/Components/ApiPremV1MigrationDrawer.qml index 113ec1f6..21aa78a0 100644 --- a/client/ui/qml/Components/ApiPremV1MigrationDrawer.qml +++ b/client/ui/qml/Components/ApiPremV1MigrationDrawer.qml @@ -73,7 +73,7 @@ DrawerType2 { var str = qsTr("We'll preserve all remaining days of your current subscription and give you an extra month as a thank you. ") str += qsTr("This new subscription type will be actively developed with more locations and features added regularly. Currently available:") str += "
    " - str += qsTr("
  • 9 locations (with more coming soon)
  • ") + str += qsTr("
  • 13 locations (with more coming soon)
  • ") str += qsTr("
  • Easier switching between countries in the app
  • ") str += qsTr("
  • Personal dashboard to manage your subscription
  • ") str += "
" diff --git a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml index f6e0f5d7..444eb415 100644 --- a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml +++ b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml @@ -49,7 +49,7 @@ PageType { if (!ConnectionController.isConnected) { SettingsController.isKillSwitchEnabled = checked } else { - PageController.showNotificationMessage(qsTr("Cannot change killSwitch settings during active connection")) + PageController.showNotificationMessage(qsTr("KillSwitch settings cannot be changed during an active connection")) switcher.checked = SettingsController.isKillSwitchEnabled } } @@ -66,7 +66,7 @@ PageType { checked: !SettingsController.strictKillSwitchEnabled text: qsTr("Soft KillSwitch") - descriptionText: qsTr("Internet connection is blocked if VPN connection drops accidentally") + descriptionText: qsTr("Internet access is blocked if the VPN disconnects unexpectedly") onClicked: function() { SettingsController.strictKillSwitchEnabled = false @@ -85,11 +85,11 @@ PageType { checked: SettingsController.strictKillSwitchEnabled text: qsTr("Strict KillSwitch") - descriptionText: qsTr("Internet connection is blocked even if VPN was turned off manually or not started") + descriptionText: qsTr("Internet connection is blocked even when VPN is turned off manually or hasn't started") onClicked: function() { var headerText = qsTr("Just a little heads-up") - var descriptionText = qsTr("If you disconnect from VPN or the VPN connection drops while the Strict Kill Switch is turned on, your internet access will be disabled. To restore it, connect to VPN, change the Kill Switch mode or turn the Kill Switch off.") + var descriptionText = qsTr("If the VPN disconnects or drops while Strict KillSwitch is enabled, internet access will be blocked. To restore access, reconnect VPN or disable/change the KillSwitch.") var yesButtonText = qsTr("Continue") var noButtonText = qsTr("Cancel") @@ -111,7 +111,7 @@ PageType { enabled: true text: qsTr("DNS Exceptions") - descriptionText: qsTr("DNS servers from the list will remain accessible when Kill Switch is triggered") + descriptionText: qsTr("DNS servers listed here will remain accessible when KillSwitch is active.") rightImageSource: "qrc:/images/controls/chevron-right.svg" clickedFunction: function() { From df7bf204eac5576e03074b0f45dbfc9cb2c26534 Mon Sep 17 00:00:00 2001 From: Nethius Date: Tue, 20 May 2025 12:58:57 +0800 Subject: [PATCH 37/65] chore: minor ui changes (#1597) --- .gitignore | 6 ++- client/translations/amneziavpn_ru_RU.ts | 40 +++++++++++-------- .../ui/qml/Pages2/PageSettingsApiSupport.qml | 1 + .../PageSettingsKillSwitchExceptions.qml | 2 +- 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index 5b90fd55..503adc2d 100644 --- a/.gitignore +++ b/.gitignore @@ -133,4 +133,8 @@ client/3rd/ShadowSocks/ss_ios.xcconfig out/ # CMake files -CMakeFiles/ \ No newline at end of file +CMakeFiles/ + +ios-ne-build.sh +macos-ne-build.sh +macos-signed-build.sh diff --git a/client/translations/amneziavpn_ru_RU.ts b/client/translations/amneziavpn_ru_RU.ts index af47ffb3..833db3d9 100644 --- a/client/translations/amneziavpn_ru_RU.ts +++ b/client/translations/amneziavpn_ru_RU.ts @@ -89,17 +89,17 @@ ApiConfigsController - + %1 installed successfully. %1 успешно установлен. - + API config reloaded Конфигурация API перезагружена - + Successfully changed the country of connection to %1 Страна подключения изменена на %1 @@ -149,7 +149,7 @@ mail@example.com - + mail@example.com @@ -242,7 +242,7 @@ - + @@ -378,9 +378,6 @@ Выбрать всё - - ExportController - HomeContainersListView @@ -1763,9 +1760,6 @@ Thank you for staying with us! Инструкции по настройке - - PageSettingsApiLanguageList - PageSettingsApiNativeConfigs @@ -2424,7 +2418,7 @@ Thank you for staying with us! - Internet connection is blocked even when VPN is turned off manually or hasn't started + Internet connection is blocked even when VPN is turned off manually or hasn't started Доступ в интернет блокируется, даже если VPN отключен вручную или не был запущен @@ -2466,9 +2460,17 @@ Thank you for staying with us! Исключения для DNS - DNS servers listed here will be excluded from KillSwitch restrictions and remain accessible when KillSwitch is active. - Перечисленные DNS-серверы будут исключены из ограничений KillSwitch и останутся доступными при активном KillSwitch. + Перечисленные DNS-серверы будут исключены из ограничений KillSwitch и останутся доступными при активном KillSwitch. + + + DNS servers from the list will remain accessible when KillSwitch is triggered + DNS-серверы из этого списка будут исключены из ограничений KillSwitch и останутся доступными при активном KillSwitch. + + + + DNS servers listed here will remain accessible when KillSwitch is active + DNS-серверы из этого списка останутся доступными при активном KillSwitch @@ -4077,6 +4079,11 @@ Thank you for staying with us! This config has already been added to the application Данная конфигурация уже была добавлена в приложение + + + A migration error has occurred. Please contact our technical support + Произошла ошибка миграции. Обратитесь в нашу техническую поддержку + ErrorCode: %1. @@ -4185,9 +4192,8 @@ Thank you for staying with us! Превышен лимит разрешенных конфигураций для одной подписки - A migration has error occurred. Please contact our technical support - Произошла ошибка миграции. Обратитесь в нашу техническую поддержку + Произошла ошибка миграции. Обратитесь в нашу техническую поддержку @@ -4342,7 +4348,7 @@ REALITY распознаёт системы блокировки во время - + diff --git a/client/ui/qml/Pages2/PageSettingsApiSupport.qml b/client/ui/qml/Pages2/PageSettingsApiSupport.qml index af629ebe..e10eb325 100644 --- a/client/ui/qml/Pages2/PageSettingsApiSupport.qml +++ b/client/ui/qml/Pages2/PageSettingsApiSupport.qml @@ -88,6 +88,7 @@ PageType { LabelWithButtonType { Layout.fillWidth: true + visible: link !== "" text: title descriptionText: description rightImageSource: "qrc:/images/controls/external-link.svg" diff --git a/client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml b/client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml index d442b60c..31d1721b 100644 --- a/client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml +++ b/client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml @@ -43,7 +43,7 @@ PageType { Layout.leftMargin: 16 headerText: qsTr("DNS Exceptions") - descriptionText: qsTr("DNS servers from the list will remain accessible when Kill Switch is triggered") + descriptionText: qsTr("DNS servers listed here will remain accessible when KillSwitch is active") } } From a3e73797c29d0901f387fe930bc4e1f96dc11ced Mon Sep 17 00:00:00 2001 From: Nethius Date: Tue, 20 May 2025 13:02:37 +0800 Subject: [PATCH 38/65] chore: bump version (#1598) --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0896973e..f0e1ca99 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.6.0 +project(${PROJECT} VERSION 4.8.7.0 DESCRIPTION "AmneziaVPN" HOMEPAGE_URL "https://amnezia.org/" ) @@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d") set(RELEASE_DATE "${CURRENT_DATE}") set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}) -set(APP_ANDROID_VERSION_CODE 2083) +set(APP_ANDROID_VERSION_CODE 2084) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(MZ_PLATFORM_NAME "linux") From a6508e642a3cc222589ec63688b936f76e4c447f Mon Sep 17 00:00:00 2001 From: Nethius Date: Tue, 20 May 2025 13:08:05 +0800 Subject: [PATCH 39/65] bugfix: fixed sending requests if there are no premium v1 keys in the application (#1599) --- .../api/apiPremV1MigrationController.cpp | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/client/ui/controllers/api/apiPremV1MigrationController.cpp b/client/ui/controllers/api/apiPremV1MigrationController.cpp index 7b0ff100..77352003 100644 --- a/client/ui/controllers/api/apiPremV1MigrationController.cpp +++ b/client/ui/controllers/api/apiPremV1MigrationController.cpp @@ -29,18 +29,20 @@ bool ApiPremV1MigrationController::hasConfigsToMigration() vpnKeys.append(vpnKey); } - GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, - m_settings->isStrictKillSwitchEnabled()); - QJsonObject apiPayload; + if (!vpnKeys.isEmpty()) { + GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, + m_settings->isStrictKillSwitchEnabled()); + QJsonObject apiPayload; - apiPayload["configs"] = vpnKeys; - QByteArray responseBody; - ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/is-active-subscription"), apiPayload, responseBody); + apiPayload["configs"] = vpnKeys; + QByteArray responseBody; + ErrorCode errorCode = gatewayController.post(QString("%1v1/prem-v1/is-active-subscription"), apiPayload, responseBody); - auto migrationsStatus = QJsonDocument::fromJson(responseBody).object(); - for (const auto &migrationStatus : migrationsStatus) { - if (migrationStatus == "not_found") { - return true; + auto migrationsStatus = QJsonDocument::fromJson(responseBody).object(); + for (const auto &migrationStatus : migrationsStatus) { + if (migrationStatus == "not_found") { + return true; + } } } From c2f9340db6623543cac2f567ea620ef9f303c18c Mon Sep 17 00:00:00 2001 From: Nethius Date: Wed, 21 May 2025 20:05:08 +0800 Subject: [PATCH 40/65] chore/ru translation (#1606) * chore: fix ru translation * chore: bump version --- CMakeLists.txt | 4 ++-- client/translations/amneziavpn_ru_RU.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f0e1ca99..dc76a6c3 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.0 +project(${PROJECT} VERSION 4.8.7.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 2084) +set(APP_ANDROID_VERSION_CODE 2085) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(MZ_PLATFORM_NAME "linux") diff --git a/client/translations/amneziavpn_ru_RU.ts b/client/translations/amneziavpn_ru_RU.ts index 833db3d9..14de59d1 100644 --- a/client/translations/amneziavpn_ru_RU.ts +++ b/client/translations/amneziavpn_ru_RU.ts @@ -134,7 +134,7 @@ <li>Personal dashboard to manage your subscription</li> - Личный кабинет для управления подпиской + <li>Личный кабинет для управления подпиской</li> @@ -2013,12 +2013,12 @@ Thank you for staying with us! Наши специалисты технической поддержки всегда готовы помочь вам. - + Support tag - + Copied Скопировано From 48a5452a6567a5cbd3bf7116c2cb17dd494c1e3e Mon Sep 17 00:00:00 2001 From: Nethius Date: Fri, 23 May 2025 14:53:55 +0800 Subject: [PATCH 41/65] chore/minor fixes (#1610) * bugfix: fixed the migration form appearing on app start * feature: added app version to api requests payload * chore: remove unused file * feature: extended logging in service part * chore: bump version * chore: update ru translation file --- CMakeLists.txt | 4 +- client/core/api/apiDefs.h | 1 + client/core/api/apiUtils.cpp | 23 +++++---- client/core/controllers/gatewayController.cpp | 9 ++++ client/core/defs.h | 1 + client/core/errorstrings.cpp | 1 + client/daemon/daemon.cpp | 3 +- .../platforms/linux/daemon/iputilslinux.cpp | 4 +- .../platforms/macos/daemon/iputilsmacos.cpp | 4 +- .../macos/daemon/macosroutemonitor.cpp | 10 ++-- .../windows/daemon/windowsroutemonitor.cpp | 15 +++--- client/translations/amneziavpn_ru_RU.ts | 49 ++++++++++--------- client/ui/Controls2 | 34 ------------- .../controllers/api/apiConfigsController.cpp | 6 +++ .../controllers/api/apiSettingsController.cpp | 2 + 15 files changed, 78 insertions(+), 88 deletions(-) delete mode 100644 client/ui/Controls2 diff --git a/CMakeLists.txt b/CMakeLists.txt index dc76a6c3..424dcf3a 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.1 +project(${PROJECT} VERSION 4.8.7.2 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 2085) +set(APP_ANDROID_VERSION_CODE 2086) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(MZ_PLATFORM_NAME "linux") diff --git a/client/core/api/apiDefs.h b/client/core/api/apiDefs.h index 6d1a27fa..4588ef04 100644 --- a/client/core/api/apiDefs.h +++ b/client/core/api/apiDefs.h @@ -31,6 +31,7 @@ namespace apiDefs constexpr QLatin1String apiConfig("api_config"); constexpr QLatin1String stackType("stack_type"); constexpr QLatin1String serviceType("service_type"); + constexpr QLatin1String cliVersion("cli_version"); constexpr QLatin1String vpnKey("vpn_key"); constexpr QLatin1String config("config"); diff --git a/client/core/api/apiUtils.cpp b/client/core/api/apiUtils.cpp index f85d2207..7f3e6db3 100644 --- a/client/core/api/apiUtils.cpp +++ b/client/core/api/apiUtils.cpp @@ -41,32 +41,34 @@ bool apiUtils::isServerFromApi(const QJsonObject &serverConfigObject) apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObject) { auto configVersion = serverConfigObject.value(apiDefs::key::configVersion).toInt(); + switch (configVersion) { case apiDefs::ConfigSource::Telegram: { + constexpr QLatin1String freeV2Endpoint(FREE_V2_ENDPOINT); + constexpr QLatin1String premiumV1Endpoint(PREM_V1_ENDPOINT); + + auto apiEndpoint = serverConfigObject.value(apiDefs::key::apiEndpoint).toString(); + + if (apiEndpoint.contains(premiumV1Endpoint)) { + return apiDefs::ConfigType::AmneziaPremiumV1; + } else if (apiEndpoint.contains(freeV2Endpoint)) { + return apiDefs::ConfigType::AmneziaFreeV2; + } }; case apiDefs::ConfigSource::AmneziaGateway: { constexpr QLatin1String servicePremium("amnezia-premium"); constexpr QLatin1String serviceFree("amnezia-free"); constexpr QLatin1String serviceExternalPremium("external-premium"); - constexpr QLatin1String freeV2Endpoint(FREE_V2_ENDPOINT); - constexpr QLatin1String premiumV1Endpoint(PREM_V1_ENDPOINT); - auto apiConfigObject = serverConfigObject.value(apiDefs::key::apiConfig).toObject(); auto serviceType = apiConfigObject.value(apiDefs::key::serviceType).toString(); - auto apiEndpoint = serverConfigObject.value(apiDefs::key::apiEndpoint).toString(); - if (serviceType == servicePremium) { return apiDefs::ConfigType::AmneziaPremiumV2; } else if (serviceType == serviceFree) { return apiDefs::ConfigType::AmneziaFreeV3; } else if (serviceType == serviceExternalPremium) { return apiDefs::ConfigType::ExternalPremium; - } else if (apiEndpoint.contains(premiumV1Endpoint)) { - return apiDefs::ConfigType::AmneziaPremiumV1; - } else if (apiEndpoint.contains(freeV2Endpoint)) { - return apiDefs::ConfigType::AmneziaFreeV2; } } default: { @@ -94,6 +96,9 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList &ssl || reply->error() == QNetworkReply::NetworkError::TimeoutError) { qDebug() << reply->error(); return amnezia::ErrorCode::ApiConfigTimeoutError; + } else if (reply->error() == QNetworkReply::NetworkError::OperationNotImplementedError) { + qDebug() << reply->error(); + return amnezia::ErrorCode::ApiUpdateRequestError; } else { QString err = reply->errorString(); int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); diff --git a/client/core/controllers/gatewayController.cpp b/client/core/controllers/gatewayController.cpp index 9a7ee6e5..26855ae6 100644 --- a/client/core/controllers/gatewayController.cpp +++ b/client/core/controllers/gatewayController.cpp @@ -36,6 +36,8 @@ namespace constexpr QLatin1String errorResponsePattern1("No active configuration found for"); constexpr QLatin1String errorResponsePattern2("No non-revoked public key found for"); constexpr QLatin1String errorResponsePattern3("Account not found."); + + constexpr QLatin1String updateRequestResponsePattern("client version update is required"); } GatewayController::GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs, @@ -311,6 +313,13 @@ bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray qDebug() << reply->error(); return true; } + } else if (reply->error() == QNetworkReply::NetworkError::OperationNotImplementedError) { + if (responseBody.contains(updateRequestResponsePattern)) { + return false; + } else { + qDebug() << reply->error(); + return true; + } } else if (reply->error() != QNetworkReply::NetworkError::NoError) { qDebug() << reply->error(); return true; diff --git a/client/core/defs.h b/client/core/defs.h index 674d1add..df6a1342 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -118,6 +118,7 @@ namespace amnezia ApiConfigLimitError = 1108, ApiNotFoundError = 1109, ApiMigrationError = 1110, + ApiUpdateRequestError = 1111, // QFile errors OpenError = 1200, diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp index e141b3c7..7cc46220 100644 --- a/client/core/errorstrings.cpp +++ b/client/core/errorstrings.cpp @@ -75,6 +75,7 @@ QString errorString(ErrorCode code) { case (ErrorCode::ApiConfigLimitError): errorMessage = QObject::tr("The limit of allowed configurations per subscription has been exceeded"); break; case (ErrorCode::ApiNotFoundError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break; case (ErrorCode::ApiMigrationError): errorMessage = QObject::tr("A migration error has occurred. Please contact our technical support"); break; + case (ErrorCode::ApiUpdateRequestError): errorMessage = QObject::tr("Please update the application to use this feature"); break; // QFile errors case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break; diff --git a/client/daemon/daemon.cpp b/client/daemon/daemon.cpp index e4b0ab3d..24e72629 100644 --- a/client/daemon/daemon.cpp +++ b/client/daemon/daemon.cpp @@ -149,8 +149,7 @@ bool Daemon::activate(const InterfaceConfig& config) { // set routing for (const IPAddress& ip : config.m_allowedIPAddressRanges) { if (!wgutils()->updateRoutePrefix(ip)) { - logger.debug() << "Routing configuration failed for" - << logger.sensitive(ip.toString()); + logger.debug() << "Routing configuration failed for" << ip.toString(); return false; } } diff --git a/client/platforms/linux/daemon/iputilslinux.cpp b/client/platforms/linux/daemon/iputilslinux.cpp index 63bd92f9..25d4f631 100644 --- a/client/platforms/linux/daemon/iputilslinux.cpp +++ b/client/platforms/linux/daemon/iputilslinux.cpp @@ -97,7 +97,7 @@ bool IPUtilsLinux::addIP4AddressToDevice(const InterfaceConfig& config) { // Set ifr to interface int ret = ioctl(sockfd, SIOCSIFADDR, &ifr); if (ret) { - logger.error() << "Failed to set IPv4: " << logger.sensitive(deviceAddr) + logger.error() << "Failed to set IPv4: " << deviceAddr << "error:" << strerror(errno); return false; } @@ -138,7 +138,7 @@ bool IPUtilsLinux::addIP6AddressToDevice(const InterfaceConfig& config) { // Set ifr6 to the interface ret = ioctl(sockfd, SIOCSIFADDR, &ifr6); if (ret && (errno != EEXIST)) { - logger.error() << "Failed to set IPv6: " << logger.sensitive(deviceAddr) + logger.error() << "Failed to set IPv6: " << deviceAddr << "error:" << strerror(errno); return false; } diff --git a/client/platforms/macos/daemon/iputilsmacos.cpp b/client/platforms/macos/daemon/iputilsmacos.cpp index 0599e2eb..901436ae 100644 --- a/client/platforms/macos/daemon/iputilsmacos.cpp +++ b/client/platforms/macos/daemon/iputilsmacos.cpp @@ -122,7 +122,7 @@ bool IPUtilsMacos::addIP4AddressToDevice(const InterfaceConfig& config) { // Set ifr to interface int ret = ioctl(sockfd, SIOCAIFADDR, &ifr); if (ret) { - logger.error() << "Failed to set IPv4: " << logger.sensitive(deviceAddr) + logger.error() << "Failed to set IPv4: " << deviceAddr << "error:" << strerror(errno); return false; } @@ -162,7 +162,7 @@ bool IPUtilsMacos::addIP6AddressToDevice(const InterfaceConfig& config) { // Set ifr to interface int ret = ioctl(sockfd, SIOCAIFADDR_IN6, &ifr6); if (ret) { - logger.error() << "Failed to set IPv6: " << logger.sensitive(deviceAddr) + logger.error() << "Failed to set IPv6: " << deviceAddr << "error:" << strerror(errno); return false; } diff --git a/client/platforms/macos/daemon/macosroutemonitor.cpp b/client/platforms/macos/daemon/macosroutemonitor.cpp index bd991c01..062f97f3 100644 --- a/client/platforms/macos/daemon/macosroutemonitor.cpp +++ b/client/platforms/macos/daemon/macosroutemonitor.cpp @@ -144,7 +144,7 @@ void MacosRouteMonitor::handleRtmDelete(const struct rt_msghdr* rtm, for (const IPAddress& prefix : m_exclusionRoutes) { if (prefix.address().protocol() == protocol) { logger.debug() << "Removing exclusion route to" - << logger.sensitive(prefix.toString()); + << prefix.toString(); rtmSendRoute(RTM_DELETE, prefix, rtm->rtm_index, nullptr); } } @@ -259,7 +259,7 @@ void MacosRouteMonitor::handleRtmUpdate(const struct rt_msghdr* rtm, for (const IPAddress& prefix : m_exclusionRoutes) { if (prefix.address().protocol() == protocol) { logger.debug() << "Updating exclusion route to" - << logger.sensitive(prefix.toString()); + << prefix.toString(); rtmSendRoute(rtm_type, prefix, ifindex, addrlist[1].constData()); } } @@ -510,8 +510,7 @@ bool MacosRouteMonitor::deleteRoute(const IPAddress& prefix, int flags) { } bool MacosRouteMonitor::addExclusionRoute(const IPAddress& prefix) { - logger.debug() << "Adding exclusion route for" - << logger.sensitive(prefix.toString()); + logger.debug() << "Adding exclusion route for" << prefix.toString(); if (m_exclusionRoutes.contains(prefix)) { logger.warning() << "Exclusion route already exists"; @@ -536,8 +535,7 @@ bool MacosRouteMonitor::addExclusionRoute(const IPAddress& prefix) { } bool MacosRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) { - logger.debug() << "Deleting exclusion route for" - << logger.sensitive(prefix.toString()); + logger.debug() << "Deleting exclusion route for" << prefix.toString(); m_exclusionRoutes.removeAll(prefix); if (prefix.address().protocol() == QAbstractSocket::IPv4Protocol) { diff --git a/client/platforms/windows/daemon/windowsroutemonitor.cpp b/client/platforms/windows/daemon/windowsroutemonitor.cpp index fb0fbf7e..1d0ce4c2 100644 --- a/client/platforms/windows/daemon/windowsroutemonitor.cpp +++ b/client/platforms/windows/daemon/windowsroutemonitor.cpp @@ -303,8 +303,7 @@ void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) { data->Age++; continue; } - logger.debug() << "Capturing route to" - << logger.sensitive(prefix.toString()); + logger.debug() << "Capturing route to" << prefix.toString(); // Clone the route and direct it into the VPN tunnel. data = new MIB_IPFORWARD_ROW2; @@ -354,8 +353,7 @@ void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) { continue; } - logger.debug() << "Removing route capture for" - << logger.sensitive(i.key().toString()); + logger.debug() << "Removing route capture for" << i.key().toString(); // Otherwise, this route is no longer in use. DWORD result = DeleteIpForwardEntry2(data); @@ -368,8 +366,7 @@ void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) { } bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) { - logger.debug() << "Adding exclusion route for" - << logger.sensitive(prefix.toString()); + logger.debug() << "Adding exclusion route for" << prefix.toString(); // Silently ignore non-routeable addresses. QHostAddress addr = prefix.address(); @@ -437,7 +434,7 @@ bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) { bool WindowsRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) { logger.debug() << "Deleting exclusion route for" - << logger.sensitive(prefix.address().toString()); + << prefix.address().toString(); MIB_IPFORWARD_ROW2* data = m_exclusionRoutes.take(prefix); if (data == nullptr) { @@ -447,7 +444,7 @@ bool WindowsRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) { DWORD result = DeleteIpForwardEntry2(data); if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) { logger.error() << "Failed to delete route to" - << logger.sensitive(prefix.toString()) + << prefix.toString() << "result:" << result; } @@ -465,7 +462,7 @@ void WindowsRouteMonitor::flushRouteTable( DWORD result = DeleteIpForwardEntry2(data); if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) { logger.error() << "Failed to delete route to" - << logger.sensitive(i.key().toString()) + << i.key().toString() << "result:" << result; } delete data; diff --git a/client/translations/amneziavpn_ru_RU.ts b/client/translations/amneziavpn_ru_RU.ts index 14de59d1..1d8766ac 100644 --- a/client/translations/amneziavpn_ru_RU.ts +++ b/client/translations/amneziavpn_ru_RU.ts @@ -89,17 +89,17 @@ ApiConfigsController - + %1 installed successfully. %1 успешно установлен. - + API config reloaded Конфигурация API перезагружена - + Successfully changed the country of connection to %1 Страна подключения изменена на %1 @@ -358,7 +358,7 @@ ContextMenuType - + C&ut Вырезать @@ -368,12 +368,12 @@ Копировать - + &Paste Вставить - + &SelectAll Выбрать всё @@ -2412,42 +2412,42 @@ Thank you for staying with us! Доступ в интернет блокируется при разрыве VPN-соединения - + Strict KillSwitch Strict KillSwitch - + Internet connection is blocked even when VPN is turned off manually or hasn't started Доступ в интернет блокируется, даже если VPN отключен вручную или не был запущен - + Just a little heads-up Небольшое предупреждение - + If the VPN disconnects or drops while Strict KillSwitch is enabled, internet access will be blocked. To restore access, reconnect VPN or disable/change the KillSwitch. Если VPN отключится или соединение прервётся при включённом Strict KillSwitch, доступ в интернет будет заблокирован. Чтобы восстановить доступ, снова подключитесь к VPN или отключите (измените) режим KillSwitch. - + Continue Продолжить - + Cancel Отменить - + DNS Exceptions Исключения для DNS - + DNS servers listed here will remain accessible when KillSwitch is active. DNS-серверы из этого списка останутся доступными при активном KillSwitch. @@ -4085,7 +4085,12 @@ Thank you for staying with us! Произошла ошибка миграции. Обратитесь в нашу техническую поддержку
- + + Please update the application to use this feature + Пожалуйста, обновите приложение, чтобы использовать эту функцию + + + ErrorCode: %1. Код ошибки: %1. @@ -4196,37 +4201,37 @@ Thank you for staying with us! Произошла ошибка миграции. Обратитесь в нашу техническую поддержку
- + QFile error: The file could not be opened Ошибка QFile: не удалось открыть файл - + QFile error: An error occurred when reading from the file Ошибка QFile: произошла ошибка при чтении из файла - + QFile error: The file could not be accessed Ошибка QFile: не удалось получить доступ к файлу - + QFile error: An unspecified error occurred Ошибка QFile: произошла неизвестная ошибка - + QFile error: A fatal error occurred Ошибка QFile: произошла фатальная ошибка - + QFile error: The operation was aborted Ошибка QFile: операция была прервана - + Internal error Внутренняя ошибка diff --git a/client/ui/Controls2 b/client/ui/Controls2 deleted file mode 100644 index 13f01bb7..00000000 --- a/client/ui/Controls2 +++ /dev/null @@ -1,34 +0,0 @@ -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts - -TextArea { - id: root - - width: parent.width - - topPadding: 16 - leftPadding: 16 - - color: "#D7D8DB" - selectionColor: "#412102" - selectedTextColor: "#D7D8DB" - placeholderTextColor: "#878B91" - - font.pixelSize: 16 - font.weight: Font.Medium - font.family: "PT Root UI VF" - - wrapMode: Text.Wrap - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.RightButton - onClicked: contextMenu.open() - } - - ContextMenuType { - id: contextMenu - textObj: textField - } -} diff --git a/client/ui/controllers/api/apiConfigsController.cpp b/client/ui/controllers/api/apiConfigsController.cpp index 21d371bb..1b70315a 100644 --- a/client/ui/controllers/api/apiConfigsController.cpp +++ b/client/ui/controllers/api/apiConfigsController.cpp @@ -77,6 +77,7 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode, 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); QByteArray responseBody; ErrorCode errorCode = gatewayController.post(QString("%1v1/native_config"), apiPayload, responseBody); @@ -109,6 +110,7 @@ bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode) 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); QByteArray responseBody; ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_native_config"), apiPayload, responseBody); @@ -188,6 +190,7 @@ bool ApiConfigsController::importServiceFromGateway() apiPayload[configKey::userCountryCode] = userCountryCode; apiPayload[configKey::serviceType] = serviceType; apiPayload[configKey::uuid] = installationUuid; + apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION); QByteArray responseBody; ErrorCode errorCode = gatewayController.post(QString("%1v1/config"), apiPayload, responseBody); @@ -233,6 +236,7 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const 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; @@ -330,6 +334,7 @@ bool ApiConfigsController::deactivateDevice() 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); QByteArray responseBody; ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody); @@ -366,6 +371,7 @@ bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const Q 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); QByteArray responseBody; ErrorCode errorCode = gatewayController.post(QString("%1v1/revoke_config"), apiPayload, responseBody); diff --git a/client/ui/controllers/api/apiSettingsController.cpp b/client/ui/controllers/api/apiSettingsController.cpp index f20f92bf..c4a75a5b 100644 --- a/client/ui/controllers/api/apiSettingsController.cpp +++ b/client/ui/controllers/api/apiSettingsController.cpp @@ -5,6 +5,7 @@ #include "core/api/apiUtils.h" #include "core/controllers/gatewayController.h" +#include "version.h" namespace { @@ -60,6 +61,7 @@ bool ApiSettingsController::getAccountInfo(bool reload) apiPayload[configKey::userCountryCode] = apiConfig.value(configKey::userCountryCode).toString(); apiPayload[configKey::serviceType] = apiConfig.value(configKey::serviceType).toString(); apiPayload[configKey::authData] = authData; + apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION); QByteArray responseBody; From 369e08844f0cb30a7d451cd5f19e269780852c38 Mon Sep 17 00:00:00 2001 From: Nethius Date: Fri, 23 May 2025 23:48:38 +0800 Subject: [PATCH 42/65] fix: temporarily hide the strict killswitch (#1612) --- client/ui/qml/Pages2/PageSettingsKillSwitch.qml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml index 444eb415..1ffcc8cf 100644 --- a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml +++ b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml @@ -81,7 +81,8 @@ PageType { Layout.leftMargin: 16 Layout.rightMargin: 16 - enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected + visible: false + enabled: false //SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected checked: SettingsController.strictKillSwitchEnabled text: qsTr("Strict KillSwitch") @@ -103,7 +104,9 @@ PageType { } } - DividerType {} + DividerType { + visible: false + } LabelWithButtonType { Layout.topMargin: 32 From 43c3ce9a6e4d083bc1990b42a7437069d7b09bcf Mon Sep 17 00:00:00 2001 From: Mitternacht822 Date: Thu, 5 Jun 2025 06:08:51 +0400 Subject: [PATCH 43/65] fix: fixed issue with not restoring autostart setting after backup (#1601) * fixed issue with not restoring autostart setting after backup * fixed bug when autostart setting was not saving innto backup file and not preserving after backup * deleted unused lines --- client/ui/controllers/settingsController.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index c5c569db..3e5cbe87 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -126,7 +126,13 @@ void SettingsController::clearLogs() void SettingsController::backupAppConfig(const QString &fileName) { - SystemController::saveFile(fileName, m_settings->backupAppConfig()); + QByteArray data = m_settings->backupAppConfig(); + QJsonDocument doc = QJsonDocument::fromJson(data); + QJsonObject config = doc.object(); + + config["Conf/autoStart"] = Autostart::isAutostart(); + + SystemController::saveFile(fileName, QJsonDocument(config).toJson()); } void SettingsController::restoreAppConfig(const QString &fileName) @@ -140,6 +146,15 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data) { bool ok = m_settings->restoreAppConfig(data); if (ok) { + QJsonObject newConfigData = QJsonDocument::fromJson(data).object(); + +#if defined(Q_OS_WINDOWS) || defined(Q_OS_LINUX) || defined(Q_OS_MACX) + bool autoStart = false; + if (newConfigData.contains("Conf/autoStart")) { + autoStart = newConfigData["Conf/autoStart"].toBool(); + } + toggleAutoStart(autoStart); +#endif m_serversModel->resetModel(); m_languageModel->changeLanguage( static_cast(m_languageModel->getCurrentLanguageIndex())); From 7a203868ec2494b28a86c99d1bf90df3d34feda2 Mon Sep 17 00:00:00 2001 From: Mitternacht822 Date: Thu, 5 Jun 2025 06:12:43 +0400 Subject: [PATCH 44/65] bugfix: fixed bug with not clearing autostart option (#1603) --- client/ui/controllers/settingsController.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index 3e5cbe87..3aa8f48b 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -182,6 +182,8 @@ void SettingsController::clearSettings() m_appSplitTunnelingModel->setRouteMode(Settings::AppsRouteMode::VpnAllExceptApps); m_appSplitTunnelingModel->toggleSplitTunneling(false); + toggleAutoStart(false); + emit changeSettingsFinished(tr("All settings have been reset to default values")); #ifdef Q_OS_IOS From a20516850c6e9495f55ca337c61cbfe9fd851e88 Mon Sep 17 00:00:00 2001 From: Mitternacht822 Date: Thu, 5 Jun 2025 06:13:37 +0400 Subject: [PATCH 45/65] fix: fixed bug when app language was not saved into backup file (#1588) --- client/settings.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/settings.h b/client/settings.h index eec6cc44..7c244cd4 100644 --- a/client/settings.h +++ b/client/settings.h @@ -174,11 +174,12 @@ public: QLocale getAppLanguage() { - return value("Conf/appLanguage", QLocale()).toLocale(); + QString localeStr = m_settings.value("Conf/appLanguage").toString(); + return QLocale(localeStr); }; void setAppLanguage(QLocale locale) { - setValue("Conf/appLanguage", locale); + setValue("Conf/appLanguage", locale.name()); }; bool isScreenshotsEnabled() const From 768ca1e73d5df195e5045507a6aa05cbc9b3402f Mon Sep 17 00:00:00 2001 From: Yaroslav Date: Thu, 5 Jun 2025 05:21:27 +0300 Subject: [PATCH 46/65] feat: add support for manual code signing and provisioning profiles for iOS builds (#1605) --- client/cmake/ios.cmake | 16 +++++++++++++++- client/ios/networkextension/CMakeLists.txt | 16 ++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/client/cmake/ios.cmake b/client/cmake/ios.cmake index 58192237..a498a5b1 100644 --- a/client/cmake/ios.cmake +++ b/client/cmake/ios.cmake @@ -76,8 +76,22 @@ set_target_properties(${PROJECT} PROPERTIES XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks" XCODE_EMBED_APP_EXTENSIONS networkextension - XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic ) + +if(DEFINED DEPLOY) + set_target_properties(${PROJECT} PROPERTIES + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution" + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development" + XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual + XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr ios.org.amnezia.AmneziaVPN" + XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev ios.org.amnezia.AmneziaVPN" + ) +else() + set_target_properties(${PROJECT} PROPERTIES + XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic + ) +endif() + set_target_properties(${PROJECT} PROPERTIES XCODE_ATTRIBUTE_SWIFT_VERSION "5.0" XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES" diff --git a/client/ios/networkextension/CMakeLists.txt b/client/ios/networkextension/CMakeLists.txt index dde03b3b..64b1c3c4 100644 --- a/client/ios/networkextension/CMakeLists.txt +++ b/client/ios/networkextension/CMakeLists.txt @@ -26,10 +26,22 @@ set_target_properties(networkextension PROPERTIES XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2" XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../Frameworks" - - XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic ) +if(DEPLOY) + set_target_properties(networkextension PROPERTIES + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution" + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development" + XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual + XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr ios.org.amnezia.AmneziaVPN" + XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev ios.org.amnezia.AmneziaVPN" + ) +else() + set_target_properties(networkextension PROPERTIES + XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic + ) +endif() + set_target_properties(networkextension PROPERTIES XCODE_ATTRIBUTE_SWIFT_VERSION "5.0" XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES" From c37662dbe2ab33413319a076f32b6e752e9761bc Mon Sep 17 00:00:00 2001 From: Mitternacht822 Date: Thu, 5 Jun 2025 17:48:23 +0400 Subject: [PATCH 47/65] fix: fixed the bug when split tunneling was not preserving after backup for Windows and Android platforms (#1584) * fixed the bug when split tunneling was not preserving after backup for Windows and Android platforms * fixed camelCase and setRouteMode() call * fixed site splitTunneling for all platforms * fixed issue with not preserving tunneling route mode --- client/ui/controllers/settingsController.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index 3aa8f48b..f8e97a1f 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -158,6 +158,18 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data) m_serversModel->resetModel(); m_languageModel->changeLanguage( static_cast(m_languageModel->getCurrentLanguageIndex())); + +#if defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID) + int appSplitTunnelingRouteMode = newConfigData.value("Conf/appsRouteMode").toInt(); + bool appSplittunnelingEnabled = newConfigData.value("Conf/appsSplitTunnelingEnabled").toBool(); + m_appSplitTunnelingModel->setRouteMode(appSplitTunnelingRouteMode); + m_appSplitTunnelingModel->toggleSplitTunneling(appSplittunnelingEnabled); +#endif + int siteSplitTunnelingRouteMode = newConfigData.value("Conf/routeMode").toInt(); + bool siteSplittunnelingEnabled = newConfigData.value("Conf/sitesSplitTunnelingEnabled").toBool(); + m_sitesModel->setRouteMode(siteSplitTunnelingRouteMode); + m_sitesModel->toggleSplitTunneling(siteSplittunnelingEnabled); + emit restoreBackupFinished(); } else { emit changeSettingsErrorOccurred(tr("Backup file is corrupted")); From d3715d00ae970aa91e28855472a9f525a5c5c763 Mon Sep 17 00:00:00 2001 From: Nethius Date: Mon, 9 Jun 2025 05:17:40 +0300 Subject: [PATCH 48/65] chore: fixed artifact names (#1635) --- deploy/deploy_s3.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/deploy_s3.sh b/deploy/deploy_s3.sh index c109a286..a139a5a5 100755 --- a/deploy/deploy_s3.sh +++ b/deploy/deploy_s3.sh @@ -28,10 +28,10 @@ wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSIO wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_armeabi-v7a.apk wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_x86.apk wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_android_7_x86_64.apk -wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_linux.tar.zip +wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_linux_x64.tar.zip wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_macos.dmg wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_macos_old.dmg -wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_x64.exe +wget -q https://github.com/amnezia-vpn/amnezia-client/releases/download/${VERSION}/AmneziaVPN_${VERSION}_windows_x64.exe cd ../ From a2d30efaab530f8fbb75394e73a59dfffa1be5e6 Mon Sep 17 00:00:00 2001 From: aiamnezia Date: Mon, 16 Jun 2025 18:01:46 +0400 Subject: [PATCH 49/65] fix: add saving custom server name if it overridden by user (#1581) * Add saving custom server name if it overridden by user * clear duplicated code --- client/protocols/protocols_defs.h | 2 ++ client/ui/controllers/api/apiConfigsController.cpp | 4 ++++ client/ui/models/servers_model.cpp | 1 + 3 files changed, 7 insertions(+) diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index feeabb2f..c2d51454 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -103,6 +103,8 @@ namespace amnezia constexpr char clientId[] = "clientId"; + constexpr char nameOverriddenByUser[] = "nameOverriddenByUser"; + } namespace protocols diff --git a/client/ui/controllers/api/apiConfigsController.cpp b/client/ui/controllers/api/apiConfigsController.cpp index 1b70315a..4c58140c 100644 --- a/client/ui/controllers/api/apiConfigsController.cpp +++ b/client/ui/controllers/api/apiConfigsController.cpp @@ -261,6 +261,10 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const newServerConfig.insert(configKey::apiConfig, newApiConfig); newServerConfig.insert(configKey::authData, authData); + if (serverConfig.value(config_key::nameOverriddenByUser).toBool()) { + newServerConfig.insert(config_key::name, serverConfig.value(config_key::name)); + newServerConfig.insert(config_key::nameOverriddenByUser, true); + } m_serversModel->editServer(newServerConfig, serverIndex); if (reloadServiceConfig) { emit reloadServerFromApiFinished(tr("API config reloaded")); diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index 0a7b2526..5a70c16f 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -66,6 +66,7 @@ bool ServersModel::setData(const QModelIndex &index, const QVariant &value, int } else { server.insert(config_key::description, value.toString()); } + server.insert(config_key::nameOverriddenByUser, true); m_settings->editServer(index.row(), server); m_servers.replace(index.row(), server); if (index.row() == m_defaultServerIndex) { From 26059788890ed25ab10f6ae01a743b9a37926e69 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Tue, 17 Jun 2025 05:00:41 -0700 Subject: [PATCH 50/65] fix: allow internet traffic for strict mode with split tunnel (#1654) --- client/platforms/windows/daemon/wireguardutilswindows.cpp | 1 + client/protocols/openvpnprotocol.cpp | 2 +- client/protocols/xrayprotocol.cpp | 2 +- client/ui/qml/Pages2/PageSettingsKillSwitch.qml | 7 ++----- service/server/killswitch.cpp | 3 +++ 5 files changed, 8 insertions(+), 7 deletions(-) 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/xrayprotocol.cpp b/client/protocols/xrayprotocol.cpp index faad8e94..9f26d1e6 100755 --- a/client/protocols/xrayprotocol.cpp +++ b/client/protocols/xrayprotocol.cpp @@ -134,7 +134,7 @@ ErrorCode XrayProtocol::startTun2Sock() // killSwitch toggle if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) { if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) { - IpcClient::Interface()->enableKillSwitch(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/ui/qml/Pages2/PageSettingsKillSwitch.qml b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml index 1ffcc8cf..444eb415 100644 --- a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml +++ b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml @@ -81,8 +81,7 @@ PageType { Layout.leftMargin: 16 Layout.rightMargin: 16 - visible: false - enabled: false //SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected + enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected checked: SettingsController.strictKillSwitchEnabled text: qsTr("Strict KillSwitch") @@ -104,9 +103,7 @@ PageType { } } - DividerType { - visible: false - } + DividerType {} LabelWithButtonType { Layout.topMargin: 32 diff --git a/service/server/killswitch.cpp b/service/server/killswitch.cpp index c44bd6a2..447be865 100644 --- a/service/server/killswitch.cpp +++ b/service/server/killswitch.cpp @@ -255,6 +255,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 From e152e84ddc949c17754509bcf5b2ddd2a4ebdcf3 Mon Sep 17 00:00:00 2001 From: lunardunno <126363523+lunardunno@users.noreply.github.com> Date: Mon, 23 Jun 2025 06:32:56 +0400 Subject: [PATCH 51/65] feat: docker pull rate limit check (#1657) * Docker pull rate limit * Error code for DockerPullRateLimit * Extended description Error 213 Extended description for the error 213: Docker Pull Rate Limit * empty line removed --- client/core/controllers/serverController.cpp | 2 ++ client/core/defs.h | 1 + client/core/errorstrings.cpp | 1 + 3 files changed, 4 insertions(+) diff --git a/client/core/controllers/serverController.cpp b/client/core/controllers/serverController.cpp index 8ff6b6c8..f86e2865 100644 --- a/client/core/controllers/serverController.cpp +++ b/client/core/controllers/serverController.cpp @@ -460,6 +460,8 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden 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; } diff --git a/client/core/defs.h b/client/core/defs.h index df6a1342..64f52ce6 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -60,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; From 979ab42c5a424ccffb0fa8b843b9fcc517236f9d Mon Sep 17 00:00:00 2001 From: lunardunno <126363523+lunardunno@users.noreply.github.com> Date: Mon, 23 Jun 2025 06:34:40 +0400 Subject: [PATCH 52/65] feat: OpenSUSE support (#1557) * LOCK_FILE for zypper Checking LOCK_FILE for zypper to support OpenSUSE * Installation for OpenSUSE Docker installation support for OpenSUSE * quiet for zypper * LOCK_CMD variable Implementing the LOCK_CMD variable for different OS. * additional exception for "server is busy" * Replacing and with or Replacing && with || * undo changes to serverController * rpm.lock rpm.lock for dnf yum and zypper * LOCK_CMD check for dnf * Added zypper in check_user_in_sudo --- client/core/controllers/serverController.cpp | 2 +- client/server_scripts/check_server_is_busy.sh | 11 ++++++----- client/server_scripts/check_user_in_sudo.sh | 1 + client/server_scripts/install_docker.sh | 1 + 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/client/core/controllers/serverController.cpp b/client/core/controllers/serverController.cpp index f86e2865..a61a638b 100644 --- a/client/core/controllers/serverController.cpp +++ b/client/core/controllers/serverController.cpp @@ -827,7 +827,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/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";\ From f0626e2ecabf1115d264834beadc59de35a6559e Mon Sep 17 00:00:00 2001 From: aiamnezia Date: Wed, 2 Jul 2025 06:07:56 +0400 Subject: [PATCH 53/65] fix: delete premium V2 migration link from Free config Settings (#1671) * delete premium V2 update link from Free config Settings * Add debug logs * Add property for checking if server config is premium * remove debug logs --- client/ui/models/servers_model.cpp | 9 ++++++++- client/ui/models/servers_model.h | 5 ++++- client/ui/qml/Pages2/PageSettingsServerData.qml | 4 ++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index 5a70c16f..22813312 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -8,6 +8,8 @@ #include #endif +#include "core/api/apiUtils.h" + namespace { namespace configKey @@ -427,7 +429,7 @@ void ServersModel::updateDefaultServerContainersModel() emit defaultServerContainersUpdated(containers); } -QJsonObject ServersModel::getServerConfig(const int serverIndex) +QJsonObject ServersModel::getServerConfig(const int serverIndex) const { return m_servers.at(serverIndex).toObject(); } @@ -814,3 +816,8 @@ const QString ServersModel::getDefaultServerImagePathCollapsed() } return QString("qrc:/countriesFlags/images/flagKit/%1.svg").arg(countryCode.toUpper()); } + +bool ServersModel::processedServerIsPremium() const +{ + return apiUtils::isPremiumServer(getServerConfig(m_processedServerIndex)); +} diff --git a/client/ui/models/servers_model.h b/client/ui/models/servers_model.h index c4803708..c36b6534 100644 --- a/client/ui/models/servers_model.h +++ b/client/ui/models/servers_model.h @@ -63,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); @@ -92,7 +95,7 @@ public slots: void removeServer(); void removeServer(const int serverIndex); - QJsonObject getServerConfig(const int serverIndex); + QJsonObject getServerConfig(const int serverIndex) const; void reloadDefaultServerContainerConfig(); void updateContainerConfig(const int containerIndex, const QJsonObject config); 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 } } } From b0a6bcc05536c9d615b835a790a90d6d42657d1a Mon Sep 17 00:00:00 2001 From: Mitternacht822 Date: Wed, 2 Jul 2025 06:11:22 +0400 Subject: [PATCH 54/65] =?UTF-8?q?fix:=20fixed=20issue=20when=20native=20co?= =?UTF-8?q?nnection=20format=20preserved=20after=20switching=20p=E2=80=A6?= =?UTF-8?q?=20(#1659)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fixed issue when native connection format preserved after switching protocol * moved newly added code into handler section --- client/ui/qml/Pages2/PageShare.qml | 5 +++++ 1 file changed, 5 insertions(+) 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(), From 9dca80de18a9f2b3fd8339044eafc0b44fd91bd6 Mon Sep 17 00:00:00 2001 From: Mitternacht822 Date: Wed, 2 Jul 2025 06:11:52 +0400 Subject: [PATCH 55/65] fix: notification not showing when changed some protocols (#1666) * added notification about disconnecting users after applying changes for SS and Cloak servers pages * added notification about changing protocol data for server and some minor changes --- .../qml/Pages2/PageProtocolCloakSettings.qml | 51 +++++++++++++++---- .../Pages2/PageProtocolOpenVpnSettings.qml | 51 ++++++++++++++----- .../PageProtocolShadowSocksSettings.qml | 49 +++++++++++------- .../Pages2/PageProtocolWireGuardSettings.qml | 2 +- .../qml/Pages2/PageProtocolXraySettings.qml | 42 +++++++++++---- 5 files changed, 146 insertions(+), 49 deletions(-) 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() } } } From 127f8ed3bbccf31383504eb2a71396015b3d67fb Mon Sep 17 00:00:00 2001 From: Nethius Date: Wed, 2 Jul 2025 10:14:56 +0800 Subject: [PATCH 56/65] fix: fixed desktop entry version for linux (#1665) --- deploy/installer/config/AmneziaVPN.desktop.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From b34193486300bc951219c655fd2734f6860feb9f Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Tue, 1 Jul 2025 19:16:58 -0700 Subject: [PATCH 57/65] fix: allow secondary DNS usage when AmneziaDNS is disabled (#1583) * Allow secondary DNS usage when AmneziaDNS is disabled * Don't setup secondary DNS for OpenVPN with AmneziaDNS --------- Co-authored-by: vladimir.kuznetsov --- client/configurators/openvpn_configurator.cpp | 12 +++++++++ client/daemon/daemon.cpp | 26 +++++++++++++----- client/daemon/interfaceconfig.cpp | 13 ++++++--- client/daemon/interfaceconfig.h | 3 ++- client/mozilla/localsocketcontroller.cpp | 9 ++++++- .../linux/daemon/wireguardutilslinux.cpp | 5 +++- .../macos/daemon/wireguardutilsmacos.cpp | 27 ++++++++++--------- .../windows/daemon/windowsfirewall.cpp | 23 +++++++++++++--- client/protocols/xrayprotocol.cpp | 7 ++++- service/server/killswitch.cpp | 23 +++++++++++++--- 10 files changed, 116 insertions(+), 32 deletions(-) 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 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")) { diff --git a/client/daemon/interfaceconfig.cpp b/client/daemon/interfaceconfig.cpp index f0adcc92..846cfebe 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"; diff --git a/client/daemon/interfaceconfig.h b/client/daemon/interfaceconfig.h index ee43a253..6ae400c2 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; diff --git a/client/mozilla/localsocketcontroller.cpp b/client/mozilla/localsocketcontroller.cpp index afa29c47..67924d47 100644 --- a/client/mozilla/localsocketcontroller.cpp +++ b/client/mozilla/localsocketcontroller.cpp @@ -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; diff --git a/client/platforms/linux/daemon/wireguardutilslinux.cpp b/client/platforms/linux/daemon/wireguardutilslinux.cpp index 0fbb65a8..a12b8582 100644 --- a/client/platforms/linux/daemon/wireguardutilslinux.cpp +++ b/client/platforms/linux/daemon/wireguardutilslinux.cpp @@ -140,7 +140,10 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) { } 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/wireguardutilsmacos.cpp b/client/platforms/macos/daemon/wireguardutilsmacos.cpp index 1d8aa6e0..37170f20 100644 --- a/client/platforms/macos/daemon/wireguardutilsmacos.cpp +++ b/client/platforms/macos/daemon/wireguardutilsmacos.cpp @@ -136,26 +136,29 @@ bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) { 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/protocols/xrayprotocol.cpp b/client/protocols/xrayprotocol.cpp index 9f26d1e6..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 diff --git a/service/server/killswitch.cpp b/service/server/killswitch.cpp index 447be865..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(); @@ -307,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"); @@ -345,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()) { From 4d17e913b52a02d80381b61d94a4a767e9bc00cf Mon Sep 17 00:00:00 2001 From: Yaroslav Date: Thu, 3 Jul 2025 04:51:11 +0300 Subject: [PATCH 58/65] feat: native macos installer distribution (#1633) * Add uninstall option and output pkg Improve installer mode detection Fix macOS installer packaging Fix default selection for uninstall choice Remove obsolete tar handling and clean script copies * Improve macOS build script * fix: update macos firewall and package scripts for better compatibility and cleanup * Add DeveloperID certificate and improve macOS signing script Use keychain option for codesign and restore login keychain to list after signing * Update build_macos.sh * feat: add script to quit GUI application during uninstall on macos * fix: handle macos post-install when app is unpacked into localized folder * fix: improve post_install script to handle missing service plist and provide error logging --- .github/workflows/deploy.yml | 18 +- .../platforms/macos/daemon/macosfirewall.cpp | 14 +- deploy/DeveloperIDG2CA.cer | Bin 0 -> 1090 bytes deploy/build_macos.sh | 258 +++++++++++------- deploy/data/macos/check_install.sh | 5 + deploy/data/macos/check_uninstall.sh | 5 + deploy/data/macos/distribution.xml | 17 ++ deploy/data/macos/distribution_uninstall.xml | 13 + deploy/data/macos/post_install.sh | 41 ++- deploy/data/macos/post_uninstall.sh | 50 ++++ deploy/data/macos/uninstall_conclusion.html | 7 + deploy/data/macos/uninstall_welcome.html | 7 + deploy/installer/config.cmake | 5 - deploy/installer/config/macos.xml.in | 27 -- 14 files changed, 311 insertions(+), 156 deletions(-) create mode 100644 deploy/DeveloperIDG2CA.cer mode change 100755 => 100644 deploy/build_macos.sh create mode 100755 deploy/data/macos/check_install.sh create mode 100755 deploy/data/macos/check_uninstall.sh create mode 100644 deploy/data/macos/distribution.xml create mode 100644 deploy/data/macos/distribution_uninstall.xml create mode 100644 deploy/data/macos/uninstall_conclusion.html create mode 100644 deploy/data/macos/uninstall_welcome.html delete mode 100644 deploy/installer/config/macos.xml.in 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/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/deploy/DeveloperIDG2CA.cer b/deploy/DeveloperIDG2CA.cer new file mode 100644 index 0000000000000000000000000000000000000000..8cbcf6f46ce8dcd0fb6e55441867a4608c032860 GIT binary patch literal 1090 zcmXqLVzD!5Vpdzg%*4pVBvQYH!T#)Y&#KeSzLS=8RTLj;iFG#MW#iOp^Jx3d%gD&h z%3zRW$Zf#M#vIDRCd?EXY$$9X2;y)Fb2%0iSS&nCU+yalX;MxjO;0c zCPpP>Z!@woFgG#sGXTZ8n3@lXWlhc{<6@_ zAeVa$Zs~tCyBS(Lx(wQVee%4v9DmC6_sxzm_*Wacv6#I#)Z>*hPs%}3Q09g3DcH!m}M zzME}-$);}wYkbz|o;?}o+I{{v$FroX4pX<9$DEPod-(p&(x<&A1GYQN=TcU6b?Bdx zUj1OoE6d9jOHD2wI{m=7UT>Su;iqa>RnM*J)hhopPh{Hk2Vdj$KD}68xZHa2OrI*H z#lG(g1g~CeYi#?H7fwq1S7-2zs_8d`#)~n?ko^>O~S}P zBuwq0phvQHX3=yH`M&5=acZK!OOzXoLyRRCDz!!3*x{YIYWcZo(++VRH_f_`f37RD zIVso6^6S55-|LI6bjZK$IKcnor?K1?f$fK41kGK{SvD+jncI`WTU{$#cV_G457+W- zMXMT?mRx?=Uwf(Jh2ioUN9FH7YKmOf(3s#R_GaGBE{9dpS`QQ3xOxisZ+hvx@ma(w zc&)N$X|k%KGE@HK=&108*Ije(pZQ)tKmPXdw<*i>UG(#PZ7V9c!nRmnw>qJnx@hvN w+Yzt&uIxN=z~GOOl<%%dJSvtmnyePgy!ZUclRbQUTGvOdj=AvX_8Ec00Cez?EdT%j literal 0 HcmV?d00001 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/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 - - - From efcc0b7efc9fd8f15081db44cf48e9ff205cb407 Mon Sep 17 00:00:00 2001 From: Nethius Date: Thu, 3 Jul 2025 09:58:23 +0800 Subject: [PATCH 59/65] feat: xray api support (#1679) * refactoring: moved shared code into reusable functions for ApiConfigsController * feat: add xray support in apiConfigsController * feat: added a temporary switch for the xray protocol on api settings page * feat: added supported protocols field processing * refactoring: moved IsProtocolSelectionSupported to apiAccountInfoModel --- client/core/api/apiDefs.h | 1 + .../controllers/api/apiConfigsController.cpp | 484 ++++++++++-------- .../ui/controllers/api/apiConfigsController.h | 18 +- client/ui/models/api/apiAccountInfoModel.cpp | 11 + client/ui/models/api/apiAccountInfoModel.h | 5 +- .../qml/Pages2/PageSettingsApiServerInfo.qml | 26 + 6 files changed, 330 insertions(+), 215 deletions(-) diff --git a/client/core/api/apiDefs.h b/client/core/api/apiDefs.h index 4588ef04..12c8051f 100644 --- a/client/core/api/apiDefs.h +++ b/client/core/api/apiDefs.h @@ -32,6 +32,7 @@ namespace apiDefs constexpr QLatin1String stackType("stack_type"); constexpr QLatin1String serviceType("service_type"); constexpr QLatin1String cliVersion("cli_version"); + constexpr QLatin1String supportedProtocols("supported_protocols"); constexpr QLatin1String vpnKey("vpn_key"); constexpr QLatin1String config("config"); diff --git a/client/ui/controllers/api/apiConfigsController.cpp b/client/ui/controllers/api/apiConfigsController.cpp index 4c58140c..eb693a9a 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,169 @@ 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(); + 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); + 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; + + qDebug() << serverConfig; + + return ErrorCode::NoError; + } } ApiConfigsController::ApiConfigsController(const QSharedPointer &serversModel, @@ -63,24 +223,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 +250,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 +258,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 +304,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 +327,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 +377,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 +412,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 +447,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 +461,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 +478,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 +486,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 +512,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 +520,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; @@ -417,108 +571,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() == apiDefs::ConfigSource::AmneziaGateway) { - serverConfig[config_key::configVersion] = newServerConfig.value(config_key::configVersion); - serverConfig[config_key::description] = newServerConfig.value(config_key::description); - serverConfig[config_key::name] = newServerConfig.value(config_key::name); - } - - auto defaultContainer = newServerConfig.value(config_key::defaultContainer).toString(); - serverConfig[config_key::defaultContainer] = defaultContainer; - - QVariantMap map = serverConfig.value(configKey::apiConfig).toObject().toVariantMap(); - map.insert(newServerConfig.value(configKey::apiConfig).toObject().toVariantMap()); - auto apiConfig = QJsonObject::fromVariantMap(map); - - if (newServerConfig.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) { - apiConfig.insert(configKey::serviceInfo, QJsonDocument::fromJson(apiResponseBody).object().value(configKey::serviceInfo).toObject()); - } - - serverConfig[configKey::apiConfig] = apiConfig; - - return; + return false; } QList ApiConfigsController::getQrCodes() @@ -535,3 +610,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/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/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 From f8bea71716826fe757be584b655f5d1f8133e6e2 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Mon, 7 Jul 2025 10:26:16 +0800 Subject: [PATCH 60/65] chore: temporarily hide the strict killswitch --- client/ui/qml/Pages2/PageSettingsKillSwitch.qml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml index 444eb415..ca1cd0d4 100644 --- a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml +++ b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml @@ -62,7 +62,8 @@ PageType { Layout.leftMargin: 16 Layout.rightMargin: 16 - enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected + visible: false + // enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected checked: !SettingsController.strictKillSwitchEnabled text: qsTr("Soft KillSwitch") @@ -73,7 +74,9 @@ PageType { } } - DividerType {} + DividerType { + visible: false + } VerticalRadioButton { id: strictKillSwitch From 42661618dc060d05794cc0dd503ed03cf579cf5d Mon Sep 17 00:00:00 2001 From: Nethius Date: Mon, 7 Jul 2025 10:44:35 +0800 Subject: [PATCH 61/65] chore: bump version (#1696) --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 424dcf3a..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") From 2380cd5cfbe75b610c2bba4c3748aef5982f50fa Mon Sep 17 00:00:00 2001 From: Nethius Date: Mon, 7 Jul 2025 12:03:25 +0800 Subject: [PATCH 62/65] feat: amneziawg 1.5 support (#1692) * Version bump 4.2.1.0 * feat: add special handshake params to ui * feat: finish adding params * feat: android/ios & fix qml * chore: fix android impl & update 3rd-prebuilt branch * chore: trigger build with windows build * fix: special handshake params to client * chore: update submodule * feat: s3, s4 * chore: update submodule * feat: s3 s4 cont * fix: kt set * chore: update submodule * feat: add default values for s3, s4 * fix: make new parameters optional * chore: update submodules * chore: restore translation files * fix: fixed awg native config import with new junk * chore: restore translation files * AWG v1.5 Build * refactoring: removed s3 s4 fileds from ui part * chore: update link to amneziawg-apple --------- Co-authored-by: pokamest Co-authored-by: Mark Puha Co-authored-by: albexk Co-authored-by: Mykola Baibuz --- .gitmodules | 1 + client/3rd-prebuilt | 2 +- client/3rd/amneziawg-apple | 2 +- .../vpn/protocol/wireguard/Wireguard.kt | 11 + .../vpn/protocol/wireguard/WireguardConfig.kt | 59 +++++- client/configurators/awg_configurator.cpp | 15 ++ client/core/controllers/serverController.cpp | 18 +- client/daemon/daemon.cpp | 37 +++- client/daemon/interfaceconfig.cpp | 16 ++ client/daemon/interfaceconfig.h | 5 + client/mozilla/localsocketcontroller.cpp | 39 +++- client/platforms/ios/WGConfig.swift | 22 +- client/platforms/ios/ios_controller.mm | 26 ++- .../linux/daemon/wireguardutilslinux.cpp | 16 ++ .../macos/daemon/wireguardutilsmacos.cpp | 16 ++ client/protocols/protocols_defs.h | 23 ++ client/resources.qrc | 1 + client/server_scripts/awg/Dockerfile | 5 +- .../server_scripts/awg/configure_container.sh | 1 + .../controllers/api/apiConfigsController.cpp | 18 ++ client/ui/controllers/importController.cpp | 56 +++-- client/ui/controllers/installController.cpp | 56 ++++- client/ui/models/protocols/awgConfigModel.cpp | 109 +++++++++- client/ui/models/protocols/awgConfigModel.h | 33 ++- client/ui/qml/Components/AwgTextField.qml | 15 ++ .../Pages2/PageProtocolAwgClientSettings.qml | 198 +++++++++++++----- .../ui/qml/Pages2/PageProtocolAwgSettings.qml | 144 +++++-------- 27 files changed, 758 insertions(+), 186 deletions(-) create mode 100644 client/ui/qml/Components/AwgTextField.qml 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/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/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/core/controllers/serverController.cpp b/client/core/controllers/serverController.cpp index a61a638b..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,7 +460,7 @@ 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")) @@ -641,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(); diff --git a/client/daemon/daemon.cpp b/client/daemon/daemon.cpp index 33ec8cbc..2faff0ef 100644 --- a/client/daemon/daemon.cpp +++ b/client/daemon/daemon.cpp @@ -405,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(); } @@ -418,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; } @@ -460,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 846cfebe..53da5d36 100644 --- a/client/daemon/interfaceconfig.cpp +++ b/client/daemon/interfaceconfig.cpp @@ -130,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"; } @@ -143,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 6ae400c2..06288e80 100644 --- a/client/daemon/interfaceconfig.h +++ b/client/daemon/interfaceconfig.h @@ -50,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 67924d47..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 @@ -244,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..8f693387 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 @@ -47,11 +53,21 @@ struct WGConfig: Decodable { Jmax = \(junkPacketMaxSize!) S1 = \(initPacketJunkSize!) S2 = \(responsePacketJunkSize!) + S3 = \(cookieReplyPacketJunkSize!) + S4 = \(transportPacketJunkSize!) H1 = \(initPacketMagicHeader!) H2 = \(responsePacketMagicHeader!) H3 = \(underloadPacketMagicHeader!) H4 = \(transportPacketMagicHeader!) - + I1 = \(specialJunk1!) + I2 = \(specialJunk2!) + I3 = \(specialJunk3!) + I4 = \(specialJunk4!) + I5 = \(specialJunk5!) + J1 = \(controlledJunk1!) + J2 = \(controlledJunk2!) + J3 = \(controlledJunk3!) + Itime = \(specialHandshakeTimeout!) """ } 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 a12b8582..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,6 +140,16 @@ 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); diff --git a/client/platforms/macos/daemon/wireguardutilsmacos.cpp b/client/platforms/macos/daemon/wireguardutilsmacos.cpp index 37170f20..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,6 +138,16 @@ 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); 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/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/ui/controllers/api/apiConfigsController.cpp b/client/ui/controllers/api/apiConfigsController.cpp index eb693a9a..0b0a9b92 100644 --- a/client/ui/controllers/api/apiConfigsController.cpp +++ b/client/ui/controllers/api/apiConfigsController.cpp @@ -162,6 +162,9 @@ namespace 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); @@ -171,6 +174,21 @@ namespace 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; 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 eab8979a..d7f9dfbc 100755 --- a/client/ui/controllers/installController.cpp +++ b/client/ui/controllers/installController.cpp @@ -8,6 +8,7 @@ #include #include +#include "core/api/apiUtils.h" #include "core/controllers/serverController.h" #include "core/controllers/vpnConfigurationController.h" #include "core/networkUtilities.h" @@ -15,7 +16,6 @@ #include "ui/models/protocols/awgConfigModel.h" #include "ui/models/protocols/wireguardConfigModel.h" #include "utilities.h" -#include "core/api/apiUtils.h" namespace { @@ -79,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 + AwgConstant::messageInitiationSize == s2 + AwgConstant::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) { @@ -108,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)); @@ -401,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); diff --git a/client/ui/models/protocols/awgConfigModel.cpp b/client/ui/models/protocols/awgConfigModel.cpp index 860c8395..e14a3152 100644 --- a/client/ui/models/protocols/awgConfigModel.cpp +++ b/client/ui/models/protocols/awgConfigModel.cpp @@ -28,7 +28,17 @@ bool AwgConfigModel::setData(const QModelIndex &index, const QVariant &value, in case Roles::ClientJunkPacketCountRole: m_clientProtocolConfig.insert(config_key::junkPacketCount, value.toString()); break; case Roles::ClientJunkPacketMinSizeRole: m_clientProtocolConfig.insert(config_key::junkPacketMinSize, value.toString()); break; case Roles::ClientJunkPacketMaxSizeRole: m_clientProtocolConfig.insert(config_key::junkPacketMaxSize, value.toString()); break; - + case Roles::ClientSpecialJunk1Role: m_clientProtocolConfig.insert(config_key::specialJunk1, value.toString()); break; + case Roles::ClientSpecialJunk2Role: m_clientProtocolConfig.insert(config_key::specialJunk2, value.toString()); break; + case Roles::ClientSpecialJunk3Role: m_clientProtocolConfig.insert(config_key::specialJunk3, value.toString()); break; + case Roles::ClientSpecialJunk4Role: m_clientProtocolConfig.insert(config_key::specialJunk4, value.toString()); break; + case Roles::ClientSpecialJunk5Role: m_clientProtocolConfig.insert(config_key::specialJunk5, value.toString()); break; + case Roles::ClientControlledJunk1Role: m_clientProtocolConfig.insert(config_key::controlledJunk1, value.toString()); break; + case Roles::ClientControlledJunk2Role: m_clientProtocolConfig.insert(config_key::controlledJunk2, value.toString()); break; + case Roles::ClientControlledJunk3Role: m_clientProtocolConfig.insert(config_key::controlledJunk3, value.toString()); break; + case Roles::ClientSpecialHandshakeTimeoutRole: + m_clientProtocolConfig.insert(config_key::specialHandshakeTimeout, value.toString()); + break; case Roles::ServerJunkPacketCountRole: m_serverProtocolConfig.insert(config_key::junkPacketCount, value.toString()); break; case Roles::ServerJunkPacketMinSizeRole: m_serverProtocolConfig.insert(config_key::junkPacketMinSize, value.toString()); break; case Roles::ServerJunkPacketMaxSizeRole: m_serverProtocolConfig.insert(config_key::junkPacketMaxSize, value.toString()); break; @@ -36,6 +46,12 @@ bool AwgConfigModel::setData(const QModelIndex &index, const QVariant &value, in case Roles::ServerResponsePacketJunkSizeRole: m_serverProtocolConfig.insert(config_key::responsePacketJunkSize, value.toString()); break; + // case Roles::ServerCookieReplyPacketJunkSizeRole: + // m_serverProtocolConfig.insert(config_key::cookieReplyPacketJunkSize, value.toString()); + // break; + // case Roles::ServerTransportPacketJunkSizeRole: + // m_serverProtocolConfig.insert(config_key::transportPacketJunkSize, value.toString()); + // break; case Roles::ServerInitPacketMagicHeaderRole: m_serverProtocolConfig.insert(config_key::initPacketMagicHeader, value.toString()); break; case Roles::ServerResponsePacketMagicHeaderRole: m_serverProtocolConfig.insert(config_key::responsePacketMagicHeader, value.toString()); @@ -66,12 +82,23 @@ QVariant AwgConfigModel::data(const QModelIndex &index, int role) const case Roles::ClientJunkPacketCountRole: return m_clientProtocolConfig.value(config_key::junkPacketCount); case Roles::ClientJunkPacketMinSizeRole: return m_clientProtocolConfig.value(config_key::junkPacketMinSize); case Roles::ClientJunkPacketMaxSizeRole: return m_clientProtocolConfig.value(config_key::junkPacketMaxSize); + case Roles::ClientSpecialJunk1Role: return m_clientProtocolConfig.value(config_key::specialJunk1); + case Roles::ClientSpecialJunk2Role: return m_clientProtocolConfig.value(config_key::specialJunk2); + case Roles::ClientSpecialJunk3Role: return m_clientProtocolConfig.value(config_key::specialJunk3); + case Roles::ClientSpecialJunk4Role: return m_clientProtocolConfig.value(config_key::specialJunk4); + case Roles::ClientSpecialJunk5Role: return m_clientProtocolConfig.value(config_key::specialJunk5); + case Roles::ClientControlledJunk1Role: return m_clientProtocolConfig.value(config_key::controlledJunk1); + case Roles::ClientControlledJunk2Role: return m_clientProtocolConfig.value(config_key::controlledJunk2); + case Roles::ClientControlledJunk3Role: return m_clientProtocolConfig.value(config_key::controlledJunk3); + case Roles::ClientSpecialHandshakeTimeoutRole: return m_clientProtocolConfig.value(config_key::specialHandshakeTimeout); case Roles::ServerJunkPacketCountRole: return m_serverProtocolConfig.value(config_key::junkPacketCount); case Roles::ServerJunkPacketMinSizeRole: return m_serverProtocolConfig.value(config_key::junkPacketMinSize); case Roles::ServerJunkPacketMaxSizeRole: return m_serverProtocolConfig.value(config_key::junkPacketMaxSize); case Roles::ServerInitPacketJunkSizeRole: return m_serverProtocolConfig.value(config_key::initPacketJunkSize); case Roles::ServerResponsePacketJunkSizeRole: return m_serverProtocolConfig.value(config_key::responsePacketJunkSize); + // case Roles::ServerCookieReplyPacketJunkSizeRole: return m_serverProtocolConfig.value(config_key::cookieReplyPacketJunkSize); + // case Roles::ServerTransportPacketJunkSizeRole: return m_serverProtocolConfig.value(config_key::transportPacketJunkSize); case Roles::ServerInitPacketMagicHeaderRole: return m_serverProtocolConfig.value(config_key::initPacketMagicHeader); case Roles::ServerResponsePacketMagicHeaderRole: return m_serverProtocolConfig.value(config_key::responsePacketMagicHeader); case Roles::ServerUnderloadPacketMagicHeaderRole: return m_serverProtocolConfig.value(config_key::underloadPacketMagicHeader); @@ -94,7 +121,8 @@ void AwgConfigModel::updateModel(const QJsonObject &config) m_serverProtocolConfig.insert(config_key::transport_proto, serverProtocolConfig.value(config_key::transport_proto).toString(defaultTransportProto)); m_serverProtocolConfig[config_key::last_config] = serverProtocolConfig.value(config_key::last_config); - m_serverProtocolConfig[config_key::subnet_address] = serverProtocolConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress); + m_serverProtocolConfig[config_key::subnet_address] = + serverProtocolConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress); m_serverProtocolConfig[config_key::port] = serverProtocolConfig.value(config_key::port).toString(protocols::awg::defaultPort); m_serverProtocolConfig[config_key::junkPacketCount] = serverProtocolConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount); @@ -106,6 +134,10 @@ void AwgConfigModel::updateModel(const QJsonObject &config) serverProtocolConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize); m_serverProtocolConfig[config_key::responsePacketJunkSize] = serverProtocolConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize); + // m_serverProtocolConfig[config_key::cookieReplyPacketJunkSize] = + // serverProtocolConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize); + // m_serverProtocolConfig[config_key::transportPacketJunkSize] = + // serverProtocolConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize); m_serverProtocolConfig[config_key::initPacketMagicHeader] = serverProtocolConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader); m_serverProtocolConfig[config_key::responsePacketMagicHeader] = @@ -124,6 +156,24 @@ void AwgConfigModel::updateModel(const QJsonObject &config) clientProtocolConfig.value(config_key::junkPacketMinSize).toString(m_serverProtocolConfig[config_key::junkPacketMinSize].toString()); m_clientProtocolConfig[config_key::junkPacketMaxSize] = clientProtocolConfig.value(config_key::junkPacketMaxSize).toString(m_serverProtocolConfig[config_key::junkPacketMaxSize].toString()); + m_clientProtocolConfig[config_key::specialJunk1] = + clientProtocolConfig.value(config_key::specialJunk1).toString(protocols::awg::defaultSpecialJunk1); + m_clientProtocolConfig[config_key::specialJunk2] = + clientProtocolConfig.value(config_key::specialJunk2).toString(protocols::awg::defaultSpecialJunk2); + m_clientProtocolConfig[config_key::specialJunk3] = + clientProtocolConfig.value(config_key::specialJunk3).toString(protocols::awg::defaultSpecialJunk3); + m_clientProtocolConfig[config_key::specialJunk4] = + clientProtocolConfig.value(config_key::specialJunk4).toString(protocols::awg::defaultSpecialJunk4); + m_clientProtocolConfig[config_key::specialJunk5] = + clientProtocolConfig.value(config_key::specialJunk5).toString(protocols::awg::defaultSpecialJunk5); + m_clientProtocolConfig[config_key::controlledJunk1] = + clientProtocolConfig.value(config_key::controlledJunk1).toString(protocols::awg::defaultControlledJunk1); + m_clientProtocolConfig[config_key::controlledJunk2] = + clientProtocolConfig.value(config_key::controlledJunk2).toString(protocols::awg::defaultControlledJunk2); + m_clientProtocolConfig[config_key::controlledJunk3] = + clientProtocolConfig.value(config_key::controlledJunk3).toString(protocols::awg::defaultControlledJunk3); + m_clientProtocolConfig[config_key::specialHandshakeTimeout] = + clientProtocolConfig.value(config_key::specialHandshakeTimeout).toString(protocols::awg::defaultSpecialHandshakeTimeout); endResetModel(); } @@ -141,6 +191,15 @@ QJsonObject AwgConfigModel::getConfig() jsonConfig[config_key::junkPacketCount] = m_clientProtocolConfig[config_key::junkPacketCount]; jsonConfig[config_key::junkPacketMinSize] = m_clientProtocolConfig[config_key::junkPacketMinSize]; jsonConfig[config_key::junkPacketMaxSize] = m_clientProtocolConfig[config_key::junkPacketMaxSize]; + jsonConfig[config_key::specialJunk1] = m_clientProtocolConfig[config_key::specialJunk1]; + jsonConfig[config_key::specialJunk2] = m_clientProtocolConfig[config_key::specialJunk2]; + jsonConfig[config_key::specialJunk3] = m_clientProtocolConfig[config_key::specialJunk3]; + jsonConfig[config_key::specialJunk4] = m_clientProtocolConfig[config_key::specialJunk4]; + jsonConfig[config_key::specialJunk5] = m_clientProtocolConfig[config_key::specialJunk5]; + jsonConfig[config_key::controlledJunk1] = m_clientProtocolConfig[config_key::controlledJunk1]; + jsonConfig[config_key::controlledJunk2] = m_clientProtocolConfig[config_key::controlledJunk2]; + jsonConfig[config_key::controlledJunk3] = m_clientProtocolConfig[config_key::controlledJunk3]; + jsonConfig[config_key::specialHandshakeTimeout] = m_clientProtocolConfig[config_key::specialHandshakeTimeout]; m_serverProtocolConfig[config_key::last_config] = QString(QJsonDocument(jsonConfig).toJson()); } @@ -159,6 +218,17 @@ bool AwgConfigModel::isPacketSizeEqual(const int s1, const int s2) return (AwgConstant::messageInitiationSize + s1 == AwgConstant::messageResponseSize + s2); } +// bool AwgConfigModel::isPacketSizeEqual(const int s1, const int s2, const int s3, const int s4) +// { +// int initSize = AwgConstant::messageInitiationSize + s1; +// int responseSize = AwgConstant::messageResponseSize + s2; +// int cookieSize = AwgConstant::messageCookieReplySize + s3; +// int transportSize = AwgConstant::messageTransportSize + s4; + +// return (initSize == responseSize || initSize == cookieSize || initSize == transportSize || responseSize == cookieSize +// || responseSize == transportSize || cookieSize == transportSize); +// } + bool AwgConfigModel::isServerSettingsEqual() { const AwgConfig oldConfig(m_fullConfig.value(config_key::awg).toObject()); @@ -178,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"; @@ -200,6 +282,16 @@ AwgConfig::AwgConfig(const QJsonObject &serverProtocolConfig) clientJunkPacketCount = clientProtocolConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount); clientJunkPacketMinSize = clientProtocolConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize); clientJunkPacketMaxSize = clientProtocolConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize); + clientSpecialJunk1 = clientProtocolConfig.value(config_key::specialJunk1).toString(protocols::awg::defaultSpecialJunk1); + clientSpecialJunk2 = clientProtocolConfig.value(config_key::specialJunk2).toString(protocols::awg::defaultSpecialJunk2); + clientSpecialJunk3 = clientProtocolConfig.value(config_key::specialJunk3).toString(protocols::awg::defaultSpecialJunk3); + clientSpecialJunk4 = clientProtocolConfig.value(config_key::specialJunk4).toString(protocols::awg::defaultSpecialJunk4); + clientSpecialJunk5 = clientProtocolConfig.value(config_key::specialJunk5).toString(protocols::awg::defaultSpecialJunk5); + clientControlledJunk1 = clientProtocolConfig.value(config_key::controlledJunk1).toString(protocols::awg::defaultControlledJunk1); + clientControlledJunk2 = clientProtocolConfig.value(config_key::controlledJunk2).toString(protocols::awg::defaultControlledJunk2); + clientControlledJunk3 = clientProtocolConfig.value(config_key::controlledJunk3).toString(protocols::awg::defaultControlledJunk3); + clientSpecialHandshakeTimeout = + clientProtocolConfig.value(config_key::specialHandshakeTimeout).toString(protocols::awg::defaultSpecialHandshakeTimeout); subnetAddress = serverProtocolConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress); port = serverProtocolConfig.value(config_key::port).toString(protocols::awg::defaultPort); @@ -209,6 +301,10 @@ AwgConfig::AwgConfig(const QJsonObject &serverProtocolConfig) serverInitPacketJunkSize = serverProtocolConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize); serverResponsePacketJunkSize = serverProtocolConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize); + // serverCookieReplyPacketJunkSize = + // serverProtocolConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize); + // serverTransportPacketJunkSize = + // serverProtocolConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize); serverInitPacketMagicHeader = serverProtocolConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader); serverResponsePacketMagicHeader = @@ -224,6 +320,8 @@ 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 @@ -236,7 +334,12 @@ bool AwgConfig::hasEqualServerSettings(const AwgConfig &other) const bool AwgConfig::hasEqualClientSettings(const AwgConfig &other) const { if (clientMtu != other.clientMtu || clientJunkPacketCount != other.clientJunkPacketCount - || clientJunkPacketMinSize != other.clientJunkPacketMinSize || clientJunkPacketMaxSize != other.clientJunkPacketMaxSize) { + || 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 c1f8bb27..0c2374fc 100644 --- a/client/ui/models/protocols/awgConfigModel.h +++ b/client/ui/models/protocols/awgConfigModel.h @@ -6,9 +6,12 @@ #include "containers/containers_defs.h" -namespace AwgConstant { +namespace AwgConstant +{ const int messageInitiationSize = 148; const int messageResponseSize = 92; + const int messageCookieReplySize = 64; + const int messageTransportSize = 32; } struct AwgConfig @@ -22,12 +25,23 @@ struct AwgConfig QString clientJunkPacketCount; QString clientJunkPacketMinSize; QString clientJunkPacketMaxSize; + QString clientSpecialJunk1; + QString clientSpecialJunk2; + QString clientSpecialJunk3; + QString clientSpecialJunk4; + QString clientSpecialJunk5; + QString clientControlledJunk1; + QString clientControlledJunk2; + QString clientControlledJunk3; + QString clientSpecialHandshakeTimeout; QString serverJunkPacketCount; QString serverJunkPacketMinSize; QString serverJunkPacketMaxSize; QString serverInitPacketJunkSize; QString serverResponsePacketJunkSize; + QString serverCookieReplyPacketJunkSize; + QString serverTransportPacketJunkSize; QString serverInitPacketMagicHeader; QString serverResponsePacketMagicHeader; QString serverUnderloadPacketMagicHeader; @@ -35,7 +49,6 @@ struct AwgConfig bool hasEqualServerSettings(const AwgConfig &other) const; bool hasEqualClientSettings(const AwgConfig &other) const; - }; class AwgConfigModel : public QAbstractListModel @@ -51,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); @@ -75,7 +100,7 @@ public slots: QJsonObject getConfig(); bool isHeadersEqual(const QString &h1, const QString &h2, const QString &h3, const QString &h4); - bool isPacketSizeEqual(const int s1, const int s2); + bool isPacketSizeEqual(const int s1, const int s2/*, const int s3, const int s4*/); bool isServerSettingsEqual(); 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?") From 5445e6637b6f126db78c3666828e2f9ed0c5e964 Mon Sep 17 00:00:00 2001 From: Nethius Date: Tue, 8 Jul 2025 14:25:03 +0800 Subject: [PATCH 63/65] chore: minor fixes (#1616) * chore: removed unnecessary qdebug * fix: return soft and hide strict killswitch --- .../ui/controllers/api/apiConfigsController.cpp | 2 -- client/ui/qml/Pages2/PageSettingsKillSwitch.qml | 15 ++++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/client/ui/controllers/api/apiConfigsController.cpp b/client/ui/controllers/api/apiConfigsController.cpp index 0b0a9b92..0f42beb7 100644 --- a/client/ui/controllers/api/apiConfigsController.cpp +++ b/client/ui/controllers/api/apiConfigsController.cpp @@ -221,8 +221,6 @@ namespace serverConfig[configKey::apiConfig] = apiConfig; - qDebug() << serverConfig; - return ErrorCode::NoError; } } diff --git a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml index ca1cd0d4..d6d73b20 100644 --- a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml +++ b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml @@ -62,8 +62,7 @@ PageType { Layout.leftMargin: 16 Layout.rightMargin: 16 - visible: false - // enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected + enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected checked: !SettingsController.strictKillSwitchEnabled text: qsTr("Soft KillSwitch") @@ -74,9 +73,7 @@ PageType { } } - DividerType { - visible: false - } + DividerType {} VerticalRadioButton { id: strictKillSwitch @@ -84,7 +81,9 @@ PageType { Layout.leftMargin: 16 Layout.rightMargin: 16 - enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected + visible: false + enabled: false + // enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected checked: SettingsController.strictKillSwitchEnabled text: qsTr("Strict KillSwitch") @@ -106,7 +105,9 @@ PageType { } } - DividerType {} + DividerType { + visible: false + } LabelWithButtonType { Layout.topMargin: 32 From 10a107716cf3a566f77e99a56066140e61bdae0b Mon Sep 17 00:00:00 2001 From: Nethius Date: Tue, 8 Jul 2025 15:06:52 +0800 Subject: [PATCH 64/65] fix: fixed awg 1.5 fields processing for ios (#1700) --- client/platforms/ios/WGConfig.swift | 76 ++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/client/platforms/ios/WGConfig.swift b/client/platforms/ios/WGConfig.swift index 8f693387..537687f1 100644 --- a/client/platforms/ios/WGConfig.swift +++ b/client/platforms/ios/WGConfig.swift @@ -46,29 +46,59 @@ struct WGConfig: Decodable { } var settings: String { - junkPacketCount == nil ? "" : - """ - Jc = \(junkPacketCount!) - Jmin = \(junkPacketMinSize!) - Jmax = \(junkPacketMaxSize!) - S1 = \(initPacketJunkSize!) - S2 = \(responsePacketJunkSize!) - S3 = \(cookieReplyPacketJunkSize!) - S4 = \(transportPacketJunkSize!) - H1 = \(initPacketMagicHeader!) - H2 = \(responsePacketMagicHeader!) - H3 = \(underloadPacketMagicHeader!) - H4 = \(transportPacketMagicHeader!) - I1 = \(specialJunk1!) - I2 = \(specialJunk2!) - I3 = \(specialJunk3!) - I4 = \(specialJunk4!) - I5 = \(specialJunk5!) - J1 = \(controlledJunk1!) - J2 = \(controlledJunk2!) - J3 = \(controlledJunk3!) - Itime = \(specialHandshakeTimeout!) - """ + 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 {