Compare commits
30 commits
dev
...
feature/al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a67e4ab8bb | ||
|
|
92b0ea6213 | ||
|
|
cafac3aa61 | ||
|
|
0466e71d49 | ||
|
|
bb883b4880 | ||
|
|
8edc60e574 | ||
|
|
8e6de2dc34 | ||
|
|
0cf9d3c9bd | ||
|
|
6ea21b852f | ||
|
|
c36f14c55a | ||
|
|
1c2b1f1e0f | ||
|
|
13127cd64c | ||
|
|
f670050282 | ||
|
|
d0fe48b8c7 | ||
|
|
ac281cee5b | ||
|
|
1f8a6114f8 | ||
|
|
7a926f2eef | ||
|
|
d371d495a9 | ||
|
|
13fd957647 | ||
|
|
8e2e3a916a | ||
|
|
84d95477cb | ||
|
|
7a3520cb20 | ||
|
|
1fa96a09a0 | ||
|
|
c186be1788 | ||
|
|
4407e2801b | ||
|
|
5fc80121b4 | ||
|
|
08e5ff2eef | ||
|
|
d65273e43e | ||
|
|
4d91245376 | ||
|
|
f5272168bc |
85 changed files with 1604 additions and 411 deletions
|
|
@ -47,6 +47,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());
|
||||||
|
|
||||||
|
|
@ -129,6 +132,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());
|
||||||
|
|
||||||
|
|
@ -213,6 +219,7 @@ void CoreController::initSignalHandlers()
|
||||||
initAutoConnectHandler();
|
initAutoConnectHandler();
|
||||||
initAmneziaDnsToggledHandler();
|
initAmneziaDnsToggledHandler();
|
||||||
initPrepareConfigHandler();
|
initPrepareConfigHandler();
|
||||||
|
initStrictKillSwitchHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::initNotificationHandler()
|
void CoreController::initNotificationHandler()
|
||||||
|
|
@ -339,6 +346,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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,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) {
|
||||||
|
|
|
||||||
|
|
@ -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,11 +182,24 @@ 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"));
|
||||||
|
|
@ -262,6 +277,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 +336,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,7 @@ 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();
|
||||||
|
|
||||||
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()->allowTrafficTo(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,24 @@ 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")
|
||||||
|
|
|
||||||
112
client/ui/qml/Pages2/PageSettingsKillSwitch.qml
Normal file
112
client/ui/qml/Pages2/PageSettingsKillSwitch.qml
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
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("Kill Switch")
|
||||||
|
descriptionText: qsTr("Enable to ensure network traffic goes through a secure VPN tunnel, preventing accidental exposure of your IP and DNS queries if the connection drops")
|
||||||
|
|
||||||
|
showSwitcher: true
|
||||||
|
switcher {
|
||||||
|
checked: SettingsController.isKillSwitchEnabled
|
||||||
|
enabled: !ConnectionController.isConnected
|
||||||
|
}
|
||||||
|
switcherFunction: function(checked) {
|
||||||
|
if (!ConnectionController.isConnected) {
|
||||||
|
SettingsController.isKillSwitchEnabled = checked
|
||||||
|
} else {
|
||||||
|
PageController.showNotificationMessage(qsTr("Cannot change killSwitch settings during active connection"))
|
||||||
|
switcher.checked = SettingsController.isKillSwitchEnabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VerticalRadioButton {
|
||||||
|
id: softKillSwitch
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 32
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
|
enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected
|
||||||
|
checked: !SettingsController.strictKillSwitchEnabled
|
||||||
|
|
||||||
|
text: qsTr("Soft Kill Switch")
|
||||||
|
descriptionText: qsTr("Internet connection is blocked if VPN connection drops accidentally")
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
SettingsController.strictKillSwitchEnabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
|
||||||
|
VerticalRadioButton {
|
||||||
|
id: strictKillSwitch
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
|
enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected
|
||||||
|
checked: SettingsController.strictKillSwitchEnabled
|
||||||
|
|
||||||
|
text: qsTr("Strict Kill Switch")
|
||||||
|
descriptionText: qsTr("Internet connection is blocked even if VPN was turned off manually or not started")
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
SettingsController.strictKillSwitchEnabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
296
client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml
Normal file
296
client/ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml
Normal file
|
|
@ -0,0 +1,296 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Dialogs
|
||||||
|
|
||||||
|
import QtCore
|
||||||
|
|
||||||
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
|
import PageEnum 1.0
|
||||||
|
import ProtocolEnum 1.0
|
||||||
|
import ContainerProps 1.0
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "./"
|
||||||
|
import "../Controls2"
|
||||||
|
import "../Controls2/TextTypes"
|
||||||
|
import "../Config"
|
||||||
|
import "../Components"
|
||||||
|
|
||||||
|
PageType {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool pageEnabled: true
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: header
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
anchors.topMargin: 20
|
||||||
|
|
||||||
|
BackButtonType {
|
||||||
|
id: backButton
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseHeaderType {
|
||||||
|
enabled: root.pageEnabled
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
|
headerText: qsTr("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: AddSitePanel {
|
||||||
|
id: addSitePanel
|
||||||
|
|
||||||
|
width: listView.width
|
||||||
|
z: 10
|
||||||
|
|
||||||
|
enabled: root.pageEnabled
|
||||||
|
placeholderText: qsTr("IPv4 address")
|
||||||
|
|
||||||
|
onAddClicked: function(text) {
|
||||||
|
PageController.showBusyIndicator(true)
|
||||||
|
AllowedDnsController.addDns(text)
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMoreClicked: {
|
||||||
|
moreActionsDrawer.openTriggered()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footerPositioning: ListView.OverlayFooter
|
||||||
|
|
||||||
|
model: SortFilterProxyModel {
|
||||||
|
id: dnsFilterModel
|
||||||
|
sourceModel: AllowedDnsModel
|
||||||
|
filters: [
|
||||||
|
RegExpFilter {
|
||||||
|
roleName: "ip"
|
||||||
|
pattern: ".*" + addSitePanel.textField.text + ".*"
|
||||||
|
caseSensitivity: Qt.CaseInsensitive
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
reuseItems: true
|
||||||
|
|
||||||
|
delegate: ColumnLayout {
|
||||||
|
id: delegateContent
|
||||||
|
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
id: site
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: ip
|
||||||
|
rightImageSource: "qrc:/images/controls/trash.svg"
|
||||||
|
rightImageColor: AmneziaStyle.color.paleGray
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
var headerText = qsTr("Delete ") + ip + "?"
|
||||||
|
var yesButtonText = qsTr("Continue")
|
||||||
|
var noButtonText = qsTr("Cancel")
|
||||||
|
|
||||||
|
var yesButtonFunction = function() {
|
||||||
|
AllowedDnsController.removeDns(dnsFilterModel.mapToSource(index))
|
||||||
|
if (!GC.isMobile()) {
|
||||||
|
site.rightButton.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var noButtonFunction = function() {
|
||||||
|
if (!GC.isMobile()) {
|
||||||
|
site.rightButton.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawerType2 {
|
||||||
|
id: moreActionsDrawer
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
expandedHeight: parent.height * 0.4375
|
||||||
|
|
||||||
|
expandedStateContent: ColumnLayout {
|
||||||
|
id: moreActionsDrawerContent
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
Header2Type {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: 16
|
||||||
|
|
||||||
|
headerText: qsTr("Import / Export addresses")
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
id: importSitesButton
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: qsTr("Import")
|
||||||
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
importSitesDrawer.openTriggered()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
id: exportSitesButton
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Save address list")
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
var fileName = ""
|
||||||
|
if (GC.isMobile()) {
|
||||||
|
fileName = "amnezia_killswitch_exceptions.json"
|
||||||
|
} else {
|
||||||
|
fileName = SystemController.getFileName(qsTr("Save addresses"),
|
||||||
|
qsTr("Address files (*.json)"),
|
||||||
|
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/amnezia_killswitch_exceptions",
|
||||||
|
true,
|
||||||
|
".json")
|
||||||
|
}
|
||||||
|
if (fileName !== "") {
|
||||||
|
PageController.showBusyIndicator(true)
|
||||||
|
AllowedDnsController.exportDns(fileName)
|
||||||
|
moreActionsDrawer.closeTriggered()
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawerType2 {
|
||||||
|
id: importSitesDrawer
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
expandedHeight: parent.height * 0.4375
|
||||||
|
|
||||||
|
expandedStateContent: Item {
|
||||||
|
implicitHeight: importSitesDrawer.expandedHeight
|
||||||
|
|
||||||
|
BackButtonType {
|
||||||
|
id: importSitesDrawerBackButton
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.topMargin: 16
|
||||||
|
|
||||||
|
backButtonFunction: function() {
|
||||||
|
importSitesDrawer.closeTriggered()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlickableType {
|
||||||
|
anchors.top: importSitesDrawerBackButton.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
|
contentHeight: importSitesDrawerContent.height
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: importSitesDrawerContent
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
Header2Type {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: 16
|
||||||
|
|
||||||
|
headerText: qsTr("Import address list")
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
id: importSitesButton2
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: qsTr("Replace address list")
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
var fileName = SystemController.getFileName(qsTr("Open address file"),
|
||||||
|
qsTr("Address files (*.json)"))
|
||||||
|
if (fileName !== "") {
|
||||||
|
importSitesDrawerContent.importSites(fileName, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
id: importSitesButton3
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTr("Add imported addresses to existing ones")
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
var fileName = SystemController.getFileName(qsTr("Open address file"),
|
||||||
|
qsTr("Address files (*.json)"))
|
||||||
|
if (fileName !== "") {
|
||||||
|
importSitesDrawerContent.importSites(fileName, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function importSites(fileName, replaceExistingSites) {
|
||||||
|
PageController.showBusyIndicator(true)
|
||||||
|
AllowedDnsController.importDns(fileName, replaceExistingSites)
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
|
importSitesDrawer.closeTriggered()
|
||||||
|
moreActionsDrawer.closeTriggered()
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,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()
|
||||||
|
|
@ -74,7 +74,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);
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,9 @@ class IpcInterface
|
||||||
SLOT( void StopRoutingIpv6() );
|
SLOT( void StopRoutingIpv6() );
|
||||||
|
|
||||||
SLOT( bool disableKillSwitch() );
|
SLOT( bool disableKillSwitch() );
|
||||||
|
SLOT( bool disableAllTraffic() );
|
||||||
|
SLOT( bool refreshKillSwitch( bool enabled ) );
|
||||||
|
SLOT( bool allowTrafficTo( 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,32 @@ void IpcServer::setLogsEnabled(bool enabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IpcServer::allowTrafficTo(QStringList ranges)
|
||||||
|
{
|
||||||
|
return KillSwitch::instance()->allowTrafficTo(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,12 @@ 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 allowTrafficTo(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}>")
|
||||||
|
|
|
||||||
337
service/server/killswitch.cpp
Normal file
337
service/server/killswitch.cpp
Normal file
|
|
@ -0,0 +1,337 @@
|
||||||
|
#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
|
||||||
|
|
||||||
|
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
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KillSwitch::allowTrafficTo(const QStringList &ranges) {
|
||||||
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), true);
|
||||||
|
LinuxFirewall::updateAllowNets(ranges);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), true);
|
||||||
|
MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), true, QStringLiteral("allownets"), ranges);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) {
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
InterfaceConfig config;
|
||||||
|
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;
|
||||||
|
}
|
||||||
29
service/server/killswitch.h
Normal file
29
service/server/killswitch.h
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
#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 allowTrafficTo(const QStringList &ranges);
|
||||||
|
bool isStrictKillSwitchEnabled();
|
||||||
|
|
||||||
|
private:
|
||||||
|
KillSwitch(QObject* parent) {};
|
||||||
|
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