Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into HEAD

This commit is contained in:
vladimir.kuznetsov 2025-05-13 10:49:11 +08:00
commit b80d5fe01a
107 changed files with 2173 additions and 946 deletions

@ -1 +1 @@
Subproject commit efad1a5b5cb8e8ab61e49ccdca18c9090a0da8d3 Subproject commit 0f3748efd7cc04e0c914304b68931f925bed1259

View file

@ -13,10 +13,10 @@
#include <QApplication> #include <QApplication>
#endif #endif
#include "core/networkUtilities.h"
#include "containers/containers_defs.h" #include "containers/containers_defs.h"
#include "core/controllers/serverController.h" #include "core/controllers/serverController.h"
#include "core/scripts_registry.h" #include "core/scripts_registry.h"
#include "core/server_defs.h"
#include "settings.h" #include "settings.h"
#include "utilities.h" #include "utilities.h"
@ -24,6 +24,7 @@
#include <openssl/rsa.h> #include <openssl/rsa.h>
#include <openssl/x509.h> #include <openssl/x509.h>
OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
QObject *parent) QObject *parent)
: ConfiguratorBase(settings, serverController, parent) : ConfiguratorBase(settings, serverController, parent)
@ -119,20 +120,14 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
if (!m_settings->isSitesSplitTunnelingEnabled()) { if (!m_settings->isSitesSplitTunnelingEnabled()) {
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n"); config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
// Prevent ipv6 leak
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
#endif
config.append("block-ipv6\n"); config.append("block-ipv6\n");
} else if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) { } else if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
// no redirect-gateway // no redirect-gateway
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) { } else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n"); config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
// Prevent ipv6 leak // Prevent ipv6 leak
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
#endif #endif
config.append("block-ipv6\n"); config.append("block-ipv6\n");
} }
@ -169,7 +164,6 @@ QString OpenVpnConfigurator::processConfigWithExportSettings(const QPair<QString
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n"); config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
// Prevent ipv6 leak // Prevent ipv6 leak
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
config.append("block-ipv6\n"); config.append("block-ipv6\n");
// remove block-outside-dns for all exported configs // remove block-outside-dns for all exported configs

View file

@ -140,98 +140,83 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
{ {
return { return {
{ DockerContainer::OpenVpn, { DockerContainer::OpenVpn,
QObject::tr( QObject::tr("OpenVPN is one of the most popular and reliable VPN protocols. "
"OpenVPN stands as one of the most popular and time-tested VPN protocols available.\n" "It uses SSL/TLS encryption, supports a wide variety of devices and operating systems, "
"It employs its unique security protocol, " "and is continuously improved by the community due to its open-source nature. "
"leveraging the strength of SSL/TLS for encryption and key exchange. " "It provides a good balance between speed and security but is easily recognized by DPI systems, "
"Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, " "making it susceptible to blocking.\n"
"catering to a wide range of devices and operating systems. " "\nFeatures:\n"
"Due to its open-source nature, OpenVPN benefits from extensive scrutiny by the global community, " "* Available on all AmneziaVPN platforms\n"
"which continually reinforces its security. " "* Normal battery consumption on mobile devices\n"
"With a strong balance of performance, security, and compatibility, " "* Flexible customization for various devices and OS\n"
"OpenVPN remains a top choice for privacy-conscious individuals and businesses alike.\n\n" "* Operates over both TCP and UDP protocols") },
"* Available in the DefaultVPN across all platforms\n"
"* Normal power consumption on mobile devices\n"
"* Flexible customisation to suit user needs to work with different operating systems and devices\n"
"* Recognised by DPI systems and therefore susceptible to blocking\n"
"* Can operate over both TCP and UDP network protocols.") },
{ DockerContainer::ShadowSocks, { DockerContainer::ShadowSocks,
QObject::tr("Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. " QObject::tr("Shadowsocks is based on the SOCKS5 protocol and encrypts connections using AEAD cipher. "
"Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection." "Although designed to be discreet, it doesn't mimic a standard HTTPS connection and can be detected by some DPI systems. "
"However, certain traffic analysis systems might still detect a Shadowsocks connection. " "Due to limited support in Amnezia, we recommend using the AmneziaWG protocol.\n"
"Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol.\n\n" "\nFeatures:\n"
"* Available in the DefaultVPN only on desktop platforms\n" "* Available in AmneziaVPN only on desktop platforms\n"
"* Configurable encryption protocol\n" "* Customizable encryption protocol\n"
"* Detectable by some DPI systems\n" "* Detectable by some DPI systems\n"
"* Works over TCP network protocol.") }, "* Operates over TCP protocol\n") },
{ DockerContainer::Cloak, { DockerContainer::Cloak,
QObject::tr("This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for " QObject::tr("This combination includes the OpenVPN protocol and the Cloak plugin, specifically designed to protect against blocking.\n"
"protecting against detection.\n\n" "\nOpenVPN securely encrypts all internet traffic between your device and the server.\n"
"OpenVPN provides a secure VPN connection by encrypting all internet traffic between the client " "\nThe Cloak plugin further protects the connection from DPI detection. "
"and the server.\n\n" "It modifies traffic metadata to disguise VPN traffic as regular web traffic and prevents detection through active probing. "
"Cloak protects OpenVPN from detection. \n\n" "If an incoming connection fails authentication, Cloak serves a fake website, making your VPN invisible to traffic analysis systems.\n"
"Cloak can modify packet metadata so that it completely masks VPN traffic as normal web traffic, " "\nIn regions with heavy internet censorship, we strongly recommend using OpenVPN with Cloak from your first connection.\n"
"and also protects the VPN from detection by Active Probing. This makes it very resistant to " "\nFeatures:\n"
"being detected\n\n" "* Available on all AmneziaVPN platforms\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 DefaultVPN across all platforms\n"
"* High power consumption on mobile devices\n" "* High power consumption on mobile devices\n"
"* Flexible settings\n" "* Flexible configuration options\n"
"* Not recognised by detection systems\n" "* Undetectable by DPI systems\n"
"* Works over TCP network protocol, 443 port.\n") }, "* Operates over TCP protocol on port 443") },
{ DockerContainer::WireGuard, { DockerContainer::WireGuard,
QObject::tr("A relatively new popular VPN protocol with a simplified architecture.\n" QObject::tr("WireGuard is a modern, streamlined VPN protocol offering stable connectivity and excellent performance across all devices. "
"WireGuard provides stable VPN connection and high performance on all devices. It uses hard-coded encryption " "It uses fixed encryption settings, delivering lower latency and higher data transfer speeds compared to OpenVPN. "
"settings. WireGuard compared to OpenVPN has lower latency and better data transfer throughput.\n" "However, WireGuard is easily identifiable by DPI systems due to its distinctive packet signatures, making it susceptible to blocking.\n"
"WireGuard is very susceptible to detection and blocking due to its distinct packet signatures. " "\nFeatures:\n"
"Unlike some other VPN protocols that employ obfuscation techniques, " "* Available on all AmneziaVPN platforms\n"
"the consistent signature patterns of WireGuard packets can be more easily identified and " "* Low power consumption on mobile devices\n"
"thus blocked by advanced Deep Packet Inspection (DPI) systems and other network monitoring tools.\n\n" "* Minimal configuration required\n"
"* Available in the DefaultVPN across all platforms\n" "* Easily detected by DPI systems (susceptible to blocking)\n"
"* Low power consumption\n" "* Operates over UDP protocol") },
"* Minimum number of settings\n"
"* Easily recognised by DPI analysis systems, susceptible to blocking\n"
"* Works over UDP network protocol.") },
{ DockerContainer::Awg, { DockerContainer::Awg,
QObject::tr("A modern iteration of the popular VPN protocol, " QObject::tr("AmneziaWG is a modern VPN protocol based on WireGuard, "
"AmneziaWG builds upon the foundation set by WireGuard, " "combining simplified architecture with high performance across all devices. "
"retaining its simplified architecture and high-performance capabilities across devices.\n" "It addresses WireGuard's main vulnerability (easy detection by DPI systems) through advanced obfuscation techniques, "
"While WireGuard is known for its efficiency, " "making VPN traffic indistinguishable from regular internet traffic.\n"
"it had issues with being easily detected due to its distinct packet signatures. " "\nAmneziaWG is an excellent choice for those seeking a fast, stealthy VPN connection.\n"
"AmneziaWG solves this problem by using better obfuscation methods, " "\nFeatures:\n"
"making its traffic blend in with regular internet traffic.\n" "* Available on all AmneziaVPN platforms\n"
"This means that AmneziaWG keeps the fast performance of the original " "* Low battery consumption on mobile devices\n"
"while adding an extra layer of stealth, " "* Minimal settings required\n"
"making it a great choice for those wanting a fast and discreet VPN connection.\n\n" "* Undetectable by traffic analysis systems (DPI)\n"
"* Available in the DefaultVPN across all platforms\n" "* Operates over UDP protocol") },
"* Low power consumption\n"
"* Minimum number of settings\n"
"* Not recognised by traffic analysis systems\n"
"* Works over UDP network protocol.") },
{ DockerContainer::Xray, { DockerContainer::Xray,
QObject::tr("The REALITY protocol, a pioneering development by the creators of XRay, " QObject::tr("REALITY is an innovative protocol developed by the creators of XRay, designed specifically to combat high levels of internet censorship. "
"is designed to provide the highest level of protection against detection through its innovative approach to security and privacy.\n" "REALITY identifies censorship systems during the TLS handshake, "
"It uniquely identifies attackers during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting attackers to genuine websites, " "redirecting suspicious traffic seamlessly to legitimate websites like google.com while providing genuine TLS certificates. "
"thus presenting an authentic TLS certificate and data. \n" "This allows VPN traffic to blend indistinguishably with regular web traffic without special configuration."
"This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, " "\nUnlike older protocols such as VMess, VLESS, and XTLS-Vision, REALITY incorporates an advanced built-in \"friend-or-foe\" detection mechanism, "
"legitimate sites without the need for specific configurations. \n" "effectively protecting against DPI and other traffic analysis methods.\n"
"Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, " "\nFeatures:\n"
"REALITY's innovative \"friend or foe\" recognition at the TLS handshake enhances security. " "* Resistant to active probing and DPI detection\n"
"This makes REALITY a robust solution for maintaining internet freedom.") "* 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, { DockerContainer::Ipsec,
QObject::tr("IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol.\n" QObject::tr("IKEv2, combined with IPSec encryption, is a modern and reliable VPN protocol. "
"One of its distinguishing features is its ability to swiftly switch between networks and devices, " "It reconnects quickly when switching networks or devices, making it ideal for dynamic network environments. "
"making it particularly adaptive in dynamic network environments. \n" "While it provides good security and speed, it's easily recognized by DPI systems and susceptible to blocking.\n"
"While it offers a blend of security, stability, and speed, " "\nFeatures:\n"
"it's essential to note that IKEv2 can be easily detected and is susceptible to blocking.\n\n" "* Available in AmneziaVPN only on Windows\n"
"* Available in the DefaultVPN only on Windows\n" "* Low battery consumption on mobile devices\n"
"* Low power consumption, on mobile devices\n" "* Minimal configuration required\n"
"* Minimal configuration\n" "* Detectable by DPI analysis systems(easily blocked)\n"
"* Recognised by DPI analysis systems\n" "* Operates over UDP protocol(ports 500 and 4500)") },
"* Works over UDP network protocol, ports 500 and 4500.") },
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") }, { DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
{ DockerContainer::Dns, QObject::tr("DNS Service") }, { DockerContainer::Dns, QObject::tr("DNS Service") },

View file

@ -48,6 +48,9 @@ void CoreController::initModels()
m_sitesModel.reset(new SitesModel(m_settings, this)); m_sitesModel.reset(new SitesModel(m_settings, this));
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get()); 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_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this));
m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get()); 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_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get()); 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_appSplitTunnelingController.reset(new AppSplitTunnelingController(m_settings, m_appSplitTunnelingModel));
m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get()); m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get());
@ -214,6 +220,7 @@ void CoreController::initSignalHandlers()
initAutoConnectHandler(); initAutoConnectHandler();
initAmneziaDnsToggledHandler(); initAmneziaDnsToggledHandler();
initPrepareConfigHandler(); initPrepareConfigHandler();
initStrictKillSwitchHandler();
} }
void CoreController::initNotificationHandler() 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<PageController> CoreController::pageController() const QSharedPointer<PageController> CoreController::pageController() const
{ {
return m_pageController; return m_pageController;

View file

@ -8,6 +8,7 @@
#include "ui/controllers/api/apiConfigsController.h" #include "ui/controllers/api/apiConfigsController.h"
#include "ui/controllers/api/apiSettingsController.h" #include "ui/controllers/api/apiSettingsController.h"
#include "ui/controllers/appSplitTunnelingController.h" #include "ui/controllers/appSplitTunnelingController.h"
#include "ui/controllers/allowedDnsController.h"
#include "ui/controllers/connectionController.h" #include "ui/controllers/connectionController.h"
#include "ui/controllers/exportController.h" #include "ui/controllers/exportController.h"
#include "ui/controllers/focusController.h" #include "ui/controllers/focusController.h"
@ -18,6 +19,7 @@
#include "ui/controllers/sitesController.h" #include "ui/controllers/sitesController.h"
#include "ui/controllers/systemController.h" #include "ui/controllers/systemController.h"
#include "ui/models/allowed_dns_model.h"
#include "ui/models/containers_model.h" #include "ui/models/containers_model.h"
#include "ui/models/languageModel.h" #include "ui/models/languageModel.h"
#include "ui/models/protocols/cloakConfigModel.h" #include "ui/models/protocols/cloakConfigModel.h"
@ -80,6 +82,7 @@ private:
void initAutoConnectHandler(); void initAutoConnectHandler();
void initAmneziaDnsToggledHandler(); void initAmneziaDnsToggledHandler();
void initPrepareConfigHandler(); void initPrepareConfigHandler();
void initStrictKillSwitchHandler();
QQmlApplicationEngine *m_engine {}; // TODO use parent child system here? QQmlApplicationEngine *m_engine {}; // TODO use parent child system here?
std::shared_ptr<Settings> m_settings; std::shared_ptr<Settings> m_settings;
@ -102,6 +105,7 @@ private:
QScopedPointer<SitesController> m_sitesController; QScopedPointer<SitesController> m_sitesController;
QScopedPointer<SystemController> m_systemController; QScopedPointer<SystemController> m_systemController;
QScopedPointer<AppSplitTunnelingController> m_appSplitTunnelingController; QScopedPointer<AppSplitTunnelingController> m_appSplitTunnelingController;
QScopedPointer<AllowedDnsController> m_allowedDnsController;
QScopedPointer<ApiSettingsController> m_apiSettingsController; QScopedPointer<ApiSettingsController> m_apiSettingsController;
QScopedPointer<ApiConfigsController> m_apiConfigsController; QScopedPointer<ApiConfigsController> m_apiConfigsController;
@ -112,6 +116,7 @@ private:
QSharedPointer<LanguageModel> m_languageModel; QSharedPointer<LanguageModel> m_languageModel;
QSharedPointer<ProtocolsModel> m_protocolsModel; QSharedPointer<ProtocolsModel> m_protocolsModel;
QSharedPointer<SitesModel> m_sitesModel; QSharedPointer<SitesModel> m_sitesModel;
QSharedPointer<AllowedDnsModel> m_allowedDnsModel;
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel; QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
QSharedPointer<ClientManagementModel> m_clientManagementModel; QSharedPointer<ClientManagementModel> m_clientManagementModel;

View file

@ -7,6 +7,7 @@
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QNetworkReply> #include <QNetworkReply>
#include <QUrl>
#include "QBlockCipher.h" #include "QBlockCipher.h"
#include "QRsa.h" #include "QRsa.h"
@ -14,6 +15,11 @@
#include "amnezia_application.h" #include "amnezia_application.h"
#include "core/api/apiUtils.h" #include "core/api/apiUtils.h"
#include "utilities.h" #include "utilities.h"
#include "core/networkUtilities.h"
#ifdef AMNEZIA_DESKTOP
#include "core/ipcclient.h"
#endif
namespace namespace
{ {
@ -50,6 +56,17 @@ ErrorCode GatewayController::get(const QString &endpoint, QByteArray &responseBo
request.setUrl(QString(endpoint).arg(m_gatewayEndpoint)); 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; QNetworkReply *reply;
reply = amnApp->networkManager()->get(request); reply = amnApp->networkManager()->get(request);
@ -101,6 +118,17 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
request.setUrl(endpoint.arg(m_gatewayEndpoint)); 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; QSimpleCrypto::QBlockCipher blockCipher;
QByteArray key = blockCipher.generatePrivateSalt(32); QByteArray key = blockCipher.generatePrivateSalt(32);
QByteArray iv = blockCipher.generatePrivateSalt(32); QByteArray iv = blockCipher.generatePrivateSalt(32);

View file

@ -138,7 +138,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
if (overwriteMode == libssh::ScpOverwriteMode::ScpOverwriteExisting) { if (overwriteMode == libssh::ScpOverwriteMode::ScpOverwriteExisting) {
e = runScript(credentials, 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)), genVarsForScript(credentials, container)),
cbReadStd, cbReadStd); cbReadStd, cbReadStd);
@ -146,7 +146,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
return e; return e;
} else if (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting) { } else if (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting) {
e = runScript(credentials, 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)), genVarsForScript(credentials, container)),
cbReadStd, cbReadStd); cbReadStd, cbReadStd);
@ -154,7 +154,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
return e; return e;
e = runScript(credentials, 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)), genVarsForScript(credentials, container)),
cbReadStd, cbReadStd); cbReadStd, cbReadStd);
@ -177,7 +177,7 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container,
errorCode = ErrorCode::NoError; 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; QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) { auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
@ -383,6 +383,13 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c
return true; 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; return false;
} }
@ -439,15 +446,22 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden
stdOut += data + "\n"; stdOut += data + "\n";
return ErrorCode::NoError; return ErrorCode::NoError;
}; };
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
errorCode = ErrorCode error =
runScript(credentials, runScript(credentials,
replaceVars(amnezia::scriptData(SharedScriptType::build_container), genVarsForScript(credentials, container, config)), replaceVars(amnezia::scriptData(SharedScriptType::build_container), genVarsForScript(credentials, container, config)),
cbReadStdOut); cbReadStdOut, cbReadStdErr);
if (errorCode)
return errorCode; 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) ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)

View file

@ -58,6 +58,8 @@ namespace amnezia
ServerUserDirectoryNotAccessible = 208, ServerUserDirectoryNotAccessible = 208,
ServerUserNotAllowedInSudoers = 209, ServerUserNotAllowedInSudoers = 209,
ServerUserPasswordRequired = 210, ServerUserPasswordRequired = 210,
ServerDockerOnCgroupsV2 = 211,
ServerCgroupMountpoint = 212,
// Ssh connection errors // Ssh connection errors
SshRequestDeniedError = 300, SshRequestDeniedError = 300,

View file

@ -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::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::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::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 // Libssh errors
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break; case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;

View file

@ -12,6 +12,7 @@
#include <winsock.h> #include <winsock.h>
#include <QNetworkInterface> #include <QNetworkInterface>
#include "qendian.h" #include "qendian.h"
#include <QSettings>
#endif #endif
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
#include <arpa/inet.h> #include <arpa/inet.h>
@ -185,6 +186,17 @@ int NetworkUtilities::AdapterIndexTo(const QHostAddress& dst) {
return 0; 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 #ifdef Q_OS_WIN
DWORD GetAdaptersAddressesWrapper(const ULONG Family, DWORD GetAdaptersAddressesWrapper(const ULONG Family,
const ULONG Flags, const ULONG Flags,

View file

@ -16,6 +16,7 @@ public:
static QString getStringBetween(const QString &s, const QString &a, const QString &b); static QString getStringBetween(const QString &s, const QString &a, const QString &b);
static bool checkIPv4Format(const QString &ip); static bool checkIPv4Format(const QString &ip);
static bool checkIpSubnetFormat(const QString &ip); static bool checkIpSubnetFormat(const QString &ip);
static bool checkIpv6Enabled();
static QString getGatewayAndIface(); static QString getGatewayAndIface();
// Returns the Interface Index that could Route to dst // Returns the Interface Index that could Route to dst
static int AdapterIndexTo(const QHostAddress& dst); static int AdapterIndexTo(const QHostAddress& dst);
@ -29,7 +30,6 @@ public:
static QString netMaskFromIpWithSubnet(const QString ip); static QString netMaskFromIpWithSubnet(const QString ip);
static QString ipAddressFromIpWithSubnet(const QString ip); static QString ipAddressFromIpWithSubnet(const QString ip);
static QStringList summarizeRoutes(const QStringList &ips, const QString cidr); static QStringList summarizeRoutes(const QStringList &ips, const QString cidr);
}; };

View file

@ -371,6 +371,9 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
if (!parseStringList(obj, "vpnDisabledApps", config.m_vpnDisabledApps)) { if (!parseStringList(obj, "vpnDisabledApps", config.m_vpnDisabledApps)) {
return false; return false;
} }
if (!parseStringList(obj, "allowedDnsServers", config.m_allowedDnsServers)) {
return false;
}
config.m_killSwitchEnabled = QVariant(obj.value("killSwitchOption").toString()).toBool(); config.m_killSwitchEnabled = QVariant(obj.value("killSwitchOption").toString()).toBool();

View file

@ -48,6 +48,13 @@ QJsonObject InterfaceConfig::toJson() const {
} }
json.insert("excludedAddresses", jsExcludedAddresses); json.insert("excludedAddresses", jsExcludedAddresses);
QJsonArray jsAllowedDnsServers;
for (const QString& i : m_allowedDnsServers) {
jsAllowedDnsServers.append(QJsonValue(i));
}
json.insert("allowedDnsServers", jsAllowedDnsServers);
QJsonArray disabledApps; QJsonArray disabledApps;
for (const QString& i : m_vpnDisabledApps) { for (const QString& i : m_vpnDisabledApps) {
disabledApps.append(QJsonValue(i)); disabledApps.append(QJsonValue(i));

View file

@ -6,6 +6,7 @@
#define INTERFACECONFIG_H #define INTERFACECONFIG_H
#include <QList> #include <QList>
#include <QMap>
#include <QString> #include <QString>
#include "ipaddress.h" #include "ipaddress.h"
@ -37,6 +38,7 @@ class InterfaceConfig {
QList<IPAddress> m_allowedIPAddressRanges; QList<IPAddress> m_allowedIPAddressRanges;
QStringList m_excludedAddresses; QStringList m_excludedAddresses;
QStringList m_vpnDisabledApps; QStringList m_vpnDisabledApps;
QStringList m_allowedDnsServers;
bool m_killSwitchEnabled; bool m_killSwitchEnabled;
#if defined(MZ_ANDROID) || defined(MZ_IOS) #if defined(MZ_ANDROID) || defined(MZ_IOS)
QString m_installationId; QString m_installationId;

View file

@ -123,6 +123,7 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
int appSplitTunnelType = rawConfig.value(amnezia::config_key::appSplitTunnelType).toInt(); int appSplitTunnelType = rawConfig.value(amnezia::config_key::appSplitTunnelType).toInt();
QJsonArray splitTunnelApps = rawConfig.value(amnezia::config_key::splitTunnelApps).toArray(); 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(); QJsonObject wgConfig = rawConfig.value(protocolName + "_config_data").toObject();
@ -226,6 +227,8 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
json.insert("vpnDisabledApps", splitTunnelApps); json.insert("vpnDisabledApps", splitTunnelApps);
json.insert("allowedDnsServers", allowedDns);
json.insert(amnezia::config_key::killSwitchOption, rawConfig.value(amnezia::config_key::killSwitchOption)); json.insert(amnezia::config_key::killSwitchOption, rawConfig.value(amnezia::config_key::killSwitchOption));
if (protocolName == amnezia::config_key::awg) { if (protocolName == amnezia::config_key::awg) {

View file

@ -31,7 +31,9 @@ IPUtilsLinux::~IPUtilsLinux() {
} }
bool IPUtilsLinux::addInterfaceIPs(const InterfaceConfig& config) { 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) { bool IPUtilsLinux::setMTUAndUp(const InterfaceConfig& config) {

View file

@ -455,9 +455,6 @@ void LinuxFirewall::updateDNSServers(const QStringList& servers)
void LinuxFirewall::updateAllowNets(const QStringList& servers) void LinuxFirewall::updateAllowNets(const QStringList& servers)
{ {
static QStringList existingServers {};
existingServers = servers;
execute(QStringLiteral("iptables -F %1.110.allowNets").arg(kAnchorName)); execute(QStringLiteral("iptables -F %1.110.allowNets").arg(kAnchorName));
for (const QString& rule : getAllowRule(servers)) for (const QString& rule : getAllowRule(servers))
execute(QStringLiteral("iptables -A %1.110.allowNets %2").arg(kAnchorName, rule)); execute(QStringLiteral("iptables -A %1.110.allowNets %2").arg(kAnchorName, rule));

View file

@ -17,6 +17,8 @@
#include "leakdetector.h" #include "leakdetector.h"
#include "logger.h" #include "logger.h"
#include "killswitch.h"
constexpr const int WG_TUN_PROC_TIMEOUT = 5000; constexpr const int WG_TUN_PROC_TIMEOUT = 5000;
constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg"; constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg";
@ -182,7 +184,7 @@ bool WireguardUtilsLinux::deleteInterface() {
QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name")); QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name"));
// double-check + ensure our firewall is installed and enabled // double-check + ensure our firewall is installed and enabled
LinuxFirewall::uninstall(); KillSwitch::instance()->disableKillSwitch();
return true; return true;
} }

View file

@ -16,6 +16,8 @@
#include "leakdetector.h" #include "leakdetector.h"
#include "logger.h" #include "logger.h"
#include "killswitch.h"
constexpr const int WG_TUN_PROC_TIMEOUT = 5000; constexpr const int WG_TUN_PROC_TIMEOUT = 5000;
constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg"; constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg";
@ -180,7 +182,7 @@ bool WireguardUtilsMacos::deleteInterface() {
QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name")); QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name"));
// double-check + ensure our firewall is installed and enabled // double-check + ensure our firewall is installed and enabled
MacOSFirewall::uninstall(); KillSwitch::instance()->disableKillSwitch();
return true; return true;
} }

View file

@ -29,6 +29,8 @@
#include "logger.h" #include "logger.h"
#include "platforms/windows/windowsutils.h" #include "platforms/windows/windowsutils.h"
#include "killswitch.h"
#define IPV6_ADDRESS_SIZE 16 #define IPV6_ADDRESS_SIZE 16
// ID for the Firewall Sublayer // 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, 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(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, FW_OK(allowTrafficForAppOnAll(getCurrentPath(), MAX_WEIGHT,
"Allow all for DefaultVPN.exe")); "Allow all for DefaultVPN.exe"));
FW_OK(blockTrafficOnPort(53, MED_WEIGHT, "Block all DNS")); FW_OK(blockTrafficOnPort(53, MED_WEIGHT, "Block all DNS"));
FW_OK( FW_OK(allowLoopbackTraffic(MED_WEIGHT,
allowLoopbackTraffic(MED_WEIGHT, "Allow Loopback traffic on device %1")); "Allow Loopback traffic on device %1"));
logger.debug() << "Killswitch on! Rules:" << m_activeRules.length(); logger.debug() << "Killswitch on! Rules:" << m_activeRules.length();
return true; return true;
@ -226,6 +241,37 @@ bool WindowsFirewall::enableLanBypass(const QList<IPAddress>& ranges) {
return true; 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), HIGH_WEIGHT, "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) { bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
// Start the firewall transaction // Start the firewall transaction
auto result = FwpmTransactionBegin(m_sessionHandle, NULL); 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()) { if (!config.m_excludedAddresses.empty()) {
for (const QString& i : config.m_excludedAddresses) { for (const QString& i : config.m_excludedAddresses) {
logger.debug() << "excludedAddresses range: " << i; logger.debug() << "excludedAddresses range: " << i;
if (!allowTrafficTo(i, HIGH_WEIGHT, if (!allowTrafficTo(i, HIGH_WEIGHT,
"Allow Ecxlude route", config.m_serverPublicKey)) { "Allow Ecxlude route", config.m_serverPublicKey)) {
return false; return false;
} }
} }
@ -313,37 +367,41 @@ bool WindowsFirewall::disablePeerTraffic(const QString& pubkey) {
} }
bool WindowsFirewall::disableKillSwitch() { bool WindowsFirewall::disableKillSwitch() {
auto result = FwpmTransactionBegin(m_sessionHandle, NULL); return KillSwitch::instance()->disableKillSwitch();
auto cleanup = qScopeGuard([&] { }
bool WindowsFirewall::allowAllTraffic() {
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
auto cleanup = qScopeGuard([&] {
if (result != ERROR_SUCCESS) {
FwpmTransactionAbort0(m_sessionHandle);
}
});
if (result != ERROR_SUCCESS) { 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()) { for (const auto& filterID : m_peerRules.values()) {
FwpmFilterDeleteById0(m_sessionHandle, filterID); FwpmFilterDeleteById0(m_sessionHandle, filterID);
} }
for (const auto& filterID : qAsConst(m_activeRules)) { for (const auto& filterID : qAsConst(m_activeRules)) {
FwpmFilterDeleteById0(m_sessionHandle, filterID); FwpmFilterDeleteById0(m_sessionHandle, filterID);
} }
// Commit! // Commit!
result = FwpmTransactionCommit0(m_sessionHandle); result = FwpmTransactionCommit0(m_sessionHandle);
if (result != ERROR_SUCCESS) { if (result != ERROR_SUCCESS) {
logger.error() << "FwpmTransactionCommit0 failed. Return value:.\n" logger.error() << "FwpmTransactionCommit0 failed. Return value:.\n"
<< result; << result;
return false; return false;
} }
m_peerRules.clear(); m_peerRules.clear();
m_activeRules.clear(); m_activeRules.clear();
logger.debug() << "Firewall Disabled!"; logger.debug() << "Firewall Disabled!";
return true; return true;
} }
bool WindowsFirewall::allowTrafficForAppOnAll(const QString& exePath, bool WindowsFirewall::allowTrafficForAppOnAll(const QString& exePath,

View file

@ -43,6 +43,8 @@ class WindowsFirewall final : public QObject {
bool enablePeerTraffic(const InterfaceConfig& config); bool enablePeerTraffic(const InterfaceConfig& config);
bool disablePeerTraffic(const QString& pubkey); bool disablePeerTraffic(const QString& pubkey);
bool disableKillSwitch(); bool disableKillSwitch();
bool allowAllTraffic();
bool allowTrafficRange(const QStringList& ranges);
private: private:
static bool initSublayer(); static bool initSublayer();

View file

@ -14,8 +14,6 @@
#include "leakdetector.h" #include "leakdetector.h"
#include "logger.h" #include "logger.h"
#include "platforms/windows/windowscommons.h"
#include "windowsdaemon.h"
#include "windowsfirewall.h" #include "windowsfirewall.h"
#pragma comment(lib, "iphlpapi.lib") #pragma comment(lib, "iphlpapi.lib")
@ -269,6 +267,13 @@ bool WireguardUtilsWindows::updateRoutePrefix(const IPAddress& prefix) {
if (result == ERROR_OBJECT_ALREADY_EXISTS) { if (result == ERROR_OBJECT_ALREADY_EXISTS) {
return true; 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) { if (result != NO_ERROR) {
logger.error() << "Failed to create route to" logger.error() << "Failed to create route to"
<< prefix.toString() << prefix.toString()

View file

@ -171,6 +171,11 @@ ErrorCode OpenVpnProtocol::start()
return lastError(); return lastError();
} }
#ifdef AMNEZIA_DESKTOP
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList(NetworkUtilities::getIPAddress(
m_configData.value(amnezia::config_key::hostName).toString())));
#endif
// Detect default gateway // Detect default gateway
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
QProcess p; QProcess p;

View file

@ -95,6 +95,8 @@ namespace amnezia
constexpr char splitTunnelApps[] = "splitTunnelApps"; constexpr char splitTunnelApps[] = "splitTunnelApps";
constexpr char appSplitTunnelType[] = "appSplitTunnelType"; constexpr char appSplitTunnelType[] = "appSplitTunnelType";
constexpr char allowedDnsServers[] = "allowedDnsServers";
constexpr char killSwitchOption[] = "killSwitchOption"; constexpr char killSwitchOption[] = "killSwitchOption";
constexpr char crc[] = "crc"; constexpr char crc[] = "crc";

View file

@ -131,6 +131,7 @@
<file>ui/qml/Components/SettingsContainersListView.qml</file> <file>ui/qml/Components/SettingsContainersListView.qml</file>
<file>ui/qml/Components/ShareConnectionDrawer.qml</file> <file>ui/qml/Components/ShareConnectionDrawer.qml</file>
<file>ui/qml/Components/TransportProtoSelector.qml</file> <file>ui/qml/Components/TransportProtoSelector.qml</file>
<file>ui/qml/Components/AddSitePanel.qml</file>
<file>ui/qml/Config/GlobalConfig.qml</file> <file>ui/qml/Config/GlobalConfig.qml</file>
<file>ui/qml/Config/qmldir</file> <file>ui/qml/Config/qmldir</file>
<file>ui/qml/Controls2/BackButtonType.qml</file> <file>ui/qml/Controls2/BackButtonType.qml</file>
@ -145,7 +146,9 @@
<file>ui/qml/Controls2/DropDownType.qml</file> <file>ui/qml/Controls2/DropDownType.qml</file>
<file>ui/qml/Controls2/FlickableType.qml</file> <file>ui/qml/Controls2/FlickableType.qml</file>
<file>ui/qml/Controls2/Header2Type.qml</file> <file>ui/qml/Controls2/Header2Type.qml</file>
<file>ui/qml/Controls2/HeaderType.qml</file> <file>ui/qml/Controls2/BaseHeaderType.qml</file>
<file>ui/qml/Controls2/HeaderTypeWithButton.qml</file>
<file>ui/qml/Controls2/HeaderTypeWithSwitcher.qml</file>
<file>ui/qml/Controls2/HorizontalRadioButton.qml</file> <file>ui/qml/Controls2/HorizontalRadioButton.qml</file>
<file>ui/qml/Controls2/ImageButtonType.qml</file> <file>ui/qml/Controls2/ImageButtonType.qml</file>
<file>ui/qml/Controls2/LabelWithButtonType.qml</file> <file>ui/qml/Controls2/LabelWithButtonType.qml</file>
@ -201,6 +204,8 @@
<file>ui/qml/Pages2/PageSettingsBackup.qml</file> <file>ui/qml/Pages2/PageSettingsBackup.qml</file>
<file>ui/qml/Pages2/PageSettingsConnection.qml</file> <file>ui/qml/Pages2/PageSettingsConnection.qml</file>
<file>ui/qml/Pages2/PageSettingsDns.qml</file> <file>ui/qml/Pages2/PageSettingsDns.qml</file>
<file>ui/qml/Pages2/PageSettingsKillSwitch.qml</file>
<file>ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml</file>
<file>ui/qml/Pages2/PageSettingsLogging.qml</file> <file>ui/qml/Pages2/PageSettingsLogging.qml</file>
<file>ui/qml/Pages2/PageSettingsServerData.qml</file> <file>ui/qml/Pages2/PageSettingsServerData.qml</file>
<file>ui/qml/Pages2/PageSettingsServerInfo.qml</file> <file>ui/qml/Pages2/PageSettingsServerInfo.qml</file>

View file

@ -1,7 +1,7 @@
#include "secure_qsettings.h" #include "secure_qsettings.h"
#include "QAead.h" #include "../client/3rd/QSimpleCrypto/src/include/QAead.h"
#include "QBlockCipher.h" #include "../client/3rd/QSimpleCrypto/src/include/QBlockCipher.h"
#include "utilities.h" #include "utilities.h"
#include <QDataStream> #include <QDataStream>
#include <QDebug> #include <QDebug>

View file

@ -6,7 +6,7 @@
#include <QObject> #include <QObject>
#include <QSettings> #include <QSettings>
#include "keychain.h" #include "../client/3rd/qtkeychain/qtkeychain/keychain.h"
class SecureQSettings : public QObject class SecureQSettings : public QObject
{ {

View file

@ -443,6 +443,16 @@ void Settings::setKillSwitchEnabled(bool enabled)
setValue("Conf/killSwitchEnabled", 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) QString Settings::getInstallationUuid(const bool needCreate)
{ {
auto uuid = value("Conf/installationUuid", "").toString(); auto uuid = value("Conf/installationUuid", "").toString();
@ -548,3 +558,13 @@ void Settings::disableHomeAdLabel()
{ {
setValue("Conf/homeAdLabelVisible", false); setValue("Conf/homeAdLabelVisible", false);
} }
QStringList Settings::allowedDnsServers() const
{
return value("Conf/allowedDnsServers").toStringList();
}
void Settings::setAllowedDnsServers(const QStringList &servers)
{
setValue("Conf/allowedDnsServers", servers);
}

View file

@ -213,6 +213,10 @@ public:
bool isKillSwitchEnabled() const; bool isKillSwitchEnabled() const;
void setKillSwitchEnabled(bool enabled); void setKillSwitchEnabled(bool enabled);
bool isStrictKillSwitchEnabled() const;
void setStrictKillSwitchEnabled(bool enabled);
QString getInstallationUuid(const bool needCreate); QString getInstallationUuid(const bool needCreate);
void resetGatewayEndpoint(); void resetGatewayEndpoint();
@ -225,6 +229,9 @@ public:
bool isHomeAdLabelVisible(); bool isHomeAdLabelVisible();
void disableHomeAdLabel(); void disableHomeAdLabel();
QStringList allowedDnsServers() const;
void setAllowedDnsServers(const QStringList &servers);
signals: signals:
void saveLogsChanged(bool enabled); void saveLogsChanged(bool enabled);
void screenshotsEnabledChanged(bool enabled); void screenshotsEnabledChanged(bool enabled);

View file

@ -100,8 +100,8 @@
<message> <message>
<location filename="../ui/models/api/apiServicesModel.cpp" line="72"/> <location filename="../ui/models/api/apiServicesModel.cpp" line="72"/>
<location filename="../ui/models/api/apiServicesModel.cpp" line="85"/> <location filename="../ui/models/api/apiServicesModel.cpp" line="85"/>
<source>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.</source> <source>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.</source>
<translation>AmneziaFree предоставляет бесплатный неограниченный доступ к базовому набору сайтов и приложений, таким как Facebook, Instagram, Twitter (X), Discord, Telegram и другим. YouTube не включен в бесплатный тариф.</translation> <translation>Amnezia Free позволяет бесплатно и без ограничений пользоваться базовым набором сайтов и приложений, включая Facebook, Instagram, Twitter (X), Discord, Telegram и другие. YouTube не входит в бесплатный тариф.</translation>
</message> </message>
<message> <message>
<source>Amnezia Premium is VPN for comfortable work, downloading large files and watching videos in 8K resolution. Works for any sites with no restrictions.</source> <source>Amnezia Premium is VPN for comfortable work, downloading large files and watching videos in 8K resolution. Works for any sites with no restrictions.</source>
@ -127,6 +127,11 @@
<source>%1 days</source> <source>%1 days</source>
<translation>%1 дней</translation> <translation>%1 дней</translation>
</message> </message>
<message>
<location filename="../ui/models/api/apiServicesModel.cpp" line="111"/>
<source></source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<location filename="../ui/models/api/apiServicesModel.cpp" line="113"/> <location filename="../ui/models/api/apiServicesModel.cpp" line="113"/>
<source>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, &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;more details on the website.&lt;/a&gt;</source> <source>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, &lt;a href=&quot;%1/free&quot; style=&quot;color: #FBB26A;&quot;&gt;more details on the website.&lt;/a&gt;</source>
@ -1752,8 +1757,12 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="80"/> <location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="80"/>
<source>This will unlink the device from your subscription. You can reconnect it anytime by pressing&#xa0;&quot;Reload API config&quot; in subscription settings on device.</source>
<translation>Это отключит устройство от вашей подписки. Вы можете повторно подключить его в любое время, нажав &quot;Перезагрузить конфигурацию API&quot; в настройках подписки на устройстве.</translation>
</message>
<message>
<source>This will unlink the device from your subscription. You can reconnect it anytime by pressing&#xa0;Connect.</source> <source>This will unlink the device from your subscription. You can reconnect it anytime by pressing&#xa0;Connect.</source>
<translation>Это устройство будет отвязано от вашей подписки. Вы можете подключить его снова в любой момент, нажав кнопку &quot;Подключиться&quot;.</translation> <translation type="vanished">Это устройство будет отвязано от вашей подписки. Вы можете подключить его снова в любой момент, нажав кнопку &quot;Подключиться&quot;.</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="81"/> <location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="81"/>
@ -2105,8 +2114,12 @@ Already installed containers were found on the server. All installed containers
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="336"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="336"/>
<source>This will unlink the device from your subscription. You can reconnect it anytime by pressing&#xa0;&quot;Reload API config&quot; in subscription settings on device.</source>
<translation>Это отключит устройство от вашей подписки. Вы можете повторно подключить его в любое время, нажав &quot;Перезагрузить конфигурацию API&quot; в настройках подписки на устройстве.</translation>
</message>
<message>
<source>This will unlink the device from your subscription. You can reconnect it anytime by pressing&#xa0;Connect.</source> <source>This will unlink the device from your subscription. You can reconnect it anytime by pressing&#xa0;Connect.</source>
<translation>Это устройство будет отвязано от вашей подписки. Вы можете подключить его снова в любой момент, нажав кнопку Подключиться.</translation> <translation type="vanished">Это устройство будет отвязано от вашей подписки. Вы можете подключить его снова в любой момент, нажав кнопку Подключиться.</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="342"/> <location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="342"/>
@ -2815,11 +2828,6 @@ Already installed containers were found on the server. All installed containers
<source>No new installed containers found</source> <source>No new installed containers found</source>
<translation>Новые установленные протоколы и сервисы не обнаружены</translation> <translation>Новые установленные протоколы и сервисы не обнаружены</translation>
</message> </message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="92"/>
<source></source>
<translation></translation>
</message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="115"/> <location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="115"/>
<location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="154"/> <location filename="../ui/qml/Pages2/PageSettingsServerData.qml" line="154"/>
@ -3023,11 +3031,6 @@ Already installed containers were found on the server. All installed containers
<source>Clear %1 profile?</source> <source>Clear %1 profile?</source>
<translation>Очистить профиль %1?</translation> <translation>Очистить профиль %1?</translation>
</message> </message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="137"/>
<source></source>
<translation></translation>
</message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="98"/> <location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="98"/>
<source> connection settings</source> <source> connection settings</source>
@ -3286,7 +3289,7 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<translation type="vanished">Что у вас есть?</translation> <translation type="vanished">Что у вас есть?</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="298"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="328"/>
<source>File with connection settings</source> <source>File with connection settings</source>
<translation>Файл с настройками подключения</translation> <translation>Файл с настройками подключения</translation>
</message> </message>
@ -3295,112 +3298,135 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
<translation type="vanished">Файл с настройками подключения или резервной копией</translation> <translation type="vanished">Файл с настройками подключения или резервной копией</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="56"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="58"/>
<source>Connection</source> <source>Connection</source>
<translation>Соединение</translation> <translation>Соединение</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="83"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="85"/>
<source>Settings</source> <source>Settings</source>
<translation>Настройки</translation> <translation>Настройки</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="93"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="95"/>
<source>Enable logs</source> <source>Enable logs</source>
<translation>Включить запись логов</translation> <translation>Включить запись логов</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="109"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="109"/>
<source>Export client logs</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="119"/>
<source>Save</source>
<translation type="unfinished">Сохранить</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="120"/>
<source>Logs files (*.log)</source>
<translation type="unfinished">Файлы логов (*.log)</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="129"/>
<source>Logs file saved</source>
<translation type="unfinished">Файл с логами сохранен</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="139"/>
<source>Support tag</source> <source>Support tag</source>
<translation>Support tag</translation> <translation>Support tag</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="120"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="150"/>
<source>Copied</source> <source>Copied</source>
<translation>Скопировано</translation> <translation>Скопировано</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="139"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="169"/>
<source>Insert the key, add a configuration file or scan the QR-code</source> <source>Insert the key, add a configuration file or scan the QR-code</source>
<translation>Вставьте ключ, добавьте файл конфигурации или отсканируйте QR-код</translation> <translation>Вставьте ключ, добавьте файл конфигурации или отсканируйте QR-код</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="149"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="179"/>
<source>Insert key</source> <source>Insert key</source>
<translation>Вставьте ключ</translation> <translation>Вставьте ключ</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="150"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="180"/>
<source>Insert</source> <source>Insert</source>
<translation>Вставить</translation> <translation>Вставить</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="168"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="198"/>
<source>Continue</source> <source>Continue</source>
<translation>Продолжить</translation> <translation>Продолжить</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="185"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="215"/>
<source>Other connection options</source> <source>Other connection options</source>
<translation>Другие варианты подключения</translation> <translation>Другие варианты подключения</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="228"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="258"/>
<source>Site Amnezia</source> <source>Site Amnezia</source>
<translation>Сайт Amnezia</translation> <translation>Сайт Amnezia</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="251"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="281"/>
<source>VPN by Amnezia</source> <source>VPN by Amnezia</source>
<translation>VPN от Amnezia</translation> <translation>VPN от Amnezia</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="252"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="282"/>
<source>Connect to classic paid and free VPN services from Amnezia</source> <source>Connect to classic paid and free VPN services from Amnezia</source>
<translation>Подключайтесь к классическим платным и бесплатным VPN-сервисам от Amnezia</translation> <translation>Подключайтесь к классическим платным и бесплатным VPN-сервисам от Amnezia</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="268"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="298"/>
<source>Self-hosted VPN</source> <source>Self-hosted VPN</source>
<translation>Self-hosted VPN</translation> <translation>Self-hosted VPN</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="269"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="299"/>
<source>Configure Amnezia VPN on your own server</source> <source>Configure Amnezia VPN on your own server</source>
<translation>Настроить VPN на собственном сервере</translation> <translation>Настроить VPN на собственном сервере</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="280"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="310"/>
<source>Restore from backup</source> <source>Restore from backup</source>
<translation>Восстановить из резервной копии</translation> <translation>Восстановить из резервной копии</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="279"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="311"/>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="329"/>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="348"/>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="363"/>
<source></source> <source></source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="285"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="315"/>
<source>Open backup file</source> <source>Open backup file</source>
<translation>Открыть резервную копию</translation> <translation>Открыть резервную копию</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="286"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="316"/>
<source>Backup files (*.backup)</source> <source>Backup files (*.backup)</source>
<translation>Файлы резервных копий (*.backup)</translation> <translation>Файлы резервных копий (*.backup)</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="305"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="335"/>
<source>Open config file</source> <source>Open config file</source>
<translation>Открыть файл с конфигурацией</translation> <translation>Открыть файл с конфигурацией</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="317"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="347"/>
<source>QR code</source> <source>QR code</source>
<translation>QR-код</translation> <translation>QR-код</translation>
</message> </message>
<message> <message>
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="332"/> <location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="362"/>
<source>I have nothing</source> <source>I have nothing</source>
<translation>У меня ничего нет</translation> <translation>У меня ничего нет</translation>
</message> </message>
@ -4674,94 +4700,56 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<translation>Создайте на сервере файловое хранилище для безопасного хранения и передачи файлов.</translation> <translation>Создайте на сервере файловое хранилище для безопасного хранения и передачи файлов.</translation>
</message> </message>
<message> <message>
<source>This is a combination of the OpenVPN protocol and the Cloak plugin designed specifically for protecting against blocking. <location filename="../containers/containers_defs.cpp" line="186"/>
<source>AmneziaWG is a modern VPN protocol based on WireGuard, combining simplified architecture with high performance across all devices. It addresses WireGuard&apos;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</source>
<translation>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. Особенности:
* Доступен во всех версиях AmneziaVPN
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.
</source>
<translation type="vanished">Это связка протокола OpenVPN и плагина Cloak, разработанная специально для защиты от блокировки.
OpenVPN обеспечивает безопасное VPN-соединение, шифруя весь интернет-трафик между клиентом и сервером.
Cloak защищает OpenVPN от обнаружения и блокировки.
Cloak изменяет метаданные пакетов таким образом, что полностью маскирует VPN-трафик под обычный веб-трафик, а также защищает VPN от обнаружения с помощью активного зондирования. Это делает его очень защищенным от обнаружения.
Сразу после получения первого пакета данных Cloak устанавливает подлинность входящего соединения. Если аутентификация не проходит, плагин маскирует сервер под фальшивый веб-сайт, и ваш VPN становится невидимым для систем анализа трафика.
Если в вашем регионе наблюдается жесткая интернет-цензура, мы советуем вам уже при первом подключении использовать только OpenVPN over Cloak.
* Доступен в AmneziaVPN на всех платформах
* Высокое энергопотребление на мобильных устройствах
* Гибкие настройки
* Не распознается системами DPI-анализа
* Работает по сетевому протоколу TCP, использует порт 443</translation>
</message>
<message>
<source>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.</source>
<translation type="vanished">Относительно новый и популярный VPN-протокол с простой архитектурой.
WireGuard обеспечивает стабильное VPN-соединение и высокую производительность на всех устройствах. Он использует строго заданные настройки шифрования. WireGuard по сравнению с OpenVPN имеет меньшую задержку и лучшую пропускную способность при передаче данных.
WireGuard очень уязвим для блокировки из-за характерных сигнатур пакетов. В отличие от некоторых других VPN-протоколов, использующих методы обфускации, последовательные сигнатуры пакетов WireGuard легче идентифицируются и, следовательно, могут блокироваться современными Deep Packet Inspection (DPI) системами и другими инструментами для сетевого мониторинга.
* Доступен в AmneziaVPN на всех платформах
* Низкое энергопотребление на мобильных устройствах * Низкое энергопотребление на мобильных устройствах
* Минимальная конфигурация * Минимум настроек
* Легко распознается системами DPI-анализа, поддается блокировке * Незаметен для систем анализа трафика (DPI)
* Работает по сетевому протоколу UDP</translation> * Работает по протоколу UDP
</translation>
</message> </message>
<message> <message>
<source>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. <location filename="../containers/containers_defs.cpp" line="198"/>
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. <source>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.
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 XTLS-Vision, REALITY incorporates an advanced built-in &quot;friend-or-foe&quot; detection mechanism, effectively protecting against DPI and other traffic analysis methods.
Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY&apos;s innovative &quot;friend or foe&quot; 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.</source>
<translation type="vanished">Протокол REALITY, новаторская разработка создателей XRay, специально спроектирован для противодействия самой строгой цензуре с помощью нового способа обхода блокировок.
Он уникальным образом идентифицирует цензоров на этапе TLS-рукопожатия, беспрепятственно работая в качестве прокси для реальных клиентов и перенаправляя цензоров на реальные сайты, такие как google.com, тем самым предъявляя подлинный TLS-сертификат и данные.
REALITY отличается от аналогичных технологий благодаря способности без специальной настройки маскировать веб-трафик так, как будто он поступает со случайных легитимных сайтов.
В отличие от более старых протоколов, таких как VMess, VLESS и транспорт XTLS-Vision, технология распознавания &quot;друг или враг&quot; на этапе TLS-рукопожатия повышает безопасность и обходит обнаружение сложными системами DPI-анализа, которые используют методы активного зондирования. Это делает REALITY эффективным решением для поддержания свободы интернета в регионах с жесткой цензурой.</translation>
</message>
<message>
<source>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&apos;s essential to note that IKEv2 can be easily detected and is susceptible to blocking.
* Available in the AmneziaVPN only on Windows Features:
* Low power consumption, on mobile devices * Resistant to active probing and DPI detection
* Minimal configuration * No special configuration required to disguise traffic
* Recognised by DPI analysis systems * Highly effective in heavily censored regions
* Works over UDP network protocol, ports 500 and 4500.</source> * Minimal battery consumption on devices
<translation type="vanished">IKEv2 в сочетании с уровнем шифрования IPSec представляет собой современный и стабильный VPN-протокол. * Operates over TCP protocol</source>
Он может быстро переключаться между сетями и устройствами, что делает его особенно адаптивным в динамичных сетевых средах. <translation>REALITY это инновационный протокол от разработчиков XRay, специально созданный для эффективного противодействия жесткой интернет-цензуре.
Несмотря на сочетание безопасности, стабильности и скорости, необходимо отметить, что IKEv2 легко обнаруживается и подвержен блокировке.
* Доступен в AmneziaVPN только для Windows REALITY распознаёт системы блокировки во время TLS-рукопожатия и незаметно перенаправляет подозрительные запросы на реальные сайты, такие как google.com, предъявляя подлинные TLS-сертификаты. Это позволяет маскировать VPN-трафик под обычный веб-трафик без дополнительных настроек.
* Низкое энергопотребление на мобильных устройствах
* Минимальная конфигурация В отличие от протоколов старого поколения (VMess, VLESS и XTLS-Vision), REALITY использует встроенную технологию распознавания «свой-чужой», надёжно защищая от DPI и других методов сетевого анализа.
* Распознается системами DPI-анализа
* Работает по сетевому протоколу UDP, использует порты 500 и 4500</translation> Особенности:
* Устойчив к активному зондированию и DPI-системам
* Не требует специальной настройки для маскировки трафика
* Эффективен в регионах с жесткой цензурой
* Минимальное энергопотребление на устройствах
* Работает по протоколу TCP
</translation>
</message> </message>
<message> <message>
<location filename="../containers/containers_defs.cpp" line="237"/> <location filename="../containers/containers_defs.cpp" line="222"/>
<source>DNS Service</source> <source>DNS Service</source>
<translation>Сервис DNS</translation> <translation>Сервис DNS</translation>
</message> </message>
@ -4772,7 +4760,7 @@ While it offers a blend of security, stability, and speed, it&apos;s essential t
</message> </message>
<message> <message>
<location filename="../containers/containers_defs.cpp" line="101"/> <location filename="../containers/containers_defs.cpp" line="101"/>
<location filename="../containers/containers_defs.cpp" line="236"/> <location filename="../containers/containers_defs.cpp" line="221"/>
<source>Website in Tor network</source> <source>Website in Tor network</source>
<translation>Веб-сайт в сети Tor</translation> <translation>Веб-сайт в сети Tor</translation>
</message> </message>
@ -4811,187 +4799,141 @@ While it offers a blend of security, stability, and speed, it&apos;s essential t
<source>XRay with REALITY masks VPN traffic as web traffic and protects against active probing. It is highly resistant to detection and offers high speed.</source> <source>XRay with REALITY masks VPN traffic as web traffic and protects against active probing. It is highly resistant to detection and offers high speed.</source>
<translation>XRay с REALITY маскирует VPN-трафик под веб-трафик. Обладает высокой устойчивостью к обнаружению и обеспечивает высокую скорость соединения.</translation> <translation>XRay с REALITY маскирует VPN-трафик под веб-трафик. Обладает высокой устойчивостью к обнаружению и обеспечивает высокую скорость соединения.</translation>
</message> </message>
<message>
<location filename="../containers/containers_defs.cpp" line="136"/>
<source></source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<location filename="../containers/containers_defs.cpp" line="143"/> <location filename="../containers/containers_defs.cpp" line="143"/>
<source>OpenVPN stands as one of the most popular and time-tested VPN protocols available. <source>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.
It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN&apos;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 DefaultVPN across all platforms Features:
* Normal power consumption on mobile devices * Available on all AmneziaVPN platforms
* Flexible customisation to suit user needs to work with different operating systems and devices * Normal battery consumption on mobile devices
* Recognised by DPI systems and therefore susceptible to blocking * Flexible customization for various devices and OS
* Can operate over both TCP and UDP network protocols.</source> * Operates over both TCP and UDP protocols</source>
<translation type="unfinished"></translation> <translation>OpenVPN один из самых популярных и надежных VPN-протоколов. Он использует шифрование SSL/TLS, совместим со множеством устройств и ОС, а благодаря открытому коду постоянно совершенствуется сообществом. Имеет хороший баланс скорости и безопасности, но легко распознаётся системами DPI, что делает его уязвимым к блокировкам.
Особенности:
* Доступен во всех приложениях AmneziaVPN
* Нормальное энергопотребление на мобильных устройствах
* Гибкие настройки под разные устройства и ОС
* Работает по TCP и UDP</translation>
</message> </message>
<message> <message>
<location filename="../containers/containers_defs.cpp" line="159"/> <location filename="../containers/containers_defs.cpp" line="154"/>
<source>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&apos;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&apos;s recommended to use AmneziaWG protocol. <source>Shadowsocks is based on the SOCKS5 protocol and encrypts connections using AEAD cipher. Although designed to be discreet, it doesn&apos;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.
* Available in the DefaultVPN only on desktop platforms Features:
* Configurable encryption protocol * Available in AmneziaVPN only on desktop platforms
* Customizable encryption protocol
* Detectable by some DPI systems * Detectable by some DPI systems
* Works over TCP network protocol.</source> * Operates over TCP protocol
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="168"/>
<source>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 DefaultVPN across all platforms
* High power consumption on mobile devices
* Flexible settings
* Not recognised by detection systems
* Works over TCP network protocol, 443 port.
</source> </source>
<translation type="unfinished"></translation> <translation>Shadowsocks основан на протоколе SOCKS5 и шифрует соединение алгоритмом AEAD. Он разработан так, чтобы быть малозаметным, однако не идентичен HTTPS, поэтому может распознаваться некоторыми системами DPI. В связи с ограниченной поддержкой в Amnezia, рекомендуем использовать протокол AmneziaWG.
Особенности:
* Доступен только на ПК в AmneziaVPN
* Настраиваемое шифрование
* Может обнаруживаться некоторыми DPI-системами
* Работает по протоколу TCP</translation>
</message> </message>
<message> <message>
<location filename="../containers/containers_defs.cpp" line="185"/> <location filename="../containers/containers_defs.cpp" line="163"/>
<source>A relatively new popular VPN protocol with a simplified architecture. <source>This combination includes the OpenVPN protocol and the Cloak plugin, specifically designed to protect against blocking.
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 DefaultVPN across all platforms OpenVPN securely encrypts all internet traffic between your device and the server.
* Low power consumption
* Minimum number of settings
* Easily recognised by DPI analysis systems, susceptible to blocking
* Works over UDP network protocol.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="198"/>
<source>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 DefaultVPN across all platforms 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.
* Low power consumption
* Minimum number of settings
* Not recognised by traffic analysis systems
* Works over UDP network protocol.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="225"/>
<source>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&apos;s essential to note that IKEv2 can be easily detected and is susceptible to blocking.
* Available in the DefaultVPN only on Windows In regions with heavy internet censorship, we strongly recommend using OpenVPN with Cloak from your first connection.
* Low power consumption, on mobile devices
* Minimal configuration
* Recognised by DPI analysis systems
* Works over UDP network protocol, ports 500 and 4500.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>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&apos;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 Features:
* Normal power consumption on mobile devices * Available on all AmneziaVPN platforms
* 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.</source>
<translation type="vanished">OpenVPN является одним из самых популярных и проверенных временем VPN-протоколов. Он использует собственный протокол безопасности, и криптографические протоколы SSL/TLS для шифрования и обмена ключами. Более того, поддержка множества методов аутентификации делает OpenVPN универсальным, адаптируемым и подходящим для широкого спектра устройств и операционных систем. Благодаря своему открытому коду, OpenVPN подвергается тщательной проверке со стороны мирового сообщества, что постоянно укрепляет его безопасность. Имея отличный баланс между производительностью, безопасностью и совместимостью OpenVPN остается лучшим выбором для людей и компаний, заботящихся о конфиденциальности, однако OpenVPN легко распознается современными системами анализа трафика.
Доступен в AmneziaVPN на всех платформах
Нормальное энергопотребление на мобильных устройствах
Гибкая настройка полезная при работе с различными операционными системами и устройствами
Распознается системами DPI и, следовательно, уязвим к блокировкам
Может работать как по TCP, так и по UDP протоколу.</translation>
</message>
<message>
<source>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 * High power consumption on mobile devices
* Flexible settings * Flexible configuration options
* Not recognised by detection systems * Undetectable by DPI systems
* Works over TCP network protocol, 443 port. * Operates over TCP protocol on port 443</source>
</source> <translation>Эта комбинация состоит из протокола OpenVPN и плагина Cloak, специально разработанных для защиты от блокировок.
<translation type="vanished">Это связка протокола OpenVPN и плагина Cloak, созданная специально для защиты от обнаружения.
OpenVPN обеспечивает безопасное VPN-соединение, шифруя весь интернет-трафик между клиентом и сервером. OpenVPN надёжно шифрует весь интернет-трафик между вами и сервером.
Плагин Cloak защищает OpenVPN от обнаружения. Плагин Cloak дополнительно защищает соединение от распознавания системами DPI. Он изменяет метаданные трафика, маскируя VPN-подключение под обычный веб-трафик, и предотвращает обнаружение с помощью активного зондирования. Если попытка подключения не прошла аутентификацию, Cloak выдаёт поддельный веб-сайт, делая VPN невидимым для анализирующих систем.
Cloak может изменять метаданные пакета, чтобы полностью замаскировать VPN-трафик под обычный веб-трафик, а также защищает VPN от обнаружения с помощью метода Active Probing. Это делает его очень устойчивым к обнаружению. Если в вашем регионе сильная интернет-цензура, мы рекомендуем сразу использовать OpenVPN с плагином Cloak.
Сразу после получения первого пакета данных Cloak аутентифицирует входящее соединение, если аутентификация не удалась, плагин маскирует сервер под настоящий веб-сайт, и ваш VPN становится невидимым для систем анализа. Имеет низкую скорость работы в сравнении с другими похожими протоколами. Особенности:
* Доступен на всех платформах AmneziaVPN
* Доступно в AmneziaVPN на всех платформах.
* Высокое энергопотребление на мобильных устройствах * Высокое энергопотребление на мобильных устройствах
* Гибкие настройки * Гибкие настройки
* Не распознается системами обнаружения. * Незаметен для систем DPI-анализа
* Работает по сетевому протоколу TCP, порт 443. * Использует протокол TCP на порту 443</translation>
</translation>
</message> </message>
<message> <message>
<source>A relatively new popular VPN protocol with a simplified architecture. <location filename="../containers/containers_defs.cpp" line="176"/>
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. <source>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.
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 Features:
* Low power consumption * Available on all AmneziaVPN platforms
* Minimum number of settings * Low power consumption on mobile devices
* Easily recognised by DPI analysis systems, susceptible to blocking * Minimal configuration required
* Works over UDP network protocol.</source> * Easily detected by DPI systems (susceptible to blocking)
<translation type="vanished">Популярный VPN-протокол с упрощенной архитектурой. * Operates over UDP protocol</source>
WireGuard обеспечивает стабильное VPN-соединение и высокую производительность на всех устройствах. Он использует закодированные настройки шифрования. WireGuard по сравнению с OpenVPN имеет меньшую задержку и лучшую пропускную способность передачи данных. <translation>WireGuard современный и простой VPN-протокол, который обеспечивает стабильное соединение и высокую скорость передачи данных на любых устройствах. Он использует фиксированные настройки шифрования, имеет меньшую задержку и выше пропускную способность по сравнению с OpenVPN.
WireGuard очень чувствителен к обнаружению и блокировке из-за различных сигнатур пакетов. В отличие от некоторых других VPN протоколов, использующих методы запутывания, последовательные шаблоны сигнатур пакетов WireGuard легко идентифицируются системами анализа трафика.
* Доступно в AmneziaVPN на всех платформах. Однако WireGuard легко распознаётся системами DPI из-за характерных сигнатур трафика, что делает его уязвимым к блокировкам.
* Низкое энергопотребление
* Минимальное количество настроек Особенности:
* Легко распознается системами анализа DPI, подвержен блокировке. * Доступен на всех платформах AmneziaVPN
* Работает по сетевому протоколу UDP.</translation> * Низкое энергопотребление на мобильных устройствах
* Минимум настроек
* Легко определяется DPI-системами (подвержен блокировкам)
* Работает по протоколу UDP</translation>
</message> </message>
<message> <message>
<source>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. <location filename="../containers/containers_defs.cpp" line="211"/>
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. <source>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&apos;s easily recognized by DPI systems and susceptible to blocking.
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 Features:
* Low power consumption * Available in AmneziaVPN only on Windows
* Minimum number of settings * Low battery consumption on mobile devices
* Not recognised by traffic analysis systems * Minimal configuration required
* Works over UDP network protocol.</source> * Detectable by DPI analysis systems(easily blocked)
<translation type="vanished">AmneziaWG это современная версия популярного VPN протокола, основанная на базе WireGuard, сохранившая упрощенную архитектуру и высокопроизводительные возможности на всех устройствах. * Operates over UDP protocol(ports 500 and 4500)</source>
Хотя WireGuard известен своей эффективностью, обнаружить его довольно легко из-за различных сигнатур пакетов. AmneziaWG решает эту проблему, используя более совершенные методы работы, смешивая свой трафик с обычным интернет-трафиком. <translation>IKEv2 современный и стабильный VPN-протокол, работающий совместно с шифрованием IPSec. Он обеспечивает быстрое переподключение при смене сети или устройства, отлично подходит для динамичных сетевых условий. Несмотря на хорошую скорость и безопасность, легко распознаётся системами DPI и подвержен блокировкам.
Это означает, что AmneziaWG сохраняет высокую производительность оригинала, добавляя при этом дополнительный уровень скрытности, что делает его отличным выбором для тех, кому нужно быстрое и незаметное VPN-соединение.
* Доступно в AmneziaVPN на всех платформах. Особенности:
* Низкое энергопотребление * Доступен в AmneziaVPN только на Windows
* Минимальное количество настроек * Низкое энергопотребление на мобильных устройствах
* Не распознается системами анализа трафика. * Минимум настроек
* Работает по сетевому протоколу UDP.</translation> * Распознаётся DPI-системами (легко блокируется)
* Работает по UDP (порты 500 и 4500)</translation>
</message> </message>
<message> <message>
<location filename="../containers/containers_defs.cpp" line="214"/> <source>
<source>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. AmneziaWG is a modern VPN protocol based on WireGuard, combining simplified architecture with high performance across all devices. It addresses WireGuards main vulnerability (easy detection by DPI systems) through advanced obfuscation techniques, making VPN traffic indistinguishable from regular internet traffic.
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. AmneziaWG is an excellent choice for those seeking a fast, stealthy VPN connection.
Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY&apos;s innovative &quot;friend or foe&quot; recognition at the TLS handshake enhances security. This makes REALITY a robust solution for maintaining internet freedom.</source>
<translation>Протокол REALITY, современная разработка от создателей XRay. Призван обеспечить высочайший уровень защиты от обнаружения благодаря инновационному подходу к безопасности и конфиденциальности. Features:
Он безошибочно идентифицирует злоумышленников на этапе установления связи TLS, беспрепятственно работая в качестве прокси-сервера для оригинального клиента и перенаправляя злоумышленников на подлинные веб-сайты, предоставляя тем самым подлинный сертификат TLS и данные.
Эта расширенная возможность отличает REALITY от аналогичных технологий тем, что способна маскироваться под случайный веб-трафик без использования специальных настроек. * Available on all AmneziaVPN platforms
В отличие от старых протоколов, таких как VMess, VLESS и транспорт XTLS-Vision, REALITY имеет инновационную технологию распознавания «свой-чужой».Это делает REALITY надежным решением для обеспечения доступа к свободному интернету.</translation> * Low battery consumption on mobile devices
* Minimal settings required
* Undetectable by traffic analysis systems (DPI)
* Operates over UDP protocol
</source>
<translation type="obsolete">AmneziaWG современный VPN-протокол на основе WireGuard, сочетающий простую архитектуру и высокую производительность на всех устройствах. Он устраняет основной недостаток WireGuard (лёгкое обнаружение трафика системами DPI) за счёт эффективного маскирования VPN-трафика под обычный интернет-трафик.
Таким образом, AmneziaWG идеально подойдёт тем, кто ищет быстрое и незаметное VPN-соединение.
Особенности:
* Доступен во всех версиях AmneziaVPN
* Низкое энергопотребление на мобильных устройствах
* Минимум настроек
* Незаметен для систем анализа трафика (DPI)
* Работает по протоколу UDP</translation>
</message> </message>
<message> <message>
<source>WireGuard - New popular VPN protocol with high performance, high speed and low power consumption. Recommended for regions with low levels of censorship.</source> <source>WireGuard - New popular VPN protocol with high performance, high speed and low power consumption. Recommended for regions with low levels of censorship.</source>
@ -5020,49 +4962,7 @@ Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REAL
<translation>Замените текущий DNS-сервер на свой собственный. Это повысит уровень вашей конфиденциальности.</translation> <translation>Замените текущий DNS-сервер на свой собственный. Это повысит уровень вашей конфиденциальности.</translation>
</message> </message>
<message> <message>
<source>OpenVPN stands as one of the most popular and time-tested VPN protocols available. <location filename="../containers/containers_defs.cpp" line="224"/>
It employs its unique security protocol, leveraging the strength of SSL/TLS for encryption and key exchange. Furthermore, OpenVPN&apos;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.</source>
<translation type="vanished">OpenVPN один из самых популярных и проверенных временем VPN-протоколов.
В нем используется уникальный протокол безопасности, опирающийся на SSL/TLS для шифрования и обмена ключами. Кроме того, OpenVPN поддерживает множество методов аутентификации, что делает его универсальным и адаптируемым к широкому спектру устройств и операционных систем. Благодаря открытому исходному коду OpenVPN подвергается тщательному анализу со стороны мирового сообщества, что постоянно повышает его безопасность. Оптимальное соотношение производительности, безопасности и совместимости делает OpenVPN лучшим выбором как для частных лиц, так и для компаний, заботящихся о конфиденциальности.
* Доступен в AmneziaVPN на всех платформах
* Нормальное энергопотребление на мобильных устройствах
* Гибкая настройка под нужды пользователя для работы с различными операционными системами и устройствами
* Распознается системами DPI-анализа и поэтому подвержен блокировке
* Может работать по сетевым протоколам TCP и UDP</translation>
</message>
<message>
<source>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&apos;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&apos;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.</source>
<translation type="vanished">Shadowsocks создан на основе протокола SOCKS5, защищает соединение с помощью шифра AEAD. Несмотря на то, что протокол Shadowsocks разработан таким образом, чтобы быть незаметным и сложным для идентификации, он не идентичен стандартному HTTPS-соединению, поэтому некоторые системы анализа трафика всё же могут обнаружить соединение Shadowsocks. В связи с ограниченной поддержкой в Amnezia рекомендуется использовать протокол AmneziaWG.
* Доступен в AmneziaVPN только для ПК и ноутбуков
* Настраиваемый протокол шифрования
* Распознается некоторыми системами DPI-анализа
* Работает по сетевому протоколу TCP.</translation>
</message>
<message>
<source>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&apos;s innovative &quot;friend or foe&quot; 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.</source>
<translation type="vanished">Протокол REALITY, новаторская разработка создателей XRay, специально спроектирован для противодействия самой строгой цензуре с помощью нового способа обхода блокировок.
Он уникальным образом идентифицирует цензоров на этапе TLS-рукопожатия, беспрепятственно работая в качестве прокси для реальных клиентов и перенаправляя цензоров на реальные сайты, такие как google.com, тем самым предъявляя подлинный TLS-сертификат и данные.
REALITY отличается от аналогичных технологий благодаря способности без специальной настройки маскировать веб-трафик так, как будто он поступает со случайных легитимных сайтов.
В отличие от более старых протоколов, таких как VMess, VLESS и XTLS-Vision, технология распознавания &quot;друг или враг&quot; на этапе TLS-рукопожатия повышает безопасность и обходит обнаружение сложными системами DPI-анализа, которые используют методы активного зондирования. Это делает REALITY эффективным решением для поддержания свободы интернета в регионах с жесткой цензурой.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="239"/>
<source>After installation, Amnezia will create a <source>After installation, Amnezia will create a
file storage on your server. You will be able to access it using file storage on your server. You will be able to access it using
@ -5079,84 +4979,6 @@ For more detailed information, you can
Более подробную информацию вы можете Более подробную информацию вы можете
найти в разделе поддержки &quot;Создание файлового хранилища SFTP.&quot;</translation> найти в разделе поддержки &quot;Создание файлового хранилища SFTP.&quot;</translation>
</message>
<message>
<source>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.
</source>
<translation type="vanished">OpenVPN over Cloak - это комбинация протокола OpenVPN и плагина Cloak, разработанного специально для защиты от обнаружения и блокировок.
Протокол OpenVPN обеспечивает безопасное VPN-соединение за счет шифрования всего интернет-трафика между клиентом и сервером.
Плагин Cloak защищает OpenVPN от обнаружения и блокировок.
Cloak может изменять метаданные пакетов. Он полностью маскирует VPN-трафик под обычный веб-трафик, а также защищает VPN от обнаружения с помощью Active Probing. Это делает его очень устойчивым к обнаружению
Сразу же после получения первого пакета данных Cloak проверяет подлинность входящего соединения. Если аутентификация не проходит, плагин маскирует сервер под поддельный сайт, и ваш VPN становится невидимым для аналитических систем.
Если в вашем регионе экстремальный уровень цензуры в Интернете, мы советуем вам с первого подключения использовать только OpenVPN over Cloak
* Доступен в AmneziaVPN для всех платформ
* Высокое энергопотребление на мобильных устройствах
* Гибкие настройки
* Не распознается системами DPI-анализа
* Работает по сетевому протоколу TCP, 443 порт.
</translation>
</message>
<message>
<source>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.</source>
<translation type="vanished">WireGuard - относительно новый популярный VPN-протокол с упрощенной архитектурой.
Обеспечивает стабильное VPN-соединение, высокую производительность на всех устройствах. Использует жестко заданные настройки шифрования. WireGuard по сравнению с OpenVPN имеет меньшую задержку и лучшую пропускную способность при передаче данных.
WireGuard очень восприимчив к блокированию из-за особенностей сигнатур пакетов. В отличие от некоторых других VPN-протоколов, использующих методы обфускации, последовательные сигнатуры пакетов WireGuard легче выявляются и, соответственно, блокируются современными системами глубокой проверки пакетов (DPI) и другими средствами сетевого мониторинга.
* Доступен в AmneziaVPN для всех платформ
* Низкое энергопотребление
* Минимальное количество настроек
* Легко распознается системами DPI-анализа, подвержен блокировке
* Работает по сетевому протоколу UDP.</translation>
</message>
<message>
<source>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.</source>
<translation type="vanished">AmneziaWG усовершенствованная версия популярного VPN-протокола WireGuard. AmneziaWG опирается на фундамент, заложенный WireGuard, сохраняя упрощенную архитектуру и высокую производительность на различных устройствах.
Хотя WireGuard известен своей эффективностью, у него были проблемы с обнаружением из-за характерных сигнатур пакетов. AmneziaWG решает эту проблему за счет использования более совершенных методов обфускации, благодаря чему его трафик сливается с обычным интернет-трафиком.
Таким образом, AmneziaWG сохраняет высокую производительность оригинального протокола, добавляя при этом дополнительный уровень скрытности, что делает его отличным выбором для тех, кому нужно быстрое и незаметное VPN-соединение.
* Доступен в AmneziaVPN на всех платформах
* Низкое энергопотребление на мобильных устройствах
* Минимальное количество настроек
* Не распознается системами DPI-анализа, устойчив к блокировке
* Работает по сетевому протоколу UDP</translation>
</message> </message>
<message> <message>
<source>AmneziaWG container</source> <source>AmneziaWG container</source>
@ -5239,7 +5061,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin
<message> <message>
<location filename="../protocols/protocols_defs.cpp" line="81"/> <location filename="../protocols/protocols_defs.cpp" line="81"/>
<location filename="../containers/containers_defs.cpp" line="104"/> <location filename="../containers/containers_defs.cpp" line="104"/>
<location filename="../containers/containers_defs.cpp" line="243"/> <location filename="../containers/containers_defs.cpp" line="228"/>
<source>SOCKS5 proxy server</source> <source>SOCKS5 proxy server</source>
<translation>Прокси-сервер SOCKS5</translation> <translation>Прокси-сервер SOCKS5</translation>
</message> </message>
@ -5657,12 +5479,12 @@ This means that AmneziaWG keeps the fast performance of the original while addin
<translation type="vanished">Хочу просто повысить уровень приватности</translation> <translation type="vanished">Хочу просто повысить уровень приватности</translation>
</message> </message>
<message> <message>
<location filename="../containers/containers_defs.cpp" line="338"/> <location filename="../containers/containers_defs.cpp" line="323"/>
<source>Automatic</source> <source>Automatic</source>
<translation>Автоматическая</translation> <translation>Автоматическая</translation>
</message> </message>
<message> <message>
<location filename="../containers/containers_defs.cpp" line="346"/> <location filename="../containers/containers_defs.cpp" line="331"/>
<source>AmneziaWG protocol will be installed. It provides high connection speed and ensures stable operation even in the most challenging network conditions.</source> <source>AmneziaWG protocol will be installed. It provides high connection speed and ensures stable operation even in the most challenging network conditions.</source>
<translation>Будет установлен протокол AmneziaWG. Он обеспечивает высокую скорость соединения и гарантирует стабильную работу даже в самых сложных условиях.</translation> <translation>Будет установлен протокол AmneziaWG. Он обеспечивает высокую скорость соединения и гарантирует стабильную работу даже в самых сложных условиях.</translation>
</message> </message>

View file

@ -0,0 +1,101 @@
#include "allowedDnsController.h"
#include <QFile>
#include <QStandardPaths>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include "systemController.h"
#include "core/networkUtilities.h"
#include "core/defs.h"
AllowedDnsController::AllowedDnsController(const std::shared_ptr<Settings> &settings,
const QSharedPointer<AllowedDnsModel> &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"));
}

View file

@ -0,0 +1,35 @@
#ifndef ALLOWEDDNSCONTROLLER_H
#define ALLOWEDDNSCONTROLLER_H
#include <QObject>
#include "settings.h"
#include "ui/models/allowed_dns_model.h"
class AllowedDnsController : public QObject
{
Q_OBJECT
public:
explicit AllowedDnsController(const std::shared_ptr<Settings> &settings,
const QSharedPointer<AllowedDnsModel> &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<Settings> m_settings;
QSharedPointer<AllowedDnsModel> m_allowedDnsModel;
};
#endif // ALLOWEDDNSCONTROLLER_H

View file

@ -145,7 +145,7 @@ void ExportController::generateOpenVpnConfig(const QString &clientName)
} }
QStringList lines = nativeConfig.value(config_key::config).toString().replace("\r", "").split("\n"); 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"); 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"); 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"); 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"); 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"); m_config.append(line + "\n");
} }
@ -211,7 +211,7 @@ void ExportController::generateShadowSocksConfig()
} }
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n"); 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"); m_config.append(line + "\n");
} }
@ -240,7 +240,7 @@ void ExportController::generateCloakConfig()
nativeConfig.insert("ProxyMethod", "shadowsocks"); nativeConfig.insert("ProxyMethod", "shadowsocks");
QStringList lines = QString(QJsonDocument(nativeConfig).toJson()).replace("\r", "").split("\n"); 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"); 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"); 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"); m_config.append(line + "\n");
} }

View file

@ -665,27 +665,27 @@ void ImportController::checkForMaliciousStrings(const QJsonObject &serverConfig)
containerConfig[ProtocolProps::protoToString(Proto::OpenVpn)].toObject()[config_key::last_config].toString(); containerConfig[ProtocolProps::protoToString(Proto::OpenVpn)].toObject()[config_key::last_config].toString();
QString protocolConfigJson = QJsonDocument::fromJson(protocolConfig.toUtf8()).object()[config_key::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 // https://github.com/OpenVPN/openvpn/blob/master/doc/man-sections/script-options.rst
QStringList dangerousTags { QStringList dangerousTags {
"up", "tls-verify", "ipchange", "client-connect", "route-up", "route-pre-down", "client-disconnect", "down", "learn-address", "auth-user-pass-verify" "up", "tls-verify", "ipchange", "client-connect", "route-up", "route-pre-down", "client-disconnect", "down", "learn-address", "auth-user-pass-verify"
}; };
QStringList maliciousStrings; QStringList maliciousStrings;
QStringList lines = protocolConfigJson.replace("\r", "").split("\n"); QStringList lines = protocolConfigJson.split('\n', Qt::SkipEmptyParts);
for (const QString &l : lines) {
QRegularExpressionMatch match = regExp.match(l); for (const QString &rawLine : lines) {
if (dangerousTags.contains(match.captured(0))) { QString line = rawLine.trimmed();
maliciousStrings << l;
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 " 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. "); "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("<br>In the imported configuration, potentially dangerous lines were found:")); m_maliciousWarningText.push_back(tr("<br>In the imported configuration, potentially dangerous lines were found:"));
for (const auto &string : maliciousStrings) { for (const auto &string : maliciousStrings) {
m_maliciousWarningText.push_back(QString("<br><i>%1</i>").arg(string)); m_maliciousWarningText.push_back(QString("<br><i>%1</i>").arg(string));

View file

@ -363,7 +363,8 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
QJsonObject config; QJsonObject config;
Proto mainProto = ContainerProps::defaultProtocol(container); Proto mainProto = ContainerProps::defaultProtocol(container);
for (auto protocol : ContainerProps::protocolsForContainer(container)) { const auto &protocols = ContainerProps::protocolsForContainer(container);
for (const auto &protocol : protocols) {
QJsonObject containerConfig; QJsonObject containerConfig;
if (protocol == mainProto) { if (protocol == mainProto) {
containerConfig.insert(config_key::port, port); 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::junkPacketCount] = serverConfigMap.value(config_key::junkPacketCount);
containerConfig[config_key::junkPacketMinSize] = serverConfigMap.value(config_key::junkPacketMinSize); containerConfig[config_key::junkPacketMinSize] = serverConfigMap.value(config_key::junkPacketMinSize);
containerConfig[config_key::junkPacketMaxSize] = serverConfigMap.value(config_key::junkPacketMaxSize); containerConfig[config_key::junkPacketMaxSize] = serverConfigMap.value(config_key::junkPacketMaxSize);
@ -398,6 +400,25 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
serverConfigMap.value(config_key::underloadPacketMagicHeader); serverConfigMap.value(config_key::underloadPacketMagicHeader);
containerConfig[config_key::transportPacketMagicHeader] = containerConfig[config_key::transportPacketMagicHeader] =
serverConfigMap.value(config_key::transportPacketMagicHeader); serverConfigMap.value(config_key::transportPacketMagicHeader);
} else if (protocol == Proto::WireGuard) {
QString serverConfig = serverController->getTextFileFromContainer(container, credentials,
protocols::wireguard::serverConfigPath, errorCode);
QMap<QString, QString> 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) { } else if (protocol == Proto::Sftp) {
stdOut.clear(); stdOut.clear();
script = QString("sudo docker inspect --format '{{.Config.Cmd}}' %1").arg(name); 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::userName, userName);
containerConfig.insert(config_key::password, password); 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)); config.insert(config_key::container, ContainerProps::containerToString(container));

View file

@ -32,13 +32,15 @@ namespace PageLoader
PageSettingsLogging, PageSettingsLogging,
PageSettingsSplitTunneling, PageSettingsSplitTunneling,
PageSettingsAppSplitTunneling, PageSettingsAppSplitTunneling,
PageSettingsKillSwitch,
PageSettingsApiServerInfo, PageSettingsApiServerInfo,
PageSettingsApiAvailableCountries, PageSettingsApiAvailableCountries,
PageSettingsApiSupport, PageSettingsApiSupport,
PageSettingsApiInstructions, PageSettingsApiInstructions,
PageSettingsApiNativeConfigs, PageSettingsApiNativeConfigs,
PageSettingsApiDevices, PageSettingsApiDevices,
PageSettingsKillSwitchExceptions,
PageServiceSftpSettings, PageServiceSftpSettings,
PageServiceTorWebsiteSettings, PageServiceTorWebsiteSettings,
PageServiceDnsSettings, PageServiceDnsSettings,

View file

@ -245,6 +245,23 @@ bool SettingsController::isKillSwitchEnabled()
void SettingsController::toggleKillSwitch(bool enable) void SettingsController::toggleKillSwitch(bool enable)
{ {
m_settings->setKillSwitchEnabled(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() bool SettingsController::isNotificationPermissionGranted()

View file

@ -24,6 +24,8 @@ public:
Q_PROPERTY(QString secondaryDns READ getSecondaryDns WRITE setSecondaryDns NOTIFY secondaryDnsChanged) Q_PROPERTY(QString secondaryDns READ getSecondaryDns WRITE setSecondaryDns NOTIFY secondaryDnsChanged)
Q_PROPERTY(bool isLoggingEnabled READ isLoggingEnabled WRITE toggleLogging NOTIFY loggingStateChanged) Q_PROPERTY(bool isLoggingEnabled READ isLoggingEnabled WRITE toggleLogging NOTIFY loggingStateChanged)
Q_PROPERTY(bool isNotificationPermissionGranted READ isNotificationPermissionGranted NOTIFY onNotificationStateChanged) 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(bool isDevModeEnabled READ isDevModeEnabled NOTIFY devModeEnabled)
Q_PROPERTY(QString gatewayEndpoint READ getGatewayEndpoint WRITE setGatewayEndpoint NOTIFY gatewayEndpointChanged) Q_PROPERTY(QString gatewayEndpoint READ getGatewayEndpoint WRITE setGatewayEndpoint NOTIFY gatewayEndpointChanged)
@ -75,6 +77,9 @@ public slots:
bool isKillSwitchEnabled(); bool isKillSwitchEnabled();
void toggleKillSwitch(bool enable); void toggleKillSwitch(bool enable);
bool isStrictKillSwitchEnabled();
void toggleStrictKillSwitch(bool enable);
bool isNotificationPermissionGranted(); bool isNotificationPermissionGranted();
void requestNotificationPermission(); void requestNotificationPermission();
@ -98,6 +103,8 @@ signals:
void primaryDnsChanged(); void primaryDnsChanged();
void secondaryDnsChanged(); void secondaryDnsChanged();
void loggingStateChanged(); void loggingStateChanged();
void killSwitchEnabledChanged();
void strictKillSwitchEnabledChanged(bool enabled);
void restoreBackupFinished(); void restoreBackupFinished();
void changeSettingsFinished(const QString &finishedMessage); void changeSettingsFinished(const QString &finishedMessage);

View file

@ -0,0 +1,86 @@
#include "allowed_dns_model.h"
AllowedDnsModel::AllowedDnsModel(std::shared_ptr<Settings> 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<int>(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<int, QByteArray> AllowedDnsModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[IpRole] = "ip";
return roles;
}
void AllowedDnsModel::fillDnsServers()
{
m_dnsServers = m_settings->allowedDnsServers();
}

View file

@ -0,0 +1,37 @@
#ifndef ALLOWEDDNSMODEL_H
#define ALLOWEDDNSMODEL_H
#include <QAbstractListModel>
#include "settings.h"
class AllowedDnsModel : public QAbstractListModel
{
Q_OBJECT
public:
enum Roles {
IpRole = Qt::UserRole + 1
};
explicit AllowedDnsModel(std::shared_ptr<Settings> 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<int, QByteArray> roleNames() const override;
private:
void fillDnsServers();
std::shared_ptr<Settings> m_settings;
QStringList m_dnsServers;
};
#endif // ALLOWEDDNSMODEL_H

View file

@ -69,7 +69,7 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
"Access all websites and online resources. Speeds up to %1 Mbps.") "Access all websites and online resources. Speeds up to %1 Mbps.")
.arg(speed); .arg(speed);
} else if (serviceType == serviceType::amneziaFree) { } 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) { if (!isServiceAvailable) {
description += tr("<p><a style=\"color: #EB5757;\">Not available in your region. If you have VPN enabled, disable it, " description += tr("<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>"); "return to the previous screen, and try again.</a>");
@ -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. " return tr("Amnezia Premium is classic VPN for for seamless work, downloading large files, and watching videos. "
"Access all websites and online resources."); "Access all websites and online resources.");
} else { } 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: { case IsServiceAvailableRole: {

View file

@ -20,6 +20,7 @@ bool XrayConfigModel::setData(const QModelIndex &index, const QVariant &value, i
switch (role) { switch (role) {
case Roles::SiteRole: m_protocolConfig.insert(config_key::site, value.toString()); break; case Roles::SiteRole: m_protocolConfig.insert(config_key::site, value.toString()); break;
case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break;
} }
emit dataChanged(index, index, QList { role }); emit dataChanged(index, index, QList { role });
@ -34,6 +35,7 @@ QVariant XrayConfigModel::data(const QModelIndex &index, int role) const
switch (role) { switch (role) {
case Roles::SiteRole: return m_protocolConfig.value(config_key::site).toString(protocols::xray::defaultSite); 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(); return QVariant();
@ -67,6 +69,7 @@ QHash<int, QByteArray> XrayConfigModel::roleNames() const
QHash<int, QByteArray> roles; QHash<int, QByteArray> roles;
roles[SiteRole] = "site"; roles[SiteRole] = "site";
roles[PortRole] = "port";
return roles; return roles;
} }

View file

@ -12,7 +12,8 @@ class XrayConfigModel : public QAbstractListModel
public: public:
enum Roles { enum Roles {
SiteRole SiteRole,
PortRole
}; };
explicit XrayConfigModel(QObject *parent = nullptr); explicit XrayConfigModel(QObject *parent = nullptr);

View file

@ -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()
}
}
}

View file

@ -18,7 +18,8 @@ ListView {
property var selectedText property var selectedText
width: rootWidth width: rootWidth
height: contentItem.height anchors.top: parent.top
anchors.bottom: parent.bottom
clip: true clip: true
snapMode: ListView.SnapToItem snapMode: ListView.SnapToItem

View file

@ -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 !== ""
}
}
}

View file

@ -239,6 +239,7 @@ Item {
sourceComponent: root.listView sourceComponent: root.listView
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true
} }
} }
} }

View file

@ -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()
}
}
}

View file

@ -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()
}
}
}

View file

@ -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)
}
}
}
}

View file

@ -20,7 +20,11 @@ RadioButton {
property string selectedColor: AmneziaStyle.color.transparent property string selectedColor: AmneziaStyle.color.transparent
property string textColor: AmneziaStyle.color.paleGray property string textColor: AmneziaStyle.color.paleGray
property string textDisabledColor: AmneziaStyle.color.mutedGray
property string selectedTextColor: AmneziaStyle.color.goldenApricot 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 string borderFocusedColor: AmneziaStyle.color.paleGray
property int borderFocusedWidth: 1 property int borderFocusedWidth: 1
@ -30,6 +34,12 @@ RadioButton {
property bool isFocusable: true 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: { Keys.onTabPressed: {
FocusController.nextKeyTabItem() FocusController.nextKeyTabItem()
} }
@ -94,14 +104,15 @@ RadioButton {
if (showImage) { if (showImage) {
return imageSource return imageSource
} else if (root.pressed) { } else if (root.pressed) {
return "qrc:/images/controls/radio-button-inner-circle-pressed.png" return root.radioButtonInnerCirclePressedSource
} else if (root.checked) { } else if (root.checked) {
return "qrc:/images/controls/radio-button-inner-circle.png" return root.radioButtonInnerCircleSource
} }
return "" return ""
} }
opacity: root.enabled ? 1.0 : 0.3
anchors.centerIn: parent anchors.centerIn: parent
width: 24 width: 24
@ -113,12 +124,13 @@ RadioButton {
if (showImage) { if (showImage) {
return "" return ""
} else if (root.pressed || root.checked) { } else if (root.pressed || root.checked) {
return "qrc:/images/controls/radio-button-pressed.svg" return root.radioButtonPressedSource
} else { } else {
return "qrc:/images/controls/radio-button.svg" return root.radioButtonDefaultSource
} }
} }
opacity: root.enabled ? 1.0 : 0.3
anchors.centerIn: parent anchors.centerIn: parent
width: 24 width: 24
@ -148,10 +160,11 @@ RadioButton {
elide: root.textElide elide: root.textElide
color: { color: {
if (root.checked) { if (root.enabled) {
return selectedTextColor return root.checked ? selectedTextColor : textColor
} else {
return root.checked ? selectedTextDisabledColor : textDisabledColor
} }
return textColor
} }
Layout.fillWidth: true Layout.fillWidth: true
@ -164,7 +177,7 @@ RadioButton {
CaptionTextType { CaptionTextType {
id: description id: description
color: AmneziaStyle.color.mutedGray color: root.enabled ? root.descriptionColor : root.descriptionDisabledColor
text: root.descriptionText text: root.descriptionText
visible: root.descriptionText !== "" visible: root.descriptionText !== ""
@ -177,6 +190,7 @@ RadioButton {
MouseArea { MouseArea {
anchors.fill: root anchors.fill: root
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
preventStealing: false
enabled: false enabled: false
} }
} }

View file

@ -56,7 +56,7 @@ PageType {
anchors.rightMargin: 16 anchors.rightMargin: 16
anchors.leftMargin: 16 anchors.leftMargin: 16
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 20 Layout.topMargin: 20

View file

@ -39,7 +39,7 @@ PageType {
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
HeaderType { BaseHeaderType {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true

View file

@ -91,7 +91,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("AmneziaWG settings") headerText: qsTr("AmneziaWG settings")

View file

@ -91,7 +91,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("AmneziaWG settings") headerText: qsTr("AmneziaWG settings")

View file

@ -76,7 +76,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("Cloak settings") headerText: qsTr("Cloak settings")

View file

@ -75,7 +75,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("OpenVPN settings") headerText: qsTr("OpenVPN settings")

View file

@ -32,7 +32,7 @@ PageType {
id: backButton id: backButton
} }
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -78,7 +78,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("Shadowsocks settings") headerText: qsTr("Shadowsocks settings")

View file

@ -85,7 +85,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("WG settings") headerText: qsTr("WG settings")

View file

@ -77,7 +77,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("WG settings") headerText: qsTr("WG settings")
} }

View file

@ -75,7 +75,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("XRay settings") headerText: qsTr("XRay settings")
} }
@ -93,9 +93,9 @@ PageType {
var tmpText = textField.text var tmpText = textField.text
tmpText = tmpText.toLocaleLowerCase() tmpText = tmpText.toLocaleLowerCase()
var indexHttps = tmpText.indexOf("https://") if (tmpText.startsWith("https://")) {
if (indexHttps === 0) {
tmpText = textField.text.substring(8) tmpText = textField.text.substring(8)
site = tmpText
} else { } else {
site = textField.text site = textField.text
} }
@ -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 { BasicButtonType {
id: basicButton id: saveButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
Layout.bottomMargin: 24 Layout.bottomMargin: 24

View file

@ -43,7 +43,7 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
HeaderType { BaseHeaderType {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true

View file

@ -85,7 +85,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -77,7 +77,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
@ -217,7 +217,7 @@ PageType {
} }
} }
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("SOCKS5 settings") headerText: qsTr("SOCKS5 settings")

View file

@ -54,7 +54,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -29,7 +29,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24

View file

@ -69,7 +69,7 @@ PageType {
Layout.topMargin: 20 Layout.topMargin: 20
} }
HeaderType { HeaderTypeWithButton {
id: headerContent id: headerContent
objectName: "headerContent" objectName: "headerContent"
@ -135,12 +135,6 @@ PageType {
} }
} }
MouseArea {
anchors.fill: containerRadioButton
cursorShape: Qt.PointingHandCursor
enabled: false
}
Keys.onEnterPressed: { Keys.onEnterPressed: {
if (checkable) { if (checkable) {
checked = true checked = true

View file

@ -35,7 +35,7 @@ PageType {
id: backButton id: backButton
} }
HeaderType { BaseHeaderType {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true
@ -77,7 +77,7 @@ PageType {
} }
var headerText = qsTr("Are you sure you want to unlink this device?") 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 yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel") var noButtonText = qsTr("Cancel")

View file

@ -91,7 +91,7 @@ PageType {
id: backButton id: backButton
} }
HeaderType { BaseHeaderType {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true

View file

@ -38,7 +38,7 @@ PageType {
id: backButton id: backButton
} }
HeaderType { BaseHeaderType {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true

View file

@ -93,7 +93,7 @@ PageType {
Layout.topMargin: 20 Layout.topMargin: 20
} }
HeaderType { HeaderTypeWithButton {
id: headerContent id: headerContent
objectName: "headerContent" objectName: "headerContent"
@ -333,7 +333,7 @@ PageType {
clickedFunc: function() { clickedFunc: function() {
var headerText = qsTr("Are you sure you want to unlink this device?") 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 yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel") var noButtonText = qsTr("Cancel")

View file

@ -71,7 +71,7 @@ PageType {
id: backButton id: backButton
} }
HeaderType { BaseHeaderType {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true

View file

@ -79,29 +79,22 @@ PageType {
id: backButton id: backButton
} }
RowLayout { HeaderTypeWithSwitcher {
HeaderType { Layout.fillWidth: true
Layout.fillWidth: true Layout.leftMargin: 16
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 enabled: root.pageEnabled
} }
switcherFunction: function(checked) {
SwitcherType { AppSplitTunnelingModel.toggleSplitTunneling(checked)
id: switcher selector.text = root.routeModesModel[getRouteModesModelIndex()].name
Layout.fillWidth: true
Layout.rightMargin: 16
enabled: root.pageEnabled
checked: AppSplitTunnelingModel.isTunnelingEnabled
onToggled: {
AppSplitTunnelingModel.toggleSplitTunneling(checked)
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
}
} }
} }

View file

@ -38,7 +38,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -60,7 +60,7 @@ PageType {
spacing: 16 spacing: 16
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("Back up your configuration") headerText: qsTr("Back up your configuration")

View file

@ -36,7 +36,7 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
@ -94,9 +94,7 @@ PageType {
} }
} }
DividerType { DividerType {}
visible: root.isAppSplitTinnelingEnabled
}
LabelWithButtonType { LabelWithButtonType {
id: splitTunnelingButton2 id: splitTunnelingButton2
@ -119,29 +117,20 @@ PageType {
visible: root.isAppSplitTinnelingEnabled visible: root.isAppSplitTinnelingEnabled
} }
SwitcherType { LabelWithButtonType {
id: killSwitchSwitcher id: killSwitchButton
visible: !GC.isMobile() visible: !GC.isMobile()
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 16
text: qsTr("KillSwitch") 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 parentFlickable: fl
checked: SettingsController.isKillSwitchEnabled() clickedFunction: function() {
checkable: !ConnectionController.isConnected PageController.goToPage(PageEnum.PageSettingsKillSwitch)
onCheckedChanged: {
if (checked !== SettingsController.isKillSwitchEnabled()) {
SettingsController.toggleKillSwitch(checked)
}
}
onClicked: {
if (!checkable) {
PageController.showNotificationMessage(qsTr("Cannot change KillSwitch settings during active connection"))
}
} }
} }

View file

@ -50,7 +50,7 @@ PageType {
spacing: 16 spacing: 16
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("DNS servers") headerText: qsTr("DNS servers")

View file

@ -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)
}
}
}
}
}

View file

@ -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 {}
}
}
}
}
}

View file

@ -40,7 +40,7 @@ PageType {
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -71,7 +71,7 @@ PageType {
objectName: "backButton" objectName: "backButton"
} }
HeaderType { HeaderTypeWithButton {
id: headerContent id: headerContent
objectName: "headerContent" objectName: "headerContent"

View file

@ -34,7 +34,7 @@ PageType {
id: backButton id: backButton
} }
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -31,7 +31,7 @@ PageType {
id: backButton id: backButton
} }
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -94,33 +94,22 @@ PageType {
id: backButton id: backButton
} }
RowLayout { HeaderTypeWithSwitcher {
HeaderType { Layout.fillWidth: true
enabled: root.pageEnabled Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.fillWidth: true headerText: qsTr("Split tunneling")
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
}
enabled: root.pageEnabled
showSwitcher: true
switcher {
checked: SitesModel.isTunnelingEnabled checked: SitesModel.isTunnelingEnabled
onToggled: { onToggledFunc() } enabled: root.pageEnabled
Keys.onEnterPressed: { onToggledFunc() } }
Keys.onReturnPressed: { onToggledFunc() } switcherFunction: function(checked) {
SitesModel.toggleSplitTunneling(checked)
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
} }
} }

View file

@ -35,7 +35,7 @@ PageType {
Layout.topMargin: 20 Layout.topMargin: 20
} }
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 8 Layout.topMargin: 8
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -28,7 +28,7 @@ PageType {
Layout.topMargin: 20 Layout.topMargin: 20
} }
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 8 Layout.topMargin: 8
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -45,7 +45,7 @@ PageType {
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
HeaderType { HeaderTypeWithButton {
id: moreButton id: moreButton
property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible() property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible()
@ -76,7 +76,7 @@ PageType {
anchors.right: parent.right anchors.right: parent.right
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.leftMargin: 16 Layout.leftMargin: 16
@ -114,11 +114,11 @@ PageType {
clickedFunction: function() { clickedFunction: function() {
var fileName = "" var fileName = ""
if (GC.isMobile()) { if (GC.isMobile()) {
fileName = "AmneziaVPN.log" fileName = "DefaultVPN.log"
} else { } else {
fileName = SystemController.getFileName(qsTr("Save"), fileName = SystemController.getFileName(qsTr("Save"),
qsTr("Logs files (*.log)"), qsTr("Logs files (*.log)"),
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/AmneziaVPN", StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/DefaultVPN",
true, true,
".log") ".log")
} }

View file

@ -66,7 +66,7 @@ PageType {
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -59,7 +59,7 @@ PageType {
spacing: 16 spacing: 16
HeaderType { BaseHeaderType {
id: header id: header
implicitWidth: parent.width implicitWidth: parent.width

View file

@ -118,7 +118,7 @@ PageType {
anchors.rightMargin: 16 anchors.rightMargin: 16
anchors.leftMargin: 16 anchors.leftMargin: 16
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 20 Layout.topMargin: 20

View file

@ -96,7 +96,7 @@ PageType {
Layout.leftMargin: -16 Layout.leftMargin: -16
} }
HeaderType { BaseHeaderType {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true

View file

@ -58,7 +58,7 @@ PageType {
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
HeaderType { BaseHeaderType {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true

View file

@ -33,7 +33,7 @@ PageType {
Layout.topMargin: 20 Layout.topMargin: 20
} }
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16

View file

@ -61,7 +61,7 @@ PageType {
anchors.rightMargin: 16 anchors.rightMargin: 16
anchors.leftMargin: 16 anchors.leftMargin: 16
HeaderType { BaseHeaderType {
headerText: qsTr("New connection") headerText: qsTr("New connection")
} }

View file

@ -169,7 +169,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { HeaderTypeWithButton {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24

View file

@ -44,7 +44,7 @@ PageType {
spacing: 0 spacing: 0
HeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24

View file

@ -52,6 +52,28 @@ void VpnConnection::onBytesChanged(quint64 receivedBytes, quint64 sentBytes)
emit bytesChanged(receivedBytes, 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) void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
{ {
@ -286,6 +308,7 @@ void VpnConnection::createProtocolConnections()
void VpnConnection::appendKillSwitchConfig() void VpnConnection::appendKillSwitchConfig()
{ {
m_vpnConfiguration.insert(config_key::killSwitchOption, QVariant(m_settings->isKillSwitchEnabled()).toString()); 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() void VpnConnection::appendSplitTunnelingConfig()

View file

@ -48,14 +48,14 @@ public:
public slots: public slots:
void connectToVpn(int serverIndex, void connectToVpn(int serverIndex,
const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration); const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration);
void disconnectFromVpn(); void disconnectFromVpn();
void addRoutes(const QStringList &ips); void addRoutes(const QStringList &ips);
void deleteRoutes(const QStringList &ips); void deleteRoutes(const QStringList &ips);
void flushDns(); void flushDns();
void onKillSwitchModeChanged(bool enabled);
signals: signals:
void bytesChanged(quint64 receivedBytes, quint64 sentBytes); void bytesChanged(quint64 receivedBytes, quint64 sentBytes);

View file

@ -41,6 +41,10 @@ if [ -z "${QT_VERSION+x}" ]; then
QT_BIN_DIR=/opt/Qt/$QT_VERSION/gcc_64/bin QT_BIN_DIR=/opt/Qt/$QT_VERSION/gcc_64/bin
elif [ -f $HOME/Qt/$QT_VERSION/gcc_64/bin/qmake ]; then elif [ -f $HOME/Qt/$QT_VERSION/gcc_64/bin/qmake ]; then
QT_BIN_DIR=$HOME/Qt/$QT_VERSION/gcc_64/bin 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
fi fi
@ -56,7 +60,7 @@ echo "Building App..."
cd $BUILD_DIR cd $BUILD_DIR
$QT_BIN_DIR/qt-cmake -S $PROJECT_DIR $QT_BIN_DIR/qt-cmake -S $PROJECT_DIR
cmake --build . --config release cmake --build . -j --config release
# Build and run tests here # Build and run tests here

Some files were not shown because too many files have changed in this diff Show more