From f5272168bc36c9c12e03a8dfb06458c340653db9 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Sun, 29 Dec 2024 23:07:06 +0200 Subject: [PATCH 01/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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 From a120f3d5bdeaf3ca918ac296c2f803433dbe5cc5 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Thu, 13 Mar 2025 22:04:03 +0200 Subject: [PATCH 23/33] Some pretty fixes --- client/platforms/windows/daemon/windowsfirewall.cpp | 8 ++++---- client/ui/controllers/settingsController.cpp | 1 - client/ui/qml/Controls2/BaseHeaderType.qml | 2 +- client/ui/qml/Controls2/HeaderTypeWithButton.qml | 2 +- client/ui/qml/Controls2/HeaderTypeWithSwitcher.qml | 2 +- client/ui/qml/Pages2/PageSettingsKillSwitch.qml | 6 +++--- service/server/killswitch.h | 4 ++-- 7 files changed, 12 insertions(+), 13 deletions(-) diff --git a/client/platforms/windows/daemon/windowsfirewall.cpp b/client/platforms/windows/daemon/windowsfirewall.cpp index c06b793e..63f7818b 100644 --- a/client/platforms/windows/daemon/windowsfirewall.cpp +++ b/client/platforms/windows/daemon/windowsfirewall.cpp @@ -192,7 +192,7 @@ bool WindowsFirewall::enableInterface(int vpnAdapterIndex) { } IPAddress allv6("::/0"); if (!blockTrafficTo(allv6, MED_WEIGHT, - "Block Internet", "killswitch")) { + "Block Internet", "killswitch")) { return false; } } else @@ -203,8 +203,8 @@ bool WindowsFirewall::enableInterface(int vpnAdapterIndex) { FW_OK(allowTrafficForAppOnAll(getCurrentPath(), MAX_WEIGHT, "Allow all for AmneziaVPN.exe")); FW_OK(blockTrafficOnPort(53, MED_WEIGHT, "Block all DNS")); - FW_OK( - allowLoopbackTraffic(MED_WEIGHT, "Allow Loopback traffic on device %1")); + FW_OK(allowLoopbackTraffic(MED_WEIGHT, + "Allow Loopback traffic on device %1")); logger.debug() << "Killswitch on! Rules:" << m_activeRules.length(); return true; @@ -290,7 +290,7 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) { logger.debug() << "excludedAddresses range: " << i; if (!allowTrafficTo(i, HIGH_WEIGHT, - "Allow Ecxlude route", config.m_serverPublicKey)) { + "Allow Ecxlude route", config.m_serverPublicKey)) { return false; } } diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index c7b224bf..c5c569db 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -251,7 +251,6 @@ void SettingsController::toggleKillSwitch(bool enable) } else { emit strictKillSwitchEnabledChanged(isStrictKillSwitchEnabled()); } - } bool SettingsController::isStrictKillSwitchEnabled() diff --git a/client/ui/qml/Controls2/BaseHeaderType.qml b/client/ui/qml/Controls2/BaseHeaderType.qml index fbb6ba18..eb7fe36f 100644 --- a/client/ui/qml/Controls2/BaseHeaderType.qml +++ b/client/ui/qml/Controls2/BaseHeaderType.qml @@ -42,4 +42,4 @@ Item { visible: root.descriptionText !== "" } } -} \ No newline at end of file +} diff --git a/client/ui/qml/Controls2/HeaderTypeWithButton.qml b/client/ui/qml/Controls2/HeaderTypeWithButton.qml index 8258f8d9..7feff3ce 100644 --- a/client/ui/qml/Controls2/HeaderTypeWithButton.qml +++ b/client/ui/qml/Controls2/HeaderTypeWithButton.qml @@ -41,4 +41,4 @@ BaseHeaderType { actionButtonFunction() } } -} \ No newline at end of file +} diff --git a/client/ui/qml/Controls2/HeaderTypeWithSwitcher.qml b/client/ui/qml/Controls2/HeaderTypeWithSwitcher.qml index 81a078d1..2fa4e735 100644 --- a/client/ui/qml/Controls2/HeaderTypeWithSwitcher.qml +++ b/client/ui/qml/Controls2/HeaderTypeWithSwitcher.qml @@ -25,4 +25,4 @@ BaseHeaderType { } } } -} \ No newline at end of file +} diff --git a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml index 4098cc40..10b7305c 100644 --- a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml +++ b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml @@ -37,7 +37,7 @@ PageType { Layout.leftMargin: 16 Layout.rightMargin: 16 - headerText: qsTr("Kill Switch") + headerText: qsTr("KillSwitch") descriptionText: qsTr("Enable to ensure network traffic goes through a secure VPN tunnel, preventing accidental exposure of your IP and DNS queries if the connection drops") showSwitcher: true @@ -65,7 +65,7 @@ PageType { enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected checked: !SettingsController.strictKillSwitchEnabled - text: qsTr("Soft Kill Switch") + text: qsTr("Soft KillSwitch") descriptionText: qsTr("Internet connection is blocked if VPN connection drops accidentally") onClicked: { @@ -84,7 +84,7 @@ PageType { enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected checked: SettingsController.strictKillSwitchEnabled - text: qsTr("Strict Kill Switch") + text: qsTr("Strict KillSwitch") descriptionText: qsTr("Internet connection is blocked even if VPN was turned off manually or not started") onClicked: { diff --git a/service/server/killswitch.h b/service/server/killswitch.h index a468c6c2..56ffbca5 100644 --- a/service/server/killswitch.h +++ b/service/server/killswitch.h @@ -15,8 +15,8 @@ public: bool refresh(bool enabled); bool disableKillSwitch(); bool disableAllTraffic(); - bool enablePeerTraffic( const QJsonObject &configStr); - bool enableKillSwitch( const QJsonObject &configStr, int vpnAdapterIndex); + bool enablePeerTraffic(const QJsonObject &configStr); + bool enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex); bool allowTrafficTo(const QStringList &ranges); bool isStrictKillSwitchEnabled(); From d1f5d8815b4c328fc9476f599f2cddf904bce881 Mon Sep 17 00:00:00 2001 From: aiamnezia Date: Tue, 18 Mar 2025 22:41:56 +0400 Subject: [PATCH 24/33] Fix problem with definition sequence of PageSettingsKillSwitchExceptions.pml elements --- .../PageSettingsKillSwitchExceptions.qml | 44 +++++++++++-------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml b/client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml index 23f740b4..d442b60c 100644 --- a/client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml +++ b/client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml @@ -65,28 +65,13 @@ PageType { displayMarginEnd: 40 ScrollBar.vertical: ScrollBarType { } - - footer: AddSitePanel { - id: addSitePanel - + + footer: Item { 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() - } + height: addSitePanel.height } - footerPositioning: ListView.OverlayFooter + footerPositioning: ListView.InlineFooter model: SortFilterProxyModel { id: dnsFilterModel @@ -142,6 +127,27 @@ PageType { } } + AddSitePanel { + id: addSitePanel + + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + + enabled: root.pageEnabled + placeholderText: qsTr("IPv4 address") + + onAddClicked: function(text) { + PageController.showBusyIndicator(true) + AllowedDnsController.addDns(text) + PageController.showBusyIndicator(false) + } + + onMoreClicked: { + moreActionsDrawer.openTriggered() + } + } + DrawerType2 { id: moreActionsDrawer From 2a546ddc28ed2162001e7cd63c7367a0aab43364 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Sat, 5 Apr 2025 12:33:59 +0300 Subject: [PATCH 25/33] Add exclusion method for Windows firewall --- .../windows/daemon/windowsfirewall.cpp | 31 +++++++++++++++++++ .../windows/daemon/windowsfirewall.h | 1 + service/server/killswitch.cpp | 4 +++ 3 files changed, 36 insertions(+) diff --git a/client/platforms/windows/daemon/windowsfirewall.cpp b/client/platforms/windows/daemon/windowsfirewall.cpp index 63f7818b..85a2a155 100644 --- a/client/platforms/windows/daemon/windowsfirewall.cpp +++ b/client/platforms/windows/daemon/windowsfirewall.cpp @@ -241,6 +241,37 @@ bool WindowsFirewall::enableLanBypass(const QList& ranges) { return true; } +// Allow unprotected traffic sent to the following address ranges. +bool WindowsFirewall::allowTrafficRange(const QStringList& ranges) { + // Start the firewall transaction + auto result = FwpmTransactionBegin(m_sessionHandle, NULL); + if (result != ERROR_SUCCESS) { + disableKillSwitch(); + return false; + } + auto cleanup = qScopeGuard([&] { + FwpmTransactionAbort0(m_sessionHandle); + disableKillSwitch(); + }); + + for (const QString& addr : ranges) { + logger.debug() << "Allow killswitch exclude: " << addr; + if (!allowTrafficTo(QHostAddress(addr), LOW_WEIGHT + 1, "Allow killswitch bypass traffic")) { + return false; + } + } + + result = FwpmTransactionCommit0(m_sessionHandle); + if (result != ERROR_SUCCESS) { + logger.error() << "FwpmTransactionCommit0 failed with error:" << result; + return false; + } + + cleanup.dismiss(); + return true; +} + + bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) { // Start the firewall transaction auto result = FwpmTransactionBegin(m_sessionHandle, NULL); diff --git a/client/platforms/windows/daemon/windowsfirewall.h b/client/platforms/windows/daemon/windowsfirewall.h index f7dc84e2..9a0062da 100644 --- a/client/platforms/windows/daemon/windowsfirewall.h +++ b/client/platforms/windows/daemon/windowsfirewall.h @@ -44,6 +44,7 @@ class WindowsFirewall final : public QObject { bool disablePeerTraffic(const QString& pubkey); bool disableKillSwitch(); bool allowAllTraffic(); + bool allowTrafficRange(const QStringList& ranges); private: static bool initSublayer(); diff --git a/service/server/killswitch.cpp b/service/server/killswitch.cpp index 30fb837c..9559e299 100644 --- a/service/server/killswitch.cpp +++ b/service/server/killswitch.cpp @@ -165,6 +165,10 @@ bool KillSwitch::allowTrafficTo(const QStringList &ranges) { MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), true, QStringLiteral("allownets"), ranges); #endif +#ifdef Q_OS_WIN + WindowsFirewall::create(this)->allowTrafficRange(ranges); +#endif + return true; } From 3faa16e892cc26c71d1f5c9b7d391d1e7aa5251d Mon Sep 17 00:00:00 2001 From: aiamnezia Date: Wed, 9 Apr 2025 15:19:25 +0400 Subject: [PATCH 26/33] Change ubuntu version in deploy script --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 23df2d8f..688b57d3 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -10,7 +10,7 @@ env: jobs: Build-Linux-Ubuntu: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 env: QT_VERSION: 6.6.2 From 298c9fef9dc9747240ea0030bf21de2f64ecdc48 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Wed, 9 Apr 2025 17:21:35 +0300 Subject: [PATCH 27/33] Update ubuntu version in GH actions --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 688b57d3..ae8768a6 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -10,7 +10,7 @@ env: jobs: Build-Linux-Ubuntu: - runs-on: ubuntu-24.04 + runs-on: ubuntu-22.04 env: QT_VERSION: 6.6.2 From 97dc075f7384717e5b32d62cfe138056afb6dd38 Mon Sep 17 00:00:00 2001 From: aiamnezia Date: Thu, 17 Apr 2025 02:19:32 +0400 Subject: [PATCH 28/33] Add confirmation popup for strict killswitch mode --- client/ui/qml/Pages2/PageSettingsKillSwitch.qml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml index 10b7305c..f6e0f5d7 100644 --- a/client/ui/qml/Pages2/PageSettingsKillSwitch.qml +++ b/client/ui/qml/Pages2/PageSettingsKillSwitch.qml @@ -68,7 +68,7 @@ PageType { text: qsTr("Soft KillSwitch") descriptionText: qsTr("Internet connection is blocked if VPN connection drops accidentally") - onClicked: { + onClicked: function() { SettingsController.strictKillSwitchEnabled = false } } @@ -87,8 +87,19 @@ PageType { text: qsTr("Strict KillSwitch") descriptionText: qsTr("Internet connection is blocked even if VPN was turned off manually or not started") - onClicked: { - SettingsController.strictKillSwitchEnabled = true + onClicked: function() { + var headerText = qsTr("Just a little heads-up") + var descriptionText = qsTr("If you disconnect from VPN or the VPN connection drops while the Strict Kill Switch is turned on, your internet access will be disabled. To restore it, connect to VPN, change the Kill Switch mode or turn the Kill Switch off.") + var yesButtonText = qsTr("Continue") + var noButtonText = qsTr("Cancel") + + var yesButtonFunction = function() { + SettingsController.strictKillSwitchEnabled = true + } + var noButtonFunction = function() { + } + + showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) } } From c69c6ef002a5f7ee75facd5c008788027f9507ff Mon Sep 17 00:00:00 2001 From: aiamnezia Date: Thu, 17 Apr 2025 02:20:29 +0400 Subject: [PATCH 29/33] Add qt standard path for build script --- deploy/build_linux.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/deploy/build_linux.sh b/deploy/build_linux.sh index 57217a1e..aaf59eb5 100755 --- a/deploy/build_linux.sh +++ b/deploy/build_linux.sh @@ -41,6 +41,10 @@ if [ -z "${QT_VERSION+x}" ]; then QT_BIN_DIR=/opt/Qt/$QT_VERSION/gcc_64/bin elif [ -f $HOME/Qt/$QT_VERSION/gcc_64/bin/qmake ]; then QT_BIN_DIR=$HOME/Qt/$QT_VERSION/gcc_64/bin + elif [ -f /usr/lib/qt6/bin/qmake ]; then + QT_BIN_DIR=/usr/lib/qt6/bin + elif [ -f /usr/lib/x86_64-linux-gnu/qt6/bin/qmake ]; then + QT_BIN_DIR=/usr/lib/x86_64-linux-gnu/qt6/bin fi fi @@ -56,7 +60,7 @@ echo "Building App..." cd $BUILD_DIR $QT_BIN_DIR/qt-cmake -S $PROJECT_DIR -cmake --build . --config release +cmake --build . -j --config release # Build and run tests here From e46b51a833cbc67c8d8fdd6793e6128947e0e6f5 Mon Sep 17 00:00:00 2001 From: aiamnezia Date: Thu, 24 Apr 2025 01:53:12 +0400 Subject: [PATCH 30/33] Add method to killswitch for expanding strickt mode exceptions list and fix allowTrafficTo() for Windows. Also Added cache in KillSwitch class for exceptions --- .../platforms/linux/daemon/linuxfirewall.cpp | 3 -- ipc/ipc_interface.rep | 1 + ipc/ipcserver.cpp | 5 ++++ ipc/ipcserver.h | 1 + service/server/killswitch.cpp | 28 +++++++++++++++++-- service/server/killswitch.h | 2 ++ 6 files changed, 34 insertions(+), 6 deletions(-) diff --git a/client/platforms/linux/daemon/linuxfirewall.cpp b/client/platforms/linux/daemon/linuxfirewall.cpp index 96194bc7..de88c962 100644 --- a/client/platforms/linux/daemon/linuxfirewall.cpp +++ b/client/platforms/linux/daemon/linuxfirewall.cpp @@ -455,9 +455,6 @@ void LinuxFirewall::updateDNSServers(const QStringList& servers) void LinuxFirewall::updateAllowNets(const QStringList& servers) { - static QStringList existingServers {}; - - existingServers = servers; execute(QStringLiteral("iptables -F %1.110.allowNets").arg(kAnchorName)); for (const QString& rule : getAllowRule(servers)) execute(QStringLiteral("iptables -A %1.110.allowNets %2").arg(kAnchorName, rule)); diff --git a/ipc/ipc_interface.rep b/ipc/ipc_interface.rep index c692817d..489f860f 100644 --- a/ipc/ipc_interface.rep +++ b/ipc/ipc_interface.rep @@ -32,6 +32,7 @@ class IpcInterface SLOT( bool disableAllTraffic() ); SLOT( bool refreshKillSwitch( bool enabled ) ); SLOT( bool allowTrafficTo( const QStringList ranges ) ); + SLOT( bool addKillSwitchExceptions( 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 ced2987e..445ee954 100644 --- a/ipc/ipcserver.cpp +++ b/ipc/ipcserver.cpp @@ -184,6 +184,11 @@ bool IpcServer::allowTrafficTo(QStringList ranges) return KillSwitch::instance()->allowTrafficTo(ranges); } +bool IpcServer::addKillSwitchExceptions(QStringList ranges) +{ + return KillSwitch::instance()->addAllowedRange(ranges); +} + bool IpcServer::disableAllTraffic() { return KillSwitch::instance()->disableAllTraffic(); diff --git a/ipc/ipcserver.h b/ipc/ipcserver.h index 31cd007f..bbd11830 100644 --- a/ipc/ipcserver.h +++ b/ipc/ipcserver.h @@ -41,6 +41,7 @@ public: virtual bool disableKillSwitch() override; virtual bool refreshKillSwitch( bool enabled ) override; virtual bool updateResolvers(const QString& ifname, const QList& resolvers) override; + virtual bool addKillSwitchExceptions(QStringList ranges) override; private: int m_localpid = 0; diff --git a/service/server/killswitch.cpp b/service/server/killswitch.cpp index 9559e299..70bfeb4d 100644 --- a/service/server/killswitch.cpp +++ b/service/server/killswitch.cpp @@ -125,6 +125,7 @@ bool KillSwitch::disableKillSwitch() { return WindowsFirewall::create(this)->allowAllTraffic(); #endif + m_allowedRanges.clear(); return true; } @@ -150,28 +151,49 @@ bool KillSwitch::disableAllTraffic() { MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true); MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true); #endif + m_allowedRanges.clear(); return true; } bool KillSwitch::allowTrafficTo(const QStringList &ranges) { + m_allowedRanges = ranges; + #ifdef Q_OS_LINUX LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), true); - LinuxFirewall::updateAllowNets(ranges); + LinuxFirewall::updateAllowNets(m_allowedRanges); #endif #ifdef Q_OS_MACOS MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), true); - MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), true, QStringLiteral("allownets"), ranges); + MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), true, QStringLiteral("allownets"), m_allowedRanges); #endif #ifdef Q_OS_WIN - WindowsFirewall::create(this)->allowTrafficRange(ranges); + if (isStrictKillSwitchEnabled()) { + WindowsFirewall::create(this)->enableInterface(-1); + } + WindowsFirewall::create(this)->allowTrafficRange(m_allowedRanges); #endif return true; } +bool KillSwitch::addAllowedRange(const QStringList &ranges) { + for (const QString &range : ranges) { + if (!range.isEmpty() && !m_allowedRanges.contains(range)) { + m_allowedRanges.append(range); + } + } + +#ifdef Q_OS_WIN + WindowsFirewall::create(this)->allowTrafficRange(ranges); + return true; +#else + return allowTrafficTo(m_allowedRanges); +#endif +} + bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) { #ifdef Q_OS_WIN InterfaceConfig config; diff --git a/service/server/killswitch.h b/service/server/killswitch.h index 56ffbca5..12343df3 100644 --- a/service/server/killswitch.h +++ b/service/server/killswitch.h @@ -18,10 +18,12 @@ public: bool enablePeerTraffic(const QJsonObject &configStr); bool enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex); bool allowTrafficTo(const QStringList &ranges); + bool addAllowedRange(const QStringList &ranges); bool isStrictKillSwitchEnabled(); private: KillSwitch(QObject* parent) {}; + QStringList m_allowedRanges; QSharedPointer m_appSettigns; }; From 5bf5cd43bda75261e1df8a057c5a26b61a27279f Mon Sep 17 00:00:00 2001 From: aiamnezia Date: Thu, 24 Apr 2025 01:55:29 +0400 Subject: [PATCH 31/33] Add insertion of gateway address to strict killswitch exceptions --- client/core/controllers/gatewayController.cpp | 25 +++++++++++++++++++ client/protocols/openvpnprotocol.cpp | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/client/core/controllers/gatewayController.cpp b/client/core/controllers/gatewayController.cpp index f8c23c1a..23fa4933 100644 --- a/client/core/controllers/gatewayController.cpp +++ b/client/core/controllers/gatewayController.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "QBlockCipher.h" #include "QRsa.h" @@ -14,6 +15,8 @@ #include "amnezia_application.h" #include "core/api/apiUtils.h" #include "utilities.h" +#include "core/ipcclient.h" +#include "core/networkUtilities.h" namespace { @@ -50,6 +53,17 @@ ErrorCode GatewayController::get(const QString &endpoint, QByteArray &responseBo request.setUrl(QString(endpoint).arg(m_gatewayEndpoint)); + // bypass killSwitch exceptions for API-gateway +#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) || defined(Q_OS_WIN) + { + QString host = QUrl(request.url()).host(); + QString ip = NetworkUtilities::getIPAddress(host); + if (!ip.isEmpty()) { + IpcClient::Interface()->addKillSwitchExceptions(QStringList{ip}); + } + } +#endif + QNetworkReply *reply; reply = amnApp->networkManager()->get(request); @@ -101,6 +115,17 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api request.setUrl(endpoint.arg(m_gatewayEndpoint)); + // bypass killSwitch exceptions for API-gateway +#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) || defined(Q_OS_WIN) + { + QString host = QUrl(request.url()).host(); + QString ip = NetworkUtilities::getIPAddress(host); + if (!ip.isEmpty()) { + IpcClient::Interface()->addKillSwitchExceptions(QStringList{ip}); + } + } +#endif + QSimpleCrypto::QBlockCipher blockCipher; QByteArray key = blockCipher.generatePrivateSalt(32); QByteArray iv = blockCipher.generatePrivateSalt(32); diff --git a/client/protocols/openvpnprotocol.cpp b/client/protocols/openvpnprotocol.cpp index 1721a45a..5ede4c42 100644 --- a/client/protocols/openvpnprotocol.cpp +++ b/client/protocols/openvpnprotocol.cpp @@ -172,7 +172,7 @@ ErrorCode OpenVpnProtocol::start() } #if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) - IpcClient::Interface()->allowTrafficTo(QStringList(NetworkUtilities::getIPAddress( + IpcClient::Interface()->addKillSwitchExceptions(QStringList(NetworkUtilities::getIPAddress( m_configData.value(amnezia::config_key::hostName).toString()))); #endif From 6be32445ba39140eb3349de5b8dae85f10c81f11 Mon Sep 17 00:00:00 2001 From: aiamnezia Date: Thu, 24 Apr 2025 16:35:37 +0400 Subject: [PATCH 32/33] Review fixes --- ipc/ipcserver.cpp | 2 +- service/server/killswitch.cpp | 9 ++------- service/server/killswitch.h | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/ipc/ipcserver.cpp b/ipc/ipcserver.cpp index 445ee954..f8a25eb6 100644 --- a/ipc/ipcserver.cpp +++ b/ipc/ipcserver.cpp @@ -181,7 +181,7 @@ void IpcServer::setLogsEnabled(bool enabled) bool IpcServer::allowTrafficTo(QStringList ranges) { - return KillSwitch::instance()->allowTrafficTo(ranges); + return KillSwitch::instance()->resetAllowedRange(ranges); } bool IpcServer::addKillSwitchExceptions(QStringList ranges) diff --git a/service/server/killswitch.cpp b/service/server/killswitch.cpp index 70bfeb4d..c44bd6a2 100644 --- a/service/server/killswitch.cpp +++ b/service/server/killswitch.cpp @@ -155,7 +155,7 @@ bool KillSwitch::disableAllTraffic() { return true; } -bool KillSwitch::allowTrafficTo(const QStringList &ranges) { +bool KillSwitch::resetAllowedRange(const QStringList &ranges) { m_allowedRanges = ranges; @@ -186,12 +186,7 @@ bool KillSwitch::addAllowedRange(const QStringList &ranges) { } } -#ifdef Q_OS_WIN - WindowsFirewall::create(this)->allowTrafficRange(ranges); - return true; -#else - return allowTrafficTo(m_allowedRanges); -#endif + return resetAllowedRange(m_allowedRanges); } bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) { diff --git a/service/server/killswitch.h b/service/server/killswitch.h index 12343df3..519e2ed2 100644 --- a/service/server/killswitch.h +++ b/service/server/killswitch.h @@ -17,7 +17,7 @@ public: bool disableAllTraffic(); bool enablePeerTraffic(const QJsonObject &configStr); bool enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex); - bool allowTrafficTo(const QStringList &ranges); + bool resetAllowedRange(const QStringList &ranges); bool addAllowedRange(const QStringList &ranges); bool isStrictKillSwitchEnabled(); From 7cc8aad8764b618292569ccda063981cdfed0b01 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Thu, 24 Apr 2025 18:31:26 +0300 Subject: [PATCH 33/33] buildfix and naming --- client/core/controllers/gatewayController.cpp | 13 ++++++++----- client/protocols/openvpnprotocol.cpp | 2 +- ipc/ipc_interface.rep | 4 ++-- ipc/ipcserver.cpp | 4 ++-- ipc/ipcserver.h | 4 ++-- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/client/core/controllers/gatewayController.cpp b/client/core/controllers/gatewayController.cpp index 23fa4933..0d86b9d5 100644 --- a/client/core/controllers/gatewayController.cpp +++ b/client/core/controllers/gatewayController.cpp @@ -15,9 +15,12 @@ #include "amnezia_application.h" #include "core/api/apiUtils.h" #include "utilities.h" -#include "core/ipcclient.h" #include "core/networkUtilities.h" +#ifdef AMNEZIA_DESKTOP + #include "core/ipcclient.h" +#endif + namespace { namespace configKey @@ -54,12 +57,12 @@ ErrorCode GatewayController::get(const QString &endpoint, QByteArray &responseBo request.setUrl(QString(endpoint).arg(m_gatewayEndpoint)); // bypass killSwitch exceptions for API-gateway -#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) || defined(Q_OS_WIN) +#ifdef AMNEZIA_DESKTOP { QString host = QUrl(request.url()).host(); QString ip = NetworkUtilities::getIPAddress(host); if (!ip.isEmpty()) { - IpcClient::Interface()->addKillSwitchExceptions(QStringList{ip}); + IpcClient::Interface()->addKillSwitchAllowedRange(QStringList{ip}); } } #endif @@ -116,12 +119,12 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api request.setUrl(endpoint.arg(m_gatewayEndpoint)); // bypass killSwitch exceptions for API-gateway -#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) || defined(Q_OS_WIN) +#ifdef AMNEZIA_DESKTOP { QString host = QUrl(request.url()).host(); QString ip = NetworkUtilities::getIPAddress(host); if (!ip.isEmpty()) { - IpcClient::Interface()->addKillSwitchExceptions(QStringList{ip}); + IpcClient::Interface()->addKillSwitchAllowedRange(QStringList{ip}); } } #endif diff --git a/client/protocols/openvpnprotocol.cpp b/client/protocols/openvpnprotocol.cpp index 5ede4c42..0c8f5907 100644 --- a/client/protocols/openvpnprotocol.cpp +++ b/client/protocols/openvpnprotocol.cpp @@ -172,7 +172,7 @@ ErrorCode OpenVpnProtocol::start() } #if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) - IpcClient::Interface()->addKillSwitchExceptions(QStringList(NetworkUtilities::getIPAddress( + IpcClient::Interface()->addKillSwitchAllowedRange(QStringList(NetworkUtilities::getIPAddress( m_configData.value(amnezia::config_key::hostName).toString()))); #endif diff --git a/ipc/ipc_interface.rep b/ipc/ipc_interface.rep index 489f860f..4ecae9bc 100644 --- a/ipc/ipc_interface.rep +++ b/ipc/ipc_interface.rep @@ -31,8 +31,8 @@ class IpcInterface SLOT( bool disableKillSwitch() ); SLOT( bool disableAllTraffic() ); SLOT( bool refreshKillSwitch( bool enabled ) ); - SLOT( bool allowTrafficTo( const QStringList ranges ) ); - SLOT( bool addKillSwitchExceptions( const QStringList ranges ) ); + SLOT( bool addKillSwitchAllowedRange( const QStringList ranges ) ); + SLOT( bool resetKillSwitchAllowedRange( const QStringList ranges ) ); SLOT( bool enablePeerTraffic( const QJsonObject &configStr) ); SLOT( bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex) ); SLOT( bool updateResolvers(const QString& ifname, const QList& resolvers) ); diff --git a/ipc/ipcserver.cpp b/ipc/ipcserver.cpp index f8a25eb6..0c7f5295 100644 --- a/ipc/ipcserver.cpp +++ b/ipc/ipcserver.cpp @@ -179,12 +179,12 @@ void IpcServer::setLogsEnabled(bool enabled) } } -bool IpcServer::allowTrafficTo(QStringList ranges) +bool IpcServer::resetKillSwitchAllowedRange(QStringList ranges) { return KillSwitch::instance()->resetAllowedRange(ranges); } -bool IpcServer::addKillSwitchExceptions(QStringList ranges) +bool IpcServer::addKillSwitchAllowedRange(QStringList ranges) { return KillSwitch::instance()->addAllowedRange(ranges); } diff --git a/ipc/ipcserver.h b/ipc/ipcserver.h index bbd11830..00d36354 100644 --- a/ipc/ipcserver.h +++ b/ipc/ipcserver.h @@ -35,13 +35,13 @@ public: virtual void StartRoutingIpv6() override; virtual void StopRoutingIpv6() override; virtual bool disableAllTraffic() override; - virtual bool allowTrafficTo(QStringList ranges) override; + virtual bool addKillSwitchAllowedRange(QStringList ranges) override; + virtual bool resetKillSwitchAllowedRange(QStringList ranges) override; virtual bool enablePeerTraffic(const QJsonObject &configStr) override; virtual bool enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) override; virtual bool disableKillSwitch() override; virtual bool refreshKillSwitch( bool enabled ) override; virtual bool updateResolvers(const QString& ifname, const QList& resolvers) override; - virtual bool addKillSwitchExceptions(QStringList ranges) override; private: int m_localpid = 0;