feature: fillswitch strict mode (#1333)
* Add allowed DNS list for killswitch * Windows killswitch strict mode backend part * Killswitch strict mode for Linux and MacOS * Windows fixes * feature: Add Kill Switch settings page with strict mode option * fix windows build after merge * Refresh killswitch mode when it toggled * Use HLM to store strictMode flag * Some Linux updates * feat: Enhance VerticalRadioButton with improved styling and disabled states * Refresh killSwitch state update * Fix build * refactor: Modularize header components * Change kill switch radio button styling * Fix strict kill switch mode handling * Refactor: Replace HeaderType with new Types for headers in QML pages * Remove deprecated HeaderType QML component * Refresh strict mode killswitch after global toggle change * Implement model, controller and UI for killswitch dns exceptions * Connect backend part and UI * Change label text to DNS exceptions * Remove HeaderType from PageSettingsApiDevices * Some pretty fixes * Fix problem with definition sequence of PageSettingsKillSwitchExceptions.pml elements * Add exclusion method for Windows firewall * Change ubuntu version in deploy script * Update ubuntu version in GH actions * Add confirmation popup for strict killswitch mode * Add qt standard path for build script * Add method to killswitch for expanding strickt mode exceptions list and fix allowTrafficTo() for Windows. Also Added cache in KillSwitch class for exceptions * Add insertion of gateway address to strict killswitch exceptions * Review fixes * buildfix and naming --------- Co-authored-by: aiamnezia <ai@amnezia.org>
This commit is contained in:
parent
5bd88ac2e9
commit
f6d7552b58
88 changed files with 1718 additions and 418 deletions
|
@ -48,6 +48,9 @@ void CoreController::initModels()
|
||||||
m_sitesModel.reset(new SitesModel(m_settings, this));
|
m_sitesModel.reset(new SitesModel(m_settings, this));
|
||||||
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
|
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
|
||||||
|
|
||||||
|
m_allowedDnsModel.reset(new AllowedDnsModel(m_settings, this));
|
||||||
|
m_engine->rootContext()->setContextProperty("AllowedDnsModel", m_allowedDnsModel.get());
|
||||||
|
|
||||||
m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this));
|
m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this));
|
||||||
m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get());
|
m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get());
|
||||||
|
|
||||||
|
@ -130,6 +133,9 @@ void CoreController::initControllers()
|
||||||
m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
|
m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
|
||||||
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
|
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
|
||||||
|
|
||||||
|
m_allowedDnsController.reset(new AllowedDnsController(m_settings, m_allowedDnsModel));
|
||||||
|
m_engine->rootContext()->setContextProperty("AllowedDnsController", m_allowedDnsController.get());
|
||||||
|
|
||||||
m_appSplitTunnelingController.reset(new AppSplitTunnelingController(m_settings, m_appSplitTunnelingModel));
|
m_appSplitTunnelingController.reset(new AppSplitTunnelingController(m_settings, m_appSplitTunnelingModel));
|
||||||
m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get());
|
m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get());
|
||||||
|
|
||||||
|
@ -214,6 +220,7 @@ void CoreController::initSignalHandlers()
|
||||||
initAutoConnectHandler();
|
initAutoConnectHandler();
|
||||||
initAmneziaDnsToggledHandler();
|
initAmneziaDnsToggledHandler();
|
||||||
initPrepareConfigHandler();
|
initPrepareConfigHandler();
|
||||||
|
initStrictKillSwitchHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::initNotificationHandler()
|
void CoreController::initNotificationHandler()
|
||||||
|
@ -356,6 +363,12 @@ void CoreController::initPrepareConfigHandler()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CoreController::initStrictKillSwitchHandler()
|
||||||
|
{
|
||||||
|
connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged,
|
||||||
|
m_vpnConnection.get(), &VpnConnection::onKillSwitchModeChanged);
|
||||||
|
}
|
||||||
|
|
||||||
QSharedPointer<PageController> CoreController::pageController() const
|
QSharedPointer<PageController> CoreController::pageController() const
|
||||||
{
|
{
|
||||||
return m_pageController;
|
return m_pageController;
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "ui/controllers/api/apiConfigsController.h"
|
#include "ui/controllers/api/apiConfigsController.h"
|
||||||
#include "ui/controllers/api/apiSettingsController.h"
|
#include "ui/controllers/api/apiSettingsController.h"
|
||||||
#include "ui/controllers/appSplitTunnelingController.h"
|
#include "ui/controllers/appSplitTunnelingController.h"
|
||||||
|
#include "ui/controllers/allowedDnsController.h"
|
||||||
#include "ui/controllers/connectionController.h"
|
#include "ui/controllers/connectionController.h"
|
||||||
#include "ui/controllers/exportController.h"
|
#include "ui/controllers/exportController.h"
|
||||||
#include "ui/controllers/focusController.h"
|
#include "ui/controllers/focusController.h"
|
||||||
|
@ -18,6 +19,7 @@
|
||||||
#include "ui/controllers/sitesController.h"
|
#include "ui/controllers/sitesController.h"
|
||||||
#include "ui/controllers/systemController.h"
|
#include "ui/controllers/systemController.h"
|
||||||
|
|
||||||
|
#include "ui/models/allowed_dns_model.h"
|
||||||
#include "ui/models/containers_model.h"
|
#include "ui/models/containers_model.h"
|
||||||
#include "ui/models/languageModel.h"
|
#include "ui/models/languageModel.h"
|
||||||
#include "ui/models/protocols/cloakConfigModel.h"
|
#include "ui/models/protocols/cloakConfigModel.h"
|
||||||
|
@ -80,6 +82,7 @@ private:
|
||||||
void initAutoConnectHandler();
|
void initAutoConnectHandler();
|
||||||
void initAmneziaDnsToggledHandler();
|
void initAmneziaDnsToggledHandler();
|
||||||
void initPrepareConfigHandler();
|
void initPrepareConfigHandler();
|
||||||
|
void initStrictKillSwitchHandler();
|
||||||
|
|
||||||
QQmlApplicationEngine *m_engine {}; // TODO use parent child system here?
|
QQmlApplicationEngine *m_engine {}; // TODO use parent child system here?
|
||||||
std::shared_ptr<Settings> m_settings;
|
std::shared_ptr<Settings> m_settings;
|
||||||
|
@ -102,6 +105,7 @@ private:
|
||||||
QScopedPointer<SitesController> m_sitesController;
|
QScopedPointer<SitesController> m_sitesController;
|
||||||
QScopedPointer<SystemController> m_systemController;
|
QScopedPointer<SystemController> m_systemController;
|
||||||
QScopedPointer<AppSplitTunnelingController> m_appSplitTunnelingController;
|
QScopedPointer<AppSplitTunnelingController> m_appSplitTunnelingController;
|
||||||
|
QScopedPointer<AllowedDnsController> m_allowedDnsController;
|
||||||
|
|
||||||
QScopedPointer<ApiSettingsController> m_apiSettingsController;
|
QScopedPointer<ApiSettingsController> m_apiSettingsController;
|
||||||
QScopedPointer<ApiConfigsController> m_apiConfigsController;
|
QScopedPointer<ApiConfigsController> m_apiConfigsController;
|
||||||
|
@ -112,6 +116,7 @@ private:
|
||||||
QSharedPointer<LanguageModel> m_languageModel;
|
QSharedPointer<LanguageModel> m_languageModel;
|
||||||
QSharedPointer<ProtocolsModel> m_protocolsModel;
|
QSharedPointer<ProtocolsModel> m_protocolsModel;
|
||||||
QSharedPointer<SitesModel> m_sitesModel;
|
QSharedPointer<SitesModel> m_sitesModel;
|
||||||
|
QSharedPointer<AllowedDnsModel> m_allowedDnsModel;
|
||||||
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
|
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
|
||||||
QSharedPointer<ClientManagementModel> m_clientManagementModel;
|
QSharedPointer<ClientManagementModel> m_clientManagementModel;
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
#include "QBlockCipher.h"
|
#include "QBlockCipher.h"
|
||||||
#include "QRsa.h"
|
#include "QRsa.h"
|
||||||
|
@ -14,6 +15,11 @@
|
||||||
#include "amnezia_application.h"
|
#include "amnezia_application.h"
|
||||||
#include "core/api/apiUtils.h"
|
#include "core/api/apiUtils.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
#include "core/networkUtilities.h"
|
||||||
|
|
||||||
|
#ifdef AMNEZIA_DESKTOP
|
||||||
|
#include "core/ipcclient.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -50,6 +56,17 @@ ErrorCode GatewayController::get(const QString &endpoint, QByteArray &responseBo
|
||||||
|
|
||||||
request.setUrl(QString(endpoint).arg(m_gatewayEndpoint));
|
request.setUrl(QString(endpoint).arg(m_gatewayEndpoint));
|
||||||
|
|
||||||
|
// bypass killSwitch exceptions for API-gateway
|
||||||
|
#ifdef AMNEZIA_DESKTOP
|
||||||
|
{
|
||||||
|
QString host = QUrl(request.url()).host();
|
||||||
|
QString ip = NetworkUtilities::getIPAddress(host);
|
||||||
|
if (!ip.isEmpty()) {
|
||||||
|
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList{ip});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
QNetworkReply *reply;
|
QNetworkReply *reply;
|
||||||
reply = amnApp->networkManager()->get(request);
|
reply = amnApp->networkManager()->get(request);
|
||||||
|
|
||||||
|
@ -101,6 +118,17 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
|
||||||
|
|
||||||
request.setUrl(endpoint.arg(m_gatewayEndpoint));
|
request.setUrl(endpoint.arg(m_gatewayEndpoint));
|
||||||
|
|
||||||
|
// bypass killSwitch exceptions for API-gateway
|
||||||
|
#ifdef AMNEZIA_DESKTOP
|
||||||
|
{
|
||||||
|
QString host = QUrl(request.url()).host();
|
||||||
|
QString ip = NetworkUtilities::getIPAddress(host);
|
||||||
|
if (!ip.isEmpty()) {
|
||||||
|
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList{ip});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
QSimpleCrypto::QBlockCipher blockCipher;
|
QSimpleCrypto::QBlockCipher blockCipher;
|
||||||
QByteArray key = blockCipher.generatePrivateSalt(32);
|
QByteArray key = blockCipher.generatePrivateSalt(32);
|
||||||
QByteArray iv = blockCipher.generatePrivateSalt(32);
|
QByteArray iv = blockCipher.generatePrivateSalt(32);
|
||||||
|
|
|
@ -371,6 +371,9 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
||||||
if (!parseStringList(obj, "vpnDisabledApps", config.m_vpnDisabledApps)) {
|
if (!parseStringList(obj, "vpnDisabledApps", config.m_vpnDisabledApps)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!parseStringList(obj, "allowedDnsServers", config.m_allowedDnsServers)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
config.m_killSwitchEnabled = QVariant(obj.value("killSwitchOption").toString()).toBool();
|
config.m_killSwitchEnabled = QVariant(obj.value("killSwitchOption").toString()).toBool();
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,13 @@ QJsonObject InterfaceConfig::toJson() const {
|
||||||
}
|
}
|
||||||
json.insert("excludedAddresses", jsExcludedAddresses);
|
json.insert("excludedAddresses", jsExcludedAddresses);
|
||||||
|
|
||||||
|
|
||||||
|
QJsonArray jsAllowedDnsServers;
|
||||||
|
for (const QString& i : m_allowedDnsServers) {
|
||||||
|
jsAllowedDnsServers.append(QJsonValue(i));
|
||||||
|
}
|
||||||
|
json.insert("allowedDnsServers", jsAllowedDnsServers);
|
||||||
|
|
||||||
QJsonArray disabledApps;
|
QJsonArray disabledApps;
|
||||||
for (const QString& i : m_vpnDisabledApps) {
|
for (const QString& i : m_vpnDisabledApps) {
|
||||||
disabledApps.append(QJsonValue(i));
|
disabledApps.append(QJsonValue(i));
|
||||||
|
|
|
@ -38,6 +38,7 @@ class InterfaceConfig {
|
||||||
QList<IPAddress> m_allowedIPAddressRanges;
|
QList<IPAddress> m_allowedIPAddressRanges;
|
||||||
QStringList m_excludedAddresses;
|
QStringList m_excludedAddresses;
|
||||||
QStringList m_vpnDisabledApps;
|
QStringList m_vpnDisabledApps;
|
||||||
|
QStringList m_allowedDnsServers;
|
||||||
bool m_killSwitchEnabled;
|
bool m_killSwitchEnabled;
|
||||||
#if defined(MZ_ANDROID) || defined(MZ_IOS)
|
#if defined(MZ_ANDROID) || defined(MZ_IOS)
|
||||||
QString m_installationId;
|
QString m_installationId;
|
||||||
|
|
|
@ -123,6 +123,7 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||||
|
|
||||||
int appSplitTunnelType = rawConfig.value(amnezia::config_key::appSplitTunnelType).toInt();
|
int appSplitTunnelType = rawConfig.value(amnezia::config_key::appSplitTunnelType).toInt();
|
||||||
QJsonArray splitTunnelApps = rawConfig.value(amnezia::config_key::splitTunnelApps).toArray();
|
QJsonArray splitTunnelApps = rawConfig.value(amnezia::config_key::splitTunnelApps).toArray();
|
||||||
|
QJsonArray allowedDns = rawConfig.value(amnezia::config_key::allowedDnsServers).toArray();
|
||||||
|
|
||||||
QJsonObject wgConfig = rawConfig.value(protocolName + "_config_data").toObject();
|
QJsonObject wgConfig = rawConfig.value(protocolName + "_config_data").toObject();
|
||||||
|
|
||||||
|
@ -226,6 +227,8 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||||
|
|
||||||
json.insert("vpnDisabledApps", splitTunnelApps);
|
json.insert("vpnDisabledApps", splitTunnelApps);
|
||||||
|
|
||||||
|
json.insert("allowedDnsServers", allowedDns);
|
||||||
|
|
||||||
json.insert(amnezia::config_key::killSwitchOption, rawConfig.value(amnezia::config_key::killSwitchOption));
|
json.insert(amnezia::config_key::killSwitchOption, rawConfig.value(amnezia::config_key::killSwitchOption));
|
||||||
|
|
||||||
if (protocolName == amnezia::config_key::awg) {
|
if (protocolName == amnezia::config_key::awg) {
|
||||||
|
|
|
@ -455,9 +455,6 @@ void LinuxFirewall::updateDNSServers(const QStringList& servers)
|
||||||
|
|
||||||
void LinuxFirewall::updateAllowNets(const QStringList& servers)
|
void LinuxFirewall::updateAllowNets(const QStringList& servers)
|
||||||
{
|
{
|
||||||
static QStringList existingServers {};
|
|
||||||
|
|
||||||
existingServers = servers;
|
|
||||||
execute(QStringLiteral("iptables -F %1.110.allowNets").arg(kAnchorName));
|
execute(QStringLiteral("iptables -F %1.110.allowNets").arg(kAnchorName));
|
||||||
for (const QString& rule : getAllowRule(servers))
|
for (const QString& rule : getAllowRule(servers))
|
||||||
execute(QStringLiteral("iptables -A %1.110.allowNets %2").arg(kAnchorName, rule));
|
execute(QStringLiteral("iptables -A %1.110.allowNets %2").arg(kAnchorName, rule));
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
#include "leakdetector.h"
|
#include "leakdetector.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
|
||||||
|
#include "killswitch.h"
|
||||||
|
|
||||||
constexpr const int WG_TUN_PROC_TIMEOUT = 5000;
|
constexpr const int WG_TUN_PROC_TIMEOUT = 5000;
|
||||||
constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg";
|
constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg";
|
||||||
|
|
||||||
|
@ -182,7 +184,7 @@ bool WireguardUtilsLinux::deleteInterface() {
|
||||||
QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name"));
|
QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name"));
|
||||||
|
|
||||||
// double-check + ensure our firewall is installed and enabled
|
// double-check + ensure our firewall is installed and enabled
|
||||||
LinuxFirewall::uninstall();
|
KillSwitch::instance()->disableKillSwitch();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
#include "leakdetector.h"
|
#include "leakdetector.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
|
||||||
|
#include "killswitch.h"
|
||||||
|
|
||||||
constexpr const int WG_TUN_PROC_TIMEOUT = 5000;
|
constexpr const int WG_TUN_PROC_TIMEOUT = 5000;
|
||||||
constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg";
|
constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg";
|
||||||
|
|
||||||
|
@ -180,7 +182,7 @@ bool WireguardUtilsMacos::deleteInterface() {
|
||||||
QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name"));
|
QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name"));
|
||||||
|
|
||||||
// double-check + ensure our firewall is installed and enabled
|
// double-check + ensure our firewall is installed and enabled
|
||||||
MacOSFirewall::uninstall();
|
KillSwitch::instance()->disableKillSwitch();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "platforms/windows/windowsutils.h"
|
#include "platforms/windows/windowsutils.h"
|
||||||
|
|
||||||
|
#include "killswitch.h"
|
||||||
|
|
||||||
#define IPV6_ADDRESS_SIZE 16
|
#define IPV6_ADDRESS_SIZE 16
|
||||||
|
|
||||||
// ID for the Firewall Sublayer
|
// ID for the Firewall Sublayer
|
||||||
|
@ -180,16 +182,29 @@ bool WindowsFirewall::enableInterface(int vpnAdapterIndex) {
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info() << "Enabling firewall Using Adapter:" << vpnAdapterIndex;
|
logger.info() << "Enabling Killswitch Using Adapter:" << vpnAdapterIndex;
|
||||||
|
if (vpnAdapterIndex < 0)
|
||||||
|
{
|
||||||
|
IPAddress allv4("0.0.0.0/0");
|
||||||
|
if (!blockTrafficTo(allv4, MED_WEIGHT,
|
||||||
|
"Block Internet", "killswitch")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
IPAddress allv6("::/0");
|
||||||
|
if (!blockTrafficTo(allv6, MED_WEIGHT,
|
||||||
|
"Block Internet", "killswitch")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else
|
||||||
FW_OK(allowTrafficOfAdapter(vpnAdapterIndex, MED_WEIGHT,
|
FW_OK(allowTrafficOfAdapter(vpnAdapterIndex, MED_WEIGHT,
|
||||||
"Allow usage of VPN Adapter"));
|
"Allow usage of VPN Adapter"));
|
||||||
FW_OK(allowDHCPTraffic(MED_WEIGHT, "Allow DHCP Traffic"));
|
FW_OK(allowDHCPTraffic(MED_WEIGHT, "Allow DHCP Traffic"));
|
||||||
FW_OK(allowHyperVTraffic(MED_WEIGHT, "Allow Hyper-V Traffic"));
|
FW_OK(allowHyperVTraffic(MAX_WEIGHT, "Allow Hyper-V Traffic"));
|
||||||
FW_OK(allowTrafficForAppOnAll(getCurrentPath(), MAX_WEIGHT,
|
FW_OK(allowTrafficForAppOnAll(getCurrentPath(), MAX_WEIGHT,
|
||||||
"Allow all for AmneziaVPN.exe"));
|
"Allow all for AmneziaVPN.exe"));
|
||||||
FW_OK(blockTrafficOnPort(53, MED_WEIGHT, "Block all DNS"));
|
FW_OK(blockTrafficOnPort(53, MED_WEIGHT, "Block all DNS"));
|
||||||
FW_OK(
|
FW_OK(allowLoopbackTraffic(MED_WEIGHT,
|
||||||
allowLoopbackTraffic(MED_WEIGHT, "Allow Loopback traffic on device %1"));
|
"Allow Loopback traffic on device %1"));
|
||||||
|
|
||||||
logger.debug() << "Killswitch on! Rules:" << m_activeRules.length();
|
logger.debug() << "Killswitch on! Rules:" << m_activeRules.length();
|
||||||
return true;
|
return true;
|
||||||
|
@ -226,6 +241,37 @@ bool WindowsFirewall::enableLanBypass(const QList<IPAddress>& ranges) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allow unprotected traffic sent to the following address ranges.
|
||||||
|
bool WindowsFirewall::allowTrafficRange(const QStringList& ranges) {
|
||||||
|
// Start the firewall transaction
|
||||||
|
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
disableKillSwitch();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto cleanup = qScopeGuard([&] {
|
||||||
|
FwpmTransactionAbort0(m_sessionHandle);
|
||||||
|
disableKillSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const QString& addr : ranges) {
|
||||||
|
logger.debug() << "Allow killswitch exclude: " << addr;
|
||||||
|
if (!allowTrafficTo(QHostAddress(addr), 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) {
|
bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
||||||
// Start the firewall transaction
|
// Start the firewall transaction
|
||||||
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
||||||
|
@ -262,6 +308,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()) {
|
if (!config.m_excludedAddresses.empty()) {
|
||||||
for (const QString& i : config.m_excludedAddresses) {
|
for (const QString& i : config.m_excludedAddresses) {
|
||||||
logger.debug() << "excludedAddresses range: " << i;
|
logger.debug() << "excludedAddresses range: " << i;
|
||||||
|
@ -313,6 +367,10 @@ bool WindowsFirewall::disablePeerTraffic(const QString& pubkey) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowsFirewall::disableKillSwitch() {
|
bool WindowsFirewall::disableKillSwitch() {
|
||||||
|
return KillSwitch::instance()->disableKillSwitch();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowsFirewall::allowAllTraffic() {
|
||||||
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
||||||
auto cleanup = qScopeGuard([&] {
|
auto cleanup = qScopeGuard([&] {
|
||||||
if (result != ERROR_SUCCESS) {
|
if (result != ERROR_SUCCESS) {
|
||||||
|
|
|
@ -43,6 +43,8 @@ class WindowsFirewall final : public QObject {
|
||||||
bool enablePeerTraffic(const InterfaceConfig& config);
|
bool enablePeerTraffic(const InterfaceConfig& config);
|
||||||
bool disablePeerTraffic(const QString& pubkey);
|
bool disablePeerTraffic(const QString& pubkey);
|
||||||
bool disableKillSwitch();
|
bool disableKillSwitch();
|
||||||
|
bool allowAllTraffic();
|
||||||
|
bool allowTrafficRange(const QStringList& ranges);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool initSublayer();
|
static bool initSublayer();
|
||||||
|
|
|
@ -171,6 +171,11 @@ ErrorCode OpenVpnProtocol::start()
|
||||||
return lastError();
|
return lastError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
|
||||||
|
IpcClient::Interface()->addKillSwitchAllowedRange(QStringList(NetworkUtilities::getIPAddress(
|
||||||
|
m_configData.value(amnezia::config_key::hostName).toString())));
|
||||||
|
#endif
|
||||||
|
|
||||||
// Detect default gateway
|
// Detect default gateway
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
QProcess p;
|
QProcess p;
|
||||||
|
|
|
@ -95,6 +95,8 @@ namespace amnezia
|
||||||
constexpr char splitTunnelApps[] = "splitTunnelApps";
|
constexpr char splitTunnelApps[] = "splitTunnelApps";
|
||||||
constexpr char appSplitTunnelType[] = "appSplitTunnelType";
|
constexpr char appSplitTunnelType[] = "appSplitTunnelType";
|
||||||
|
|
||||||
|
constexpr char allowedDnsServers[] = "allowedDnsServers";
|
||||||
|
|
||||||
constexpr char killSwitchOption[] = "killSwitchOption";
|
constexpr char killSwitchOption[] = "killSwitchOption";
|
||||||
|
|
||||||
constexpr char crc[] = "crc";
|
constexpr char crc[] = "crc";
|
||||||
|
|
|
@ -129,6 +129,7 @@
|
||||||
<file>ui/qml/Components/SettingsContainersListView.qml</file>
|
<file>ui/qml/Components/SettingsContainersListView.qml</file>
|
||||||
<file>ui/qml/Components/ShareConnectionDrawer.qml</file>
|
<file>ui/qml/Components/ShareConnectionDrawer.qml</file>
|
||||||
<file>ui/qml/Components/TransportProtoSelector.qml</file>
|
<file>ui/qml/Components/TransportProtoSelector.qml</file>
|
||||||
|
<file>ui/qml/Components/AddSitePanel.qml</file>
|
||||||
<file>ui/qml/Config/GlobalConfig.qml</file>
|
<file>ui/qml/Config/GlobalConfig.qml</file>
|
||||||
<file>ui/qml/Config/qmldir</file>
|
<file>ui/qml/Config/qmldir</file>
|
||||||
<file>ui/qml/Controls2/BackButtonType.qml</file>
|
<file>ui/qml/Controls2/BackButtonType.qml</file>
|
||||||
|
@ -143,7 +144,9 @@
|
||||||
<file>ui/qml/Controls2/DropDownType.qml</file>
|
<file>ui/qml/Controls2/DropDownType.qml</file>
|
||||||
<file>ui/qml/Controls2/FlickableType.qml</file>
|
<file>ui/qml/Controls2/FlickableType.qml</file>
|
||||||
<file>ui/qml/Controls2/Header2Type.qml</file>
|
<file>ui/qml/Controls2/Header2Type.qml</file>
|
||||||
<file>ui/qml/Controls2/HeaderType.qml</file>
|
<file>ui/qml/Controls2/BaseHeaderType.qml</file>
|
||||||
|
<file>ui/qml/Controls2/HeaderTypeWithButton.qml</file>
|
||||||
|
<file>ui/qml/Controls2/HeaderTypeWithSwitcher.qml</file>
|
||||||
<file>ui/qml/Controls2/HorizontalRadioButton.qml</file>
|
<file>ui/qml/Controls2/HorizontalRadioButton.qml</file>
|
||||||
<file>ui/qml/Controls2/ImageButtonType.qml</file>
|
<file>ui/qml/Controls2/ImageButtonType.qml</file>
|
||||||
<file>ui/qml/Controls2/LabelWithButtonType.qml</file>
|
<file>ui/qml/Controls2/LabelWithButtonType.qml</file>
|
||||||
|
@ -199,6 +202,8 @@
|
||||||
<file>ui/qml/Pages2/PageSettingsBackup.qml</file>
|
<file>ui/qml/Pages2/PageSettingsBackup.qml</file>
|
||||||
<file>ui/qml/Pages2/PageSettingsConnection.qml</file>
|
<file>ui/qml/Pages2/PageSettingsConnection.qml</file>
|
||||||
<file>ui/qml/Pages2/PageSettingsDns.qml</file>
|
<file>ui/qml/Pages2/PageSettingsDns.qml</file>
|
||||||
|
<file>ui/qml/Pages2/PageSettingsKillSwitch.qml</file>
|
||||||
|
<file>ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml</file>
|
||||||
<file>ui/qml/Pages2/PageSettingsLogging.qml</file>
|
<file>ui/qml/Pages2/PageSettingsLogging.qml</file>
|
||||||
<file>ui/qml/Pages2/PageSettingsServerData.qml</file>
|
<file>ui/qml/Pages2/PageSettingsServerData.qml</file>
|
||||||
<file>ui/qml/Pages2/PageSettingsServerInfo.qml</file>
|
<file>ui/qml/Pages2/PageSettingsServerInfo.qml</file>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#include "secure_qsettings.h"
|
#include "secure_qsettings.h"
|
||||||
|
|
||||||
#include "QAead.h"
|
#include "../client/3rd/QSimpleCrypto/src/include/QAead.h"
|
||||||
#include "QBlockCipher.h"
|
#include "../client/3rd/QSimpleCrypto/src/include/QBlockCipher.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
#include <QDataStream>
|
#include <QDataStream>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
|
||||||
#include "keychain.h"
|
#include "../client/3rd/qtkeychain/qtkeychain/keychain.h"
|
||||||
|
|
||||||
class SecureQSettings : public QObject
|
class SecureQSettings : public QObject
|
||||||
{
|
{
|
||||||
|
|
|
@ -443,6 +443,16 @@ void Settings::setKillSwitchEnabled(bool enabled)
|
||||||
setValue("Conf/killSwitchEnabled", enabled);
|
setValue("Conf/killSwitchEnabled", enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Settings::isStrictKillSwitchEnabled() const
|
||||||
|
{
|
||||||
|
return value("Conf/strictKillSwitchEnabled", false).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::setStrictKillSwitchEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
setValue("Conf/strictKillSwitchEnabled", enabled);
|
||||||
|
}
|
||||||
|
|
||||||
QString Settings::getInstallationUuid(const bool needCreate)
|
QString Settings::getInstallationUuid(const bool needCreate)
|
||||||
{
|
{
|
||||||
auto uuid = value("Conf/installationUuid", "").toString();
|
auto uuid = value("Conf/installationUuid", "").toString();
|
||||||
|
@ -548,3 +558,13 @@ void Settings::disableHomeAdLabel()
|
||||||
{
|
{
|
||||||
setValue("Conf/homeAdLabelVisible", false);
|
setValue("Conf/homeAdLabelVisible", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList Settings::allowedDnsServers() const
|
||||||
|
{
|
||||||
|
return value("Conf/allowedDnsServers").toStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::setAllowedDnsServers(const QStringList &servers)
|
||||||
|
{
|
||||||
|
setValue("Conf/allowedDnsServers", servers);
|
||||||
|
}
|
||||||
|
|
|
@ -213,6 +213,10 @@ public:
|
||||||
|
|
||||||
bool isKillSwitchEnabled() const;
|
bool isKillSwitchEnabled() const;
|
||||||
void setKillSwitchEnabled(bool enabled);
|
void setKillSwitchEnabled(bool enabled);
|
||||||
|
|
||||||
|
bool isStrictKillSwitchEnabled() const;
|
||||||
|
void setStrictKillSwitchEnabled(bool enabled);
|
||||||
|
|
||||||
QString getInstallationUuid(const bool needCreate);
|
QString getInstallationUuid(const bool needCreate);
|
||||||
|
|
||||||
void resetGatewayEndpoint();
|
void resetGatewayEndpoint();
|
||||||
|
@ -225,6 +229,9 @@ public:
|
||||||
bool isHomeAdLabelVisible();
|
bool isHomeAdLabelVisible();
|
||||||
void disableHomeAdLabel();
|
void disableHomeAdLabel();
|
||||||
|
|
||||||
|
QStringList allowedDnsServers() const;
|
||||||
|
void setAllowedDnsServers(const QStringList &servers);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void saveLogsChanged(bool enabled);
|
void saveLogsChanged(bool enabled);
|
||||||
void screenshotsEnabledChanged(bool enabled);
|
void screenshotsEnabledChanged(bool enabled);
|
||||||
|
|
101
client/ui/controllers/allowedDnsController.cpp
Normal file
101
client/ui/controllers/allowedDnsController.cpp
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
#include "allowedDnsController.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
#include "systemController.h"
|
||||||
|
#include "core/networkUtilities.h"
|
||||||
|
#include "core/defs.h"
|
||||||
|
|
||||||
|
AllowedDnsController::AllowedDnsController(const std::shared_ptr<Settings> &settings,
|
||||||
|
const QSharedPointer<AllowedDnsModel> &allowedDnsModel,
|
||||||
|
QObject *parent)
|
||||||
|
: QObject(parent), m_settings(settings), m_allowedDnsModel(allowedDnsModel)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AllowedDnsController::addDns(QString ip)
|
||||||
|
{
|
||||||
|
if (ip.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NetworkUtilities::ipAddressRegExp().match(ip).hasMatch()) {
|
||||||
|
emit errorOccurred(tr("The address does not look like a valid IP address"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_allowedDnsModel->addDns(ip)) {
|
||||||
|
emit finished(tr("New DNS server added: %1").arg(ip));
|
||||||
|
} else {
|
||||||
|
emit errorOccurred(tr("DNS server already exists: %1").arg(ip));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AllowedDnsController::removeDns(int index)
|
||||||
|
{
|
||||||
|
auto modelIndex = m_allowedDnsModel->index(index);
|
||||||
|
auto ip = m_allowedDnsModel->data(modelIndex, AllowedDnsModel::Roles::IpRole).toString();
|
||||||
|
m_allowedDnsModel->removeDns(modelIndex);
|
||||||
|
|
||||||
|
emit finished(tr("DNS server removed: %1").arg(ip));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AllowedDnsController::importDns(const QString &fileName, bool replaceExisting)
|
||||||
|
{
|
||||||
|
QByteArray jsonData;
|
||||||
|
if (!SystemController::readFile(fileName, jsonData)) {
|
||||||
|
emit errorOccurred(tr("Can't open file: %1").arg(fileName));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonData);
|
||||||
|
if (jsonDocument.isNull()) {
|
||||||
|
emit errorOccurred(tr("Failed to parse JSON data from file: %1").arg(fileName));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!jsonDocument.isArray()) {
|
||||||
|
emit errorOccurred(tr("The JSON data is not an array in file: %1").arg(fileName));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto jsonArray = jsonDocument.array();
|
||||||
|
QStringList dnsServers;
|
||||||
|
|
||||||
|
for (auto jsonValue : jsonArray) {
|
||||||
|
auto ip = jsonValue.toString();
|
||||||
|
|
||||||
|
if (!NetworkUtilities::ipAddressRegExp().match(ip).hasMatch()) {
|
||||||
|
qDebug() << ip << " is not a valid IP address";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
dnsServers.append(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_allowedDnsModel->addDnsList(dnsServers, replaceExisting);
|
||||||
|
|
||||||
|
emit finished(tr("Import completed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AllowedDnsController::exportDns(const QString &fileName)
|
||||||
|
{
|
||||||
|
auto dnsServers = m_allowedDnsModel->getCurrentDnsServers();
|
||||||
|
|
||||||
|
QJsonArray jsonArray;
|
||||||
|
|
||||||
|
for (const auto &ip : dnsServers) {
|
||||||
|
jsonArray.append(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonDocument jsonDocument(jsonArray);
|
||||||
|
QByteArray jsonData = jsonDocument.toJson();
|
||||||
|
|
||||||
|
SystemController::saveFile(fileName, jsonData);
|
||||||
|
|
||||||
|
emit finished(tr("Export completed"));
|
||||||
|
}
|
35
client/ui/controllers/allowedDnsController.h
Normal file
35
client/ui/controllers/allowedDnsController.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef ALLOWEDDNSCONTROLLER_H
|
||||||
|
#define ALLOWEDDNSCONTROLLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "settings.h"
|
||||||
|
#include "ui/models/allowed_dns_model.h"
|
||||||
|
|
||||||
|
class AllowedDnsController : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit AllowedDnsController(const std::shared_ptr<Settings> &settings,
|
||||||
|
const QSharedPointer<AllowedDnsModel> &allowedDnsModel,
|
||||||
|
QObject *parent = nullptr);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void addDns(QString ip);
|
||||||
|
void removeDns(int index);
|
||||||
|
|
||||||
|
void importDns(const QString &fileName, bool replaceExisting);
|
||||||
|
void exportDns(const QString &fileName);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void errorOccurred(const QString &errorMessage);
|
||||||
|
void finished(const QString &message);
|
||||||
|
|
||||||
|
void saveFile(const QString &fileName, const QString &data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<Settings> m_settings;
|
||||||
|
QSharedPointer<AllowedDnsModel> m_allowedDnsModel;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ALLOWEDDNSCONTROLLER_H
|
|
@ -31,12 +31,14 @@ namespace PageLoader
|
||||||
PageSettingsLogging,
|
PageSettingsLogging,
|
||||||
PageSettingsSplitTunneling,
|
PageSettingsSplitTunneling,
|
||||||
PageSettingsAppSplitTunneling,
|
PageSettingsAppSplitTunneling,
|
||||||
|
PageSettingsKillSwitch,
|
||||||
PageSettingsApiServerInfo,
|
PageSettingsApiServerInfo,
|
||||||
PageSettingsApiAvailableCountries,
|
PageSettingsApiAvailableCountries,
|
||||||
PageSettingsApiSupport,
|
PageSettingsApiSupport,
|
||||||
PageSettingsApiInstructions,
|
PageSettingsApiInstructions,
|
||||||
PageSettingsApiNativeConfigs,
|
PageSettingsApiNativeConfigs,
|
||||||
PageSettingsApiDevices,
|
PageSettingsApiDevices,
|
||||||
|
PageSettingsKillSwitchExceptions,
|
||||||
|
|
||||||
PageServiceSftpSettings,
|
PageServiceSftpSettings,
|
||||||
PageServiceTorWebsiteSettings,
|
PageServiceTorWebsiteSettings,
|
||||||
|
|
|
@ -245,6 +245,23 @@ bool SettingsController::isKillSwitchEnabled()
|
||||||
void SettingsController::toggleKillSwitch(bool enable)
|
void SettingsController::toggleKillSwitch(bool enable)
|
||||||
{
|
{
|
||||||
m_settings->setKillSwitchEnabled(enable);
|
m_settings->setKillSwitchEnabled(enable);
|
||||||
|
emit killSwitchEnabledChanged();
|
||||||
|
if (enable == false) {
|
||||||
|
emit strictKillSwitchEnabledChanged(false);
|
||||||
|
} else {
|
||||||
|
emit strictKillSwitchEnabledChanged(isStrictKillSwitchEnabled());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SettingsController::isStrictKillSwitchEnabled()
|
||||||
|
{
|
||||||
|
return m_settings->isStrictKillSwitchEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsController::toggleStrictKillSwitch(bool enable)
|
||||||
|
{
|
||||||
|
m_settings->setStrictKillSwitchEnabled(enable);
|
||||||
|
emit strictKillSwitchEnabledChanged(enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SettingsController::isNotificationPermissionGranted()
|
bool SettingsController::isNotificationPermissionGranted()
|
||||||
|
|
|
@ -24,6 +24,8 @@ public:
|
||||||
Q_PROPERTY(QString secondaryDns READ getSecondaryDns WRITE setSecondaryDns NOTIFY secondaryDnsChanged)
|
Q_PROPERTY(QString secondaryDns READ getSecondaryDns WRITE setSecondaryDns NOTIFY secondaryDnsChanged)
|
||||||
Q_PROPERTY(bool isLoggingEnabled READ isLoggingEnabled WRITE toggleLogging NOTIFY loggingStateChanged)
|
Q_PROPERTY(bool isLoggingEnabled READ isLoggingEnabled WRITE toggleLogging NOTIFY loggingStateChanged)
|
||||||
Q_PROPERTY(bool isNotificationPermissionGranted READ isNotificationPermissionGranted NOTIFY onNotificationStateChanged)
|
Q_PROPERTY(bool isNotificationPermissionGranted READ isNotificationPermissionGranted NOTIFY onNotificationStateChanged)
|
||||||
|
Q_PROPERTY(bool isKillSwitchEnabled READ isKillSwitchEnabled WRITE toggleKillSwitch NOTIFY killSwitchEnabledChanged)
|
||||||
|
Q_PROPERTY(bool strictKillSwitchEnabled READ isStrictKillSwitchEnabled WRITE toggleStrictKillSwitch NOTIFY strictKillSwitchEnabledChanged)
|
||||||
|
|
||||||
Q_PROPERTY(bool isDevModeEnabled READ isDevModeEnabled NOTIFY devModeEnabled)
|
Q_PROPERTY(bool isDevModeEnabled READ isDevModeEnabled NOTIFY devModeEnabled)
|
||||||
Q_PROPERTY(QString gatewayEndpoint READ getGatewayEndpoint WRITE setGatewayEndpoint NOTIFY gatewayEndpointChanged)
|
Q_PROPERTY(QString gatewayEndpoint READ getGatewayEndpoint WRITE setGatewayEndpoint NOTIFY gatewayEndpointChanged)
|
||||||
|
@ -75,6 +77,9 @@ public slots:
|
||||||
bool isKillSwitchEnabled();
|
bool isKillSwitchEnabled();
|
||||||
void toggleKillSwitch(bool enable);
|
void toggleKillSwitch(bool enable);
|
||||||
|
|
||||||
|
bool isStrictKillSwitchEnabled();
|
||||||
|
void toggleStrictKillSwitch(bool enable);
|
||||||
|
|
||||||
bool isNotificationPermissionGranted();
|
bool isNotificationPermissionGranted();
|
||||||
void requestNotificationPermission();
|
void requestNotificationPermission();
|
||||||
|
|
||||||
|
@ -98,6 +103,8 @@ signals:
|
||||||
void primaryDnsChanged();
|
void primaryDnsChanged();
|
||||||
void secondaryDnsChanged();
|
void secondaryDnsChanged();
|
||||||
void loggingStateChanged();
|
void loggingStateChanged();
|
||||||
|
void killSwitchEnabledChanged();
|
||||||
|
void strictKillSwitchEnabledChanged(bool enabled);
|
||||||
|
|
||||||
void restoreBackupFinished();
|
void restoreBackupFinished();
|
||||||
void changeSettingsFinished(const QString &finishedMessage);
|
void changeSettingsFinished(const QString &finishedMessage);
|
||||||
|
|
86
client/ui/models/allowed_dns_model.cpp
Normal file
86
client/ui/models/allowed_dns_model.cpp
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
#include "allowed_dns_model.h"
|
||||||
|
|
||||||
|
AllowedDnsModel::AllowedDnsModel(std::shared_ptr<Settings> settings, QObject *parent)
|
||||||
|
: QAbstractListModel(parent), m_settings(settings)
|
||||||
|
{
|
||||||
|
fillDnsServers();
|
||||||
|
}
|
||||||
|
|
||||||
|
int AllowedDnsModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent)
|
||||||
|
return m_dnsServers.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant AllowedDnsModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid() || index.row() < 0 || index.row() >= static_cast<int>(rowCount()))
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case IpRole:
|
||||||
|
return m_dnsServers.at(index.row());
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AllowedDnsModel::addDns(const QString &ip)
|
||||||
|
{
|
||||||
|
if (m_dnsServers.contains(ip)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
||||||
|
m_dnsServers.append(ip);
|
||||||
|
m_settings->setAllowedDnsServers(m_dnsServers);
|
||||||
|
endInsertRows();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AllowedDnsModel::addDnsList(const QStringList &dnsServers, bool replaceExisting)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
|
||||||
|
if (replaceExisting) {
|
||||||
|
m_dnsServers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const QString &ip : dnsServers) {
|
||||||
|
if (!m_dnsServers.contains(ip)) {
|
||||||
|
m_dnsServers.append(ip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_settings->setAllowedDnsServers(m_dnsServers);
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AllowedDnsModel::removeDns(QModelIndex index)
|
||||||
|
{
|
||||||
|
if (!index.isValid() || index.row() >= m_dnsServers.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
beginRemoveRows(QModelIndex(), index.row(), index.row());
|
||||||
|
m_dnsServers.removeAt(index.row());
|
||||||
|
m_settings->setAllowedDnsServers(m_dnsServers);
|
||||||
|
endRemoveRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList AllowedDnsModel::getCurrentDnsServers()
|
||||||
|
{
|
||||||
|
return m_dnsServers;
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> AllowedDnsModel::roleNames() const
|
||||||
|
{
|
||||||
|
QHash<int, QByteArray> roles;
|
||||||
|
roles[IpRole] = "ip";
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AllowedDnsModel::fillDnsServers()
|
||||||
|
{
|
||||||
|
m_dnsServers = m_settings->allowedDnsServers();
|
||||||
|
}
|
37
client/ui/models/allowed_dns_model.h
Normal file
37
client/ui/models/allowed_dns_model.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef ALLOWEDDNSMODEL_H
|
||||||
|
#define ALLOWEDDNSMODEL_H
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
class AllowedDnsModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum Roles {
|
||||||
|
IpRole = Qt::UserRole + 1
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit AllowedDnsModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
bool addDns(const QString &ip);
|
||||||
|
void addDnsList(const QStringList &dnsServers, bool replaceExisting);
|
||||||
|
void removeDns(QModelIndex index);
|
||||||
|
QStringList getCurrentDnsServers();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void fillDnsServers();
|
||||||
|
|
||||||
|
std::shared_ptr<Settings> m_settings;
|
||||||
|
QStringList m_dnsServers;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ALLOWEDDNSMODEL_H
|
73
client/ui/qml/Components/AddSitePanel.qml
Normal file
73
client/ui/qml/Components/AddSitePanel.qml
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
import "../Controls2"
|
||||||
|
import "../Controls2/TextTypes"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool enabled: true
|
||||||
|
property string placeholderText: ""
|
||||||
|
property alias textField: searchField.textField
|
||||||
|
|
||||||
|
signal addClicked(string text)
|
||||||
|
signal moreClicked()
|
||||||
|
|
||||||
|
implicitWidth: 360
|
||||||
|
implicitHeight: 96
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: background
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "#0E0F12"
|
||||||
|
opacity: 0.85
|
||||||
|
z: -1
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: addSiteButton
|
||||||
|
|
||||||
|
enabled: root.enabled
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
topMargin: 16
|
||||||
|
leftMargin: 16
|
||||||
|
rightMargin: 16
|
||||||
|
bottomMargin: 24
|
||||||
|
}
|
||||||
|
|
||||||
|
TextFieldWithHeaderType {
|
||||||
|
id: searchField
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
rightButtonClickedOnEnter: true
|
||||||
|
|
||||||
|
textField.placeholderText: root.placeholderText
|
||||||
|
buttonImageSource: "qrc:/images/controls/plus.svg"
|
||||||
|
|
||||||
|
clickedFunc: function() {
|
||||||
|
root.addClicked(textField.text)
|
||||||
|
textField.text = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageButtonType {
|
||||||
|
id: addSiteButtonImage
|
||||||
|
implicitWidth: 56
|
||||||
|
implicitHeight: 56
|
||||||
|
|
||||||
|
image: "qrc:/images/controls/more-vertical.svg"
|
||||||
|
imageColor: AmneziaStyle.color.paleGray
|
||||||
|
|
||||||
|
onClicked: root.moreClicked()
|
||||||
|
|
||||||
|
Keys.onReturnPressed: addSiteButtonImage.clicked()
|
||||||
|
Keys.onEnterPressed: addSiteButtonImage.clicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
client/ui/qml/Controls2/BaseHeaderType.qml
Normal file
45
client/ui/qml/Controls2/BaseHeaderType.qml
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "TextTypes"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string headerText
|
||||||
|
property int headerTextMaximumLineCount: 2
|
||||||
|
property int headerTextElide: Qt.ElideRight
|
||||||
|
property string descriptionText
|
||||||
|
property alias headerRow: headerRow
|
||||||
|
|
||||||
|
implicitWidth: content.implicitWidth
|
||||||
|
implicitHeight: content.implicitHeight
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: content
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: headerRow
|
||||||
|
|
||||||
|
Header1TextType {
|
||||||
|
id: header
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: root.headerText
|
||||||
|
maximumLineCount: root.headerTextMaximumLineCount
|
||||||
|
elide: root.headerTextElide
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ParagraphTextType {
|
||||||
|
id: description
|
||||||
|
Layout.topMargin: 16
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: root.descriptionText
|
||||||
|
color: AmneziaStyle.color.mutedGray
|
||||||
|
visible: root.descriptionText !== ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,86 +0,0 @@
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
import "TextTypes"
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property string actionButtonImage
|
|
||||||
property var actionButtonFunction
|
|
||||||
|
|
||||||
property alias actionButton: headerActionButton
|
|
||||||
|
|
||||||
property string headerText
|
|
||||||
property int headerTextMaximumLineCount: 2
|
|
||||||
property int headerTextElide: Qt.ElideRight
|
|
||||||
|
|
||||||
property string descriptionText
|
|
||||||
|
|
||||||
implicitWidth: content.implicitWidth
|
|
||||||
implicitHeight: content.implicitHeight
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: content
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Header1TextType {
|
|
||||||
id: header
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: root.headerText
|
|
||||||
maximumLineCount: root.headerTextMaximumLineCount
|
|
||||||
elide: root.headerTextElide
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageButtonType {
|
|
||||||
id: headerActionButton
|
|
||||||
|
|
||||||
implicitWidth: 40
|
|
||||||
implicitHeight: 40
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
|
|
||||||
image: root.actionButtonImage
|
|
||||||
imageColor: AmneziaStyle.color.paleGray
|
|
||||||
|
|
||||||
visible: image ? true : false
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
if (actionButtonFunction && typeof actionButtonFunction === "function") {
|
|
||||||
actionButtonFunction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ParagraphTextType {
|
|
||||||
id: description
|
|
||||||
|
|
||||||
Layout.topMargin: 16
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: root.descriptionText
|
|
||||||
|
|
||||||
color: AmneziaStyle.color.mutedGray
|
|
||||||
|
|
||||||
visible: root.descriptionText !== ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onEnterPressed: {
|
|
||||||
if (actionButtonFunction && typeof actionButtonFunction === "function") {
|
|
||||||
actionButtonFunction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onReturnPressed: {
|
|
||||||
if (actionButtonFunction && typeof actionButtonFunction === "function") {
|
|
||||||
actionButtonFunction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
44
client/ui/qml/Controls2/HeaderTypeWithButton.qml
Normal file
44
client/ui/qml/Controls2/HeaderTypeWithButton.qml
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
BaseHeaderType {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string actionButtonImage
|
||||||
|
property var actionButtonFunction
|
||||||
|
property alias actionButton: headerActionButton
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
headerRow.children.push(headerActionButton)
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageButtonType {
|
||||||
|
id: headerActionButton
|
||||||
|
implicitWidth: 40
|
||||||
|
implicitHeight: 40
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
image: root.actionButtonImage
|
||||||
|
imageColor: AmneziaStyle.color.paleGray
|
||||||
|
visible: image ? true : false
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (actionButtonFunction && typeof actionButtonFunction === "function") {
|
||||||
|
actionButtonFunction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onEnterPressed: {
|
||||||
|
if (actionButtonFunction && typeof actionButtonFunction === "function") {
|
||||||
|
actionButtonFunction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onReturnPressed: {
|
||||||
|
if (actionButtonFunction && typeof actionButtonFunction === "function") {
|
||||||
|
actionButtonFunction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
client/ui/qml/Controls2/HeaderTypeWithSwitcher.qml
Normal file
28
client/ui/qml/Controls2/HeaderTypeWithSwitcher.qml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
BaseHeaderType {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var switcherFunction
|
||||||
|
property bool showSwitcher: false
|
||||||
|
property alias switcher: headerSwitcher
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
headerRow.children.push(headerSwitcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitcherType {
|
||||||
|
id: headerSwitcher
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
visible: root.showSwitcher
|
||||||
|
|
||||||
|
onToggled: {
|
||||||
|
if (switcherFunction && typeof switcherFunction === "function") {
|
||||||
|
switcherFunction(checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,11 @@ RadioButton {
|
||||||
property string selectedColor: AmneziaStyle.color.transparent
|
property string selectedColor: AmneziaStyle.color.transparent
|
||||||
|
|
||||||
property string textColor: AmneziaStyle.color.paleGray
|
property string textColor: AmneziaStyle.color.paleGray
|
||||||
|
property string textDisabledColor: AmneziaStyle.color.mutedGray
|
||||||
property string selectedTextColor: AmneziaStyle.color.goldenApricot
|
property string selectedTextColor: AmneziaStyle.color.goldenApricot
|
||||||
|
property string selectedTextDisabledColor: AmneziaStyle.color.burntOrange
|
||||||
|
property string descriptionColor: AmneziaStyle.color.mutedGray
|
||||||
|
property string descriptionDisabledColor: AmneziaStyle.color.charcoalGray
|
||||||
|
|
||||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||||
property int borderFocusedWidth: 1
|
property int borderFocusedWidth: 1
|
||||||
|
@ -30,6 +34,12 @@ RadioButton {
|
||||||
|
|
||||||
property bool isFocusable: true
|
property bool isFocusable: true
|
||||||
|
|
||||||
|
|
||||||
|
property string radioButtonInnerCirclePressedSource: "qrc:/images/controls/radio-button-inner-circle-pressed.png"
|
||||||
|
property string radioButtonInnerCircleSource: "qrc:/images/controls/radio-button-inner-circle.png"
|
||||||
|
property string radioButtonPressedSource: "qrc:/images/controls/radio-button-pressed.svg"
|
||||||
|
property string radioButtonDefaultSource: "qrc:/images/controls/radio-button.svg"
|
||||||
|
|
||||||
Keys.onTabPressed: {
|
Keys.onTabPressed: {
|
||||||
FocusController.nextKeyTabItem()
|
FocusController.nextKeyTabItem()
|
||||||
}
|
}
|
||||||
|
@ -94,14 +104,15 @@ RadioButton {
|
||||||
if (showImage) {
|
if (showImage) {
|
||||||
return imageSource
|
return imageSource
|
||||||
} else if (root.pressed) {
|
} else if (root.pressed) {
|
||||||
return "qrc:/images/controls/radio-button-inner-circle-pressed.png"
|
return root.radioButtonInnerCirclePressedSource
|
||||||
} else if (root.checked) {
|
} else if (root.checked) {
|
||||||
return "qrc:/images/controls/radio-button-inner-circle.png"
|
return root.radioButtonInnerCircleSource
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opacity: root.enabled ? 1.0 : 0.3
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|
||||||
width: 24
|
width: 24
|
||||||
|
@ -113,12 +124,13 @@ RadioButton {
|
||||||
if (showImage) {
|
if (showImage) {
|
||||||
return ""
|
return ""
|
||||||
} else if (root.pressed || root.checked) {
|
} else if (root.pressed || root.checked) {
|
||||||
return "qrc:/images/controls/radio-button-pressed.svg"
|
return root.radioButtonPressedSource
|
||||||
} else {
|
} else {
|
||||||
return "qrc:/images/controls/radio-button.svg"
|
return root.radioButtonDefaultSource
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opacity: root.enabled ? 1.0 : 0.3
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|
||||||
width: 24
|
width: 24
|
||||||
|
@ -148,10 +160,11 @@ RadioButton {
|
||||||
elide: root.textElide
|
elide: root.textElide
|
||||||
|
|
||||||
color: {
|
color: {
|
||||||
if (root.checked) {
|
if (root.enabled) {
|
||||||
return selectedTextColor
|
return root.checked ? selectedTextColor : textColor
|
||||||
|
} else {
|
||||||
|
return root.checked ? selectedTextDisabledColor : textDisabledColor
|
||||||
}
|
}
|
||||||
return textColor
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
@ -164,7 +177,7 @@ RadioButton {
|
||||||
CaptionTextType {
|
CaptionTextType {
|
||||||
id: description
|
id: description
|
||||||
|
|
||||||
color: AmneziaStyle.color.mutedGray
|
color: root.enabled ? root.descriptionColor : root.descriptionDisabledColor
|
||||||
text: root.descriptionText
|
text: root.descriptionText
|
||||||
|
|
||||||
visible: root.descriptionText !== ""
|
visible: root.descriptionText !== ""
|
||||||
|
|
|
@ -56,7 +56,7 @@ PageType {
|
||||||
anchors.rightMargin: 16
|
anchors.rightMargin: 16
|
||||||
anchors.leftMargin: 16
|
anchors.leftMargin: 16
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 20
|
Layout.topMargin: 20
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ PageType {
|
||||||
header: ColumnLayout {
|
header: ColumnLayout {
|
||||||
width: listView.width
|
width: listView.width
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
id: header
|
id: header
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
|
@ -91,7 +91,7 @@ PageType {
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
headerText: qsTr("AmneziaWG settings")
|
headerText: qsTr("AmneziaWG settings")
|
||||||
|
|
|
@ -91,7 +91,7 @@ PageType {
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
headerText: qsTr("AmneziaWG settings")
|
headerText: qsTr("AmneziaWG settings")
|
||||||
|
|
|
@ -76,7 +76,7 @@ PageType {
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
headerText: qsTr("Cloak settings")
|
headerText: qsTr("Cloak settings")
|
||||||
|
|
|
@ -75,7 +75,7 @@ PageType {
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
headerText: qsTr("OpenVPN settings")
|
headerText: qsTr("OpenVPN settings")
|
||||||
|
|
|
@ -32,7 +32,7 @@ PageType {
|
||||||
id: backButton
|
id: backButton
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
|
|
@ -78,7 +78,7 @@ PageType {
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
headerText: qsTr("Shadowsocks settings")
|
headerText: qsTr("Shadowsocks settings")
|
||||||
|
|
|
@ -85,7 +85,7 @@ PageType {
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
headerText: qsTr("WG settings")
|
headerText: qsTr("WG settings")
|
||||||
|
|
|
@ -77,7 +77,7 @@ PageType {
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
headerText: qsTr("WG settings")
|
headerText: qsTr("WG settings")
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ PageType {
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
headerText: qsTr("XRay settings")
|
headerText: qsTr("XRay settings")
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ PageType {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
id: header
|
id: header
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
|
@ -85,7 +85,7 @@ PageType {
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
|
|
@ -77,7 +77,7 @@ PageType {
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
@ -217,7 +217,7 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
headerText: qsTr("SOCKS5 settings")
|
headerText: qsTr("SOCKS5 settings")
|
||||||
|
|
|
@ -54,7 +54,7 @@ PageType {
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
|
|
@ -29,7 +29,7 @@ PageType {
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
id: header
|
id: header
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 24
|
Layout.topMargin: 24
|
||||||
|
|
|
@ -69,7 +69,7 @@ PageType {
|
||||||
Layout.topMargin: 20
|
Layout.topMargin: 20
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderType {
|
HeaderTypeWithButton {
|
||||||
id: headerContent
|
id: headerContent
|
||||||
objectName: "headerContent"
|
objectName: "headerContent"
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ PageType {
|
||||||
id: backButton
|
id: backButton
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
id: header
|
id: header
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
|
@ -91,7 +91,7 @@ PageType {
|
||||||
id: backButton
|
id: backButton
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
id: header
|
id: header
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
|
@ -38,7 +38,7 @@ PageType {
|
||||||
id: backButton
|
id: backButton
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
id: header
|
id: header
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
|
@ -93,7 +93,7 @@ PageType {
|
||||||
Layout.topMargin: 20
|
Layout.topMargin: 20
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderType {
|
HeaderTypeWithButton {
|
||||||
id: headerContent
|
id: headerContent
|
||||||
objectName: "headerContent"
|
objectName: "headerContent"
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ PageType {
|
||||||
id: backButton
|
id: backButton
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
id: header
|
id: header
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
|
@ -79,31 +79,24 @@ PageType {
|
||||||
id: backButton
|
id: backButton
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
HeaderTypeWithSwitcher {
|
||||||
HeaderType {
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
headerText: qsTr("App split tunneling")
|
headerText: qsTr("App split tunneling")
|
||||||
|
|
||||||
enabled: root.pageEnabled
|
enabled: root.pageEnabled
|
||||||
}
|
showSwitcher: true
|
||||||
|
switcher {
|
||||||
SwitcherType {
|
|
||||||
id: switcher
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
|
|
||||||
enabled: root.pageEnabled
|
|
||||||
|
|
||||||
checked: AppSplitTunnelingModel.isTunnelingEnabled
|
checked: AppSplitTunnelingModel.isTunnelingEnabled
|
||||||
onToggled: {
|
enabled: root.pageEnabled
|
||||||
|
}
|
||||||
|
switcherFunction: function(checked) {
|
||||||
AppSplitTunnelingModel.toggleSplitTunneling(checked)
|
AppSplitTunnelingModel.toggleSplitTunneling(checked)
|
||||||
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
|
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
DropDownType {
|
DropDownType {
|
||||||
id: selector
|
id: selector
|
||||||
|
|
|
@ -38,7 +38,7 @@ PageType {
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
|
|
@ -60,7 +60,7 @@ PageType {
|
||||||
|
|
||||||
spacing: 16
|
spacing: 16
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
headerText: qsTr("Back up your configuration")
|
headerText: qsTr("Back up your configuration")
|
||||||
|
|
|
@ -36,7 +36,7 @@ PageType {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
@ -94,9 +94,7 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {
|
DividerType {}
|
||||||
visible: root.isAppSplitTinnelingEnabled
|
|
||||||
}
|
|
||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
id: splitTunnelingButton2
|
id: splitTunnelingButton2
|
||||||
|
@ -119,29 +117,20 @@ PageType {
|
||||||
visible: root.isAppSplitTinnelingEnabled
|
visible: root.isAppSplitTinnelingEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
SwitcherType {
|
LabelWithButtonType {
|
||||||
id: killSwitchSwitcher
|
id: killSwitchButton
|
||||||
visible: !GC.isMobile()
|
visible: !GC.isMobile()
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.margins: 16
|
|
||||||
|
|
||||||
text: qsTr("KillSwitch")
|
text: qsTr("KillSwitch")
|
||||||
descriptionText: qsTr("Disables your internet if your encrypted VPN connection drops out for any reason.")
|
descriptionText: qsTr("Blocks network connections without VPN")
|
||||||
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
parentFlickable: fl
|
parentFlickable: fl
|
||||||
|
|
||||||
checked: SettingsController.isKillSwitchEnabled()
|
clickedFunction: function() {
|
||||||
checkable: !ConnectionController.isConnected
|
PageController.goToPage(PageEnum.PageSettingsKillSwitch)
|
||||||
onCheckedChanged: {
|
|
||||||
if (checked !== SettingsController.isKillSwitchEnabled()) {
|
|
||||||
SettingsController.toggleKillSwitch(checked)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onClicked: {
|
|
||||||
if (!checkable) {
|
|
||||||
PageController.showNotificationMessage(qsTr("Cannot change KillSwitch settings during active connection"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ PageType {
|
||||||
|
|
||||||
spacing: 16
|
spacing: 16
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
headerText: qsTr("DNS servers")
|
headerText: qsTr("DNS servers")
|
||||||
|
|
123
client/ui/qml/Pages2/PageSettingsKillSwitch.qml
Normal file
123
client/ui/qml/Pages2/PageSettingsKillSwitch.qml
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import PageEnum 1.0
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "./"
|
||||||
|
import "../Controls2"
|
||||||
|
import "../Config"
|
||||||
|
|
||||||
|
PageType {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
BackButtonType {
|
||||||
|
id: backButton
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.topMargin: 20
|
||||||
|
}
|
||||||
|
|
||||||
|
FlickableType {
|
||||||
|
id: fl
|
||||||
|
anchors.top: backButton.bottom
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
contentHeight: content.height
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: content
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
HeaderTypeWithSwitcher {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
|
headerText: qsTr("KillSwitch")
|
||||||
|
descriptionText: qsTr("Enable to ensure network traffic goes through a secure VPN tunnel, preventing accidental exposure of your IP and DNS queries if the connection drops")
|
||||||
|
|
||||||
|
showSwitcher: true
|
||||||
|
switcher {
|
||||||
|
checked: SettingsController.isKillSwitchEnabled
|
||||||
|
enabled: !ConnectionController.isConnected
|
||||||
|
}
|
||||||
|
switcherFunction: function(checked) {
|
||||||
|
if (!ConnectionController.isConnected) {
|
||||||
|
SettingsController.isKillSwitchEnabled = checked
|
||||||
|
} else {
|
||||||
|
PageController.showNotificationMessage(qsTr("Cannot change killSwitch settings during active connection"))
|
||||||
|
switcher.checked = SettingsController.isKillSwitchEnabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VerticalRadioButton {
|
||||||
|
id: softKillSwitch
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 32
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
|
enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected
|
||||||
|
checked: !SettingsController.strictKillSwitchEnabled
|
||||||
|
|
||||||
|
text: qsTr("Soft KillSwitch")
|
||||||
|
descriptionText: qsTr("Internet connection is blocked if VPN connection drops accidentally")
|
||||||
|
|
||||||
|
onClicked: function() {
|
||||||
|
SettingsController.strictKillSwitchEnabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
|
||||||
|
VerticalRadioButton {
|
||||||
|
id: strictKillSwitch
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
|
enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected
|
||||||
|
checked: SettingsController.strictKillSwitchEnabled
|
||||||
|
|
||||||
|
text: qsTr("Strict KillSwitch")
|
||||||
|
descriptionText: qsTr("Internet connection is blocked even if VPN was turned off manually or not started")
|
||||||
|
|
||||||
|
onClicked: function() {
|
||||||
|
var headerText = qsTr("Just a little heads-up")
|
||||||
|
var descriptionText = qsTr("If you disconnect from VPN or the VPN connection drops while the Strict Kill Switch is turned on, your internet access will be disabled. To restore it, connect to VPN, change the Kill Switch mode or turn the Kill Switch off.")
|
||||||
|
var yesButtonText = qsTr("Continue")
|
||||||
|
var noButtonText = qsTr("Cancel")
|
||||||
|
|
||||||
|
var yesButtonFunction = function() {
|
||||||
|
SettingsController.strictKillSwitchEnabled = true
|
||||||
|
}
|
||||||
|
var noButtonFunction = function() {
|
||||||
|
}
|
||||||
|
|
||||||
|
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
Layout.topMargin: 32
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
enabled: true
|
||||||
|
text: qsTr("DNS Exceptions")
|
||||||
|
descriptionText: qsTr("DNS servers from the list will remain accessible when Kill Switch is triggered")
|
||||||
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
PageController.goToPage(PageEnum.PageSettingsKillSwitchExceptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
302
client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml
Normal file
302
client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml
Normal file
|
@ -0,0 +1,302 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Dialogs
|
||||||
|
|
||||||
|
import QtCore
|
||||||
|
|
||||||
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
|
import PageEnum 1.0
|
||||||
|
import ProtocolEnum 1.0
|
||||||
|
import ContainerProps 1.0
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "./"
|
||||||
|
import "../Controls2"
|
||||||
|
import "../Controls2/TextTypes"
|
||||||
|
import "../Config"
|
||||||
|
import "../Components"
|
||||||
|
|
||||||
|
PageType {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool pageEnabled: true
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: header
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
anchors.topMargin: 20
|
||||||
|
|
||||||
|
BackButtonType {
|
||||||
|
id: backButton
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseHeaderType {
|
||||||
|
enabled: root.pageEnabled
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
|
headerText: qsTr("DNS Exceptions")
|
||||||
|
descriptionText: qsTr("DNS servers from the list will remain accessible when Kill Switch is triggered")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: listView
|
||||||
|
|
||||||
|
anchors.top: header.bottom
|
||||||
|
anchors.topMargin: 16
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
enabled: root.pageEnabled
|
||||||
|
|
||||||
|
property bool isFocusable: true
|
||||||
|
|
||||||
|
cacheBuffer: 200
|
||||||
|
displayMarginBeginning: 40
|
||||||
|
displayMarginEnd: 40
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBarType { }
|
||||||
|
|
||||||
|
footer: Item {
|
||||||
|
width: listView.width
|
||||||
|
height: addSitePanel.height
|
||||||
|
}
|
||||||
|
|
||||||
|
footerPositioning: ListView.InlineFooter
|
||||||
|
|
||||||
|
model: SortFilterProxyModel {
|
||||||
|
id: dnsFilterModel
|
||||||
|
sourceModel: AllowedDnsModel
|
||||||
|
filters: [
|
||||||
|
RegExpFilter {
|
||||||
|
roleName: "ip"
|
||||||
|
pattern: ".*" + addSitePanel.textField.text + ".*"
|
||||||
|
caseSensitivity: Qt.CaseInsensitive
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
reuseItems: true
|
||||||
|
|
||||||
|
delegate: ColumnLayout {
|
||||||
|
id: delegateContent
|
||||||
|
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
id: site
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: ip
|
||||||
|
rightImageSource: "qrc:/images/controls/trash.svg"
|
||||||
|
rightImageColor: AmneziaStyle.color.paleGray
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
var headerText = qsTr("Delete ") + ip + "?"
|
||||||
|
var yesButtonText = qsTr("Continue")
|
||||||
|
var noButtonText = qsTr("Cancel")
|
||||||
|
|
||||||
|
var yesButtonFunction = function() {
|
||||||
|
AllowedDnsController.removeDns(dnsFilterModel.mapToSource(index))
|
||||||
|
if (!GC.isMobile()) {
|
||||||
|
site.rightButton.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var noButtonFunction = function() {
|
||||||
|
if (!GC.isMobile()) {
|
||||||
|
site.rightButton.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddSitePanel {
|
||||||
|
id: addSitePanel
|
||||||
|
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
enabled: root.pageEnabled
|
||||||
|
placeholderText: qsTr("IPv4 address")
|
||||||
|
|
||||||
|
onAddClicked: function(text) {
|
||||||
|
PageController.showBusyIndicator(true)
|
||||||
|
AllowedDnsController.addDns(text)
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMoreClicked: {
|
||||||
|
moreActionsDrawer.openTriggered()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawerType2 {
|
||||||
|
id: moreActionsDrawer
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
expandedHeight: parent.height * 0.4375
|
||||||
|
|
||||||
|
expandedStateContent: ColumnLayout {
|
||||||
|
id: moreActionsDrawerContent
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
Header2Type {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: 16
|
||||||
|
|
||||||
|
headerText: qsTr("Import / Export addresses")
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
id: importSitesButton
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: qsTr("Import")
|
||||||
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
importSitesDrawer.openTriggered()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
id: exportSitesButton
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Save address list")
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
var fileName = ""
|
||||||
|
if (GC.isMobile()) {
|
||||||
|
fileName = "amnezia_killswitch_exceptions.json"
|
||||||
|
} else {
|
||||||
|
fileName = SystemController.getFileName(qsTr("Save addresses"),
|
||||||
|
qsTr("Address files (*.json)"),
|
||||||
|
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/amnezia_killswitch_exceptions",
|
||||||
|
true,
|
||||||
|
".json")
|
||||||
|
}
|
||||||
|
if (fileName !== "") {
|
||||||
|
PageController.showBusyIndicator(true)
|
||||||
|
AllowedDnsController.exportDns(fileName)
|
||||||
|
moreActionsDrawer.closeTriggered()
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawerType2 {
|
||||||
|
id: importSitesDrawer
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
expandedHeight: parent.height * 0.4375
|
||||||
|
|
||||||
|
expandedStateContent: Item {
|
||||||
|
implicitHeight: importSitesDrawer.expandedHeight
|
||||||
|
|
||||||
|
BackButtonType {
|
||||||
|
id: importSitesDrawerBackButton
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.topMargin: 16
|
||||||
|
|
||||||
|
backButtonFunction: function() {
|
||||||
|
importSitesDrawer.closeTriggered()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlickableType {
|
||||||
|
anchors.top: importSitesDrawerBackButton.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
|
contentHeight: importSitesDrawerContent.height
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: importSitesDrawerContent
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
Header2Type {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: 16
|
||||||
|
|
||||||
|
headerText: qsTr("Import address list")
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
id: importSitesButton2
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: qsTr("Replace address list")
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
var fileName = SystemController.getFileName(qsTr("Open address file"),
|
||||||
|
qsTr("Address files (*.json)"))
|
||||||
|
if (fileName !== "") {
|
||||||
|
importSitesDrawerContent.importSites(fileName, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
id: importSitesButton3
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Add imported addresses to existing ones")
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
var fileName = SystemController.getFileName(qsTr("Open address file"),
|
||||||
|
qsTr("Address files (*.json)"))
|
||||||
|
if (fileName !== "") {
|
||||||
|
importSitesDrawerContent.importSites(fileName, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function importSites(fileName, replaceExistingSites) {
|
||||||
|
PageController.showBusyIndicator(true)
|
||||||
|
AllowedDnsController.importDns(fileName, replaceExistingSites)
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
|
importSitesDrawer.closeTriggered()
|
||||||
|
moreActionsDrawer.closeTriggered()
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,7 +40,7 @@ PageType {
|
||||||
header: ColumnLayout {
|
header: ColumnLayout {
|
||||||
width: listView.width
|
width: listView.width
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
|
|
@ -71,7 +71,7 @@ PageType {
|
||||||
objectName: "backButton"
|
objectName: "backButton"
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderType {
|
HeaderTypeWithButton {
|
||||||
id: headerContent
|
id: headerContent
|
||||||
objectName: "headerContent"
|
objectName: "headerContent"
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ PageType {
|
||||||
id: backButton
|
id: backButton
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
|
|
@ -31,7 +31,7 @@ PageType {
|
||||||
id: backButton
|
id: backButton
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
|
|
@ -94,33 +94,22 @@ PageType {
|
||||||
id: backButton
|
id: backButton
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
HeaderTypeWithSwitcher {
|
||||||
HeaderType {
|
|
||||||
enabled: root.pageEnabled
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
headerText: qsTr("Split tunneling")
|
|
||||||
}
|
|
||||||
|
|
||||||
SwitcherType {
|
|
||||||
id: switcher
|
|
||||||
|
|
||||||
enabled: root.pageEnabled
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
function onToggledFunc() {
|
headerText: qsTr("Split tunneling")
|
||||||
SitesModel.toggleSplitTunneling(this.checked)
|
|
||||||
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
|
|
||||||
}
|
|
||||||
|
|
||||||
|
enabled: root.pageEnabled
|
||||||
|
showSwitcher: true
|
||||||
|
switcher {
|
||||||
checked: SitesModel.isTunnelingEnabled
|
checked: SitesModel.isTunnelingEnabled
|
||||||
onToggled: { onToggledFunc() }
|
enabled: root.pageEnabled
|
||||||
Keys.onEnterPressed: { onToggledFunc() }
|
}
|
||||||
Keys.onReturnPressed: { onToggledFunc() }
|
switcherFunction: function(checked) {
|
||||||
|
SitesModel.toggleSplitTunneling(checked)
|
||||||
|
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ PageType {
|
||||||
Layout.topMargin: 20
|
Layout.topMargin: 20
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 8
|
Layout.topMargin: 8
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
|
|
@ -28,7 +28,7 @@ PageType {
|
||||||
Layout.topMargin: 20
|
Layout.topMargin: 20
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 8
|
Layout.topMargin: 8
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
|
|
@ -45,7 +45,7 @@ PageType {
|
||||||
header: ColumnLayout {
|
header: ColumnLayout {
|
||||||
width: listView.width
|
width: listView.width
|
||||||
|
|
||||||
HeaderType {
|
HeaderTypeWithButton {
|
||||||
id: moreButton
|
id: moreButton
|
||||||
|
|
||||||
property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible()
|
property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible()
|
||||||
|
@ -76,7 +76,7 @@ PageType {
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 32
|
Layout.topMargin: 32
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
|
|
@ -66,7 +66,7 @@ PageType {
|
||||||
header: ColumnLayout {
|
header: ColumnLayout {
|
||||||
width: listView.width
|
width: listView.width
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
|
|
@ -59,7 +59,7 @@ PageType {
|
||||||
|
|
||||||
spacing: 16
|
spacing: 16
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
id: header
|
id: header
|
||||||
|
|
||||||
implicitWidth: parent.width
|
implicitWidth: parent.width
|
||||||
|
|
|
@ -118,7 +118,7 @@ PageType {
|
||||||
anchors.rightMargin: 16
|
anchors.rightMargin: 16
|
||||||
anchors.leftMargin: 16
|
anchors.leftMargin: 16
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 20
|
Layout.topMargin: 20
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ PageType {
|
||||||
Layout.leftMargin: -16
|
Layout.leftMargin: -16
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
id: header
|
id: header
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
|
@ -58,7 +58,7 @@ PageType {
|
||||||
header: ColumnLayout {
|
header: ColumnLayout {
|
||||||
width: listView.width
|
width: listView.width
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
id: header
|
id: header
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
|
@ -33,7 +33,7 @@ PageType {
|
||||||
Layout.topMargin: 20
|
Layout.topMargin: 20
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
|
|
@ -61,7 +61,7 @@ PageType {
|
||||||
anchors.rightMargin: 16
|
anchors.rightMargin: 16
|
||||||
anchors.leftMargin: 16
|
anchors.leftMargin: 16
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
headerText: qsTr("New connection")
|
headerText: qsTr("New connection")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -169,7 +169,7 @@ PageType {
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
HeaderType {
|
HeaderTypeWithButton {
|
||||||
id: header
|
id: header
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 24
|
Layout.topMargin: 24
|
||||||
|
|
|
@ -44,7 +44,7 @@ PageType {
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
HeaderType {
|
BaseHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 24
|
Layout.topMargin: 24
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,28 @@ void VpnConnection::onBytesChanged(quint64 receivedBytes, quint64 sentBytes)
|
||||||
emit bytesChanged(receivedBytes, sentBytes);
|
emit bytesChanged(receivedBytes, sentBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VpnConnection::onKillSwitchModeChanged(bool enabled)
|
||||||
|
{
|
||||||
|
#ifdef AMNEZIA_DESKTOP
|
||||||
|
if (!m_IpcClient) {
|
||||||
|
m_IpcClient = new IpcClient(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_IpcClient->isSocketConnected()) {
|
||||||
|
if (!IpcClient::init(m_IpcClient)) {
|
||||||
|
qWarning() << "Error occurred when init IPC client";
|
||||||
|
emit serviceIsNotReady();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IpcClient::Interface()) {
|
||||||
|
qDebug() << "Set KillSwitch Strict mode enabled " << enabled;
|
||||||
|
IpcClient::Interface()->refreshKillSwitch(enabled);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
|
void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -286,6 +308,7 @@ void VpnConnection::createProtocolConnections()
|
||||||
void VpnConnection::appendKillSwitchConfig()
|
void VpnConnection::appendKillSwitchConfig()
|
||||||
{
|
{
|
||||||
m_vpnConfiguration.insert(config_key::killSwitchOption, QVariant(m_settings->isKillSwitchEnabled()).toString());
|
m_vpnConfiguration.insert(config_key::killSwitchOption, QVariant(m_settings->isKillSwitchEnabled()).toString());
|
||||||
|
m_vpnConfiguration.insert(config_key::allowedDnsServers, QVariant(m_settings->allowedDnsServers()).toJsonValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void VpnConnection::appendSplitTunnelingConfig()
|
void VpnConnection::appendSplitTunnelingConfig()
|
||||||
|
|
|
@ -52,10 +52,10 @@ public slots:
|
||||||
|
|
||||||
void disconnectFromVpn();
|
void disconnectFromVpn();
|
||||||
|
|
||||||
|
|
||||||
void addRoutes(const QStringList &ips);
|
void addRoutes(const QStringList &ips);
|
||||||
void deleteRoutes(const QStringList &ips);
|
void deleteRoutes(const QStringList &ips);
|
||||||
void flushDns();
|
void flushDns();
|
||||||
|
void onKillSwitchModeChanged(bool enabled);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void bytesChanged(quint64 receivedBytes, quint64 sentBytes);
|
void bytesChanged(quint64 receivedBytes, quint64 sentBytes);
|
||||||
|
|
|
@ -41,6 +41,10 @@ if [ -z "${QT_VERSION+x}" ]; then
|
||||||
QT_BIN_DIR=/opt/Qt/$QT_VERSION/gcc_64/bin
|
QT_BIN_DIR=/opt/Qt/$QT_VERSION/gcc_64/bin
|
||||||
elif [ -f $HOME/Qt/$QT_VERSION/gcc_64/bin/qmake ]; then
|
elif [ -f $HOME/Qt/$QT_VERSION/gcc_64/bin/qmake ]; then
|
||||||
QT_BIN_DIR=$HOME/Qt/$QT_VERSION/gcc_64/bin
|
QT_BIN_DIR=$HOME/Qt/$QT_VERSION/gcc_64/bin
|
||||||
|
elif [ -f /usr/lib/qt6/bin/qmake ]; then
|
||||||
|
QT_BIN_DIR=/usr/lib/qt6/bin
|
||||||
|
elif [ -f /usr/lib/x86_64-linux-gnu/qt6/bin/qmake ]; then
|
||||||
|
QT_BIN_DIR=/usr/lib/x86_64-linux-gnu/qt6/bin
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -56,7 +60,7 @@ echo "Building App..."
|
||||||
cd $BUILD_DIR
|
cd $BUILD_DIR
|
||||||
|
|
||||||
$QT_BIN_DIR/qt-cmake -S $PROJECT_DIR
|
$QT_BIN_DIR/qt-cmake -S $PROJECT_DIR
|
||||||
cmake --build . --config release
|
cmake --build . -j --config release
|
||||||
|
|
||||||
# Build and run tests here
|
# Build and run tests here
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,10 @@ class IpcInterface
|
||||||
SLOT( void StopRoutingIpv6() );
|
SLOT( void StopRoutingIpv6() );
|
||||||
|
|
||||||
SLOT( bool disableKillSwitch() );
|
SLOT( bool disableKillSwitch() );
|
||||||
|
SLOT( bool disableAllTraffic() );
|
||||||
|
SLOT( bool refreshKillSwitch( bool enabled ) );
|
||||||
|
SLOT( bool addKillSwitchAllowedRange( const QStringList ranges ) );
|
||||||
|
SLOT( bool resetKillSwitchAllowedRange( const QStringList ranges ) );
|
||||||
SLOT( bool enablePeerTraffic( const QJsonObject &configStr) );
|
SLOT( bool enablePeerTraffic( const QJsonObject &configStr) );
|
||||||
SLOT( bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex) );
|
SLOT( bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex) );
|
||||||
SLOT( bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) );
|
SLOT( bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) );
|
||||||
|
|
|
@ -8,21 +8,12 @@
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "router.h"
|
#include "router.h"
|
||||||
|
|
||||||
#include "../core/networkUtilities.h"
|
#include "killswitch.h"
|
||||||
#include "../client/protocols/protocols_defs.h"
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
#include "../client/platforms/windows/daemon/windowsdaemon.h"
|
|
||||||
#include "../client/platforms/windows/daemon/windowsfirewall.h"
|
|
||||||
#include "tapcontroller_win.h"
|
#include "tapcontroller_win.h"
|
||||||
#endif
|
#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)
|
IpcServer::IpcServer(QObject *parent) : IpcInterfaceSource(parent)
|
||||||
|
|
||||||
|
@ -188,174 +179,37 @@ void IpcServer::setLogsEnabled(bool enabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IpcServer::resetKillSwitchAllowedRange(QStringList ranges)
|
||||||
|
{
|
||||||
|
return KillSwitch::instance()->resetAllowedRange(ranges);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IpcServer::addKillSwitchAllowedRange(QStringList ranges)
|
||||||
|
{
|
||||||
|
return KillSwitch::instance()->addAllowedRange(ranges);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IpcServer::disableAllTraffic()
|
||||||
|
{
|
||||||
|
return KillSwitch::instance()->disableAllTraffic();
|
||||||
|
}
|
||||||
|
|
||||||
bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex)
|
bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WIN
|
return KillSwitch::instance()->enableKillSwitch(configStr, vpnAdapterIndex);
|
||||||
auto firewallManager = WindowsFirewall::create(this);
|
|
||||||
Q_ASSERT(firewallManager != nullptr);
|
|
||||||
return firewallManager->enableInterface(vpnAdapterIndex);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
|
|
||||||
int splitTunnelType = configStr.value("splitTunnelType").toInt();
|
|
||||||
QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray();
|
|
||||||
bool blockAll = 0;
|
|
||||||
bool allowNets = 0;
|
|
||||||
bool blockNets = 0;
|
|
||||||
QStringList allownets;
|
|
||||||
QStringList blocknets;
|
|
||||||
|
|
||||||
if (splitTunnelType == 0) {
|
|
||||||
blockAll = true;
|
|
||||||
allowNets = true;
|
|
||||||
allownets.append(configStr.value("vpnServer").toString());
|
|
||||||
} else if (splitTunnelType == 1) {
|
|
||||||
blockNets = true;
|
|
||||||
for (auto v : splitTunnelSites) {
|
|
||||||
blocknets.append(v.toString());
|
|
||||||
}
|
|
||||||
} else if (splitTunnelType == 2) {
|
|
||||||
blockAll = true;
|
|
||||||
allowNets = true;
|
|
||||||
allownets.append(configStr.value("vpnServer").toString());
|
|
||||||
for (auto v : splitTunnelSites) {
|
|
||||||
allownets.append(v.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
|
||||||
// double-check + ensure our firewall is installed and enabled
|
|
||||||
if (!LinuxFirewall::isInstalled())
|
|
||||||
LinuxFirewall::install();
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), blockAll);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), allowNets);
|
|
||||||
LinuxFirewall::updateAllowNets(allownets);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), blockAll);
|
|
||||||
LinuxFirewall::updateBlockNets(blocknets);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), true);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), true);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), true);
|
|
||||||
QStringList dnsServers;
|
|
||||||
dnsServers.append(configStr.value(amnezia::config_key::dns1).toString());
|
|
||||||
dnsServers.append(configStr.value(amnezia::config_key::dns2).toString());
|
|
||||||
dnsServers.append("127.0.0.1");
|
|
||||||
dnsServers.append("127.0.0.53");
|
|
||||||
LinuxFirewall::updateDNSServers(dnsServers);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef Q_OS_MACOS
|
|
||||||
|
|
||||||
// double-check + ensure our firewall is installed and enabled. This is necessary as
|
|
||||||
// other software may disable pfctl before re-enabling with their own rules (e.g other VPNs)
|
|
||||||
if (!MacOSFirewall::isInstalled())
|
|
||||||
MacOSFirewall::install();
|
|
||||||
|
|
||||||
MacOSFirewall::ensureRootAnchorPriority();
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true);
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), blockAll);
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), allowNets);
|
|
||||||
MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), allowNets, QStringLiteral("allownets"), allownets);
|
|
||||||
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("120.blockNets"), blockNets);
|
|
||||||
MacOSFirewall::setAnchorTable(QStringLiteral("120.blockNets"), blockNets, QStringLiteral("blocknets"), blocknets);
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("200.allowVPN"), true);
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true);
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), true);
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), true);
|
|
||||||
|
|
||||||
QStringList dnsServers;
|
|
||||||
dnsServers.append(configStr.value(amnezia::config_key::dns1).toString());
|
|
||||||
dnsServers.append(configStr.value(amnezia::config_key::dns2).toString());
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true);
|
|
||||||
MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), dnsServers);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IpcServer::disableKillSwitch()
|
bool IpcServer::disableKillSwitch()
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WIN
|
return KillSwitch::instance()->disableKillSwitch();
|
||||||
auto firewallManager = WindowsFirewall::create(this);
|
|
||||||
Q_ASSERT(firewallManager != nullptr);
|
|
||||||
return firewallManager->disableKillSwitch();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
|
||||||
LinuxFirewall::uninstall();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef Q_OS_MACOS
|
|
||||||
MacOSFirewall::uninstall();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IpcServer::enablePeerTraffic(const QJsonObject &configStr)
|
bool IpcServer::enablePeerTraffic(const QJsonObject &configStr)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WIN
|
return KillSwitch::instance()->enablePeerTraffic(configStr);
|
||||||
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) {
|
bool IpcServer::refreshKillSwitch(bool enabled)
|
||||||
for (auto v : splitTunnelSites) {
|
{
|
||||||
QString ipRange = v.toString();
|
return KillSwitch::instance()->refresh(enabled);
|
||||||
if (ipRange.split('/').size() > 1) {
|
|
||||||
config.m_allowedIPAddressRanges.append(
|
|
||||||
IPAddress(QHostAddress(ipRange.split('/')[0]), atoi(ipRange.split('/')[1].toLocal8Bit())));
|
|
||||||
} else {
|
|
||||||
config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress(ipRange), 32));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config.m_excludedAddresses.append(configStr.value("vpnServer").toString());
|
|
||||||
if (splitTunnelType == 2) {
|
|
||||||
for (auto v : splitTunnelSites) {
|
|
||||||
QString ipRange = v.toString();
|
|
||||||
config.m_excludedAddresses.append(ipRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const QJsonValue &i : configStr.value(amnezia::config_key::splitTunnelApps).toArray()) {
|
|
||||||
if (!i.isString()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
config.m_vpnDisabledApps.append(i.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
// killSwitch toggle
|
|
||||||
if (QVariant(configStr.value(amnezia::config_key::killSwitchOption).toString()).toBool()) {
|
|
||||||
auto firewallManager = WindowsFirewall::create(this);
|
|
||||||
Q_ASSERT(firewallManager != nullptr);
|
|
||||||
firewallManager->enablePeerTraffic(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
WindowsDaemon::instance()->prepareActivation(config, inetAdapterIndex);
|
|
||||||
WindowsDaemon::instance()->activateSplitTunnel(config, vpnAdapterIndex);
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,9 +34,13 @@ public:
|
||||||
virtual bool deleteTun(const QString &dev) override;
|
virtual bool deleteTun(const QString &dev) override;
|
||||||
virtual void StartRoutingIpv6() override;
|
virtual void StartRoutingIpv6() override;
|
||||||
virtual void StopRoutingIpv6() override;
|
virtual void StopRoutingIpv6() override;
|
||||||
|
virtual bool disableAllTraffic() override;
|
||||||
|
virtual bool addKillSwitchAllowedRange(QStringList ranges) override;
|
||||||
|
virtual bool resetKillSwitchAllowedRange(QStringList ranges) override;
|
||||||
virtual bool enablePeerTraffic(const QJsonObject &configStr) override;
|
virtual bool enablePeerTraffic(const QJsonObject &configStr) override;
|
||||||
virtual bool enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) override;
|
virtual bool enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) override;
|
||||||
virtual bool disableKillSwitch() override;
|
virtual bool disableKillSwitch() override;
|
||||||
|
virtual bool refreshKillSwitch( bool enabled ) override;
|
||||||
virtual bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) override;
|
virtual bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -12,8 +12,47 @@ qt_standard_project_setup()
|
||||||
|
|
||||||
configure_file(${CMAKE_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
|
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
|
set(HEADERS
|
||||||
${CMAKE_CURRENT_LIST_DIR}/../../client/utilities.h
|
${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}/../../client/core/networkUtilities.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipc.h
|
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipc.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.h
|
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.h
|
||||||
|
@ -22,12 +61,20 @@ set(HEADERS
|
||||||
${CMAKE_CURRENT_LIST_DIR}/localserver.h
|
${CMAKE_CURRENT_LIST_DIR}/localserver.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.h
|
${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/router.h
|
${CMAKE_CURRENT_LIST_DIR}/router.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/killswitch.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/systemservice.h
|
${CMAKE_CURRENT_LIST_DIR}/systemservice.h
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/version.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
|
set(SOURCES
|
||||||
${CMAKE_CURRENT_LIST_DIR}/../../client/utilities.cpp
|
${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}/../../client/core/networkUtilities.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.cpp
|
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserverprocess.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}/../../common/logger/logger.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/main.cpp
|
${CMAKE_CURRENT_LIST_DIR}/main.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/router.cpp
|
${CMAKE_CURRENT_LIST_DIR}/router.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/killswitch.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/systemservice.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
|
# Mozilla headres
|
||||||
|
@ -133,6 +186,7 @@ if(WIN32)
|
||||||
set(SOURCES ${SOURCES}
|
set(SOURCES ${SOURCES}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/tapcontroller_win.cpp
|
${CMAKE_CURRENT_LIST_DIR}/tapcontroller_win.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/router_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/windowsdaemon.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/daemon/windowsdaemontunnel.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/daemon/windowsdaemontunnel.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/daemon/windowsfirewall.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/daemon/windowsfirewall.cpp
|
||||||
|
@ -159,6 +213,8 @@ if(WIN32)
|
||||||
gdi32
|
gdi32
|
||||||
Advapi32
|
Advapi32
|
||||||
Kernel32
|
Kernel32
|
||||||
|
${OPENSSL_LIB_CRYPTO_PATH}
|
||||||
|
qt6keychain
|
||||||
)
|
)
|
||||||
|
|
||||||
add_compile_definitions(_WINSOCKAPI_)
|
add_compile_definitions(_WINSOCKAPI_)
|
||||||
|
@ -203,6 +259,9 @@ if(APPLE)
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/macos/daemon/wireguardutilsmacos.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/macos/daemon/wireguardutilsmacos.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/macos/daemon/macosfirewall.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/macos/daemon/macosfirewall.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(LIBS ${OPENSSL_LIB_CRYPTO_PATH} qt6keychain)
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(LINUX)
|
if(LINUX)
|
||||||
|
@ -233,6 +292,9 @@ if(LINUX)
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxroutemonitor.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxroutemonitor.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxfirewall.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxfirewall.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(LIBS ${OPENSSL_LIB_CRYPTO_PATH} qt6keychain -static-libstdc++ -static-libgcc -ldl)
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
include(${CMAKE_CURRENT_LIST_DIR}/../src/qtservice.cmake)
|
include(${CMAKE_CURRENT_LIST_DIR}/../src/qtservice.cmake)
|
||||||
|
@ -245,6 +307,7 @@ include_directories(
|
||||||
${CMAKE_CURRENT_BINARY_DIR}
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
add_executable(${PROJECT} ${SOURCES} ${HEADERS})
|
add_executable(${PROJECT} ${SOURCES} ${HEADERS})
|
||||||
target_link_libraries(${PROJECT} PRIVATE Qt6::Core Qt6::Widgets Qt6::Network Qt6::RemoteObjects Qt6::Core5Compat Qt6::DBus ${LIBS})
|
target_link_libraries(${PROJECT} PRIVATE Qt6::Core Qt6::Widgets Qt6::Network Qt6::RemoteObjects Qt6::Core5Compat Qt6::DBus ${LIBS})
|
||||||
target_compile_definitions(${PROJECT} PRIVATE "MZ_$<UPPER_CASE:${MZ_PLATFORM_NAME}>")
|
target_compile_definitions(${PROJECT} PRIVATE "MZ_$<UPPER_CASE:${MZ_PLATFORM_NAME}>")
|
||||||
|
|
358
service/server/killswitch.cpp
Normal file
358
service/server/killswitch.cpp
Normal file
|
@ -0,0 +1,358 @@
|
||||||
|
#include "killswitch.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QHostAddress>
|
||||||
|
|
||||||
|
#include "../client/protocols/protocols_defs.h"
|
||||||
|
#include "qjsonarray.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include "../client/platforms/windows/daemon/windowsfirewall.h"
|
||||||
|
#include "../client/platforms/windows/daemon/windowsdaemon.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
#include "../client/platforms/linux/daemon/linuxfirewall.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
#include "../client/platforms/macos/daemon/macosfirewall.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
KillSwitch* s_instance = nullptr;
|
||||||
|
|
||||||
|
KillSwitch* KillSwitch::instance()
|
||||||
|
{
|
||||||
|
if (s_instance == nullptr) {
|
||||||
|
s_instance = new KillSwitch(qApp);
|
||||||
|
}
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KillSwitch::init()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
if (!LinuxFirewall::isInstalled()) {
|
||||||
|
LinuxFirewall::install();
|
||||||
|
}
|
||||||
|
m_appSettigns = QSharedPointer<SecureQSettings>(new SecureQSettings(ORGANIZATION_NAME, APPLICATION_NAME, nullptr));
|
||||||
|
#endif
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
if (!MacOSFirewall::isInstalled()) {
|
||||||
|
MacOSFirewall::install();
|
||||||
|
}
|
||||||
|
m_appSettigns = QSharedPointer<SecureQSettings>(new SecureQSettings(ORGANIZATION_NAME, APPLICATION_NAME, nullptr));
|
||||||
|
#endif
|
||||||
|
if (isStrictKillSwitchEnabled()) {
|
||||||
|
return disableAllTraffic();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KillSwitch::refresh(bool enabled)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
QSettings RegHLM("HKEY_LOCAL_MACHINE\\Software\\" + QString(ORGANIZATION_NAME)
|
||||||
|
+ "\\" + QString(APPLICATION_NAME), QSettings::NativeFormat);
|
||||||
|
RegHLM.setValue("strictKillSwitchEnabled", enabled);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
|
||||||
|
m_appSettigns->setValue("Conf/strictKillSwitchEnabled", enabled);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (isStrictKillSwitchEnabled()) {
|
||||||
|
return disableAllTraffic();
|
||||||
|
} else {
|
||||||
|
return disableKillSwitch();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KillSwitch::isStrictKillSwitchEnabled()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
QSettings RegHLM("HKEY_LOCAL_MACHINE\\Software\\" + QString(ORGANIZATION_NAME)
|
||||||
|
+ "\\" + QString(APPLICATION_NAME), QSettings::NativeFormat);
|
||||||
|
return RegHLM.value("strictKillSwitchEnabled", false).toBool();
|
||||||
|
#endif
|
||||||
|
m_appSettigns->sync();
|
||||||
|
return m_appSettigns->value("Conf/strictKillSwitchEnabled", false).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KillSwitch::disableKillSwitch() {
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
if (isStrictKillSwitchEnabled()) {
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), false);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), false);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), false);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), false);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), false);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), false);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), false);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), false);
|
||||||
|
} else {
|
||||||
|
LinuxFirewall::uninstall();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
if (isStrictKillSwitchEnabled()) {
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), true);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), false);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("120.blockNets"), false);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("200.allowVPN"), false);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), false);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), false);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), false);
|
||||||
|
} else {
|
||||||
|
MacOSFirewall::uninstall();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
if (isStrictKillSwitchEnabled()) {
|
||||||
|
return disableAllTraffic();
|
||||||
|
}
|
||||||
|
return WindowsFirewall::create(this)->allowAllTraffic();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_allowedRanges.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KillSwitch::disableAllTraffic() {
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
WindowsFirewall::create(this)->enableInterface(-1);
|
||||||
|
#endif
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
if (!LinuxFirewall::isInstalled()) {
|
||||||
|
LinuxFirewall::install();
|
||||||
|
}
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
|
||||||
|
#endif
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
// double-check + ensure our firewall is installed and enabled. This is necessary as
|
||||||
|
// other software may disable pfctl before re-enabling with their own rules (e.g other VPNs)
|
||||||
|
if (!MacOSFirewall::isInstalled())
|
||||||
|
MacOSFirewall::install();
|
||||||
|
MacOSFirewall::ensureRootAnchorPriority();
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), true);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true);
|
||||||
|
#endif
|
||||||
|
m_allowedRanges.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KillSwitch::resetAllowedRange(const QStringList &ranges) {
|
||||||
|
|
||||||
|
m_allowedRanges = ranges;
|
||||||
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), true);
|
||||||
|
LinuxFirewall::updateAllowNets(m_allowedRanges);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), true);
|
||||||
|
MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), true, QStringLiteral("allownets"), m_allowedRanges);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
if (isStrictKillSwitchEnabled()) {
|
||||||
|
WindowsFirewall::create(this)->enableInterface(-1);
|
||||||
|
}
|
||||||
|
WindowsFirewall::create(this)->allowTrafficRange(m_allowedRanges);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KillSwitch::addAllowedRange(const QStringList &ranges) {
|
||||||
|
for (const QString &range : ranges) {
|
||||||
|
if (!range.isEmpty() && !m_allowedRanges.contains(range)) {
|
||||||
|
m_allowedRanges.append(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resetAllowedRange(m_allowedRanges);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) {
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
InterfaceConfig config;
|
||||||
|
config.m_dnsServer = configStr.value(amnezia::config_key::dns1).toString();
|
||||||
|
config.m_serverPublicKey = "openvpn";
|
||||||
|
config.m_serverIpv4Gateway = configStr.value("vpnGateway").toString();
|
||||||
|
config.m_serverIpv4AddrIn = configStr.value("vpnServer").toString();
|
||||||
|
int vpnAdapterIndex = configStr.value("vpnAdapterIndex").toInt();
|
||||||
|
int inetAdapterIndex = configStr.value("inetAdapterIndex").toInt();
|
||||||
|
|
||||||
|
int splitTunnelType = configStr.value("splitTunnelType").toInt();
|
||||||
|
QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray();
|
||||||
|
|
||||||
|
// Use APP split tunnel
|
||||||
|
if (splitTunnelType == 0 || splitTunnelType == 2) {
|
||||||
|
config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("0.0.0.0"), 0));
|
||||||
|
config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("::"), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (splitTunnelType == 1) {
|
||||||
|
for (auto v : splitTunnelSites) {
|
||||||
|
QString ipRange = v.toString();
|
||||||
|
if (ipRange.split('/').size() > 1) {
|
||||||
|
config.m_allowedIPAddressRanges.append(
|
||||||
|
IPAddress(QHostAddress(ipRange.split('/')[0]), atoi(ipRange.split('/')[1].toLocal8Bit())));
|
||||||
|
} else {
|
||||||
|
config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress(ipRange), 32));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config.m_excludedAddresses.append(configStr.value("vpnServer").toString());
|
||||||
|
if (splitTunnelType == 2) {
|
||||||
|
for (auto v : splitTunnelSites) {
|
||||||
|
QString ipRange = v.toString();
|
||||||
|
config.m_excludedAddresses.append(ipRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const QJsonValue &i : configStr.value(amnezia::config_key::splitTunnelApps).toArray()) {
|
||||||
|
if (!i.isString()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
config.m_vpnDisabledApps.append(i.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto dns : configStr.value(amnezia::config_key::allowedDnsServers).toArray()) {
|
||||||
|
if (!dns.isString()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
config.m_allowedDnsServers.append(dns.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// killSwitch toggle
|
||||||
|
if (QVariant(configStr.value(amnezia::config_key::killSwitchOption).toString()).toBool()) {
|
||||||
|
WindowsFirewall::create(this)->enablePeerTraffic(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowsDaemon::instance()->prepareActivation(config, inetAdapterIndex);
|
||||||
|
WindowsDaemon::instance()->activateSplitTunnel(config, vpnAdapterIndex);
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex) {
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
return WindowsFirewall::create(this)->enableInterface(vpnAdapterIndex);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
|
||||||
|
int splitTunnelType = configStr.value("splitTunnelType").toInt();
|
||||||
|
QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray();
|
||||||
|
bool blockAll = 0;
|
||||||
|
bool allowNets = 0;
|
||||||
|
bool blockNets = 0;
|
||||||
|
QStringList allownets;
|
||||||
|
QStringList blocknets;
|
||||||
|
|
||||||
|
if (splitTunnelType == 0) {
|
||||||
|
blockAll = true;
|
||||||
|
allowNets = true;
|
||||||
|
allownets.append(configStr.value("vpnServer").toString());
|
||||||
|
} else if (splitTunnelType == 1) {
|
||||||
|
blockNets = true;
|
||||||
|
for (auto v : splitTunnelSites) {
|
||||||
|
blocknets.append(v.toString());
|
||||||
|
}
|
||||||
|
} else if (splitTunnelType == 2) {
|
||||||
|
blockAll = true;
|
||||||
|
allowNets = true;
|
||||||
|
allownets.append(configStr.value("vpnServer").toString());
|
||||||
|
for (auto v : splitTunnelSites) {
|
||||||
|
allownets.append(v.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
if (!LinuxFirewall::isInstalled()) {
|
||||||
|
LinuxFirewall::install();
|
||||||
|
}
|
||||||
|
|
||||||
|
// double-check + ensure our firewall is installed and enabled
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), blockAll);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), allowNets);
|
||||||
|
LinuxFirewall::updateAllowNets(allownets);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), blockAll);
|
||||||
|
LinuxFirewall::updateBlockNets(blocknets);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), true);
|
||||||
|
QStringList dnsServers;
|
||||||
|
dnsServers.append(configStr.value(amnezia::config_key::dns1).toString());
|
||||||
|
dnsServers.append(configStr.value(amnezia::config_key::dns2).toString());
|
||||||
|
dnsServers.append("127.0.0.1");
|
||||||
|
dnsServers.append("127.0.0.53");
|
||||||
|
|
||||||
|
for (auto dns : configStr.value(amnezia::config_key::allowedDnsServers).toArray()) {
|
||||||
|
if (!dns.isString()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dnsServers.append(dns.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
LinuxFirewall::updateDNSServers(dnsServers);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
// double-check + ensure our firewall is installed and enabled. This is necessary as
|
||||||
|
// other software may disable pfctl before re-enabling with their own rules (e.g other VPNs)
|
||||||
|
if (!MacOSFirewall::isInstalled())
|
||||||
|
MacOSFirewall::install();
|
||||||
|
|
||||||
|
MacOSFirewall::ensureRootAnchorPriority();
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), blockAll);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), allowNets);
|
||||||
|
MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), allowNets, QStringLiteral("allownets"), allownets);
|
||||||
|
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("120.blockNets"), blockNets);
|
||||||
|
MacOSFirewall::setAnchorTable(QStringLiteral("120.blockNets"), blockNets, QStringLiteral("blocknets"), blocknets);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("200.allowVPN"), true);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), true);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), true);
|
||||||
|
|
||||||
|
QStringList dnsServers;
|
||||||
|
dnsServers.append(configStr.value(amnezia::config_key::dns1).toString());
|
||||||
|
dnsServers.append(configStr.value(amnezia::config_key::dns2).toString());
|
||||||
|
|
||||||
|
for (auto dns : configStr.value(amnezia::config_key::allowedDnsServers).toArray()) {
|
||||||
|
if (!dns.isString()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dnsServers.append(dns.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true);
|
||||||
|
MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), dnsServers);
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
31
service/server/killswitch.h
Normal file
31
service/server/killswitch.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef KILLSWITCH_H
|
||||||
|
#define KILLSWITCH_H
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
#include "secure_qsettings.h"
|
||||||
|
|
||||||
|
class KillSwitch : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
static KillSwitch *instance();
|
||||||
|
bool init();
|
||||||
|
bool refresh(bool enabled);
|
||||||
|
bool disableKillSwitch();
|
||||||
|
bool disableAllTraffic();
|
||||||
|
bool enablePeerTraffic(const QJsonObject &configStr);
|
||||||
|
bool enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex);
|
||||||
|
bool resetAllowedRange(const QStringList &ranges);
|
||||||
|
bool addAllowedRange(const QStringList &ranges);
|
||||||
|
bool isStrictKillSwitchEnabled();
|
||||||
|
|
||||||
|
private:
|
||||||
|
KillSwitch(QObject* parent) {};
|
||||||
|
QStringList m_allowedRanges;
|
||||||
|
QSharedPointer<SecureQSettings> m_appSettigns;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KILLSWITCH_H
|
|
@ -5,9 +5,8 @@
|
||||||
|
|
||||||
#include "ipc.h"
|
#include "ipc.h"
|
||||||
#include "localserver.h"
|
#include "localserver.h"
|
||||||
#include "utilities.h"
|
|
||||||
|
|
||||||
#include "router.h"
|
#include "killswitch.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
|
@ -47,6 +46,8 @@ LocalServer::LocalServer(QObject *parent) : QObject(parent),
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KillSwitch::instance()->init();
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
// Signal handling for a proper shutdown.
|
// Signal handling for a proper shutdown.
|
||||||
QObject::connect(qApp, &QCoreApplication::aboutToQuit,
|
QObject::connect(qApp, &QCoreApplication::aboutToQuit,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue