From f5272168bc36c9c12e03a8dfb06458c340653db9 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Sun, 29 Dec 2024 23:07:06 +0200 Subject: [PATCH 01/22] Add allowed DNS list for killswitch --- client/daemon/interfaceconfig.h | 1 + .../windows/daemon/windowsfirewall.cpp | 8 ++++++ client/protocols/protocols_defs.h | 2 ++ ipc/ipcserver.cpp | 27 ++++++++++++++++--- 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/client/daemon/interfaceconfig.h b/client/daemon/interfaceconfig.h index 6a816f87..3e1c08b9 100644 --- a/client/daemon/interfaceconfig.h +++ b/client/daemon/interfaceconfig.h @@ -37,6 +37,7 @@ class InterfaceConfig { QList m_allowedIPAddressRanges; QStringList m_excludedAddresses; QStringList m_vpnDisabledApps; + QStringList m_allowedDnsServers; bool m_killSwitchEnabled; #if defined(MZ_ANDROID) || defined(MZ_IOS) QString m_installationId; diff --git a/client/platforms/windows/daemon/windowsfirewall.cpp b/client/platforms/windows/daemon/windowsfirewall.cpp index 3d45f228..23ee9cca 100644 --- a/client/platforms/windows/daemon/windowsfirewall.cpp +++ b/client/platforms/windows/daemon/windowsfirewall.cpp @@ -236,6 +236,14 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) { } } + for (const QString& dns : config.m_allowedDnsServers) { + logger.debug() << "Allow DNS: " << dns; + if (!allowTrafficTo(QHostAddress(dns), 53, HIGH_WEIGHT, + "Allow DNS-Server", config.m_serverPublicKey)) { + return false; + } + } + if (!config.m_excludedAddresses.empty()) { for (const QString& i : config.m_excludedAddresses) { logger.debug() << "range: " << i; diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index 865edae4..feeabb2f 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -95,6 +95,8 @@ namespace amnezia constexpr char splitTunnelApps[] = "splitTunnelApps"; constexpr char appSplitTunnelType[] = "appSplitTunnelType"; + constexpr char allowedDnsServers[] = "allowedDnsServers"; + constexpr char killSwitchOption[] = "killSwitchOption"; constexpr char crc[] = "crc"; diff --git a/ipc/ipcserver.cpp b/ipc/ipcserver.cpp index bb8a4182..4fd12a8a 100644 --- a/ipc/ipcserver.cpp +++ b/ipc/ipcserver.cpp @@ -244,6 +244,12 @@ bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterInd dnsServers.append(configStr.value(amnezia::config_key::dns2).toString()); dnsServers.append("127.0.0.1"); dnsServers.append("127.0.0.53"); + for (auto dns : configStr.value(amnezia::config_key::allowedDnsServers).toArray()) { + if (!dns.isString()) { + break; + } + dnsServers.append(dns.toString()); + } LinuxFirewall::updateDNSServers(dnsServers); LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true); LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true); @@ -272,6 +278,13 @@ bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterInd QStringList dnsServers; dnsServers.append(configStr.value(amnezia::config_key::dns1).toString()); dnsServers.append(configStr.value(amnezia::config_key::dns2).toString()); + for (auto dns : configStr.value(amnezia::config_key::allowedDnsServers).toArray()) { + if (!dns.isString()) { + break; + } + dnsServers.append(dns.toString()); + } + MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true); MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), dnsServers); #endif @@ -310,8 +323,6 @@ bool IpcServer::enablePeerTraffic(const QJsonObject &configStr) int splitTunnelType = configStr.value("splitTunnelType").toInt(); QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray(); - QStringList AllowedIPAddesses; - // Use APP split tunnel if (splitTunnelType == 0 || splitTunnelType == 2) { config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("0.0.0.0"), 0)); @@ -338,11 +349,19 @@ bool IpcServer::enablePeerTraffic(const QJsonObject &configStr) } } - for (const QJsonValue &i : configStr.value(amnezia::config_key::splitTunnelApps).toArray()) { - if (!i.isString()) { + for (auto app : configStr.value(amnezia::config_key::splitTunnelApps).toArray()) { + if (!app.isString()) { + break; + } + config.m_vpnDisabledApps.append(app.toString()); + } + + for (auto dns : configStr.value(amnezia::config_key::allowedDnsServers).toArray()) { + if (!dns.isString()) { break; } config.m_vpnDisabledApps.append(i.toString()); + config.m_allowedDnsServers.append(dns.toString()); } // killSwitch toggle From 4d912453761ebb5248b1126ebdd59349a4715eae Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Mon, 30 Dec 2024 22:50:33 +0200 Subject: [PATCH 02/22] Windows killswitch strict mode backend part --- .../windows/daemon/windowsfirewall.cpp | 75 +++--- .../windows/daemon/windowsfirewall.h | 1 + client/secure_qsettings.cpp | 4 +- client/secure_qsettings.h | 2 +- ipc/ipc_interface.rep | 1 + ipc/ipcserver.cpp | 177 +------------- ipc/ipcserver.h | 1 + service/server/CMakeLists.txt | 56 +++++ service/server/killswitch.cpp | 228 ++++++++++++++++++ service/server/killswitch.h | 27 +++ service/server/localserver.cpp | 7 +- 11 files changed, 378 insertions(+), 201 deletions(-) create mode 100644 service/server/killswitch.cpp create mode 100644 service/server/killswitch.h diff --git a/client/platforms/windows/daemon/windowsfirewall.cpp b/client/platforms/windows/daemon/windowsfirewall.cpp index 3d45f228..9213ad0c 100644 --- a/client/platforms/windows/daemon/windowsfirewall.cpp +++ b/client/platforms/windows/daemon/windowsfirewall.cpp @@ -29,6 +29,8 @@ #include "platforms/windows/windowsutils.h" #include "winsock.h" +#include "killswitch.h" + #define IPV6_ADDRESS_SIZE 16 // ID for the Firewall Sublayer @@ -185,10 +187,23 @@ bool WindowsFirewall::enableKillSwitch(int vpnAdapterIndex) { } logger.info() << "Enabling Killswitch Using Adapter:" << vpnAdapterIndex; + if (vpnAdapterIndex < 0) + { + IPAddress allv4("0.0.0.0/0"); + if (!blockTrafficTo(allv4, MED_WEIGHT, + "Block Internet", "killswitch")) { + return false; + } + IPAddress allv6("::/0"); + if (!blockTrafficTo(allv6, MED_WEIGHT, + "Block Internet", "killswitch")) { + return false; + } + } else FW_OK(allowTrafficOfAdapter(vpnAdapterIndex, MED_WEIGHT, - "Allow usage of VPN Adapter")); + "Allow usage of VPN Adapter")); FW_OK(allowDHCPTraffic(MED_WEIGHT, "Allow DHCP Traffic")); - FW_OK(allowHyperVTraffic(MED_WEIGHT, "Allow Hyper-V Traffic")); + FW_OK(allowHyperVTraffic(MAX_WEIGHT, "Allow Hyper-V Traffic")); FW_OK(allowTrafficForAppOnAll(getCurrentPath(), MAX_WEIGHT, "Allow all for AmneziaVPN.exe")); FW_OK(blockTrafficOnPort(53, MED_WEIGHT, "Block all DNS")); @@ -287,37 +302,41 @@ bool WindowsFirewall::disablePeerTraffic(const QString& pubkey) { } bool WindowsFirewall::disableKillSwitch() { - auto result = FwpmTransactionBegin(m_sessionHandle, NULL); - auto cleanup = qScopeGuard([&] { + return KillSwitch::instance()->disableKillSwitch(); +} + +bool WindowsFirewall::allowAllTraffic() { + auto result = FwpmTransactionBegin(m_sessionHandle, NULL); + auto cleanup = qScopeGuard([&] { + if (result != ERROR_SUCCESS) { + FwpmTransactionAbort0(m_sessionHandle); + } + }); if (result != ERROR_SUCCESS) { - FwpmTransactionAbort0(m_sessionHandle); + logger.error() << "FwpmTransactionBegin0 failed. Return value:.\n" + << result; + return false; } - }); - if (result != ERROR_SUCCESS) { - logger.error() << "FwpmTransactionBegin0 failed. Return value:.\n" - << result; - return false; - } - for (const auto& filterID : m_peerRules.values()) { - FwpmFilterDeleteById0(m_sessionHandle, filterID); - } + for (const auto& filterID : m_peerRules.values()) { + FwpmFilterDeleteById0(m_sessionHandle, filterID); + } - for (const auto& filterID : qAsConst(m_activeRules)) { - FwpmFilterDeleteById0(m_sessionHandle, filterID); - } + for (const auto& filterID : qAsConst(m_activeRules)) { + FwpmFilterDeleteById0(m_sessionHandle, filterID); + } - // Commit! - result = FwpmTransactionCommit0(m_sessionHandle); - if (result != ERROR_SUCCESS) { - logger.error() << "FwpmTransactionCommit0 failed. Return value:.\n" - << result; - return false; - } - m_peerRules.clear(); - m_activeRules.clear(); - logger.debug() << "Firewall Disabled!"; - return true; + // Commit! + result = FwpmTransactionCommit0(m_sessionHandle); + if (result != ERROR_SUCCESS) { + logger.error() << "FwpmTransactionCommit0 failed. Return value:.\n" + << result; + return false; + } + m_peerRules.clear(); + m_activeRules.clear(); + logger.debug() << "Firewall Disabled!"; + return true; } bool WindowsFirewall::allowTrafficForAppOnAll(const QString& exePath, diff --git a/client/platforms/windows/daemon/windowsfirewall.h b/client/platforms/windows/daemon/windowsfirewall.h index e0d5ebe8..0e586894 100644 --- a/client/platforms/windows/daemon/windowsfirewall.h +++ b/client/platforms/windows/daemon/windowsfirewall.h @@ -35,6 +35,7 @@ class WindowsFirewall final : public QObject { bool enablePeerTraffic(const InterfaceConfig& config); bool disablePeerTraffic(const QString& pubkey); bool disableKillSwitch(); + bool allowAllTraffic(); private: WindowsFirewall(QObject* parent); diff --git a/client/secure_qsettings.cpp b/client/secure_qsettings.cpp index 88c0242b..9431873b 100644 --- a/client/secure_qsettings.cpp +++ b/client/secure_qsettings.cpp @@ -1,7 +1,7 @@ #include "secure_qsettings.h" -#include "QAead.h" -#include "QBlockCipher.h" +#include "../client/3rd/QSimpleCrypto/src/include/QAead.h" +#include "../client/3rd/QSimpleCrypto/src/include/QBlockCipher.h" #include "utilities.h" #include #include diff --git a/client/secure_qsettings.h b/client/secure_qsettings.h index 43890578..e55fe153 100644 --- a/client/secure_qsettings.h +++ b/client/secure_qsettings.h @@ -6,7 +6,7 @@ #include #include -#include "keychain.h" +#include "../client/3rd/qtkeychain/qtkeychain/keychain.h" constexpr const char *settingsKeyTag = "settingsKeyTag"; constexpr const char *settingsIvTag = "settingsIvTag"; diff --git a/ipc/ipc_interface.rep b/ipc/ipc_interface.rep index c0f031fe..77bdeadd 100644 --- a/ipc/ipc_interface.rep +++ b/ipc/ipc_interface.rep @@ -29,6 +29,7 @@ class IpcInterface SLOT( void StopRoutingIpv6() ); SLOT( bool disableKillSwitch() ); + SLOT( bool disableAllTraffic() ); SLOT( bool enablePeerTraffic( const QJsonObject &configStr) ); SLOT( bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex) ); SLOT( bool updateResolvers(const QString& ifname, const QList& resolvers) ); diff --git a/ipc/ipcserver.cpp b/ipc/ipcserver.cpp index bb8a4182..4e6bb4fe 100644 --- a/ipc/ipcserver.cpp +++ b/ipc/ipcserver.cpp @@ -8,21 +8,12 @@ #include "logger.h" #include "router.h" -#include "../core/networkUtilities.h" -#include "../client/protocols/protocols_defs.h" +#include "killswitch.h" + #ifdef Q_OS_WIN - #include "../client/platforms/windows/daemon/windowsdaemon.h" - #include "../client/platforms/windows/daemon/windowsfirewall.h" #include "tapcontroller_win.h" #endif -#ifdef Q_OS_LINUX - #include "../client/platforms/linux/daemon/linuxfirewall.h" -#endif - -#ifdef Q_OS_MACOS - #include "../client/platforms/macos/daemon/macosfirewall.h" -#endif IpcServer::IpcServer(QObject *parent) : IpcInterfaceSource(parent) @@ -35,10 +26,6 @@ int IpcServer::createPrivilegedProcess() qDebug() << "IpcServer::createPrivilegedProcess"; #endif -#ifdef Q_OS_WIN - WindowsFirewall::instance()->init(); -#endif - m_localpid++; ProcessDescriptor pd(this); @@ -192,166 +179,22 @@ void IpcServer::setLogsEnabled(bool enabled) } } +bool IpcServer::disableAllTraffic() +{ + return KillSwitch::instance()->disableAllTraffic(); +} + bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex) { -#ifdef Q_OS_WIN - return WindowsFirewall::instance()->enableKillSwitch(vpnAdapterIndex); -#endif - -#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) - int splitTunnelType = configStr.value("splitTunnelType").toInt(); - QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray(); - bool blockAll = 0; - bool allowNets = 0; - bool blockNets = 0; - QStringList allownets; - QStringList blocknets; - - if (splitTunnelType == 0) { - blockAll = true; - allowNets = true; - allownets.append(configStr.value("vpnServer").toString()); - } else if (splitTunnelType == 1) { - blockNets = true; - for (auto v : splitTunnelSites) { - blocknets.append(v.toString()); - } - } else if (splitTunnelType == 2) { - blockAll = true; - allowNets = true; - allownets.append(configStr.value("vpnServer").toString()); - for (auto v : splitTunnelSites) { - allownets.append(v.toString()); - } - } -#endif - -#ifdef Q_OS_LINUX - // double-check + ensure our firewall is installed and enabled - LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true); - LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), blockAll); - LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), allowNets); - LinuxFirewall::updateAllowNets(allownets); - LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), blockAll); - LinuxFirewall::updateBlockNets(blocknets); - LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), true); - LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true); - LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true); - LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), true); - LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), true); - QStringList dnsServers; - dnsServers.append(configStr.value(amnezia::config_key::dns1).toString()); - dnsServers.append(configStr.value(amnezia::config_key::dns2).toString()); - dnsServers.append("127.0.0.1"); - dnsServers.append("127.0.0.53"); - LinuxFirewall::updateDNSServers(dnsServers); - LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true); - LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true); -#endif - -#ifdef Q_OS_MACOS - - // double-check + ensure our firewall is installed and enabled. This is necessary as - // other software may disable pfctl before re-enabling with their own rules (e.g other VPNs) - if (!MacOSFirewall::isInstalled()) - MacOSFirewall::install(); - - MacOSFirewall::ensureRootAnchorPriority(); - MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true); - MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), blockAll); - MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), allowNets); - MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), allowNets, QStringLiteral("allownets"), allownets); - - MacOSFirewall::setAnchorEnabled(QStringLiteral("120.blockNets"), blockNets); - MacOSFirewall::setAnchorTable(QStringLiteral("120.blockNets"), blockNets, QStringLiteral("blocknets"), blocknets); - MacOSFirewall::setAnchorEnabled(QStringLiteral("200.allowVPN"), true); - MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true); - MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), true); - MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), true); - - QStringList dnsServers; - dnsServers.append(configStr.value(amnezia::config_key::dns1).toString()); - dnsServers.append(configStr.value(amnezia::config_key::dns2).toString()); - MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true); - MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), dnsServers); -#endif - - return true; + return KillSwitch::instance()->enableKillSwitch(configStr, vpnAdapterIndex); } bool IpcServer::disableKillSwitch() { -#ifdef Q_OS_WIN - return WindowsFirewall::instance()->disableKillSwitch(); -#endif - -#ifdef Q_OS_LINUX - LinuxFirewall::uninstall(); -#endif - -#ifdef Q_OS_MACOS - MacOSFirewall::uninstall(); -#endif - - return true; + return KillSwitch::instance()->disableKillSwitch(); } bool IpcServer::enablePeerTraffic(const QJsonObject &configStr) { -#ifdef Q_OS_WIN - InterfaceConfig config; - config.m_dnsServer = configStr.value(amnezia::config_key::dns1).toString(); - config.m_serverPublicKey = "openvpn"; - config.m_serverIpv4Gateway = configStr.value("vpnGateway").toString(); - config.m_serverIpv4AddrIn = configStr.value("vpnServer").toString(); - int vpnAdapterIndex = configStr.value("vpnAdapterIndex").toInt(); - int inetAdapterIndex = configStr.value("inetAdapterIndex").toInt(); - - int splitTunnelType = configStr.value("splitTunnelType").toInt(); - QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray(); - - QStringList AllowedIPAddesses; - - // Use APP split tunnel - if (splitTunnelType == 0 || splitTunnelType == 2) { - config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("0.0.0.0"), 0)); - config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("::"), 0)); - } - - if (splitTunnelType == 1) { - for (auto v : splitTunnelSites) { - QString ipRange = v.toString(); - if (ipRange.split('/').size() > 1) { - config.m_allowedIPAddressRanges.append( - IPAddress(QHostAddress(ipRange.split('/')[0]), atoi(ipRange.split('/')[1].toLocal8Bit()))); - } else { - config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress(ipRange), 32)); - } - } - } - - config.m_excludedAddresses.append(configStr.value("vpnServer").toString()); - if (splitTunnelType == 2) { - for (auto v : splitTunnelSites) { - QString ipRange = v.toString(); - config.m_excludedAddresses.append(ipRange); - } - } - - for (const QJsonValue &i : configStr.value(amnezia::config_key::splitTunnelApps).toArray()) { - if (!i.isString()) { - break; - } - config.m_vpnDisabledApps.append(i.toString()); - } - - // killSwitch toggle - if (QVariant(configStr.value(amnezia::config_key::killSwitchOption).toString()).toBool()) { - WindowsFirewall::instance()->enablePeerTraffic(config); - } - - WindowsDaemon::instance()->prepareActivation(config, inetAdapterIndex); - WindowsDaemon::instance()->activateSplitTunnel(config, vpnAdapterIndex); -#endif - return true; + return KillSwitch::instance()->enablePeerTraffic(configStr); } diff --git a/ipc/ipcserver.h b/ipc/ipcserver.h index 9810046b..0ab98467 100644 --- a/ipc/ipcserver.h +++ b/ipc/ipcserver.h @@ -34,6 +34,7 @@ public: virtual bool deleteTun(const QString &dev) override; virtual void StartRoutingIpv6() override; virtual void StopRoutingIpv6() override; + virtual bool disableAllTraffic() override; virtual bool enablePeerTraffic(const QJsonObject &configStr) override; virtual bool enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) override; virtual bool disableKillSwitch() override; diff --git a/service/server/CMakeLists.txt b/service/server/CMakeLists.txt index 0f101087..0189ee08 100644 --- a/service/server/CMakeLists.txt +++ b/service/server/CMakeLists.txt @@ -12,8 +12,47 @@ qt_standard_project_setup() configure_file(${CMAKE_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h) +set(QSIMPLECRYPTO_DIR ${CMAKE_CURRENT_LIST_DIR}/../../client/3rd/QSimpleCrypto/src) + + +set(OPENSSL_ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/../../client/3rd-prebuilt/3rd-prebuilt/openssl/") +set(OPENSSL_LIBRARIES_DIR "${OPENSSL_ROOT_DIR}/lib") + +set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/windows/include") +if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8") + set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/windows/win64/libssl.lib") + set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win64/libcrypto.lib") +else() + set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libssl.lib") + set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libcrypto.lib") +endif() + + +if(WIN32) + set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/windows/include") + if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8") + set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win64/libcrypto.lib") + else() + set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libcrypto.lib") + endif() +elseif(APPLE AND NOT IOS) + set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/macos/include") + set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libcrypto.a") +elseif(LINUX) + set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/linux/include") + set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/linux/x86_64/libcrypto.a") +endif() + +set(OPENSSL_USE_STATIC_LIBS TRUE) + +include_directories( + ${OPENSSL_INCLUDE_DIR} + ${QSIMPLECRYPTO_DIR} +) + set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/../../client/utilities.h + ${CMAKE_CURRENT_LIST_DIR}/../../client/secure_qsettings.h ${CMAKE_CURRENT_LIST_DIR}/../../client/core/networkUtilities.h ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipc.h ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.h @@ -22,12 +61,20 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/localserver.h ${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.h ${CMAKE_CURRENT_LIST_DIR}/router.h + ${CMAKE_CURRENT_LIST_DIR}/killswitch.h ${CMAKE_CURRENT_LIST_DIR}/systemservice.h ${CMAKE_CURRENT_BINARY_DIR}/version.h + ${QSIMPLECRYPTO_DIR}/include/QAead.h + ${QSIMPLECRYPTO_DIR}/include/QBlockCipher.h + ${QSIMPLECRYPTO_DIR}/include/QRsa.h + ${QSIMPLECRYPTO_DIR}/include/QSimpleCrypto_global.h + ${QSIMPLECRYPTO_DIR}/include/QX509.h + ${QSIMPLECRYPTO_DIR}/include/QX509Store.h ) set(SOURCES ${CMAKE_CURRENT_LIST_DIR}/../../client/utilities.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../client/secure_qsettings.cpp ${CMAKE_CURRENT_LIST_DIR}/../../client/core/networkUtilities.cpp ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.cpp ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserverprocess.cpp @@ -36,7 +83,13 @@ set(SOURCES ${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.cpp ${CMAKE_CURRENT_LIST_DIR}/main.cpp ${CMAKE_CURRENT_LIST_DIR}/router.cpp + ${CMAKE_CURRENT_LIST_DIR}/killswitch.cpp ${CMAKE_CURRENT_LIST_DIR}/systemservice.cpp + ${QSIMPLECRYPTO_DIR}/sources/QAead.cpp + ${QSIMPLECRYPTO_DIR}/sources/QBlockCipher.cpp + ${QSIMPLECRYPTO_DIR}/sources/QRsa.cpp + ${QSIMPLECRYPTO_DIR}/sources/QX509.cpp + ${QSIMPLECRYPTO_DIR}/sources/QX509Store.cpp ) # Mozilla headres @@ -132,6 +185,7 @@ if(WIN32) set(SOURCES ${SOURCES} ${CMAKE_CURRENT_LIST_DIR}/tapcontroller_win.cpp ${CMAKE_CURRENT_LIST_DIR}/router_win.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/daemon/windowsdaemon.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/daemon/windowsdaemontunnel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/daemon/windowsfirewall.cpp @@ -158,6 +212,8 @@ if(WIN32) gdi32 Advapi32 Kernel32 + ${OPENSSL_LIB_CRYPTO_PATH} + qt6keychain ) add_compile_definitions(_WINSOCKAPI_) diff --git a/service/server/killswitch.cpp b/service/server/killswitch.cpp new file mode 100644 index 00000000..60fab803 --- /dev/null +++ b/service/server/killswitch.cpp @@ -0,0 +1,228 @@ +#include "killswitch.h" + +#include "../client/protocols/protocols_defs.h" +#include "qjsonarray.h" +#include "version.h" + +#ifdef Q_OS_WIN + #include "../client/platforms/windows/daemon/windowsfirewall.h" + #include "../client/platforms/windows/daemon/windowsdaemon.h" +#endif + +#ifdef Q_OS_LINUX + #include "../client/platforms/linux/daemon/linuxfirewall.h" +#endif + +#ifdef Q_OS_MACOS + #include "../client/platforms/macos/daemon/macosfirewall.h" +#endif + +KillSwitch* s_instance = nullptr; + +KillSwitch* KillSwitch::instance() +{ + if (s_instance == nullptr) { + s_instance = new KillSwitch(qApp); + } + return s_instance; +} + +bool KillSwitch::init() +{ + WindowsFirewall::instance()->init(); + m_appSettigns = QSharedPointer(new SecureQSettings(ORGANIZATION_NAME, APPLICATION_NAME, nullptr)); + if (isStrictKillSwitchEnabled()) { + return disableAllTraffic(); + } +} + +bool KillSwitch::isStrictKillSwitchEnabled() +{ + return m_appSettigns->value("Conf/strictKillSwitchEnabled", false).toBool(); +} + +bool KillSwitch::disableKillSwitch() { + if (isStrictKillSwitchEnabled()) { + return disableAllTraffic(); + } + +#ifdef Q_OS_WIN + return WindowsFirewall::instance()->allowAllTraffic(); +#endif + +#ifdef Q_OS_LINUX + LinuxFirewall::uninstall(); +#endif + +#ifdef Q_OS_MACOS + MacOSFirewall::uninstall(); +#endif +} + +bool KillSwitch::disableAllTraffic() { +#ifdef Q_OS_WIN + WindowsFirewall::instance()->enableKillSwitch(-1); +#endif +#ifdef Q_OS_LINUX + if (!LinuxFirewall::isInstalled()) { + LinuxFirewall::install(); + } + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), true); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true); +#endif +#ifdef Q_OS_MACOS + // double-check + ensure our firewall is installed and enabled. This is necessary as + // other software may disable pfctl before re-enabling with their own rules (e.g other VPNs) + if (!MacOSFirewall::isInstalled()) + MacOSFirewall::install(); + MacOSFirewall::ensureRootAnchorPriority(); + MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), true); + MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true); +#endif + return true; +} + +bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) { +#ifdef Q_OS_WIN + InterfaceConfig config; + config.m_dnsServer = configStr.value(amnezia::config_key::dns1).toString(); + config.m_serverPublicKey = "openvpn"; + config.m_serverIpv4Gateway = configStr.value("vpnGateway").toString(); + config.m_serverIpv4AddrIn = configStr.value("vpnServer").toString(); + int vpnAdapterIndex = configStr.value("vpnAdapterIndex").toInt(); + int inetAdapterIndex = configStr.value("inetAdapterIndex").toInt(); + + int splitTunnelType = configStr.value("splitTunnelType").toInt(); + QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray(); + + // Use APP split tunnel + if (splitTunnelType == 0 || splitTunnelType == 2) { + config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("0.0.0.0"), 0)); + config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("::"), 0)); + } + + if (splitTunnelType == 1) { + for (auto v : splitTunnelSites) { + QString ipRange = v.toString(); + if (ipRange.split('/').size() > 1) { + config.m_allowedIPAddressRanges.append( + IPAddress(QHostAddress(ipRange.split('/')[0]), atoi(ipRange.split('/')[1].toLocal8Bit()))); + } else { + config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress(ipRange), 32)); + } + } + } + + config.m_excludedAddresses.append(configStr.value("vpnServer").toString()); + if (splitTunnelType == 2) { + for (auto v : splitTunnelSites) { + QString ipRange = v.toString(); + config.m_excludedAddresses.append(ipRange); + } + } + + for (const QJsonValue &i : configStr.value(amnezia::config_key::splitTunnelApps).toArray()) { + if (!i.isString()) { + break; + } + config.m_vpnDisabledApps.append(i.toString()); + } + + // killSwitch toggle + if (QVariant(configStr.value(amnezia::config_key::killSwitchOption).toString()).toBool()) { + WindowsFirewall::instance()->enablePeerTraffic(config); + } + + WindowsDaemon::instance()->prepareActivation(config, inetAdapterIndex); + WindowsDaemon::instance()->activateSplitTunnel(config, vpnAdapterIndex); +#endif + return true; +} + + +bool KillSwitch::enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) { +#ifdef Q_OS_WIN + return WindowsFirewall::instance()->enableKillSwitch(vpnAdapterIndex); +#endif + +#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) + int splitTunnelType = configStr.value("splitTunnelType").toInt(); + QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray(); + bool blockAll = 0; + bool allowNets = 0; + bool blockNets = 0; + QStringList allownets; + QStringList blocknets; + + + if (splitTunnelType == 0) { + blockAll = true; + allowNets = true; + allownets.append(configStr.value("vpnServer").toString()); + } else if (splitTunnelType == 1) { + blockNets = true; + for (auto v : splitTunnelSites) { + blocknets.append(v.toString()); + } + } else if (splitTunnelType == 2) { + blockAll = true; + allowNets = true; + allownets.append(configStr.value("vpnServer").toString()); + for (auto v : splitTunnelSites) { + allownets.append(v.toString()); + } + } +#endif + +#ifdef Q_OS_LINUX + if (!LinuxFirewall::isInstalled()) { + LinuxFirewall::install(); + // double-check + ensure our firewall is installed and enabled + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), blockAll); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), allowNets); + LinuxFirewall::updateAllowNets(allownets); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), blockAll); + LinuxFirewall::updateBlockNets(blocknets); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), true); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), true); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), true); + QStringList dnsServers; + dnsServers.append(configStr.value(amnezia::config_key::dns1).toString()); + dnsServers.append(configStr.value(amnezia::config_key::dns2).toString()); + dnsServers.append("127.0.0.1"); + dnsServers.append("127.0.0.53"); + LinuxFirewall::updateDNSServers(dnsServers); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true); +#endif + +#ifdef Q_OS_MACOS + // double-check + ensure our firewall is installed and enabled. This is necessary as + // other software may disable pfctl before re-enabling with their own rules (e.g other VPNs) + if (!MacOSFirewall::isInstalled()) + MacOSFirewall::install(); + + MacOSFirewall::ensureRootAnchorPriority(); + MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true); + MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), blockAll); + MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), allowNets); + MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), allowNets, QStringLiteral("allownets"), allownets); + + MacOSFirewall::setAnchorEnabled(QStringLiteral("120.blockNets"), blockNets); + MacOSFirewall::setAnchorTable(QStringLiteral("120.blockNets"), blockNets, QStringLiteral("blocknets"), blocknets); + MacOSFirewall::setAnchorEnabled(QStringLiteral("200.allowVPN"), true); + MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true); + MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), true); + MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), true); + + QStringList dnsServers; + dnsServers.append(configStr.value(amnezia::config_key::dns1).toString()); + dnsServers.append(configStr.value(amnezia::config_key::dns2).toString()); + MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true); + MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), dnsServers); +#endif + return true; +} diff --git a/service/server/killswitch.h b/service/server/killswitch.h new file mode 100644 index 00000000..36e64622 --- /dev/null +++ b/service/server/killswitch.h @@ -0,0 +1,27 @@ +#ifndef KILLSWITCH_H +#define KILLSWITCH_H + +#include +#include + +#include "secure_qsettings.h" + +class KillSwitch : public QObject +{ + Q_OBJECT +public: + static KillSwitch *instance(); + bool init(); + bool disableKillSwitch(); + bool disableAllTraffic(); + bool enablePeerTraffic( const QJsonObject &configStr); + bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex); + bool isStrictKillSwitchEnabled(); + +private: + KillSwitch(QObject* parent) {}; + QSharedPointer m_appSettigns; + +}; + +#endif // KILLSWITCH_H diff --git a/service/server/localserver.cpp b/service/server/localserver.cpp index 8a5079cb..4f005a59 100644 --- a/service/server/localserver.cpp +++ b/service/server/localserver.cpp @@ -5,13 +5,12 @@ #include "ipc.h" #include "localserver.h" -#include "utilities.h" -#include "router.h" +#include "killswitch.h" #include "logger.h" #ifdef Q_OS_WIN -#include "tapcontroller_win.h" + #include "tapcontroller_win.h" #endif namespace { @@ -47,6 +46,8 @@ LocalServer::LocalServer(QObject *parent) : QObject(parent), return; } + KillSwitch::instance()->init(); + #ifdef Q_OS_LINUX // Signal handling for a proper shutdown. QObject::connect(qApp, &QCoreApplication::aboutToQuit, From 08e5ff2eef05e4b5009499f6ba3ffe6b85d22094 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Tue, 31 Dec 2024 19:33:12 +0200 Subject: [PATCH 03/22] Killswitch strict mode for Linux and MacOS --- .../linux/daemon/wireguardutilslinux.cpp | 4 +- .../macos/daemon/wireguardutilsmacos.cpp | 4 +- client/protocols/openvpnprotocol.cpp | 5 ++ ipc/ipc_interface.rep | 1 + ipc/ipcserver.cpp | 5 ++ ipc/ipcserver.h | 1 + service/server/CMakeLists.txt | 7 ++ service/server/killswitch.cpp | 81 ++++++++++++++++--- service/server/killswitch.h | 3 +- 9 files changed, 97 insertions(+), 14 deletions(-) diff --git a/client/platforms/linux/daemon/wireguardutilslinux.cpp b/client/platforms/linux/daemon/wireguardutilslinux.cpp index 460a7fe1..81ae20db 100644 --- a/client/platforms/linux/daemon/wireguardutilslinux.cpp +++ b/client/platforms/linux/daemon/wireguardutilslinux.cpp @@ -17,6 +17,8 @@ #include "leakdetector.h" #include "logger.h" +#include "killswitch.h" + constexpr const int WG_TUN_PROC_TIMEOUT = 5000; constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg"; @@ -182,7 +184,7 @@ bool WireguardUtilsLinux::deleteInterface() { QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name")); // double-check + ensure our firewall is installed and enabled - LinuxFirewall::uninstall(); + KillSwitch::instance()->disableKillSwitch(); return true; } diff --git a/client/platforms/macos/daemon/wireguardutilsmacos.cpp b/client/platforms/macos/daemon/wireguardutilsmacos.cpp index e2802ebc..4e0e994e 100644 --- a/client/platforms/macos/daemon/wireguardutilsmacos.cpp +++ b/client/platforms/macos/daemon/wireguardutilsmacos.cpp @@ -15,6 +15,8 @@ #include "leakdetector.h" #include "logger.h" +#include "killswitch.h" + constexpr const int WG_TUN_PROC_TIMEOUT = 5000; constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg"; @@ -180,7 +182,7 @@ bool WireguardUtilsMacos::deleteInterface() { QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name")); // double-check + ensure our firewall is installed and enabled - MacOSFirewall::uninstall(); + KillSwitch::instance()->disableKillSwitch(); return true; } diff --git a/client/protocols/openvpnprotocol.cpp b/client/protocols/openvpnprotocol.cpp index 4c2feb52..1721a45a 100644 --- a/client/protocols/openvpnprotocol.cpp +++ b/client/protocols/openvpnprotocol.cpp @@ -171,6 +171,11 @@ ErrorCode OpenVpnProtocol::start() return lastError(); } +#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) + IpcClient::Interface()->allowTrafficTo(QStringList(NetworkUtilities::getIPAddress( + m_configData.value(amnezia::config_key::hostName).toString()))); +#endif + // Detect default gateway #ifdef Q_OS_MAC QProcess p; diff --git a/ipc/ipc_interface.rep b/ipc/ipc_interface.rep index 77bdeadd..0ce7626a 100644 --- a/ipc/ipc_interface.rep +++ b/ipc/ipc_interface.rep @@ -30,6 +30,7 @@ class IpcInterface SLOT( bool disableKillSwitch() ); SLOT( bool disableAllTraffic() ); + SLOT( bool allowTrafficTo( const QStringList ranges ) ); SLOT( bool enablePeerTraffic( const QJsonObject &configStr) ); SLOT( bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex) ); SLOT( bool updateResolvers(const QString& ifname, const QList& resolvers) ); diff --git a/ipc/ipcserver.cpp b/ipc/ipcserver.cpp index 4e6bb4fe..e3f729a0 100644 --- a/ipc/ipcserver.cpp +++ b/ipc/ipcserver.cpp @@ -179,6 +179,11 @@ void IpcServer::setLogsEnabled(bool enabled) } } +bool IpcServer::allowTrafficTo(QStringList ranges) +{ + return KillSwitch::instance()->allowTrafficTo(ranges); +} + bool IpcServer::disableAllTraffic() { return KillSwitch::instance()->disableAllTraffic(); diff --git a/ipc/ipcserver.h b/ipc/ipcserver.h index 0ab98467..452534cd 100644 --- a/ipc/ipcserver.h +++ b/ipc/ipcserver.h @@ -35,6 +35,7 @@ public: virtual void StartRoutingIpv6() override; virtual void StopRoutingIpv6() override; virtual bool disableAllTraffic() override; + virtual bool allowTrafficTo(QStringList ranges) override; virtual bool enablePeerTraffic(const QJsonObject &configStr) override; virtual bool enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) override; virtual bool disableKillSwitch() override; diff --git a/service/server/CMakeLists.txt b/service/server/CMakeLists.txt index 0189ee08..863a918c 100644 --- a/service/server/CMakeLists.txt +++ b/service/server/CMakeLists.txt @@ -258,6 +258,9 @@ if(APPLE) ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/macos/daemon/wireguardutilsmacos.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/macos/daemon/macosfirewall.cpp ) + + set(LIBS ${OPENSSL_LIB_CRYPTO_PATH} qt6keychain) + endif() if(LINUX) @@ -288,6 +291,9 @@ if(LINUX) ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxroutemonitor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxfirewall.cpp ) + + set(LIBS ${OPENSSL_LIB_CRYPTO_PATH} qt6keychain -static-libstdc++ -static-libgcc -ldl) + endif() include(${CMAKE_CURRENT_LIST_DIR}/../src/qtservice.cmake) @@ -300,6 +306,7 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) + add_executable(${PROJECT} ${SOURCES} ${HEADERS}) target_link_libraries(${PROJECT} PRIVATE Qt6::Core Qt6::Widgets Qt6::Network Qt6::RemoteObjects Qt6::Core5Compat Qt6::DBus ${LIBS}) target_compile_definitions(${PROJECT} PRIVATE "MZ_$") diff --git a/service/server/killswitch.cpp b/service/server/killswitch.cpp index 60fab803..1e823e13 100644 --- a/service/server/killswitch.cpp +++ b/service/server/killswitch.cpp @@ -1,5 +1,9 @@ #include "killswitch.h" + +#include +#include + #include "../client/protocols/protocols_defs.h" #include "qjsonarray.h" #include "version.h" @@ -29,11 +33,24 @@ KillSwitch* KillSwitch::instance() bool KillSwitch::init() { +#ifdef Q_OS_WIN WindowsFirewall::instance()->init(); +#endif +#ifdef Q_OS_LINUX + if (!LinuxFirewall::isInstalled()) { + LinuxFirewall::install(); + } +#endif +#ifdef Q_OS_MACOS + if (!MacOSFirewall::isInstalled()) { + MacOSFirewall::install(); + } +#endif m_appSettigns = QSharedPointer(new SecureQSettings(ORGANIZATION_NAME, APPLICATION_NAME, nullptr)); if (isStrictKillSwitchEnabled()) { return disableAllTraffic(); } + return true; } bool KillSwitch::isStrictKillSwitchEnabled() @@ -42,21 +59,46 @@ bool KillSwitch::isStrictKillSwitchEnabled() } bool KillSwitch::disableKillSwitch() { +#ifdef Q_OS_LINUX if (isStrictKillSwitchEnabled()) { - return disableAllTraffic(); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), true); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), false); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), false); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), false); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), false); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), false); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), false); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), false); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), false); + } else { + LinuxFirewall::uninstall(); } +#endif + +#ifdef Q_OS_MACOS + if (isStrictKillSwitchEnabled()) { + MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true); + MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), true); + MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), false); + MacOSFirewall::setAnchorEnabled(QStringLiteral("120.blockNets"), false); + MacOSFirewall::setAnchorEnabled(QStringLiteral("200.allowVPN"), false); + MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true); + MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), false); + MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), false); + MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), false); + } else { + MacOSFirewall::uninstall(); + } +#endif #ifdef Q_OS_WIN return WindowsFirewall::instance()->allowAllTraffic(); #endif -#ifdef Q_OS_LINUX - LinuxFirewall::uninstall(); -#endif + return true; -#ifdef Q_OS_MACOS - MacOSFirewall::uninstall(); -#endif } bool KillSwitch::disableAllTraffic() { @@ -69,6 +111,7 @@ bool KillSwitch::disableAllTraffic() { } LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), true); LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true); #endif #ifdef Q_OS_MACOS // double-check + ensure our firewall is installed and enabled. This is necessary as @@ -78,10 +121,26 @@ bool KillSwitch::disableAllTraffic() { MacOSFirewall::ensureRootAnchorPriority(); MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), true); MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true); + MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true); #endif return true; } +bool KillSwitch::allowTrafficTo(const QStringList &ranges) { + +#ifdef Q_OS_LINUX + LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), true); + LinuxFirewall::updateAllowNets(ranges); +#endif + +#ifdef Q_OS_MACOS + MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), true); + MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), true, QStringLiteral("allownets"), ranges); +#endif + + return true; +} + bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) { #ifdef Q_OS_WIN InterfaceConfig config; @@ -95,7 +154,7 @@ bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) { int splitTunnelType = configStr.value("splitTunnelType").toInt(); QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray(); - // Use APP split tunnel + // Use APP split tunnel if (splitTunnelType == 0 || splitTunnelType == 2) { config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("0.0.0.0"), 0)); config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("::"), 0)); @@ -139,8 +198,7 @@ bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) { return true; } - -bool KillSwitch::enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) { +bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex) { #ifdef Q_OS_WIN return WindowsFirewall::instance()->enableKillSwitch(vpnAdapterIndex); #endif @@ -154,7 +212,6 @@ bool KillSwitch::enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapter QStringList allownets; QStringList blocknets; - if (splitTunnelType == 0) { blockAll = true; allowNets = true; @@ -177,6 +234,8 @@ bool KillSwitch::enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapter #ifdef Q_OS_LINUX if (!LinuxFirewall::isInstalled()) { LinuxFirewall::install(); + } + // double-check + ensure our firewall is installed and enabled LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true); LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), blockAll); diff --git a/service/server/killswitch.h b/service/server/killswitch.h index 36e64622..d6fc0a0e 100644 --- a/service/server/killswitch.h +++ b/service/server/killswitch.h @@ -15,7 +15,8 @@ public: bool disableKillSwitch(); bool disableAllTraffic(); bool enablePeerTraffic( const QJsonObject &configStr); - bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex); + bool enableKillSwitch( const QJsonObject &configStr, int vpnAdapterIndex); + bool allowTrafficTo(const QStringList &ranges); bool isStrictKillSwitchEnabled(); private: From 5fc80121b488d571bff06c75a205e4e13b1c6a73 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Wed, 1 Jan 2025 07:43:53 +0200 Subject: [PATCH 04/22] Windows fixes --- service/server/killswitch.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/service/server/killswitch.cpp b/service/server/killswitch.cpp index 1e823e13..eca86854 100644 --- a/service/server/killswitch.cpp +++ b/service/server/killswitch.cpp @@ -94,6 +94,9 @@ bool KillSwitch::disableKillSwitch() { #endif #ifdef Q_OS_WIN + if (isStrictKillSwitchEnabled()) { + return disableAllTraffic(); + } return WindowsFirewall::instance()->allowAllTraffic(); #endif From 4407e2801b5ea204af2360ef5b921195951419f5 Mon Sep 17 00:00:00 2001 From: aiamnezia Date: Mon, 17 Feb 2025 21:35:08 +0400 Subject: [PATCH 05/22] feature: Add Kill Switch settings page with strict mode option --- client/resources.qrc | 1 + client/settings.cpp | 10 ++ client/settings.h | 4 + client/ui/controllers/pageController.h | 1 + client/ui/controllers/settingsController.cpp | 12 +++ client/ui/controllers/settingsController.h | 7 ++ client/ui/qml/Controls2/HeaderType.qml | 15 +++ .../ui/qml/Pages2/PageSettingsConnection.qml | 25 ++--- .../ui/qml/Pages2/PageSettingsKillSwitch.qml | 98 +++++++++++++++++++ 9 files changed, 155 insertions(+), 18 deletions(-) create mode 100644 client/ui/qml/Pages2/PageSettingsKillSwitch.qml diff --git a/client/resources.qrc b/client/resources.qrc index 38f0e79a..dc19cd15 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -197,6 +197,7 @@ ui/qml/Pages2/PageSettingsBackup.qml ui/qml/Pages2/PageSettingsConnection.qml ui/qml/Pages2/PageSettingsDns.qml + ui/qml/Pages2/PageSettingsKillSwitch.qml ui/qml/Pages2/PageSettingsLogging.qml ui/qml/Pages2/PageSettingsServerData.qml ui/qml/Pages2/PageSettingsServerInfo.qml diff --git a/client/settings.cpp b/client/settings.cpp index 7a572a13..c5679844 100644 --- a/client/settings.cpp +++ b/client/settings.cpp @@ -443,6 +443,16 @@ void Settings::setKillSwitchEnabled(bool enabled) setValue("Conf/killSwitchEnabled", enabled); } +bool Settings::isStrictKillSwitchEnabled() const +{ + return value("Conf/strictKillSwitchEnabled", false).toBool(); +} + +void Settings::setStrictKillSwitchEnabled(bool enabled) +{ + setValue("Conf/strictKillSwitchEnabled", enabled); +} + QString Settings::getInstallationUuid(const bool needCreate) { auto uuid = value("Conf/installationUuid", "").toString(); diff --git a/client/settings.h b/client/settings.h index f41f4d29..7f6d487e 100644 --- a/client/settings.h +++ b/client/settings.h @@ -213,6 +213,10 @@ public: bool isKillSwitchEnabled() const; void setKillSwitchEnabled(bool enabled); + + bool isStrictKillSwitchEnabled() const; + void setStrictKillSwitchEnabled(bool enabled); + QString getInstallationUuid(const bool needCreate); void resetGatewayEndpoint(); diff --git a/client/ui/controllers/pageController.h b/client/ui/controllers/pageController.h index ffbdd3a1..0894e5aa 100644 --- a/client/ui/controllers/pageController.h +++ b/client/ui/controllers/pageController.h @@ -31,6 +31,7 @@ namespace PageLoader PageSettingsLogging, PageSettingsSplitTunneling, PageSettingsAppSplitTunneling, + PageSettingsKillSwitch, PageServiceSftpSettings, PageServiceTorWebsiteSettings, diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index 6d777bd8..877d4a6e 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -245,6 +245,18 @@ bool SettingsController::isKillSwitchEnabled() void SettingsController::toggleKillSwitch(bool enable) { m_settings->setKillSwitchEnabled(enable); + emit killSwitchEnabledChanged(); +} + +bool SettingsController::isStrictKillSwitchEnabled() +{ + return m_settings->isStrictKillSwitchEnabled(); +} + +void SettingsController::toggleStrictKillSwitch(bool enable) +{ + m_settings->setStrictKillSwitchEnabled(enable); + emit strictKillSwitchEnabledChanged(); } bool SettingsController::isNotificationPermissionGranted() diff --git a/client/ui/controllers/settingsController.h b/client/ui/controllers/settingsController.h index efc18a7d..83d824a8 100644 --- a/client/ui/controllers/settingsController.h +++ b/client/ui/controllers/settingsController.h @@ -24,6 +24,8 @@ public: Q_PROPERTY(QString secondaryDns READ getSecondaryDns WRITE setSecondaryDns NOTIFY secondaryDnsChanged) Q_PROPERTY(bool isLoggingEnabled READ isLoggingEnabled WRITE toggleLogging NOTIFY loggingStateChanged) Q_PROPERTY(bool isNotificationPermissionGranted READ isNotificationPermissionGranted NOTIFY onNotificationStateChanged) + Q_PROPERTY(bool isKillSwitchEnabled READ isKillSwitchEnabled WRITE toggleKillSwitch NOTIFY killSwitchEnabledChanged) + Q_PROPERTY(bool strictKillSwitchEnabled READ isStrictKillSwitchEnabled WRITE toggleStrictKillSwitch NOTIFY strictKillSwitchEnabledChanged) Q_PROPERTY(bool isDevModeEnabled READ isDevModeEnabled NOTIFY devModeEnabled) Q_PROPERTY(QString gatewayEndpoint READ getGatewayEndpoint WRITE setGatewayEndpoint NOTIFY gatewayEndpointChanged) @@ -73,6 +75,9 @@ public slots: bool isKillSwitchEnabled(); void toggleKillSwitch(bool enable); + bool isStrictKillSwitchEnabled(); + void toggleStrictKillSwitch(bool enable); + bool isNotificationPermissionGranted(); void requestNotificationPermission(); @@ -93,6 +98,8 @@ signals: void primaryDnsChanged(); void secondaryDnsChanged(); void loggingStateChanged(); + void killSwitchEnabledChanged(); + void strictKillSwitchEnabledChanged(); void restoreBackupFinished(); void changeSettingsFinished(const QString &finishedMessage); diff --git a/client/ui/qml/Controls2/HeaderType.qml b/client/ui/qml/Controls2/HeaderType.qml index 1366148d..54f1386d 100644 --- a/client/ui/qml/Controls2/HeaderType.qml +++ b/client/ui/qml/Controls2/HeaderType.qml @@ -10,8 +10,11 @@ Item { property string actionButtonImage property var actionButtonFunction + property var switcherFunction + property bool showSwitcher: false property alias actionButton: headerActionButton + property alias switcher: headerSwitcher property string headerText property int headerTextMaximumLineCount: 2 @@ -56,6 +59,18 @@ Item { } } } + + SwitcherType { + id: headerSwitcher + Layout.alignment: Qt.AlignRight + visible: root.showSwitcher + + onToggled: { + if (switcherFunction && typeof switcherFunction === "function") { + switcherFunction(checked) + } + } + } } ParagraphTextType { diff --git a/client/ui/qml/Pages2/PageSettingsConnection.qml b/client/ui/qml/Pages2/PageSettingsConnection.qml index d3743b96..fe57d1c4 100644 --- a/client/ui/qml/Pages2/PageSettingsConnection.qml +++ b/client/ui/qml/Pages2/PageSettingsConnection.qml @@ -94,9 +94,7 @@ PageType { } } - DividerType { - visible: root.isAppSplitTinnelingEnabled - } + DividerType {} LabelWithButtonType { id: splitTunnelingButton2 @@ -119,29 +117,20 @@ PageType { visible: root.isAppSplitTinnelingEnabled } - SwitcherType { - id: killSwitchSwitcher + LabelWithButtonType { + id: killSwitchButton visible: !GC.isMobile() Layout.fillWidth: true - Layout.margins: 16 text: qsTr("KillSwitch") - descriptionText: qsTr("Disables your internet if your encrypted VPN connection drops out for any reason.") + descriptionText: qsTr("Blocks network connections without VPN") + rightImageSource: "qrc:/images/controls/chevron-right.svg" parentFlickable: fl - checked: SettingsController.isKillSwitchEnabled() - checkable: !ConnectionController.isConnected - onCheckedChanged: { - if (checked !== SettingsController.isKillSwitchEnabled()) { - SettingsController.toggleKillSwitch(checked) - } - } - onClicked: { - if (!checkable) { - PageController.showNotificationMessage(qsTr("Cannot change killSwitch settings during active connection")) - } + clickedFunction: function() { + PageController.goToPage(PageEnum.PageSettingsKillSwitch) } } diff --git a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml new file mode 100644 index 00000000..62f8dd71 --- /dev/null +++ b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml @@ -0,0 +1,98 @@ +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 + + HeaderType { + Layout.fillWidth: true + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + headerText: qsTr("Kill Switch") + 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 Kill Switch") + descriptionText: qsTr("Internet connection is blocked if VPN connection drops accidentally") + + onClicked: { + 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 Kill Switch") + descriptionText: qsTr("Internet connection is blocked even if VPN was turned off manually or not started") + + onClicked: { + SettingsController.strictKillSwitchEnabled = true + } + } + + DividerType {} + } + } +} \ No newline at end of file From 1fa96a09a08a686022e1a4b2a34b4d48a44c5bee Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Mon, 17 Feb 2025 21:49:20 +0200 Subject: [PATCH 06/22] fix windows build after merge --- service/server/killswitch.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/service/server/killswitch.cpp b/service/server/killswitch.cpp index eca86854..89dc0917 100644 --- a/service/server/killswitch.cpp +++ b/service/server/killswitch.cpp @@ -33,9 +33,6 @@ KillSwitch* KillSwitch::instance() bool KillSwitch::init() { -#ifdef Q_OS_WIN - WindowsFirewall::instance()->init(); -#endif #ifdef Q_OS_LINUX if (!LinuxFirewall::isInstalled()) { LinuxFirewall::install(); @@ -97,7 +94,7 @@ bool KillSwitch::disableKillSwitch() { if (isStrictKillSwitchEnabled()) { return disableAllTraffic(); } - return WindowsFirewall::instance()->allowAllTraffic(); + return WindowsFirewall::create(this)->allowAllTraffic(); #endif return true; @@ -106,7 +103,7 @@ bool KillSwitch::disableKillSwitch() { bool KillSwitch::disableAllTraffic() { #ifdef Q_OS_WIN - WindowsFirewall::instance()->enableKillSwitch(-1); + WindowsFirewall::create(this)->enableInterface(-1); #endif #ifdef Q_OS_LINUX if (!LinuxFirewall::isInstalled()) { @@ -192,7 +189,7 @@ bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) { // killSwitch toggle if (QVariant(configStr.value(amnezia::config_key::killSwitchOption).toString()).toBool()) { - WindowsFirewall::instance()->enablePeerTraffic(config); + WindowsFirewall::create(this)->enablePeerTraffic(config); } WindowsDaemon::instance()->prepareActivation(config, inetAdapterIndex); @@ -203,7 +200,7 @@ bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) { bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex) { #ifdef Q_OS_WIN - return WindowsFirewall::instance()->enableKillSwitch(vpnAdapterIndex); + return WindowsFirewall::create(this)->enableInterface(vpnAdapterIndex); #endif #if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) From 7a3520cb201ada95ac05eb34d7ff66839ca2394a Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Mon, 17 Feb 2025 22:43:38 +0200 Subject: [PATCH 07/22] Refresh killswitch mode when it toggled --- client/amnezia_application.cpp | 1 + client/vpnconnection.cpp | 7 +++++++ client/vpnconnection.h | 4 ++-- ipc/ipc_interface.rep | 1 + ipc/ipcserver.cpp | 5 +++++ ipc/ipcserver.h | 1 + service/server/killswitch.cpp | 12 ++++++++++-- service/server/killswitch.h | 1 + 8 files changed, 28 insertions(+), 4 deletions(-) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index 8706be58..7bac2cf6 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -457,6 +457,7 @@ void AmneziaApplication::initControllers() QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); }); } connect(m_settingsController.get(), &SettingsController::amneziaDnsToggled, m_serversModel.get(), &ServersModel::toggleAmneziaDns); + connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged, m_vpnConnection.get(), &VpnConnection::onKillswitchModeChanged); m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel)); m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get()); diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 042c51c7..bdd44c61 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -52,6 +52,13 @@ void VpnConnection::onBytesChanged(quint64 receivedBytes, quint64 sentBytes) emit bytesChanged(receivedBytes, sentBytes); } +void VpnConnection::onKillswitchModeChanged() +{ +#ifdef AMNEZIA_DESKTOP + IpcClient::Interface()->refreshKillSwitch(); +#endif +} + void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state) { diff --git a/client/vpnconnection.h b/client/vpnconnection.h index 0160edce..cc7bf0f5 100644 --- a/client/vpnconnection.h +++ b/client/vpnconnection.h @@ -48,14 +48,14 @@ public: public slots: void connectToVpn(int serverIndex, - const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration); + const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration); void disconnectFromVpn(); - void addRoutes(const QStringList &ips); void deleteRoutes(const QStringList &ips); void flushDns(); + void onKillswitchModeChanged(); signals: void bytesChanged(quint64 receivedBytes, quint64 sentBytes); diff --git a/ipc/ipc_interface.rep b/ipc/ipc_interface.rep index 0ce7626a..c36893b9 100644 --- a/ipc/ipc_interface.rep +++ b/ipc/ipc_interface.rep @@ -30,6 +30,7 @@ class IpcInterface SLOT( bool disableKillSwitch() ); SLOT( bool disableAllTraffic() ); + SLOT( bool refreshKillSwitch() ); SLOT( bool allowTrafficTo( const QStringList ranges ) ); SLOT( bool enablePeerTraffic( const QJsonObject &configStr) ); SLOT( bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex) ); diff --git a/ipc/ipcserver.cpp b/ipc/ipcserver.cpp index e3f729a0..598c7144 100644 --- a/ipc/ipcserver.cpp +++ b/ipc/ipcserver.cpp @@ -203,3 +203,8 @@ bool IpcServer::enablePeerTraffic(const QJsonObject &configStr) { return KillSwitch::instance()->enablePeerTraffic(configStr); } + +bool IpcServer::refreshKillSwitch() +{ + return KillSwitch::instance()->refresh(); +} diff --git a/ipc/ipcserver.h b/ipc/ipcserver.h index 452534cd..93d827a1 100644 --- a/ipc/ipcserver.h +++ b/ipc/ipcserver.h @@ -39,6 +39,7 @@ public: virtual bool enablePeerTraffic(const QJsonObject &configStr) override; virtual bool enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) override; virtual bool disableKillSwitch() override; + virtual bool refreshKillSwitch() override; virtual bool updateResolvers(const QString& ifname, const QList& resolvers) override; private: diff --git a/service/server/killswitch.cpp b/service/server/killswitch.cpp index 89dc0917..261ac789 100644 --- a/service/server/killswitch.cpp +++ b/service/server/killswitch.cpp @@ -43,15 +43,24 @@ bool KillSwitch::init() MacOSFirewall::install(); } #endif - m_appSettigns = QSharedPointer(new SecureQSettings(ORGANIZATION_NAME, APPLICATION_NAME, nullptr)); if (isStrictKillSwitchEnabled()) { return disableAllTraffic(); } return true; } +bool KillSwitch::refresh() +{ + if (isStrictKillSwitchEnabled()) { + return disableAllTraffic(); + } else { + return disableKillSwitch(); + } +} + bool KillSwitch::isStrictKillSwitchEnabled() { + m_appSettigns = QSharedPointer(new SecureQSettings(ORGANIZATION_NAME, APPLICATION_NAME, nullptr)); return m_appSettigns->value("Conf/strictKillSwitchEnabled", false).toBool(); } @@ -98,7 +107,6 @@ bool KillSwitch::disableKillSwitch() { #endif return true; - } bool KillSwitch::disableAllTraffic() { diff --git a/service/server/killswitch.h b/service/server/killswitch.h index d6fc0a0e..4659606d 100644 --- a/service/server/killswitch.h +++ b/service/server/killswitch.h @@ -12,6 +12,7 @@ class KillSwitch : public QObject public: static KillSwitch *instance(); bool init(); + bool refresh(); bool disableKillSwitch(); bool disableAllTraffic(); bool enablePeerTraffic( const QJsonObject &configStr); From 84d95477cbe5ab85dd7abd5f83818d53c773db60 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Tue, 18 Feb 2025 22:26:56 +0200 Subject: [PATCH 08/22] Use HLM to store strictMode flag --- client/amnezia_application.cpp | 2 +- client/ui/controllers/settingsController.cpp | 2 +- client/ui/controllers/settingsController.h | 2 +- client/vpnconnection.cpp | 6 ++++-- client/vpnconnection.h | 2 +- ipc/ipc_interface.rep | 2 +- ipc/ipcserver.cpp | 4 ++-- ipc/ipcserver.h | 2 +- service/server/killswitch.cpp | 12 +++++++++++- service/server/killswitch.h | 2 +- 10 files changed, 24 insertions(+), 12 deletions(-) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index 7bac2cf6..60397994 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -457,7 +457,7 @@ void AmneziaApplication::initControllers() QTimer::singleShot(1000, this, [this]() { m_connectionController->openConnection(); }); } connect(m_settingsController.get(), &SettingsController::amneziaDnsToggled, m_serversModel.get(), &ServersModel::toggleAmneziaDns); - connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged, m_vpnConnection.get(), &VpnConnection::onKillswitchModeChanged); + connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged, m_vpnConnection.get(), &VpnConnection::onKillSwitchModeChanged); m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel)); m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get()); diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index 8c43e16f..3808375a 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -256,7 +256,7 @@ bool SettingsController::isStrictKillSwitchEnabled() void SettingsController::toggleStrictKillSwitch(bool enable) { m_settings->setStrictKillSwitchEnabled(enable); - emit strictKillSwitchEnabledChanged(); + emit strictKillSwitchEnabledChanged(enable); } bool SettingsController::isNotificationPermissionGranted() diff --git a/client/ui/controllers/settingsController.h b/client/ui/controllers/settingsController.h index 0956a82c..1485e1a0 100644 --- a/client/ui/controllers/settingsController.h +++ b/client/ui/controllers/settingsController.h @@ -104,7 +104,7 @@ signals: void secondaryDnsChanged(); void loggingStateChanged(); void killSwitchEnabledChanged(); - void strictKillSwitchEnabledChanged(); + void strictKillSwitchEnabledChanged(bool enabled); void restoreBackupFinished(); void changeSettingsFinished(const QString &finishedMessage); diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index bdd44c61..3e3ef23b 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -52,10 +52,12 @@ void VpnConnection::onBytesChanged(quint64 receivedBytes, quint64 sentBytes) emit bytesChanged(receivedBytes, sentBytes); } -void VpnConnection::onKillswitchModeChanged() +void VpnConnection::onKillSwitchModeChanged(bool enabled) { #ifdef AMNEZIA_DESKTOP - IpcClient::Interface()->refreshKillSwitch(); + if (IpcClient::Interface()) { + IpcClient::Interface()->refreshKillSwitch(enabled); + } #endif } diff --git a/client/vpnconnection.h b/client/vpnconnection.h index cc7bf0f5..cb5aaaf9 100644 --- a/client/vpnconnection.h +++ b/client/vpnconnection.h @@ -55,7 +55,7 @@ public slots: void addRoutes(const QStringList &ips); void deleteRoutes(const QStringList &ips); void flushDns(); - void onKillswitchModeChanged(); + void onKillSwitchModeChanged(bool enabled); signals: void bytesChanged(quint64 receivedBytes, quint64 sentBytes); diff --git a/ipc/ipc_interface.rep b/ipc/ipc_interface.rep index c36893b9..c692817d 100644 --- a/ipc/ipc_interface.rep +++ b/ipc/ipc_interface.rep @@ -30,7 +30,7 @@ class IpcInterface SLOT( bool disableKillSwitch() ); SLOT( bool disableAllTraffic() ); - SLOT( bool refreshKillSwitch() ); + SLOT( bool refreshKillSwitch( bool enabled ) ); SLOT( bool allowTrafficTo( const QStringList ranges ) ); SLOT( bool enablePeerTraffic( const QJsonObject &configStr) ); SLOT( bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex) ); diff --git a/ipc/ipcserver.cpp b/ipc/ipcserver.cpp index 598c7144..ced2987e 100644 --- a/ipc/ipcserver.cpp +++ b/ipc/ipcserver.cpp @@ -204,7 +204,7 @@ bool IpcServer::enablePeerTraffic(const QJsonObject &configStr) return KillSwitch::instance()->enablePeerTraffic(configStr); } -bool IpcServer::refreshKillSwitch() +bool IpcServer::refreshKillSwitch(bool enabled) { - return KillSwitch::instance()->refresh(); + return KillSwitch::instance()->refresh(enabled); } diff --git a/ipc/ipcserver.h b/ipc/ipcserver.h index 93d827a1..31cd007f 100644 --- a/ipc/ipcserver.h +++ b/ipc/ipcserver.h @@ -39,7 +39,7 @@ public: virtual bool enablePeerTraffic(const QJsonObject &configStr) override; virtual bool enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) override; virtual bool disableKillSwitch() override; - virtual bool refreshKillSwitch() override; + virtual bool refreshKillSwitch( bool enabled ) override; virtual bool updateResolvers(const QString& ifname, const QList& resolvers) override; private: diff --git a/service/server/killswitch.cpp b/service/server/killswitch.cpp index 261ac789..24e162aa 100644 --- a/service/server/killswitch.cpp +++ b/service/server/killswitch.cpp @@ -49,8 +49,13 @@ bool KillSwitch::init() return true; } -bool KillSwitch::refresh() +bool KillSwitch::refresh(bool enabled) { +#ifdef Q_OS_WIN + QSettings RegHLM("HKEY_LOCAL_MACHINE\\Software\\" + QString(ORGANIZATION_NAME) + + "\\" + QString(APPLICATION_NAME), QSettings::NativeFormat); + RegHLM.setValue("strictKillSwitchEnabled", enabled); +#endif if (isStrictKillSwitchEnabled()) { return disableAllTraffic(); } else { @@ -60,6 +65,11 @@ bool KillSwitch::refresh() bool KillSwitch::isStrictKillSwitchEnabled() { +#ifdef Q_OS_WIN + QSettings RegHLM("HKEY_LOCAL_MACHINE\\Software\\" + QString(ORGANIZATION_NAME) + + "\\" + QString(APPLICATION_NAME), QSettings::NativeFormat); + return RegHLM.value("strictKillSwitchEnabled", false).toBool(); +#endif m_appSettigns = QSharedPointer(new SecureQSettings(ORGANIZATION_NAME, APPLICATION_NAME, nullptr)); return m_appSettigns->value("Conf/strictKillSwitchEnabled", false).toBool(); } diff --git a/service/server/killswitch.h b/service/server/killswitch.h index 4659606d..a468c6c2 100644 --- a/service/server/killswitch.h +++ b/service/server/killswitch.h @@ -12,7 +12,7 @@ class KillSwitch : public QObject public: static KillSwitch *instance(); bool init(); - bool refresh(); + bool refresh(bool enabled); bool disableKillSwitch(); bool disableAllTraffic(); bool enablePeerTraffic( const QJsonObject &configStr); From 8e2e3a916acb748f7997a67a5c81e94d02488472 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Wed, 19 Feb 2025 00:02:16 +0200 Subject: [PATCH 09/22] Some Linux updates --- client/vpnconnection.cpp | 13 +++++++++++++ service/server/killswitch.cpp | 8 +++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 3e3ef23b..474bd887 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -55,7 +55,20 @@ void VpnConnection::onBytesChanged(quint64 receivedBytes, quint64 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 diff --git a/service/server/killswitch.cpp b/service/server/killswitch.cpp index 24e162aa..ae4462a6 100644 --- a/service/server/killswitch.cpp +++ b/service/server/killswitch.cpp @@ -37,15 +37,18 @@ bool KillSwitch::init() if (!LinuxFirewall::isInstalled()) { LinuxFirewall::install(); } + m_appSettigns = QSharedPointer(new SecureQSettings(ORGANIZATION_NAME, APPLICATION_NAME, nullptr)); #endif #ifdef Q_OS_MACOS if (!MacOSFirewall::isInstalled()) { MacOSFirewall::install(); } + m_appSettigns = QSharedPointer(new SecureQSettings(ORGANIZATION_NAME, APPLICATION_NAME, nullptr)); #endif if (isStrictKillSwitchEnabled()) { return disableAllTraffic(); } + return true; } @@ -56,6 +59,9 @@ bool KillSwitch::refresh(bool enabled) + "\\" + QString(APPLICATION_NAME), QSettings::NativeFormat); RegHLM.setValue("strictKillSwitchEnabled", enabled); #endif + + m_appSettigns->setValue("Conf/strictKillSwitchEnabled", enabled); + if (isStrictKillSwitchEnabled()) { return disableAllTraffic(); } else { @@ -70,7 +76,7 @@ bool KillSwitch::isStrictKillSwitchEnabled() + "\\" + QString(APPLICATION_NAME), QSettings::NativeFormat); return RegHLM.value("strictKillSwitchEnabled", false).toBool(); #endif - m_appSettigns = QSharedPointer(new SecureQSettings(ORGANIZATION_NAME, APPLICATION_NAME, nullptr)); + m_appSettigns->sync(); return m_appSettigns->value("Conf/strictKillSwitchEnabled", false).toBool(); } From 13fd957647efb1890d28fa45143439dcd3b1e88c Mon Sep 17 00:00:00 2001 From: aiamnezia Date: Wed, 19 Feb 2025 16:03:24 +0400 Subject: [PATCH 10/22] feat: Enhance VerticalRadioButton with improved styling and disabled states --- .../ui/qml/Controls2/VerticalRadioButton.qml | 29 ++++++++++++++----- .../ui/qml/Pages2/PageSettingsKillSwitch.qml | 6 ++++ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/client/ui/qml/Controls2/VerticalRadioButton.qml b/client/ui/qml/Controls2/VerticalRadioButton.qml index bee8ef7b..1c878f15 100644 --- a/client/ui/qml/Controls2/VerticalRadioButton.qml +++ b/client/ui/qml/Controls2/VerticalRadioButton.qml @@ -20,7 +20,11 @@ RadioButton { property string selectedColor: AmneziaStyle.color.transparent property string textColor: AmneziaStyle.color.paleGray + property string textDisabledColor: AmneziaStyle.color.mutedGray property string selectedTextColor: AmneziaStyle.color.goldenApricot + property string selectedTextDisabledColor: AmneziaStyle.color.burntOrange + property string descriptionColor: AmneziaStyle.color.mutedGray + property string descriptionDisabledColor: AmneziaStyle.color.charcoalGray property string borderFocusedColor: AmneziaStyle.color.paleGray property int borderFocusedWidth: 1 @@ -30,6 +34,12 @@ RadioButton { property bool isFocusable: true + + property string radioButtonInnerCirclePressedSource: "qrc:/images/controls/radio-button-inner-circle-pressed.png" + property string radioButtonInnerCircleSource: "qrc:/images/controls/radio-button-inner-circle.png" + property string radioButtonPressedSource: "qrc:/images/controls/radio-button-pressed.svg" + property string radioButtonDefaultSource: "qrc:/images/controls/radio-button.svg" + Keys.onTabPressed: { FocusController.nextKeyTabItem() } @@ -94,14 +104,15 @@ RadioButton { if (showImage) { return imageSource } else if (root.pressed) { - return "qrc:/images/controls/radio-button-inner-circle-pressed.png" + return root.radioButtonInnerCirclePressedSource } else if (root.checked) { - return "qrc:/images/controls/radio-button-inner-circle.png" + return root.radioButtonInnerCircleSource } return "" } + opacity: root.enabled ? 1.0 : 0.3 anchors.centerIn: parent width: 24 @@ -113,12 +124,13 @@ RadioButton { if (showImage) { return "" } else if (root.pressed || root.checked) { - return "qrc:/images/controls/radio-button-pressed.svg" + return root.radioButtonPressedSource } else { - return "qrc:/images/controls/radio-button.svg" + return root.radioButtonDefaultSource } } + opacity: root.enabled ? 1.0 : 0.3 anchors.centerIn: parent width: 24 @@ -148,10 +160,11 @@ RadioButton { elide: root.textElide color: { - if (root.checked) { - return selectedTextColor + if (root.enabled) { + return root.checked ? selectedTextColor : textColor + } else { + return root.checked ? selectedTextDisabledColor : textDisabledColor } - return textColor } Layout.fillWidth: true @@ -164,7 +177,7 @@ RadioButton { CaptionTextType { id: description - color: AmneziaStyle.color.mutedGray + color: root.enabled ? root.descriptionColor : root.descriptionDisabledColor text: root.descriptionText visible: root.descriptionText !== "" diff --git a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml index 62f8dd71..08b06397 100644 --- a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml +++ b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml @@ -62,6 +62,9 @@ PageType { Layout.leftMargin: 16 Layout.rightMargin: 16 + selectedTextColor: AmneziaStyle.color.paleGray + selectedTextDisabledColor: AmneziaStyle.color.mutedGray + radioButtonPressedSource: "qrc:/images/controls/radio-button.svg" enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected checked: !SettingsController.strictKillSwitchEnabled @@ -81,6 +84,9 @@ PageType { Layout.leftMargin: 16 Layout.rightMargin: 16 + selectedTextColor: AmneziaStyle.color.paleGray + selectedTextDisabledColor: AmneziaStyle.color.mutedGray + radioButtonPressedSource: "qrc:/images/controls/radio-button.svg" enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected checked: SettingsController.strictKillSwitchEnabled From 7a926f2eefd403fa61a98c05631fd83b7d835352 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Wed, 19 Feb 2025 15:54:21 +0200 Subject: [PATCH 11/22] Refresh killSwitch state update --- service/server/killswitch.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/service/server/killswitch.cpp b/service/server/killswitch.cpp index ae4462a6..88874113 100644 --- a/service/server/killswitch.cpp +++ b/service/server/killswitch.cpp @@ -54,19 +54,19 @@ bool KillSwitch::init() bool KillSwitch::refresh(bool enabled) { -#ifdef Q_OS_WIN +#if defined(Q_OS_WIN) QSettings RegHLM("HKEY_LOCAL_MACHINE\\Software\\" + QString(ORGANIZATION_NAME) + "\\" + QString(APPLICATION_NAME), QSettings::NativeFormat); RegHLM.setValue("strictKillSwitchEnabled", enabled); -#endif - +#elif m_appSettigns->setValue("Conf/strictKillSwitchEnabled", enabled); - +#endif if (isStrictKillSwitchEnabled()) { return disableAllTraffic(); } else { return disableKillSwitch(); } + } bool KillSwitch::isStrictKillSwitchEnabled() From 1f8a6114f86529a9555358842bbe431ce26d4f4e Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Wed, 19 Feb 2025 16:04:29 +0200 Subject: [PATCH 12/22] Fix build --- service/server/killswitch.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/service/server/killswitch.cpp b/service/server/killswitch.cpp index 88874113..fb3feccb 100644 --- a/service/server/killswitch.cpp +++ b/service/server/killswitch.cpp @@ -54,13 +54,16 @@ bool KillSwitch::init() bool KillSwitch::refresh(bool enabled) { -#if defined(Q_OS_WIN) +#ifdef Q_OS_WIN QSettings RegHLM("HKEY_LOCAL_MACHINE\\Software\\" + QString(ORGANIZATION_NAME) + "\\" + QString(APPLICATION_NAME), QSettings::NativeFormat); RegHLM.setValue("strictKillSwitchEnabled", enabled); -#elif +#endif + +#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) m_appSettigns->setValue("Conf/strictKillSwitchEnabled", enabled); #endif + if (isStrictKillSwitchEnabled()) { return disableAllTraffic(); } else { From ac281cee5b6b7c7c4de63e215cc2a3ee285643fa Mon Sep 17 00:00:00 2001 From: aiamnezia Date: Thu, 20 Feb 2025 16:36:28 +0400 Subject: [PATCH 13/22] refactor: Modularize header components --- client/resources.qrc | 3 + client/ui/qml/Controls2/BaseHeaderType.qml | 45 ++++++++ client/ui/qml/Controls2/HeaderType.qml | 101 +----------------- .../ui/qml/Controls2/HeaderTypeWithButton.qml | 44 ++++++++ .../qml/Controls2/HeaderTypeWithSwitcher.qml | 28 +++++ .../ui/qml/Pages2/PageSettingsKillSwitch.qml | 2 +- 6 files changed, 123 insertions(+), 100 deletions(-) create mode 100644 client/ui/qml/Controls2/BaseHeaderType.qml create mode 100644 client/ui/qml/Controls2/HeaderTypeWithButton.qml create mode 100644 client/ui/qml/Controls2/HeaderTypeWithSwitcher.qml diff --git a/client/resources.qrc b/client/resources.qrc index 8006b3d3..f03abd17 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -144,6 +144,9 @@ ui/qml/Controls2/FlickableType.qml ui/qml/Controls2/Header2Type.qml ui/qml/Controls2/HeaderType.qml + ui/qml/Controls2/BaseHeaderType.qml + ui/qml/Controls2/HeaderTypeWithButton.qml + ui/qml/Controls2/HeaderTypeWithSwitcher.qml ui/qml/Controls2/HorizontalRadioButton.qml ui/qml/Controls2/ImageButtonType.qml ui/qml/Controls2/LabelWithButtonType.qml diff --git a/client/ui/qml/Controls2/BaseHeaderType.qml b/client/ui/qml/Controls2/BaseHeaderType.qml new file mode 100644 index 00000000..fbb6ba18 --- /dev/null +++ b/client/ui/qml/Controls2/BaseHeaderType.qml @@ -0,0 +1,45 @@ +import QtQuick +import QtQuick.Layouts + +import Style 1.0 + +import "TextTypes" + +Item { + id: root + + property string headerText + property int headerTextMaximumLineCount: 2 + property int headerTextElide: Qt.ElideRight + property string descriptionText + property alias headerRow: headerRow + + implicitWidth: content.implicitWidth + implicitHeight: content.implicitHeight + + ColumnLayout { + id: content + anchors.fill: parent + + RowLayout { + id: headerRow + + Header1TextType { + id: header + Layout.fillWidth: true + text: root.headerText + maximumLineCount: root.headerTextMaximumLineCount + elide: root.headerTextElide + } + } + + ParagraphTextType { + id: description + Layout.topMargin: 16 + Layout.fillWidth: true + text: root.descriptionText + color: AmneziaStyle.color.mutedGray + visible: root.descriptionText !== "" + } + } +} \ No newline at end of file diff --git a/client/ui/qml/Controls2/HeaderType.qml b/client/ui/qml/Controls2/HeaderType.qml index 54f1386d..1f074d54 100644 --- a/client/ui/qml/Controls2/HeaderType.qml +++ b/client/ui/qml/Controls2/HeaderType.qml @@ -1,101 +1,4 @@ import QtQuick -import QtQuick.Layouts -import Style 1.0 - -import "TextTypes" - -Item { - id: root - - property string actionButtonImage - property var actionButtonFunction - property var switcherFunction - property bool showSwitcher: false - - property alias actionButton: headerActionButton - property alias switcher: headerSwitcher - - 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() - } - } - } - - SwitcherType { - id: headerSwitcher - Layout.alignment: Qt.AlignRight - visible: root.showSwitcher - - onToggled: { - if (switcherFunction && typeof switcherFunction === "function") { - switcherFunction(checked) - } - } - } - } - - 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() - } - } -} +// @deprecated Use BaseHeaderType, HeaderTypeWithButton or HeaderTypeWithSwitcher instead +HeaderTypeWithButton {} diff --git a/client/ui/qml/Controls2/HeaderTypeWithButton.qml b/client/ui/qml/Controls2/HeaderTypeWithButton.qml new file mode 100644 index 00000000..8258f8d9 --- /dev/null +++ b/client/ui/qml/Controls2/HeaderTypeWithButton.qml @@ -0,0 +1,44 @@ +import QtQuick +import QtQuick.Layouts + +import Style 1.0 + +BaseHeaderType { + id: root + + property string actionButtonImage + property var actionButtonFunction + property alias actionButton: headerActionButton + + Component.onCompleted: { + headerRow.children.push(headerActionButton) + } + + ImageButtonType { + id: headerActionButton + implicitWidth: 40 + implicitHeight: 40 + Layout.alignment: Qt.AlignRight + image: root.actionButtonImage + imageColor: AmneziaStyle.color.paleGray + visible: image ? true : false + + onClicked: { + if (actionButtonFunction && typeof actionButtonFunction === "function") { + actionButtonFunction() + } + } + } + + Keys.onEnterPressed: { + if (actionButtonFunction && typeof actionButtonFunction === "function") { + actionButtonFunction() + } + } + + Keys.onReturnPressed: { + if (actionButtonFunction && typeof actionButtonFunction === "function") { + actionButtonFunction() + } + } +} \ No newline at end of file diff --git a/client/ui/qml/Controls2/HeaderTypeWithSwitcher.qml b/client/ui/qml/Controls2/HeaderTypeWithSwitcher.qml new file mode 100644 index 00000000..81a078d1 --- /dev/null +++ b/client/ui/qml/Controls2/HeaderTypeWithSwitcher.qml @@ -0,0 +1,28 @@ +import QtQuick +import QtQuick.Layouts + +import Style 1.0 + +BaseHeaderType { + id: root + + property var switcherFunction + property bool showSwitcher: false + property alias switcher: headerSwitcher + + Component.onCompleted: { + headerRow.children.push(headerSwitcher) + } + + SwitcherType { + id: headerSwitcher + Layout.alignment: Qt.AlignRight + visible: root.showSwitcher + + onToggled: { + if (switcherFunction && typeof switcherFunction === "function") { + switcherFunction(checked) + } + } + } +} \ No newline at end of file diff --git a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml index 08b06397..18cd9626 100644 --- a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml +++ b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml @@ -32,7 +32,7 @@ PageType { anchors.left: parent.left anchors.right: parent.right - HeaderType { + HeaderTypeWithSwitcher { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 From d0fe48b8c7ef0df3ce192f803a02d64c0159dfd7 Mon Sep 17 00:00:00 2001 From: aiamnezia Date: Mon, 24 Feb 2025 16:16:56 +0400 Subject: [PATCH 14/22] Change kill switch radio button styling --- client/ui/qml/Pages2/PageSettingsKillSwitch.qml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml index 18cd9626..211a1507 100644 --- a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml +++ b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml @@ -62,9 +62,6 @@ PageType { Layout.leftMargin: 16 Layout.rightMargin: 16 - selectedTextColor: AmneziaStyle.color.paleGray - selectedTextDisabledColor: AmneziaStyle.color.mutedGray - radioButtonPressedSource: "qrc:/images/controls/radio-button.svg" enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected checked: !SettingsController.strictKillSwitchEnabled @@ -84,9 +81,6 @@ PageType { Layout.leftMargin: 16 Layout.rightMargin: 16 - selectedTextColor: AmneziaStyle.color.paleGray - selectedTextDisabledColor: AmneziaStyle.color.mutedGray - radioButtonPressedSource: "qrc:/images/controls/radio-button.svg" enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected checked: SettingsController.strictKillSwitchEnabled From 13127cd64c303254271f77a2de93c5148807fb21 Mon Sep 17 00:00:00 2001 From: aiamnezia Date: Mon, 24 Feb 2025 17:52:40 +0400 Subject: [PATCH 15/22] Fix strict kill switch mode handling --- client/core/controllers/coreController.cpp | 7 +++++++ client/core/controllers/coreController.h | 1 + 2 files changed, 8 insertions(+) diff --git a/client/core/controllers/coreController.cpp b/client/core/controllers/coreController.cpp index 3d71ce80..2b83f13c 100644 --- a/client/core/controllers/coreController.cpp +++ b/client/core/controllers/coreController.cpp @@ -206,6 +206,7 @@ void CoreController::initSignalHandlers() initAutoConnectHandler(); initAmneziaDnsToggledHandler(); initPrepareConfigHandler(); + initStrictKillSwitchHandler(); } void CoreController::initNotificationHandler() @@ -332,6 +333,12 @@ void CoreController::initPrepareConfigHandler() }); } +void CoreController::initStrictKillSwitchHandler() +{ + connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged, + m_vpnConnection.get(), &VpnConnection::onKillSwitchModeChanged); +} + QSharedPointer CoreController::pageController() const { return m_pageController; diff --git a/client/core/controllers/coreController.h b/client/core/controllers/coreController.h index 029044c4..9c6e8d9e 100644 --- a/client/core/controllers/coreController.h +++ b/client/core/controllers/coreController.h @@ -79,6 +79,7 @@ private: void initAutoConnectHandler(); void initAmneziaDnsToggledHandler(); void initPrepareConfigHandler(); + void initStrictKillSwitchHandler(); QQmlApplicationEngine *m_engine {}; // TODO use parent child system here? std::shared_ptr m_settings; From 1c2b1f1e0f479a657b6d35600efe0e63249efb14 Mon Sep 17 00:00:00 2001 From: aiamnezia Date: Mon, 24 Feb 2025 19:45:50 +0400 Subject: [PATCH 16/22] Refactor: Replace HeaderType with new Types for headers in QML pages --- client/ui/qml/Pages2/PageDeinstalling.qml | 2 +- client/ui/qml/Pages2/PageDevMenu.qml | 2 +- .../Pages2/PageProtocolAwgClientSettings.qml | 2 +- .../ui/qml/Pages2/PageProtocolAwgSettings.qml | 2 +- .../qml/Pages2/PageProtocolCloakSettings.qml | 2 +- .../Pages2/PageProtocolOpenVpnSettings.qml | 2 +- client/ui/qml/Pages2/PageProtocolRaw.qml | 2 +- .../PageProtocolShadowSocksSettings.qml | 2 +- .../PageProtocolWireGuardClientSettings.qml | 2 +- .../Pages2/PageProtocolWireGuardSettings.qml | 2 +- .../qml/Pages2/PageProtocolXraySettings.qml | 2 +- .../ui/qml/Pages2/PageServiceDnsSettings.qml | 2 +- .../ui/qml/Pages2/PageServiceSftpSettings.qml | 2 +- .../Pages2/PageServiceSocksProxySettings.qml | 4 +- .../Pages2/PageServiceTorWebsiteSettings.qml | 2 +- client/ui/qml/Pages2/PageSettings.qml | 2 +- .../PageSettingsApiAvailableCountries.qml | 2 +- .../Pages2/PageSettingsApiInstructions.qml | 2 +- .../Pages2/PageSettingsApiNativeConfigs.qml | 2 +- .../qml/Pages2/PageSettingsApiServerInfo.qml | 2 +- .../ui/qml/Pages2/PageSettingsApiSupport.qml | 2 +- .../Pages2/PageSettingsAppSplitTunneling.qml | 31 ++++++---------- .../ui/qml/Pages2/PageSettingsApplication.qml | 2 +- client/ui/qml/Pages2/PageSettingsBackup.qml | 2 +- .../ui/qml/Pages2/PageSettingsConnection.qml | 2 +- client/ui/qml/Pages2/PageSettingsDns.qml | 2 +- client/ui/qml/Pages2/PageSettingsLogging.qml | 2 +- .../ui/qml/Pages2/PageSettingsServerInfo.qml | 2 +- .../qml/Pages2/PageSettingsServerProtocol.qml | 2 +- .../ui/qml/Pages2/PageSettingsServersList.qml | 2 +- .../qml/Pages2/PageSettingsSplitTunneling.qml | 37 +++++++------------ .../Pages2/PageSetupWizardApiServiceInfo.qml | 2 +- .../Pages2/PageSetupWizardApiServicesList.qml | 2 +- .../Pages2/PageSetupWizardConfigSource.qml | 4 +- .../qml/Pages2/PageSetupWizardCredentials.qml | 2 +- client/ui/qml/Pages2/PageSetupWizardEasy.qml | 2 +- .../qml/Pages2/PageSetupWizardInstalling.qml | 2 +- .../PageSetupWizardProtocolSettings.qml | 2 +- .../qml/Pages2/PageSetupWizardProtocols.qml | 2 +- .../ui/qml/Pages2/PageSetupWizardTextKey.qml | 2 +- .../qml/Pages2/PageSetupWizardViewConfig.qml | 2 +- client/ui/qml/Pages2/PageShare.qml | 2 +- client/ui/qml/Pages2/PageShareFullAccess.qml | 2 +- 43 files changed, 68 insertions(+), 86 deletions(-) diff --git a/client/ui/qml/Pages2/PageDeinstalling.qml b/client/ui/qml/Pages2/PageDeinstalling.qml index f5fdb29a..69b1f319 100644 --- a/client/ui/qml/Pages2/PageDeinstalling.qml +++ b/client/ui/qml/Pages2/PageDeinstalling.qml @@ -56,7 +56,7 @@ PageType { anchors.rightMargin: 16 anchors.leftMargin: 16 - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.topMargin: 20 diff --git a/client/ui/qml/Pages2/PageDevMenu.qml b/client/ui/qml/Pages2/PageDevMenu.qml index d7afde1d..5fccb43a 100644 --- a/client/ui/qml/Pages2/PageDevMenu.qml +++ b/client/ui/qml/Pages2/PageDevMenu.qml @@ -39,7 +39,7 @@ PageType { header: ColumnLayout { width: listView.width - HeaderType { + BaseHeaderType { id: header Layout.fillWidth: true diff --git a/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml b/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml index c22fdf0c..b8cf5f93 100644 --- a/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml @@ -91,7 +91,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true headerText: qsTr("AmneziaWG settings") diff --git a/client/ui/qml/Pages2/PageProtocolAwgSettings.qml b/client/ui/qml/Pages2/PageProtocolAwgSettings.qml index 8c629b68..e8fd2b94 100644 --- a/client/ui/qml/Pages2/PageProtocolAwgSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolAwgSettings.qml @@ -91,7 +91,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true headerText: qsTr("AmneziaWG settings") diff --git a/client/ui/qml/Pages2/PageProtocolCloakSettings.qml b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml index 686ccd7b..7a0fafbd 100644 --- a/client/ui/qml/Pages2/PageProtocolCloakSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml @@ -76,7 +76,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true headerText: qsTr("Cloak settings") diff --git a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml index 9cc628b7..2e00d54a 100644 --- a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml @@ -75,7 +75,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true headerText: qsTr("OpenVPN settings") diff --git a/client/ui/qml/Pages2/PageProtocolRaw.qml b/client/ui/qml/Pages2/PageProtocolRaw.qml index 03b4e297..bba3eafe 100644 --- a/client/ui/qml/Pages2/PageProtocolRaw.qml +++ b/client/ui/qml/Pages2/PageProtocolRaw.qml @@ -32,7 +32,7 @@ PageType { id: backButton } - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 diff --git a/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml index 5786012b..63e60dcb 100644 --- a/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml @@ -78,7 +78,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true headerText: qsTr("Shadowsocks settings") diff --git a/client/ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml b/client/ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml index a30c17e7..96ec1dc6 100644 --- a/client/ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml @@ -85,7 +85,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true headerText: qsTr("WG settings") diff --git a/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml b/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml index 10523b74..7b5180f3 100644 --- a/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml @@ -77,7 +77,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true headerText: qsTr("WG settings") } diff --git a/client/ui/qml/Pages2/PageProtocolXraySettings.qml b/client/ui/qml/Pages2/PageProtocolXraySettings.qml index 90705d3e..ca30e0a9 100644 --- a/client/ui/qml/Pages2/PageProtocolXraySettings.qml +++ b/client/ui/qml/Pages2/PageProtocolXraySettings.qml @@ -75,7 +75,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true headerText: qsTr("XRay settings") } diff --git a/client/ui/qml/Pages2/PageServiceDnsSettings.qml b/client/ui/qml/Pages2/PageServiceDnsSettings.qml index cef29813..d534f991 100644 --- a/client/ui/qml/Pages2/PageServiceDnsSettings.qml +++ b/client/ui/qml/Pages2/PageServiceDnsSettings.qml @@ -43,7 +43,7 @@ PageType { anchors.left: parent.left anchors.right: parent.right - HeaderType { + BaseHeaderType { id: header Layout.fillWidth: true diff --git a/client/ui/qml/Pages2/PageServiceSftpSettings.qml b/client/ui/qml/Pages2/PageServiceSftpSettings.qml index 2deb315c..b58cb2e0 100644 --- a/client/ui/qml/Pages2/PageServiceSftpSettings.qml +++ b/client/ui/qml/Pages2/PageServiceSftpSettings.qml @@ -85,7 +85,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 diff --git a/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml b/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml index 1b77267a..b1daa0fb 100644 --- a/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml +++ b/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml @@ -77,7 +77,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 @@ -217,7 +217,7 @@ PageType { } } - HeaderType { + BaseHeaderType { Layout.fillWidth: true headerText: qsTr("SOCKS5 settings") diff --git a/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml b/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml index 249c70c7..200beeb8 100644 --- a/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml +++ b/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml @@ -54,7 +54,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 diff --git a/client/ui/qml/Pages2/PageSettings.qml b/client/ui/qml/Pages2/PageSettings.qml index a47bb535..bb83ec92 100644 --- a/client/ui/qml/Pages2/PageSettings.qml +++ b/client/ui/qml/Pages2/PageSettings.qml @@ -29,7 +29,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { id: header Layout.fillWidth: true Layout.topMargin: 24 diff --git a/client/ui/qml/Pages2/PageSettingsApiAvailableCountries.qml b/client/ui/qml/Pages2/PageSettingsApiAvailableCountries.qml index 43fbb160..30302982 100644 --- a/client/ui/qml/Pages2/PageSettingsApiAvailableCountries.qml +++ b/client/ui/qml/Pages2/PageSettingsApiAvailableCountries.qml @@ -69,7 +69,7 @@ PageType { Layout.topMargin: 20 } - HeaderType { + HeaderTypeWithButton { id: headerContent objectName: "headerContent" diff --git a/client/ui/qml/Pages2/PageSettingsApiInstructions.qml b/client/ui/qml/Pages2/PageSettingsApiInstructions.qml index 70a0df74..39118963 100644 --- a/client/ui/qml/Pages2/PageSettingsApiInstructions.qml +++ b/client/ui/qml/Pages2/PageSettingsApiInstructions.qml @@ -91,7 +91,7 @@ PageType { id: backButton } - HeaderType { + BaseHeaderType { id: header Layout.fillWidth: true diff --git a/client/ui/qml/Pages2/PageSettingsApiNativeConfigs.qml b/client/ui/qml/Pages2/PageSettingsApiNativeConfigs.qml index 7fa2b8f4..1c8b078b 100644 --- a/client/ui/qml/Pages2/PageSettingsApiNativeConfigs.qml +++ b/client/ui/qml/Pages2/PageSettingsApiNativeConfigs.qml @@ -38,7 +38,7 @@ PageType { id: backButton } - HeaderType { + BaseHeaderType { id: header Layout.fillWidth: true diff --git a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml index 44ed6f4b..90db522e 100644 --- a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml @@ -93,7 +93,7 @@ PageType { Layout.topMargin: 20 } - HeaderType { + HeaderTypeWithButton { id: headerContent objectName: "headerContent" diff --git a/client/ui/qml/Pages2/PageSettingsApiSupport.qml b/client/ui/qml/Pages2/PageSettingsApiSupport.qml index 64921a61..69e01355 100644 --- a/client/ui/qml/Pages2/PageSettingsApiSupport.qml +++ b/client/ui/qml/Pages2/PageSettingsApiSupport.qml @@ -30,7 +30,7 @@ PageType { } - HeaderType { + BaseHeaderType { id: header Layout.fillWidth: true diff --git a/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml b/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml index b6920a8f..e31c92db 100644 --- a/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml +++ b/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml @@ -79,29 +79,22 @@ PageType { id: backButton } - RowLayout { - HeaderType { - Layout.fillWidth: true - Layout.leftMargin: 16 + HeaderTypeWithSwitcher { + Layout.fillWidth: true + Layout.leftMargin: 16 + Layout.rightMargin: 16 - headerText: qsTr("App split tunneling") + headerText: qsTr("App split tunneling") + enabled: root.pageEnabled + showSwitcher: true + switcher { + checked: AppSplitTunnelingModel.isTunnelingEnabled enabled: root.pageEnabled } - - SwitcherType { - id: switcher - - Layout.fillWidth: true - Layout.rightMargin: 16 - - enabled: root.pageEnabled - - checked: AppSplitTunnelingModel.isTunnelingEnabled - onToggled: { - AppSplitTunnelingModel.toggleSplitTunneling(checked) - selector.text = root.routeModesModel[getRouteModesModelIndex()].name - } + switcherFunction: function(checked) { + AppSplitTunnelingModel.toggleSplitTunneling(checked) + selector.text = root.routeModesModel[getRouteModesModelIndex()].name } } diff --git a/client/ui/qml/Pages2/PageSettingsApplication.qml b/client/ui/qml/Pages2/PageSettingsApplication.qml index 6f77a521..cbc04075 100644 --- a/client/ui/qml/Pages2/PageSettingsApplication.qml +++ b/client/ui/qml/Pages2/PageSettingsApplication.qml @@ -38,7 +38,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 diff --git a/client/ui/qml/Pages2/PageSettingsBackup.qml b/client/ui/qml/Pages2/PageSettingsBackup.qml index d2dd4f2a..83e0f567 100644 --- a/client/ui/qml/Pages2/PageSettingsBackup.qml +++ b/client/ui/qml/Pages2/PageSettingsBackup.qml @@ -60,7 +60,7 @@ PageType { spacing: 16 - HeaderType { + BaseHeaderType { Layout.fillWidth: true headerText: qsTr("Back up your configuration") diff --git a/client/ui/qml/Pages2/PageSettingsConnection.qml b/client/ui/qml/Pages2/PageSettingsConnection.qml index fe57d1c4..84b98230 100644 --- a/client/ui/qml/Pages2/PageSettingsConnection.qml +++ b/client/ui/qml/Pages2/PageSettingsConnection.qml @@ -36,7 +36,7 @@ PageType { anchors.left: parent.left anchors.right: parent.right - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 diff --git a/client/ui/qml/Pages2/PageSettingsDns.qml b/client/ui/qml/Pages2/PageSettingsDns.qml index d78c5aa8..d5e2c52b 100644 --- a/client/ui/qml/Pages2/PageSettingsDns.qml +++ b/client/ui/qml/Pages2/PageSettingsDns.qml @@ -50,7 +50,7 @@ PageType { spacing: 16 - HeaderType { + BaseHeaderType { Layout.fillWidth: true headerText: qsTr("DNS servers") diff --git a/client/ui/qml/Pages2/PageSettingsLogging.qml b/client/ui/qml/Pages2/PageSettingsLogging.qml index 2c760e37..5b20936c 100644 --- a/client/ui/qml/Pages2/PageSettingsLogging.qml +++ b/client/ui/qml/Pages2/PageSettingsLogging.qml @@ -40,7 +40,7 @@ PageType { header: ColumnLayout { width: listView.width - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 diff --git a/client/ui/qml/Pages2/PageSettingsServerInfo.qml b/client/ui/qml/Pages2/PageSettingsServerInfo.qml index d350ebef..6ac81764 100644 --- a/client/ui/qml/Pages2/PageSettingsServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsServerInfo.qml @@ -71,7 +71,7 @@ PageType { objectName: "backButton" } - HeaderType { + HeaderTypeWithButton { id: headerContent objectName: "headerContent" diff --git a/client/ui/qml/Pages2/PageSettingsServerProtocol.qml b/client/ui/qml/Pages2/PageSettingsServerProtocol.qml index ade94ebb..fce9b2a3 100644 --- a/client/ui/qml/Pages2/PageSettingsServerProtocol.qml +++ b/client/ui/qml/Pages2/PageSettingsServerProtocol.qml @@ -34,7 +34,7 @@ PageType { id: backButton } - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 diff --git a/client/ui/qml/Pages2/PageSettingsServersList.qml b/client/ui/qml/Pages2/PageSettingsServersList.qml index 554b6cbb..57e39ae8 100644 --- a/client/ui/qml/Pages2/PageSettingsServersList.qml +++ b/client/ui/qml/Pages2/PageSettingsServersList.qml @@ -31,7 +31,7 @@ PageType { id: backButton } - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 diff --git a/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml b/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml index f5978687..292f903a 100644 --- a/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml +++ b/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml @@ -94,33 +94,22 @@ PageType { id: backButton } - RowLayout { - HeaderType { - enabled: root.pageEnabled + HeaderTypeWithSwitcher { + Layout.fillWidth: true + Layout.leftMargin: 16 + Layout.rightMargin: 16 - Layout.fillWidth: true - Layout.leftMargin: 16 - - headerText: qsTr("Split tunneling") - } - - SwitcherType { - id: switcher - - enabled: root.pageEnabled - - Layout.fillWidth: true - Layout.rightMargin: 16 - - function onToggledFunc() { - SitesModel.toggleSplitTunneling(this.checked) - selector.text = root.routeModesModel[getRouteModesModelIndex()].name - } + headerText: qsTr("Split tunneling") + enabled: root.pageEnabled + showSwitcher: true + switcher { checked: SitesModel.isTunnelingEnabled - onToggled: { onToggledFunc() } - Keys.onEnterPressed: { onToggledFunc() } - Keys.onReturnPressed: { onToggledFunc() } + enabled: root.pageEnabled + } + switcherFunction: function(checked) { + SitesModel.toggleSplitTunneling(checked) + selector.text = root.routeModesModel[getRouteModesModelIndex()].name } } diff --git a/client/ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml b/client/ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml index 134e73b6..30128de6 100644 --- a/client/ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml +++ b/client/ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml @@ -35,7 +35,7 @@ PageType { Layout.topMargin: 20 } - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.topMargin: 8 Layout.rightMargin: 16 diff --git a/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml b/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml index c3e3edbc..549eb381 100644 --- a/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml +++ b/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml @@ -28,7 +28,7 @@ PageType { Layout.topMargin: 20 } - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.topMargin: 8 Layout.rightMargin: 16 diff --git a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml index 38a1da52..2db57835 100644 --- a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml +++ b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml @@ -43,7 +43,7 @@ PageType { header: ColumnLayout { width: listView.width - HeaderType { + HeaderTypeWithButton { id: moreButton property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible() @@ -74,7 +74,7 @@ PageType { anchors.right: parent.right spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.topMargin: 32 Layout.leftMargin: 16 diff --git a/client/ui/qml/Pages2/PageSetupWizardCredentials.qml b/client/ui/qml/Pages2/PageSetupWizardCredentials.qml index ca7e3a7c..6abeb372 100644 --- a/client/ui/qml/Pages2/PageSetupWizardCredentials.qml +++ b/client/ui/qml/Pages2/PageSetupWizardCredentials.qml @@ -66,7 +66,7 @@ PageType { header: ColumnLayout { width: listView.width - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.leftMargin: 16 Layout.rightMargin: 16 diff --git a/client/ui/qml/Pages2/PageSetupWizardEasy.qml b/client/ui/qml/Pages2/PageSetupWizardEasy.qml index 353eeb32..5d126d9a 100644 --- a/client/ui/qml/Pages2/PageSetupWizardEasy.qml +++ b/client/ui/qml/Pages2/PageSetupWizardEasy.qml @@ -59,7 +59,7 @@ PageType { spacing: 16 - HeaderType { + BaseHeaderType { id: header implicitWidth: parent.width diff --git a/client/ui/qml/Pages2/PageSetupWizardInstalling.qml b/client/ui/qml/Pages2/PageSetupWizardInstalling.qml index 1128761d..822931b8 100644 --- a/client/ui/qml/Pages2/PageSetupWizardInstalling.qml +++ b/client/ui/qml/Pages2/PageSetupWizardInstalling.qml @@ -118,7 +118,7 @@ PageType { anchors.rightMargin: 16 anchors.leftMargin: 16 - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.topMargin: 20 diff --git a/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml b/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml index 50d1ea81..ac7fc4b2 100644 --- a/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml +++ b/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml @@ -96,7 +96,7 @@ PageType { Layout.leftMargin: -16 } - HeaderType { + BaseHeaderType { id: header Layout.fillWidth: true diff --git a/client/ui/qml/Pages2/PageSetupWizardProtocols.qml b/client/ui/qml/Pages2/PageSetupWizardProtocols.qml index 6b6b6038..7afab630 100644 --- a/client/ui/qml/Pages2/PageSetupWizardProtocols.qml +++ b/client/ui/qml/Pages2/PageSetupWizardProtocols.qml @@ -58,7 +58,7 @@ PageType { header: ColumnLayout { width: listView.width - HeaderType { + BaseHeaderType { id: header Layout.fillWidth: true diff --git a/client/ui/qml/Pages2/PageSetupWizardTextKey.qml b/client/ui/qml/Pages2/PageSetupWizardTextKey.qml index 3cf154e4..930efb57 100644 --- a/client/ui/qml/Pages2/PageSetupWizardTextKey.qml +++ b/client/ui/qml/Pages2/PageSetupWizardTextKey.qml @@ -33,7 +33,7 @@ PageType { Layout.topMargin: 20 } - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.rightMargin: 16 Layout.leftMargin: 16 diff --git a/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml b/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml index 14096742..cfa9c90f 100644 --- a/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml +++ b/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml @@ -61,7 +61,7 @@ PageType { anchors.rightMargin: 16 anchors.leftMargin: 16 - HeaderType { + BaseHeaderType { headerText: qsTr("New connection") } diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index af208544..48f74acf 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -169,7 +169,7 @@ PageType { spacing: 0 - HeaderType { + HeaderTypeWithButton { id: header Layout.fillWidth: true Layout.topMargin: 24 diff --git a/client/ui/qml/Pages2/PageShareFullAccess.qml b/client/ui/qml/Pages2/PageShareFullAccess.qml index 70fd6292..82effb57 100644 --- a/client/ui/qml/Pages2/PageShareFullAccess.qml +++ b/client/ui/qml/Pages2/PageShareFullAccess.qml @@ -44,7 +44,7 @@ PageType { spacing: 0 - HeaderType { + BaseHeaderType { Layout.fillWidth: true Layout.topMargin: 24 From c36f14c55ae15eb743cc9047ffadd2da4cab6a58 Mon Sep 17 00:00:00 2001 From: aiamnezia Date: Mon, 24 Feb 2025 19:50:27 +0400 Subject: [PATCH 17/22] Remove deprecated HeaderType QML component --- client/resources.qrc | 1 - client/ui/qml/Controls2/HeaderType.qml | 4 ---- 2 files changed, 5 deletions(-) delete mode 100644 client/ui/qml/Controls2/HeaderType.qml diff --git a/client/resources.qrc b/client/resources.qrc index cd9c6555..2892547e 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -143,7 +143,6 @@ ui/qml/Controls2/DropDownType.qml ui/qml/Controls2/FlickableType.qml ui/qml/Controls2/Header2Type.qml - ui/qml/Controls2/HeaderType.qml ui/qml/Controls2/BaseHeaderType.qml ui/qml/Controls2/HeaderTypeWithButton.qml ui/qml/Controls2/HeaderTypeWithSwitcher.qml diff --git a/client/ui/qml/Controls2/HeaderType.qml b/client/ui/qml/Controls2/HeaderType.qml deleted file mode 100644 index 1f074d54..00000000 --- a/client/ui/qml/Controls2/HeaderType.qml +++ /dev/null @@ -1,4 +0,0 @@ -import QtQuick - -// @deprecated Use BaseHeaderType, HeaderTypeWithButton or HeaderTypeWithSwitcher instead -HeaderTypeWithButton {} From 0cf9d3c9bd8be79437d3a1afa647e4928ed9b0bc Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Sat, 1 Mar 2025 20:54:57 +0200 Subject: [PATCH 18/22] Refresh strict mode killswitch after global toggle change --- client/ui/controllers/settingsController.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index 3808375a..c7b224bf 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -246,6 +246,12 @@ void SettingsController::toggleKillSwitch(bool enable) { m_settings->setKillSwitchEnabled(enable); emit killSwitchEnabledChanged(); + if (enable == false) { + emit strictKillSwitchEnabledChanged(false); + } else { + emit strictKillSwitchEnabledChanged(isStrictKillSwitchEnabled()); + } + } bool SettingsController::isStrictKillSwitchEnabled() From 0466e71d49fcd47d8429b826e81b6873b1b734d8 Mon Sep 17 00:00:00 2001 From: aiamnezia Date: Mon, 10 Mar 2025 01:41:14 +0400 Subject: [PATCH 19/22] Implement model, controller and UI for killswitch dns exceptions --- client/core/controllers/coreController.cpp | 6 + client/core/controllers/coreController.h | 4 + client/resources.qrc | 2 + client/settings.cpp | 10 + client/settings.h | 3 + .../ui/controllers/allowedDnsController.cpp | 101 ++++++ client/ui/controllers/allowedDnsController.h | 35 +++ client/ui/controllers/pageController.h | 3 +- client/ui/models/allowed_dns_model.cpp | 86 +++++ client/ui/models/allowed_dns_model.h | 37 +++ client/ui/qml/Components/AddSitePanel.qml | 73 +++++ .../ui/qml/Pages2/PageSettingsKillSwitch.qml | 14 + .../PageSettingsKillSwitchExceptions.qml | 296 ++++++++++++++++++ service/server/killswitch.cpp | 25 +- 14 files changed, 693 insertions(+), 2 deletions(-) create mode 100644 client/ui/controllers/allowedDnsController.cpp create mode 100644 client/ui/controllers/allowedDnsController.h create mode 100644 client/ui/models/allowed_dns_model.cpp create mode 100644 client/ui/models/allowed_dns_model.h create mode 100644 client/ui/qml/Components/AddSitePanel.qml create mode 100644 client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml diff --git a/client/core/controllers/coreController.cpp b/client/core/controllers/coreController.cpp index 41dac373..42dedbc1 100644 --- a/client/core/controllers/coreController.cpp +++ b/client/core/controllers/coreController.cpp @@ -47,6 +47,9 @@ void CoreController::initModels() m_sitesModel.reset(new SitesModel(m_settings, this)); m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get()); + m_allowedDnsModel.reset(new AllowedDnsModel(m_settings, this)); + m_engine->rootContext()->setContextProperty("AllowedDnsModel", m_allowedDnsModel.get()); + m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this)); m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get()); @@ -129,6 +132,9 @@ void CoreController::initControllers() m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel)); m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get()); + m_allowedDnsController.reset(new AllowedDnsController(m_settings, m_allowedDnsModel)); + m_engine->rootContext()->setContextProperty("AllowedDnsController", m_allowedDnsController.get()); + m_appSplitTunnelingController.reset(new AppSplitTunnelingController(m_settings, m_appSplitTunnelingModel)); m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get()); diff --git a/client/core/controllers/coreController.h b/client/core/controllers/coreController.h index 6db297be..6342d738 100644 --- a/client/core/controllers/coreController.h +++ b/client/core/controllers/coreController.h @@ -8,6 +8,7 @@ #include "ui/controllers/api/apiConfigsController.h" #include "ui/controllers/api/apiSettingsController.h" #include "ui/controllers/appSplitTunnelingController.h" +#include "ui/controllers/allowedDnsController.h" #include "ui/controllers/connectionController.h" #include "ui/controllers/exportController.h" #include "ui/controllers/focusController.h" @@ -18,6 +19,7 @@ #include "ui/controllers/sitesController.h" #include "ui/controllers/systemController.h" +#include "ui/models/allowed_dns_model.h" #include "ui/models/containers_model.h" #include "ui/models/languageModel.h" #include "ui/models/protocols/cloakConfigModel.h" @@ -103,6 +105,7 @@ private: QScopedPointer m_sitesController; QScopedPointer m_systemController; QScopedPointer m_appSplitTunnelingController; + QScopedPointer m_allowedDnsController; QScopedPointer m_apiSettingsController; QScopedPointer m_apiConfigsController; @@ -113,6 +116,7 @@ private: QSharedPointer m_languageModel; QSharedPointer m_protocolsModel; QSharedPointer m_sitesModel; + QSharedPointer m_allowedDnsModel; QSharedPointer m_appSplitTunnelingModel; QSharedPointer m_clientManagementModel; diff --git a/client/resources.qrc b/client/resources.qrc index 591c0579..a36b60d1 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -129,6 +129,7 @@ ui/qml/Components/SettingsContainersListView.qml ui/qml/Components/ShareConnectionDrawer.qml ui/qml/Components/TransportProtoSelector.qml + ui/qml/Components/AddSitePanel.qml ui/qml/Config/GlobalConfig.qml ui/qml/Config/qmldir ui/qml/Controls2/BackButtonType.qml @@ -202,6 +203,7 @@ ui/qml/Pages2/PageSettingsConnection.qml ui/qml/Pages2/PageSettingsDns.qml ui/qml/Pages2/PageSettingsKillSwitch.qml + ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml ui/qml/Pages2/PageSettingsLogging.qml ui/qml/Pages2/PageSettingsServerData.qml ui/qml/Pages2/PageSettingsServerInfo.qml diff --git a/client/settings.cpp b/client/settings.cpp index e8f5c0d0..9a0a32e5 100644 --- a/client/settings.cpp +++ b/client/settings.cpp @@ -558,3 +558,13 @@ void Settings::disableHomeAdLabel() { setValue("Conf/homeAdLabelVisible", false); } + +QStringList Settings::allowedDnsServers() const +{ + return value("Conf/allowedDnsServers").toStringList(); +} + +void Settings::setAllowedDnsServers(const QStringList &servers) +{ + setValue("Conf/allowedDnsServers", servers); +} diff --git a/client/settings.h b/client/settings.h index 6cd7cddc..01155c0c 100644 --- a/client/settings.h +++ b/client/settings.h @@ -229,6 +229,9 @@ public: bool isHomeAdLabelVisible(); void disableHomeAdLabel(); + QStringList allowedDnsServers() const; + void setAllowedDnsServers(const QStringList &servers); + signals: void saveLogsChanged(bool enabled); void screenshotsEnabledChanged(bool enabled); diff --git a/client/ui/controllers/allowedDnsController.cpp b/client/ui/controllers/allowedDnsController.cpp new file mode 100644 index 00000000..05b9c175 --- /dev/null +++ b/client/ui/controllers/allowedDnsController.cpp @@ -0,0 +1,101 @@ +#include "allowedDnsController.h" + +#include +#include +#include +#include +#include + +#include "systemController.h" +#include "core/networkUtilities.h" +#include "core/defs.h" + +AllowedDnsController::AllowedDnsController(const std::shared_ptr &settings, + const QSharedPointer &allowedDnsModel, + QObject *parent) + : QObject(parent), m_settings(settings), m_allowedDnsModel(allowedDnsModel) +{ +} + +void AllowedDnsController::addDns(QString ip) +{ + if (ip.isEmpty()) { + return; + } + + if (!NetworkUtilities::ipAddressRegExp().match(ip).hasMatch()) { + emit errorOccurred(tr("The address does not look like a valid IP address")); + return; + } + + if (m_allowedDnsModel->addDns(ip)) { + emit finished(tr("New DNS server added: %1").arg(ip)); + } else { + emit errorOccurred(tr("DNS server already exists: %1").arg(ip)); + } +} + +void AllowedDnsController::removeDns(int index) +{ + auto modelIndex = m_allowedDnsModel->index(index); + auto ip = m_allowedDnsModel->data(modelIndex, AllowedDnsModel::Roles::IpRole).toString(); + m_allowedDnsModel->removeDns(modelIndex); + + emit finished(tr("DNS server removed: %1").arg(ip)); +} + +void AllowedDnsController::importDns(const QString &fileName, bool replaceExisting) +{ + QByteArray jsonData; + if (!SystemController::readFile(fileName, jsonData)) { + emit errorOccurred(tr("Can't open file: %1").arg(fileName)); + return; + } + + QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonData); + if (jsonDocument.isNull()) { + emit errorOccurred(tr("Failed to parse JSON data from file: %1").arg(fileName)); + return; + } + + if (!jsonDocument.isArray()) { + emit errorOccurred(tr("The JSON data is not an array in file: %1").arg(fileName)); + return; + } + + auto jsonArray = jsonDocument.array(); + QStringList dnsServers; + + for (auto jsonValue : jsonArray) { + auto ip = jsonValue.toString(); + + if (!NetworkUtilities::ipAddressRegExp().match(ip).hasMatch()) { + qDebug() << ip << " is not a valid IP address"; + continue; + } + + dnsServers.append(ip); + } + + m_allowedDnsModel->addDnsList(dnsServers, replaceExisting); + + emit finished(tr("Import completed")); +} + +void AllowedDnsController::exportDns(const QString &fileName) +{ + auto dnsServers = m_allowedDnsModel->getCurrentDnsServers(); + + QJsonArray jsonArray; + + for (const auto &ip : dnsServers) { + jsonArray.append(ip); + } + + QJsonDocument jsonDocument(jsonArray); + QByteArray jsonData = jsonDocument.toJson(); + + SystemController::saveFile(fileName, jsonData); + + emit finished(tr("Export completed")); +} \ No newline at end of file diff --git a/client/ui/controllers/allowedDnsController.h b/client/ui/controllers/allowedDnsController.h new file mode 100644 index 00000000..ff48817b --- /dev/null +++ b/client/ui/controllers/allowedDnsController.h @@ -0,0 +1,35 @@ +#ifndef ALLOWEDDNSCONTROLLER_H +#define ALLOWEDDNSCONTROLLER_H + +#include + +#include "settings.h" +#include "ui/models/allowed_dns_model.h" + +class AllowedDnsController : public QObject +{ + Q_OBJECT +public: + explicit AllowedDnsController(const std::shared_ptr &settings, + const QSharedPointer &allowedDnsModel, + QObject *parent = nullptr); + +public slots: + void addDns(QString ip); + void removeDns(int index); + + void importDns(const QString &fileName, bool replaceExisting); + void exportDns(const QString &fileName); + +signals: + void errorOccurred(const QString &errorMessage); + void finished(const QString &message); + + void saveFile(const QString &fileName, const QString &data); + +private: + std::shared_ptr m_settings; + QSharedPointer m_allowedDnsModel; +}; + +#endif // ALLOWEDDNSCONTROLLER_H \ No newline at end of file diff --git a/client/ui/controllers/pageController.h b/client/ui/controllers/pageController.h index 8a3c12b4..fc981091 100644 --- a/client/ui/controllers/pageController.h +++ b/client/ui/controllers/pageController.h @@ -38,7 +38,8 @@ namespace PageLoader PageSettingsApiInstructions, PageSettingsApiNativeConfigs, PageSettingsApiDevices, - + PageSettingsKillSwitchExceptions, + PageServiceSftpSettings, PageServiceTorWebsiteSettings, PageServiceDnsSettings, diff --git a/client/ui/models/allowed_dns_model.cpp b/client/ui/models/allowed_dns_model.cpp new file mode 100644 index 00000000..358c1786 --- /dev/null +++ b/client/ui/models/allowed_dns_model.cpp @@ -0,0 +1,86 @@ +#include "allowed_dns_model.h" + +AllowedDnsModel::AllowedDnsModel(std::shared_ptr settings, QObject *parent) + : QAbstractListModel(parent), m_settings(settings) +{ + fillDnsServers(); +} + +int AllowedDnsModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return m_dnsServers.size(); +} + +QVariant AllowedDnsModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() < 0 || index.row() >= static_cast(rowCount())) + return QVariant(); + + switch (role) { + case IpRole: + return m_dnsServers.at(index.row()); + default: + return QVariant(); + } +} + +bool AllowedDnsModel::addDns(const QString &ip) +{ + if (m_dnsServers.contains(ip)) { + return false; + } + + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + m_dnsServers.append(ip); + m_settings->setAllowedDnsServers(m_dnsServers); + endInsertRows(); + return true; +} + +void AllowedDnsModel::addDnsList(const QStringList &dnsServers, bool replaceExisting) +{ + beginResetModel(); + + if (replaceExisting) { + m_dnsServers.clear(); + } + + for (const QString &ip : dnsServers) { + if (!m_dnsServers.contains(ip)) { + m_dnsServers.append(ip); + } + } + + m_settings->setAllowedDnsServers(m_dnsServers); + endResetModel(); +} + +void AllowedDnsModel::removeDns(QModelIndex index) +{ + if (!index.isValid() || index.row() >= m_dnsServers.size()) { + return; + } + + beginRemoveRows(QModelIndex(), index.row(), index.row()); + m_dnsServers.removeAt(index.row()); + m_settings->setAllowedDnsServers(m_dnsServers); + endRemoveRows(); +} + +QStringList AllowedDnsModel::getCurrentDnsServers() +{ + return m_dnsServers; +} + +QHash AllowedDnsModel::roleNames() const +{ + QHash roles; + roles[IpRole] = "ip"; + return roles; +} + +void AllowedDnsModel::fillDnsServers() +{ + m_dnsServers = m_settings->allowedDnsServers(); +} \ No newline at end of file diff --git a/client/ui/models/allowed_dns_model.h b/client/ui/models/allowed_dns_model.h new file mode 100644 index 00000000..346f9e18 --- /dev/null +++ b/client/ui/models/allowed_dns_model.h @@ -0,0 +1,37 @@ +#ifndef ALLOWEDDNSMODEL_H +#define ALLOWEDDNSMODEL_H + +#include +#include "settings.h" + +class AllowedDnsModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Roles { + IpRole = Qt::UserRole + 1 + }; + + explicit AllowedDnsModel(std::shared_ptr settings, QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + +public slots: + bool addDns(const QString &ip); + void addDnsList(const QStringList &dnsServers, bool replaceExisting); + void removeDns(QModelIndex index); + QStringList getCurrentDnsServers(); + +protected: + QHash roleNames() const override; + +private: + void fillDnsServers(); + + std::shared_ptr m_settings; + QStringList m_dnsServers; +}; + +#endif // ALLOWEDDNSMODEL_H \ No newline at end of file diff --git a/client/ui/qml/Components/AddSitePanel.qml b/client/ui/qml/Components/AddSitePanel.qml new file mode 100644 index 00000000..f57f0a77 --- /dev/null +++ b/client/ui/qml/Components/AddSitePanel.qml @@ -0,0 +1,73 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import Style 1.0 +import "../Controls2" +import "../Controls2/TextTypes" + +Item { + id: root + + property bool enabled: true + property string placeholderText: "" + property alias textField: searchField.textField + + signal addClicked(string text) + signal moreClicked() + + implicitWidth: 360 + implicitHeight: 96 + + Rectangle { + id: background + anchors.fill: parent + color: "#0E0F12" + opacity: 0.85 + z: -1 + } + + RowLayout { + id: addSiteButton + + enabled: root.enabled + spacing: 2 + + anchors { + fill: parent + topMargin: 16 + leftMargin: 16 + rightMargin: 16 + bottomMargin: 24 + } + + TextFieldWithHeaderType { + id: searchField + + Layout.fillWidth: true + rightButtonClickedOnEnter: true + + textField.placeholderText: root.placeholderText + buttonImageSource: "qrc:/images/controls/plus.svg" + + clickedFunc: function() { + root.addClicked(textField.text) + textField.text = "" + } + } + + ImageButtonType { + id: addSiteButtonImage + implicitWidth: 56 + implicitHeight: 56 + + image: "qrc:/images/controls/more-vertical.svg" + imageColor: AmneziaStyle.color.paleGray + + onClicked: root.moreClicked() + + Keys.onReturnPressed: addSiteButtonImage.clicked() + Keys.onEnterPressed: addSiteButtonImage.clicked() + } + } +} \ No newline at end of file diff --git a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml index 211a1507..f0203d42 100644 --- a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml +++ b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml @@ -93,6 +93,20 @@ PageType { } DividerType {} + + LabelWithButtonType { + Layout.topMargin: 32 + Layout.fillWidth: true + + enabled: true + text: qsTr("Kill Switch Exceptions") + descriptionText: qsTr("IP addresses that will remain accessible even when Kill Switch is activated") + rightImageSource: "qrc:/images/controls/chevron-right.svg" + + clickedFunction: function() { + PageController.goToPage(PageEnum.PageSettingsKillSwitchExceptions) + } + } } } } \ No newline at end of file diff --git a/client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml b/client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml new file mode 100644 index 00000000..3623b762 --- /dev/null +++ b/client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml @@ -0,0 +1,296 @@ +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("Kill Switch Exceptions") + descriptionText: qsTr("Addresses 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: AddSitePanel { + id: addSitePanel + + width: listView.width + z: 10 + + enabled: root.pageEnabled + placeholderText: qsTr("IPv4 address") + + onAddClicked: function(text) { + PageController.showBusyIndicator(true) + AllowedDnsController.addDns(text) + PageController.showBusyIndicator(false) + } + + onMoreClicked: { + moreActionsDrawer.openTriggered() + } + } + + footerPositioning: ListView.OverlayFooter + + 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 {} + } + } + + DrawerType2 { + id: moreActionsDrawer + + anchors.fill: parent + expandedHeight: parent.height * 0.4375 + + expandedStateContent: ColumnLayout { + id: moreActionsDrawerContent + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + Header2Type { + Layout.fillWidth: true + Layout.margins: 16 + + headerText: qsTr("Import / Export addresses") + } + + LabelWithButtonType { + id: importSitesButton + Layout.fillWidth: true + + text: qsTr("Import") + rightImageSource: "qrc:/images/controls/chevron-right.svg" + + clickedFunction: function() { + importSitesDrawer.openTriggered() + } + } + + DividerType {} + + LabelWithButtonType { + id: exportSitesButton + Layout.fillWidth: true + text: qsTr("Save address list") + + clickedFunction: function() { + var fileName = "" + if (GC.isMobile()) { + fileName = "amnezia_killswitch_exceptions.json" + } else { + fileName = SystemController.getFileName(qsTr("Save addresses"), + qsTr("Address files (*.json)"), + StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/amnezia_killswitch_exceptions", + true, + ".json") + } + if (fileName !== "") { + PageController.showBusyIndicator(true) + AllowedDnsController.exportDns(fileName) + moreActionsDrawer.closeTriggered() + PageController.showBusyIndicator(false) + } + } + } + + DividerType {} + } + } + + DrawerType2 { + id: importSitesDrawer + + anchors.fill: parent + expandedHeight: parent.height * 0.4375 + + expandedStateContent: Item { + implicitHeight: importSitesDrawer.expandedHeight + + BackButtonType { + id: importSitesDrawerBackButton + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: 16 + + backButtonFunction: function() { + importSitesDrawer.closeTriggered() + } + } + + FlickableType { + anchors.top: importSitesDrawerBackButton.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + + contentHeight: importSitesDrawerContent.height + + ColumnLayout { + id: importSitesDrawerContent + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + Header2Type { + Layout.fillWidth: true + Layout.margins: 16 + + headerText: qsTr("Import address list") + } + + LabelWithButtonType { + id: importSitesButton2 + Layout.fillWidth: true + + text: qsTr("Replace address list") + + clickedFunction: function() { + var fileName = SystemController.getFileName(qsTr("Open address file"), + qsTr("Address files (*.json)")) + if (fileName !== "") { + importSitesDrawerContent.importSites(fileName, true) + } + } + } + + DividerType {} + + LabelWithButtonType { + id: importSitesButton3 + Layout.fillWidth: true + text: qsTr("Add imported addresses to existing ones") + + clickedFunction: function() { + var fileName = SystemController.getFileName(qsTr("Open address file"), + qsTr("Address files (*.json)")) + if (fileName !== "") { + importSitesDrawerContent.importSites(fileName, false) + } + } + } + + function importSites(fileName, replaceExistingSites) { + PageController.showBusyIndicator(true) + AllowedDnsController.importDns(fileName, replaceExistingSites) + PageController.showBusyIndicator(false) + importSitesDrawer.closeTriggered() + moreActionsDrawer.closeTriggered() + } + + DividerType {} + } + } + } + } +} diff --git a/service/server/killswitch.cpp b/service/server/killswitch.cpp index fb3feccb..4246bb28 100644 --- a/service/server/killswitch.cpp +++ b/service/server/killswitch.cpp @@ -214,7 +214,14 @@ bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) { config.m_vpnDisabledApps.append(i.toString()); } - // killSwitch toggle + for (auto dns : configStr.value(amnezia::config_key::allowedDnsServers).toArray()) { + if (!dns.isString()) { + break; + } + config.m_allowedDnsServers.append(dns.toString()); + } + + // killSwitch toggle if (QVariant(configStr.value(amnezia::config_key::killSwitchOption).toString()).toBool()) { WindowsFirewall::create(this)->enablePeerTraffic(config); } @@ -280,6 +287,14 @@ bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIn dnsServers.append(configStr.value(amnezia::config_key::dns2).toString()); dnsServers.append("127.0.0.1"); dnsServers.append("127.0.0.53"); + + for (auto dns : configStr.value(amnezia::config_key::allowedDnsServers).toArray()) { + if (!dns.isString()) { + break; + } + dnsServers.append(dns.toString()); + } + LinuxFirewall::updateDNSServers(dnsServers); LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true); LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true); @@ -307,6 +322,14 @@ bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIn QStringList dnsServers; dnsServers.append(configStr.value(amnezia::config_key::dns1).toString()); dnsServers.append(configStr.value(amnezia::config_key::dns2).toString()); + + for (auto dns : configStr.value(amnezia::config_key::allowedDnsServers).toArray()) { + if (!dns.isString()) { + break; + } + dnsServers.append(dns.toString()); + } + MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true); MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), dnsServers); #endif From cafac3aa6103e240e9bc84f4ad44c13ede7eb5f3 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Mon, 10 Mar 2025 20:19:51 +0200 Subject: [PATCH 20/22] Connect backend part and UI --- client/daemon/daemon.cpp | 3 +++ client/daemon/interfaceconfig.cpp | 7 +++++++ client/mozilla/localsocketcontroller.cpp | 3 +++ client/vpnconnection.cpp | 1 + 4 files changed, 14 insertions(+) diff --git a/client/daemon/daemon.cpp b/client/daemon/daemon.cpp index 081a7a90..e4b0ab3d 100644 --- a/client/daemon/daemon.cpp +++ b/client/daemon/daemon.cpp @@ -371,6 +371,9 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) { if (!parseStringList(obj, "vpnDisabledApps", config.m_vpnDisabledApps)) { return false; } + if (!parseStringList(obj, "allowedDnsServers", config.m_allowedDnsServers)) { + return false; + } config.m_killSwitchEnabled = QVariant(obj.value("killSwitchOption").toString()).toBool(); diff --git a/client/daemon/interfaceconfig.cpp b/client/daemon/interfaceconfig.cpp index b2ad31c6..f0adcc92 100644 --- a/client/daemon/interfaceconfig.cpp +++ b/client/daemon/interfaceconfig.cpp @@ -48,6 +48,13 @@ QJsonObject InterfaceConfig::toJson() const { } json.insert("excludedAddresses", jsExcludedAddresses); + + QJsonArray jsAllowedDnsServers; + for (const QString& i : m_allowedDnsServers) { + jsAllowedDnsServers.append(QJsonValue(i)); + } + json.insert("allowedDnsServers", jsAllowedDnsServers); + QJsonArray disabledApps; for (const QString& i : m_vpnDisabledApps) { disabledApps.append(QJsonValue(i)); diff --git a/client/mozilla/localsocketcontroller.cpp b/client/mozilla/localsocketcontroller.cpp index 1081bcae..afa29c47 100644 --- a/client/mozilla/localsocketcontroller.cpp +++ b/client/mozilla/localsocketcontroller.cpp @@ -123,6 +123,7 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) { int appSplitTunnelType = rawConfig.value(amnezia::config_key::appSplitTunnelType).toInt(); QJsonArray splitTunnelApps = rawConfig.value(amnezia::config_key::splitTunnelApps).toArray(); + QJsonArray allowedDns = rawConfig.value(amnezia::config_key::allowedDnsServers).toArray(); QJsonObject wgConfig = rawConfig.value(protocolName + "_config_data").toObject(); @@ -226,6 +227,8 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) { json.insert("vpnDisabledApps", splitTunnelApps); + json.insert("allowedDnsServers", allowedDns); + json.insert(amnezia::config_key::killSwitchOption, rawConfig.value(amnezia::config_key::killSwitchOption)); if (protocolName == amnezia::config_key::awg) { diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 474bd887..492fa1df 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -308,6 +308,7 @@ void VpnConnection::createProtocolConnections() void VpnConnection::appendKillSwitchConfig() { m_vpnConfiguration.insert(config_key::killSwitchOption, QVariant(m_settings->isKillSwitchEnabled()).toString()); + m_vpnConfiguration.insert(config_key::allowedDnsServers, QVariant(m_settings->allowedDnsServers()).toJsonValue()); } void VpnConnection::appendSplitTunnelingConfig() From 92b0ea6213e4c3427ddbef1a3ed5f338b98d2fbb Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Mon, 10 Mar 2025 20:43:46 +0200 Subject: [PATCH 21/22] Change label text to DNS exceptions --- client/ui/controllers/allowedDnsController.cpp | 6 +++--- client/ui/controllers/allowedDnsController.h | 6 +++--- client/ui/models/allowed_dns_model.cpp | 2 +- client/ui/models/allowed_dns_model.h | 2 +- client/ui/qml/Components/AddSitePanel.qml | 2 +- client/ui/qml/Pages2/PageSettingsKillSwitch.qml | 6 +++--- client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml | 4 ++-- service/server/killswitch.cpp | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/client/ui/controllers/allowedDnsController.cpp b/client/ui/controllers/allowedDnsController.cpp index 05b9c175..12e8b599 100644 --- a/client/ui/controllers/allowedDnsController.cpp +++ b/client/ui/controllers/allowedDnsController.cpp @@ -11,8 +11,8 @@ #include "core/defs.h" AllowedDnsController::AllowedDnsController(const std::shared_ptr &settings, - const QSharedPointer &allowedDnsModel, - QObject *parent) + const QSharedPointer &allowedDnsModel, + QObject *parent) : QObject(parent), m_settings(settings), m_allowedDnsModel(allowedDnsModel) { } @@ -98,4 +98,4 @@ void AllowedDnsController::exportDns(const QString &fileName) SystemController::saveFile(fileName, jsonData); emit finished(tr("Export completed")); -} \ No newline at end of file +} diff --git a/client/ui/controllers/allowedDnsController.h b/client/ui/controllers/allowedDnsController.h index ff48817b..5509a036 100644 --- a/client/ui/controllers/allowedDnsController.h +++ b/client/ui/controllers/allowedDnsController.h @@ -11,8 +11,8 @@ class AllowedDnsController : public QObject Q_OBJECT public: explicit AllowedDnsController(const std::shared_ptr &settings, - const QSharedPointer &allowedDnsModel, - QObject *parent = nullptr); + const QSharedPointer &allowedDnsModel, + QObject *parent = nullptr); public slots: void addDns(QString ip); @@ -32,4 +32,4 @@ private: QSharedPointer m_allowedDnsModel; }; -#endif // ALLOWEDDNSCONTROLLER_H \ No newline at end of file +#endif // ALLOWEDDNSCONTROLLER_H diff --git a/client/ui/models/allowed_dns_model.cpp b/client/ui/models/allowed_dns_model.cpp index 358c1786..e3c59945 100644 --- a/client/ui/models/allowed_dns_model.cpp +++ b/client/ui/models/allowed_dns_model.cpp @@ -83,4 +83,4 @@ QHash AllowedDnsModel::roleNames() const void AllowedDnsModel::fillDnsServers() { m_dnsServers = m_settings->allowedDnsServers(); -} \ No newline at end of file +} diff --git a/client/ui/models/allowed_dns_model.h b/client/ui/models/allowed_dns_model.h index 346f9e18..fdefcc0e 100644 --- a/client/ui/models/allowed_dns_model.h +++ b/client/ui/models/allowed_dns_model.h @@ -34,4 +34,4 @@ private: QStringList m_dnsServers; }; -#endif // ALLOWEDDNSMODEL_H \ No newline at end of file +#endif // ALLOWEDDNSMODEL_H diff --git a/client/ui/qml/Components/AddSitePanel.qml b/client/ui/qml/Components/AddSitePanel.qml index f57f0a77..18fdfa57 100644 --- a/client/ui/qml/Components/AddSitePanel.qml +++ b/client/ui/qml/Components/AddSitePanel.qml @@ -70,4 +70,4 @@ Item { Keys.onEnterPressed: addSiteButtonImage.clicked() } } -} \ No newline at end of file +} diff --git a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml index f0203d42..4098cc40 100644 --- a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml +++ b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml @@ -99,8 +99,8 @@ PageType { Layout.fillWidth: true enabled: true - text: qsTr("Kill Switch Exceptions") - descriptionText: qsTr("IP addresses that will remain accessible even when Kill Switch is activated") + 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() { @@ -109,4 +109,4 @@ PageType { } } } -} \ No newline at end of file +} diff --git a/client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml b/client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml index 3623b762..23f740b4 100644 --- a/client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml +++ b/client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml @@ -42,8 +42,8 @@ PageType { Layout.fillWidth: true Layout.leftMargin: 16 - headerText: qsTr("Kill Switch Exceptions") - descriptionText: qsTr("Addresses from the list will remain accessible when Kill Switch is triggered") + headerText: qsTr("DNS Exceptions") + descriptionText: qsTr("DNS servers from the list will remain accessible when Kill Switch is triggered") } } diff --git a/service/server/killswitch.cpp b/service/server/killswitch.cpp index 4246bb28..30fb837c 100644 --- a/service/server/killswitch.cpp +++ b/service/server/killswitch.cpp @@ -56,7 +56,7 @@ bool KillSwitch::refresh(bool enabled) { #ifdef Q_OS_WIN QSettings RegHLM("HKEY_LOCAL_MACHINE\\Software\\" + QString(ORGANIZATION_NAME) - + "\\" + QString(APPLICATION_NAME), QSettings::NativeFormat); + + "\\" + QString(APPLICATION_NAME), QSettings::NativeFormat); RegHLM.setValue("strictKillSwitchEnabled", enabled); #endif From a67e4ab8bb7fb4a9bd65abb6c2554fa5c354b53b Mon Sep 17 00:00:00 2001 From: aiamnezia Date: Wed, 12 Mar 2025 17:13:16 +0400 Subject: [PATCH 22/22] Remove HeaderType from PageSettingsApiDevices --- client/ui/qml/Pages2/PageSettingsApiDevices.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ui/qml/Pages2/PageSettingsApiDevices.qml b/client/ui/qml/Pages2/PageSettingsApiDevices.qml index c6a2f98c..d8e24d42 100644 --- a/client/ui/qml/Pages2/PageSettingsApiDevices.qml +++ b/client/ui/qml/Pages2/PageSettingsApiDevices.qml @@ -35,7 +35,7 @@ PageType { id: backButton } - HeaderType { + BaseHeaderType { id: header Layout.fillWidth: true