mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-01 00:00:36 +03:00
Compare commits
40 Commits
bugfix/qr-
...
feature/up
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b03acc95c4 | ||
|
|
24759c92ad | ||
|
|
9e92ee020e | ||
|
|
7a4f6b628b | ||
|
|
7e2f223d7f | ||
|
|
eb48e4b668 | ||
|
|
0b0766ce8b | ||
|
|
26825c2898 | ||
|
|
9ace09a604 | ||
|
|
702735c2ca | ||
|
|
174f2ac3db | ||
|
|
e3b5b4a9d9 | ||
|
|
72ba012765 | ||
|
|
0f9bbcd060 | ||
|
|
a9d038d8bf | ||
|
|
bf67e3491e | ||
|
|
856bc23b5d | ||
|
|
54a6845315 | ||
|
|
0c7059a476 | ||
|
|
5bed92ab0b | ||
|
|
49a14785c6 | ||
|
|
2c78c06dda | ||
|
|
cf8a0efd0d | ||
|
|
5211cdd4c0 | ||
|
|
d10aa43d8b | ||
|
|
6b0f1ed429 | ||
|
|
4bde1ccb44 | ||
|
|
03c18c44e2 | ||
|
|
72ffc7ce6a | ||
|
|
87b738ef16 | ||
|
|
b868831bcb | ||
|
|
477d7214c5 | ||
|
|
f3cd3d4f06 | ||
|
|
aea4cc2389 | ||
|
|
245aa8eb8c | ||
|
|
f14a2add0f | ||
|
|
89703ba58f | ||
|
|
23715fca8b | ||
|
|
d90685600e | ||
|
|
f007e5eb5c |
2
.github/workflows/deploy.yml
vendored
2
.github/workflows/deploy.yml
vendored
@@ -233,7 +233,7 @@ jobs:
|
||||
- name: 'Setup xcode'
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: '13.4'
|
||||
xcode-version: '14.3.1'
|
||||
|
||||
- name: 'Install Qt'
|
||||
uses: jurplel/install-qt-action@v3
|
||||
|
||||
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||
|
||||
set(PROJECT AmneziaVPN)
|
||||
|
||||
project(${PROJECT} VERSION 4.5.2.0
|
||||
project(${PROJECT} VERSION 4.5.3.0
|
||||
DESCRIPTION "AmneziaVPN"
|
||||
HOMEPAGE_URL "https://amnezia.org/"
|
||||
)
|
||||
@@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
||||
set(RELEASE_DATE "${CURRENT_DATE}")
|
||||
|
||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||
set(APP_ANDROID_VERSION_CODE 51)
|
||||
set(APP_ANDROID_VERSION_CODE 52)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(MZ_PLATFORM_NAME "linux")
|
||||
|
||||
@@ -69,6 +69,8 @@ set(AMNEZIAVPN_TS_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ar_EG.ts
|
||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_my_MM.ts
|
||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_uk_UA.ts
|
||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ur_PK.ts
|
||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_hi_IN.ts
|
||||
)
|
||||
|
||||
file(GLOB_RECURSE AMNEZIAVPN_TS_SOURCES *.qrc *.cpp *.h *.ui)
|
||||
|
||||
@@ -118,12 +118,12 @@ abstract class Protocol {
|
||||
}
|
||||
|
||||
for (app in config.includedApplications) {
|
||||
Log.d(TAG, "addAllowedApplication: $app")
|
||||
Log.d(TAG, "addAllowedApplication")
|
||||
vpnBuilder.addAllowedApplication(app)
|
||||
}
|
||||
|
||||
for (app in config.excludedApplications) {
|
||||
Log.d(TAG, "addDisallowedApplication: $app")
|
||||
Log.d(TAG, "addDisallowedApplication")
|
||||
vpnBuilder.addDisallowedApplication(app)
|
||||
}
|
||||
|
||||
|
||||
@@ -453,7 +453,7 @@ class AmneziaActivity : QtActivity() {
|
||||
|
||||
@Suppress("unused")
|
||||
fun setSaveLogs(enabled: Boolean) {
|
||||
Log.d(TAG, "Set save logs: $enabled")
|
||||
Log.v(TAG, "Set save logs: $enabled")
|
||||
mainScope.launch {
|
||||
Log.saveLogs = enabled
|
||||
vpnServiceMessenger.send {
|
||||
@@ -473,7 +473,9 @@ class AmneziaActivity : QtActivity() {
|
||||
@Suppress("unused")
|
||||
fun clearLogs() {
|
||||
Log.v(TAG, "Clear logs")
|
||||
Log.clearLogs()
|
||||
mainScope.launch {
|
||||
Log.clearLogs()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
@@ -509,7 +511,7 @@ class AmneziaActivity : QtActivity() {
|
||||
|
||||
@Suppress("unused")
|
||||
fun getAppIcon(packageName: String, width: Int, height: Int): Bitmap {
|
||||
Log.v(TAG, "Get app icon: $packageName")
|
||||
Log.v(TAG, "Get app icon")
|
||||
return AppListProvider.getAppIcon(packageManager, packageName, width, height)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,9 +109,11 @@ object Log {
|
||||
"${deviceInfo()}\n${readLogs()}\nLOGCAT:\n${getLogcat()}"
|
||||
|
||||
fun clearLogs() {
|
||||
withLock {
|
||||
logFile.delete()
|
||||
rotateLogFile.delete()
|
||||
if (logDir.exists()) {
|
||||
withLock {
|
||||
logFile.delete()
|
||||
rotateLogFile.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -116,8 +116,8 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
|
||||
if (!isApiConfig) {
|
||||
QRegularExpression regex("redirect-gateway.*");
|
||||
config.replace(regex, "");
|
||||
|
||||
if (!m_settings->getSitesSplitTunnelingEnabled()) {
|
||||
|
||||
if (!m_settings->isSitesSplitTunnelingEnabled()) {
|
||||
config.append("\nredirect-gateway def1 ipv6 bypass-dhcp\n");
|
||||
// Prevent ipv6 leak
|
||||
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
||||
|
||||
@@ -321,7 +321,7 @@ bool ContainerProps::isEasySetupContainer(DockerContainer container)
|
||||
switch (container) {
|
||||
case DockerContainer::WireGuard: return true;
|
||||
case DockerContainer::Awg: return true;
|
||||
case DockerContainer::Cloak: return true;
|
||||
// case DockerContainer::Cloak: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
@@ -330,8 +330,8 @@ QString ContainerProps::easySetupHeader(DockerContainer container)
|
||||
{
|
||||
switch (container) {
|
||||
case DockerContainer::WireGuard: return tr("Low");
|
||||
case DockerContainer::Awg: return tr("Medium or High");
|
||||
case DockerContainer::Cloak: return tr("Extreme");
|
||||
case DockerContainer::Awg: return tr("High");
|
||||
// case DockerContainer::Cloak: return tr("Extreme");
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
@@ -341,8 +341,8 @@ QString ContainerProps::easySetupDescription(DockerContainer container)
|
||||
switch (container) {
|
||||
case DockerContainer::WireGuard: return tr("I just want to increase the level of my privacy.");
|
||||
case DockerContainer::Awg: return tr("I want to bypass censorship. This option recommended in most cases.");
|
||||
case DockerContainer::Cloak:
|
||||
return tr("Most VPN protocols are blocked. Recommended if other options are not working.");
|
||||
// case DockerContainer::Cloak:
|
||||
// return tr("Most VPN protocols are blocked. Recommended if other options are not working.");
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
@@ -352,7 +352,7 @@ int ContainerProps::easySetupOrder(DockerContainer container)
|
||||
switch (container) {
|
||||
case DockerContainer::WireGuard: return 3;
|
||||
case DockerContainer::Awg: return 2;
|
||||
case DockerContainer::Cloak: return 1;
|
||||
// case DockerContainer::Cloak: return 1;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,9 +248,10 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
||||
|
||||
GETVALUE("privateKey", config.m_privateKey, String);
|
||||
GETVALUE("serverPublicKey", config.m_serverPublicKey, String);
|
||||
GETVALUE("serverPskKey", config.m_serverPskKey, String);
|
||||
GETVALUE("serverPort", config.m_serverPort, Double);
|
||||
|
||||
config.m_serverPskKey = obj.value("serverPskKey").toString();
|
||||
|
||||
if (!obj.contains("deviceMTU") || obj.value("deviceMTU").toString().toInt() == 0)
|
||||
{
|
||||
config.m_deviceMTU = 1420;
|
||||
@@ -373,6 +374,8 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
config.m_killSwitchEnabled = QVariant(obj.value("killSwitchOption").toString()).toBool();
|
||||
|
||||
if (!obj.value("Jc").isNull()) {
|
||||
config.m_junkPacketCount = obj.value("Jc").toString();
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ class InterfaceConfig {
|
||||
QList<IPAddress> m_allowedIPAddressRanges;
|
||||
QStringList m_excludedAddresses;
|
||||
QStringList m_vpnDisabledApps;
|
||||
bool m_killSwitchEnabled;
|
||||
#if defined(MZ_ANDROID) || defined(MZ_IOS)
|
||||
QString m_installationId;
|
||||
#endif
|
||||
|
||||
@@ -221,7 +221,9 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
json.insert("excludedAddresses", jsExcludedAddresses);
|
||||
|
||||
json.insert("vpnDisabledApps", splitTunnelApps);
|
||||
|
||||
|
||||
json.insert(amnezia::config_key::killSwitchOption, rawConfig.value(amnezia::config_key::killSwitchOption));
|
||||
|
||||
if (protocolName == amnezia::config_key::awg) {
|
||||
json.insert(amnezia::config_key::junkPacketCount, wgConfig.value(amnezia::config_key::junkPacketCount));
|
||||
json.insert(amnezia::config_key::junkPacketMinSize, wgConfig.value(amnezia::config_key::junkPacketMinSize));
|
||||
|
||||
@@ -171,9 +171,15 @@ void NetworkWatcher::unsecuredNetwork(const QString& networkName,
|
||||
}
|
||||
|
||||
|
||||
QString NetworkWatcher::getCurrentTransport() {
|
||||
auto type = m_impl->getTransportType();
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<NetworkWatcherImpl::TransportType>();
|
||||
return QString(metaEnum.valueToKey(type))
|
||||
.remove("TransportType_", Qt::CaseSensitive);
|
||||
QNetworkInformation::Reachability NetworkWatcher::getReachability() {
|
||||
if (m_simulatedDisconnection) {
|
||||
return QNetworkInformation::Reachability::Disconnected;
|
||||
} else if (QNetworkInformation::instance()) {
|
||||
return QNetworkInformation::instance()->reachability();
|
||||
}
|
||||
return QNetworkInformation::Reachability::Unknown;
|
||||
}
|
||||
|
||||
void NetworkWatcher::simulateDisconnection(bool simulatedDisconnection) {
|
||||
m_simulatedDisconnection = simulatedDisconnection;
|
||||
}
|
||||
|
||||
@@ -7,45 +7,50 @@
|
||||
|
||||
#include <QElapsedTimer>
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <QNetworkInformation>
|
||||
|
||||
|
||||
class NetworkWatcherImpl;
|
||||
|
||||
// This class watches for network changes to detect unsecured wifi.
|
||||
class NetworkWatcher final : public QObject {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(NetworkWatcher)
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(NetworkWatcher)
|
||||
|
||||
public:
|
||||
NetworkWatcher();
|
||||
~NetworkWatcher();
|
||||
public:
|
||||
NetworkWatcher();
|
||||
~NetworkWatcher();
|
||||
|
||||
void initialize();
|
||||
void initialize();
|
||||
|
||||
// public for the inspector.
|
||||
void unsecuredNetwork(const QString& networkName, const QString& networkId);
|
||||
// Public for the Inspector.
|
||||
void unsecuredNetwork(const QString& networkName, const QString& networkId);
|
||||
// Used for the Inspector. simulateOffline = true to mock being disconnected,
|
||||
// false to restore.
|
||||
void simulateDisconnection(bool simulatedDisconnection);
|
||||
|
||||
QString getCurrentTransport();
|
||||
QNetworkInformation::Reachability getReachability();
|
||||
|
||||
signals:
|
||||
void networkChange();
|
||||
signals:
|
||||
void networkChange();
|
||||
|
||||
private:
|
||||
void settingsChanged();
|
||||
private:
|
||||
void settingsChanged();
|
||||
|
||||
// void notificationClicked(NotificationHandler::Message message);
|
||||
private:
|
||||
bool m_active = false;
|
||||
bool m_reportUnsecuredNetwork = false;
|
||||
|
||||
private:
|
||||
bool m_active = false;
|
||||
bool m_reportUnsecuredNetwork = false;
|
||||
// Platform-specific implementation.
|
||||
NetworkWatcherImpl* m_impl = nullptr;
|
||||
|
||||
// Platform-specific implementation.
|
||||
NetworkWatcherImpl* m_impl = nullptr;
|
||||
QMap<QString, QElapsedTimer> m_networks;
|
||||
|
||||
QMap<QString, QElapsedTimer> m_networks;
|
||||
// This is used to connect NotificationHandler lazily.
|
||||
bool m_firstNotification = true;
|
||||
|
||||
// This is used to connect NotificationHandler lazily.
|
||||
bool m_firstNotification = true;
|
||||
// Used to simulate network disconnection in the Inspector
|
||||
bool m_simulatedDisconnection = false;
|
||||
};
|
||||
|
||||
#endif // NETWORKWATCHER_H
|
||||
|
||||
@@ -5,50 +5,45 @@
|
||||
#ifndef NETWORKWATCHERIMPL_H
|
||||
#define NETWORKWATCHERIMPL_H
|
||||
|
||||
#include <QNetworkInformation>
|
||||
#include <QObject>
|
||||
|
||||
class NetworkWatcherImpl : public QObject {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(NetworkWatcherImpl)
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(NetworkWatcherImpl)
|
||||
|
||||
public:
|
||||
NetworkWatcherImpl(QObject* parent) : QObject(parent) {}
|
||||
public:
|
||||
NetworkWatcherImpl(QObject* parent) : QObject(parent) {}
|
||||
|
||||
virtual ~NetworkWatcherImpl() = default;
|
||||
virtual ~NetworkWatcherImpl() = default;
|
||||
|
||||
virtual void initialize() = 0;
|
||||
virtual void initialize() = 0;
|
||||
|
||||
virtual void start() { m_active = true; }
|
||||
virtual void stop() { m_active = false; }
|
||||
virtual void start() { m_active = true; }
|
||||
virtual void stop() { m_active = false; }
|
||||
|
||||
bool isActive() const { return m_active; }
|
||||
bool isActive() const { return m_active; }
|
||||
|
||||
enum TransportType {
|
||||
TransportType_Unknown = 0,
|
||||
TransportType_Ethernet = 1,
|
||||
TransportType_WiFi = 2,
|
||||
TransportType_Cellular = 3, // In Case the API does not retun the gsm type
|
||||
TransportType_Other = 4, // I.e USB thethering
|
||||
TransportType_None = 5 // I.e Airplane Mode or no active network device
|
||||
};
|
||||
Q_ENUM(TransportType);
|
||||
enum TransportType {
|
||||
TransportType_Unknown = 0,
|
||||
TransportType_Ethernet = 1,
|
||||
TransportType_WiFi = 2,
|
||||
TransportType_Cellular = 3, // In Case the API does not retun the gsm type
|
||||
TransportType_Other = 4, // I.e USB thethering
|
||||
TransportType_None = 5 // I.e Airplane Mode or no active network device
|
||||
};
|
||||
Q_ENUM(TransportType);
|
||||
|
||||
// Returns the current type of Network Connection
|
||||
virtual TransportType getTransportType() = 0;
|
||||
signals:
|
||||
// Fires when the Device Connects to an unsecured Network
|
||||
void unsecuredNetwork(const QString& networkName, const QString& networkId);
|
||||
// Fires on when the connected WIFI Changes
|
||||
// TODO: Only windows-networkwatcher has this, the other plattforms should
|
||||
// too.
|
||||
void networkChanged(QString newBSSID);
|
||||
|
||||
signals:
|
||||
// Fires when the Device Connects to an unsecured Network
|
||||
void unsecuredNetwork(const QString& networkName, const QString& networkId);
|
||||
// Fires on when the connected WIFI Changes
|
||||
// TODO: Only windows-networkwatcher has this, the other plattforms should
|
||||
// too.
|
||||
void networkChanged(QString newBSSID);
|
||||
|
||||
// Fired when the Device changed the Type of Transport
|
||||
void transportChanged(NetworkWatcherImpl::TransportType transportType);
|
||||
|
||||
private:
|
||||
bool m_active = false;
|
||||
private:
|
||||
bool m_active = false;
|
||||
};
|
||||
|
||||
#endif // NETWORKWATCHERIMPL_H
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
|
||||
DummyNetworkWatcher::DummyNetworkWatcher(QObject* parent)
|
||||
: NetworkWatcherImpl(parent) {
|
||||
MZ_COUNT_CTOR(DummyNetworkWatcher);
|
||||
MZ_COUNT_CTOR(DummyNetworkWatcher);
|
||||
}
|
||||
|
||||
DummyNetworkWatcher::~DummyNetworkWatcher() {
|
||||
MZ_COUNT_DTOR(DummyNetworkWatcher);
|
||||
MZ_COUNT_DTOR(DummyNetworkWatcher);
|
||||
}
|
||||
|
||||
void DummyNetworkWatcher::initialize() {}
|
||||
|
||||
@@ -8,15 +8,11 @@
|
||||
#include "networkwatcherimpl.h"
|
||||
|
||||
class DummyNetworkWatcher final : public NetworkWatcherImpl {
|
||||
public:
|
||||
DummyNetworkWatcher(QObject* parent);
|
||||
~DummyNetworkWatcher();
|
||||
public:
|
||||
DummyNetworkWatcher(QObject* parent);
|
||||
~DummyNetworkWatcher();
|
||||
|
||||
void initialize() override;
|
||||
|
||||
NetworkWatcherImpl::TransportType getTransportType() override {
|
||||
return TransportType_Other;
|
||||
};
|
||||
void initialize() override;
|
||||
};
|
||||
|
||||
#endif // DUMMYNETWORKWATCHER_H
|
||||
|
||||
@@ -15,7 +15,6 @@ class IOSNetworkWatcher : public NetworkWatcherImpl {
|
||||
~IOSNetworkWatcher();
|
||||
|
||||
void initialize() override;
|
||||
NetworkWatcherImpl::TransportType getTransportType() override;
|
||||
|
||||
private:
|
||||
NetworkWatcherImpl::TransportType toTransportType(nw_path_t path);
|
||||
|
||||
@@ -37,16 +37,6 @@ void IOSNetworkWatcher::initialize() {
|
||||
//TODO IMPL FOR AMNEZIA
|
||||
}
|
||||
|
||||
NetworkWatcherImpl::TransportType IOSNetworkWatcher::getTransportType() {
|
||||
//TODO IMPL FOR AMNEZIA
|
||||
|
||||
if (m_observableConnection != nil) {
|
||||
return m_currentVPNTransport;
|
||||
}
|
||||
// If we don't have an open tunnel-observer, m_currentVPNTransport is probably wrong.
|
||||
return NetworkWatcherImpl::TransportType_Unknown;
|
||||
}
|
||||
|
||||
NetworkWatcherImpl::TransportType IOSNetworkWatcher::toTransportType(nw_path_t path) {
|
||||
if (path == nil) {
|
||||
return NetworkWatcherImpl::TransportType_Unknown;
|
||||
|
||||
@@ -136,24 +136,25 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) {
|
||||
if (err != 0) {
|
||||
logger.error() << "Interface configuration failed:" << strerror(err);
|
||||
} else {
|
||||
FirewallParams params { };
|
||||
params.dnsServers.append(config.m_dnsServer);
|
||||
if (config.m_allowedIPAddressRanges.at(0).toString() == "0.0.0.0/0"){
|
||||
params.blockAll = true;
|
||||
if (config.m_excludedAddresses.size()) {
|
||||
params.allowNets = true;
|
||||
foreach (auto net, config.m_excludedAddresses) {
|
||||
params.allowAddrs.append(net.toUtf8());
|
||||
if (config.m_killSwitchEnabled) {
|
||||
FirewallParams params { };
|
||||
params.dnsServers.append(config.m_dnsServer);
|
||||
if (config.m_allowedIPAddressRanges.at(0).toString() == "0.0.0.0/0"){
|
||||
params.blockAll = true;
|
||||
if (config.m_excludedAddresses.size()) {
|
||||
params.allowNets = true;
|
||||
foreach (auto net, config.m_excludedAddresses) {
|
||||
params.allowAddrs.append(net.toUtf8());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
params.blockNets = true;
|
||||
foreach (auto net, config.m_allowedIPAddressRanges) {
|
||||
params.blockAddrs.append(net.toString());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
params.blockNets = true;
|
||||
foreach (auto net, config.m_allowedIPAddressRanges) {
|
||||
params.blockAddrs.append(net.toString());
|
||||
}
|
||||
applyFirewallRules(params);
|
||||
}
|
||||
|
||||
applyFirewallRules(params);
|
||||
}
|
||||
|
||||
return (err == 0);
|
||||
@@ -199,7 +200,9 @@ bool WireguardUtilsLinux::updatePeer(const InterfaceConfig& config) {
|
||||
QTextStream out(&message);
|
||||
out << "set=1\n";
|
||||
out << "public_key=" << QString(publicKey.toHex()) << "\n";
|
||||
out << "preshared_key=" << QString(pskKey.toHex()) << "\n";
|
||||
if (!config.m_serverPskKey.isNull()) {
|
||||
out << "preshared_key=" << QString(pskKey.toHex()) << "\n";
|
||||
}
|
||||
if (!config.m_serverIpv4AddrIn.isNull()) {
|
||||
out << "endpoint=" << config.m_serverIpv4AddrIn << ":";
|
||||
} else if (!config.m_serverIpv6AddrIn.isNull()) {
|
||||
|
||||
5
client/platforms/linux/linuxnetworkwatcher.h
Normal file → Executable file
5
client/platforms/linux/linuxnetworkwatcher.h
Normal file → Executable file
@@ -22,11 +22,6 @@ class LinuxNetworkWatcher final : public NetworkWatcherImpl {
|
||||
|
||||
void start() override;
|
||||
|
||||
NetworkWatcherImpl::TransportType getTransportType() {
|
||||
// TODO: Find out how to do that on linux generally. (VPN-2382)
|
||||
return NetworkWatcherImpl::TransportType_Unknown;
|
||||
};
|
||||
|
||||
signals:
|
||||
void checkDevicesInThread();
|
||||
|
||||
|
||||
@@ -95,6 +95,11 @@ void MacosRouteMonitor::handleRtmDelete(const struct rt_msghdr* rtm,
|
||||
!(rtm->rtm_addrs & RTA_NETMASK) || (addrlist.count() < 3)) {
|
||||
return;
|
||||
}
|
||||
// Ignore interface-scoped routes, we want to find the default route to the
|
||||
// internet in the global scope.
|
||||
if (rtm->rtm_flags & RTF_IFSCOPE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for a default route, which should have a netmask of zero.
|
||||
const struct sockaddr* sa =
|
||||
@@ -156,6 +161,11 @@ void MacosRouteMonitor::handleRtmUpdate(const struct rt_msghdr* rtm,
|
||||
!(rtm->rtm_addrs & RTA_NETMASK) || (addrlist.count() < 3)) {
|
||||
return;
|
||||
}
|
||||
// Ignore interface-scoped routes, we want to find the default route to the
|
||||
// internet in the global scope.
|
||||
if (rtm->rtm_flags & RTF_IFSCOPE) {
|
||||
return;
|
||||
}
|
||||
// Ignore route changes that we caused, or routes on the tunnel interface.
|
||||
if (rtm->rtm_index == m_ifindex) {
|
||||
return;
|
||||
|
||||
@@ -134,26 +134,26 @@ bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) {
|
||||
if (err != 0) {
|
||||
logger.error() << "Interface configuration failed:" << strerror(err);
|
||||
} else {
|
||||
FirewallParams params { };
|
||||
params.dnsServers.append(config.m_dnsServer);
|
||||
if (config.m_allowedIPAddressRanges.at(0).toString() == "0.0.0.0/0"){
|
||||
params.blockAll = true;
|
||||
if (config.m_excludedAddresses.size()) {
|
||||
if (config.m_killSwitchEnabled) {
|
||||
FirewallParams params { };
|
||||
params.dnsServers.append(config.m_dnsServer);
|
||||
if (config.m_allowedIPAddressRanges.at(0).toString() == "0.0.0.0/0"){
|
||||
params.blockAll = true;
|
||||
if (config.m_excludedAddresses.size()) {
|
||||
params.allowNets = true;
|
||||
foreach (auto net, config.m_excludedAddresses) {
|
||||
params.allowAddrs.append(net.toUtf8());
|
||||
params.allowAddrs.append(net.toUtf8());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
params.blockNets = true;
|
||||
foreach (auto net, config.m_allowedIPAddressRanges) {
|
||||
}
|
||||
} else {
|
||||
params.blockNets = true;
|
||||
foreach (auto net, config.m_allowedIPAddressRanges) {
|
||||
params.blockAddrs.append(net.toString());
|
||||
}
|
||||
}
|
||||
applyFirewallRules(params);
|
||||
}
|
||||
|
||||
applyFirewallRules(params);
|
||||
}
|
||||
|
||||
return (err == 0);
|
||||
}
|
||||
|
||||
@@ -199,7 +199,9 @@ bool WireguardUtilsMacos::updatePeer(const InterfaceConfig& config) {
|
||||
QTextStream out(&message);
|
||||
out << "set=1\n";
|
||||
out << "public_key=" << QString(publicKey.toHex()) << "\n";
|
||||
out << "preshared_key=" << QString(pskKey.toHex()) << "\n";
|
||||
if (!config.m_serverPskKey.isNull()) {
|
||||
out << "preshared_key=" << QString(pskKey.toHex()) << "\n";
|
||||
}
|
||||
if (!config.m_serverIpv4AddrIn.isNull()) {
|
||||
out << "endpoint=" << config.m_serverIpv4AddrIn << ":";
|
||||
} else if (!config.m_serverIpv6AddrIn.isNull()) {
|
||||
|
||||
@@ -83,7 +83,7 @@ void MacOSPingSender::sendPing(const QHostAddress& dest, quint16 sequence) {
|
||||
packet.icmp_seq = htons(sequence);
|
||||
packet.icmp_cksum = inetChecksum(&packet, sizeof(packet));
|
||||
|
||||
if (sendto(m_socket, (char*)&packet, sizeof(packet), 0,
|
||||
if (sendto(m_socket, (char*)&packet, sizeof(packet), MSG_NOSIGNAL,
|
||||
(struct sockaddr*)&addr, sizeof(addr)) != sizeof(packet)) {
|
||||
logger.error() << "ping sending failed:" << strerror(errno);
|
||||
emit criticalPingError();
|
||||
@@ -107,9 +107,9 @@ void MacOSPingSender::socketReady() {
|
||||
iov.iov_base = packet;
|
||||
iov.iov_len = IP_MAXPACKET;
|
||||
|
||||
ssize_t rc = recvmsg(m_socket, &msg, MSG_DONTWAIT);
|
||||
ssize_t rc = recvmsg(m_socket, &msg, MSG_DONTWAIT | MSG_NOSIGNAL);
|
||||
if (rc <= 0) {
|
||||
logger.error() << "Recvmsg failed";
|
||||
logger.error() << "Recvmsg failed:" << strerror(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,11 +41,33 @@ void MacOSUtils::enableLoginItem(bool startAtBoot) {
|
||||
Q_ASSERT(appId);
|
||||
|
||||
NSString* loginItemAppId =
|
||||
QString("%1.login-item").arg(QString::fromNSString(appId)).toNSString();
|
||||
CFStringRef cfs = (__bridge CFStringRef)loginItemAppId;
|
||||
QString("%1.login-item").arg(QString::fromNSString(appId)).toNSString();
|
||||
|
||||
Boolean ok = SMLoginItemSetEnabled(cfs, startAtBoot ? YES : NO);
|
||||
logger.debug() << "Result: " << ok;
|
||||
// For macOS 13 and beyond, register() and unregister() methods
|
||||
// are used for managing login items since SMLoginItemSetEnabled() is deprecated.
|
||||
// For versions prior to macOS 13, SMLoginItemSetEnabled() is used.
|
||||
if (@available(macOS 13, *)) {
|
||||
// Use register() or unregister() based on the startAtBoot flag
|
||||
NSError* error = nil;
|
||||
|
||||
if (startAtBoot) {
|
||||
if (![[SMAppService mainAppService] registerAndReturnError: & error]) {
|
||||
logger.error() << "Failed to register Amnezia VPN LoginItem: " << error.localizedDescription;
|
||||
} else {
|
||||
logger.debug() << "Amnezia VPN LoginItem registered successfully.";
|
||||
}
|
||||
} else {
|
||||
if (![[SMAppService mainAppService] unregisterAndReturnError: & error]) {
|
||||
logger.error() << "Failed to unregister Amnezia VPN LoginItem: " << error.localizedDescription;
|
||||
} else {
|
||||
logger.debug() << "LoginItem unregistered successfully.";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
CFStringRef cfs = (__bridge CFStringRef) loginItemAppId;
|
||||
Boolean ok = SMLoginItemSetEnabled(cfs, startAtBoot ? YES : NO);
|
||||
logger.debug() << "Result: " << ok;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -4,8 +4,11 @@
|
||||
|
||||
#include "dnsutilswindows.h"
|
||||
|
||||
#include <WS2tcpip.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2ipdef.h>
|
||||
|
||||
#include <QProcess>
|
||||
#include <QTextStream>
|
||||
@@ -39,30 +42,27 @@ DnsUtilsWindows::~DnsUtilsWindows() {
|
||||
|
||||
bool DnsUtilsWindows::updateResolvers(const QString& ifname,
|
||||
const QList<QHostAddress>& resolvers) {
|
||||
NET_LUID luid;
|
||||
if (ConvertInterfaceAliasToLuid((wchar_t*)ifname.utf16(), &luid) != 0) {
|
||||
MIB_IF_ROW2 entry;
|
||||
if (ConvertInterfaceAliasToLuid((wchar_t*)ifname.utf16(),
|
||||
&entry.InterfaceLuid) != 0) {
|
||||
logger.error() << "Failed to resolve LUID for" << ifname;
|
||||
return false;
|
||||
}
|
||||
m_luid = luid.Value;
|
||||
if (GetIfEntry2(&entry) != NO_ERROR) {
|
||||
logger.error() << "Failed to resolve interface for" << ifname;
|
||||
return false;
|
||||
}
|
||||
m_luid = entry.InterfaceLuid.Value;
|
||||
|
||||
logger.debug() << "Configuring DNS for" << ifname;
|
||||
if (m_setInterfaceDnsSettingsProcAddr == nullptr) {
|
||||
return updateResolversNetsh(resolvers);
|
||||
return updateResolversNetsh(entry.InterfaceIndex, resolvers);
|
||||
}
|
||||
return updateResolversWin32(resolvers);
|
||||
return updateResolversWin32(entry.InterfaceGuid, resolvers);
|
||||
}
|
||||
|
||||
bool DnsUtilsWindows::updateResolversWin32(
|
||||
const QList<QHostAddress>& resolvers) {
|
||||
GUID guid;
|
||||
NET_LUID luid;
|
||||
luid.Value = m_luid;
|
||||
if (ConvertInterfaceLuidToGuid(&luid, &guid) != NO_ERROR) {
|
||||
logger.error() << "Failed to resolve GUID";
|
||||
return false;
|
||||
}
|
||||
|
||||
GUID guid, const QList<QHostAddress>& resolvers) {
|
||||
QStringList v4resolvers;
|
||||
QStringList v6resolvers;
|
||||
for (const QHostAddress& addr : resolvers) {
|
||||
@@ -113,16 +113,8 @@ constexpr const char* netshAddTemplate =
|
||||
"interface %1 add dnsservers name=%2 address=%3 validate=no\r\n";
|
||||
|
||||
bool DnsUtilsWindows::updateResolversNetsh(
|
||||
const QList<QHostAddress>& resolvers) {
|
||||
int ifindex, const QList<QHostAddress>& resolvers) {
|
||||
QProcess netsh;
|
||||
NET_LUID luid;
|
||||
NET_IFINDEX ifindex;
|
||||
luid.Value = m_luid;
|
||||
if (ConvertInterfaceLuidToIndex(&luid, &ifindex) != NO_ERROR) {
|
||||
logger.error() << "Failed to resolve GUID";
|
||||
return false;
|
||||
}
|
||||
|
||||
netsh.setProgram("netsh");
|
||||
netsh.start();
|
||||
if (!netsh.waitForStarted(WINDOWS_NETSH_TIMEOUT_MSEC)) {
|
||||
@@ -166,12 +158,26 @@ bool DnsUtilsWindows::updateResolversNetsh(
|
||||
|
||||
bool DnsUtilsWindows::restoreResolvers() {
|
||||
if (m_luid == 0) {
|
||||
// If the DNS hasn't been configured, there is nothing to restore.
|
||||
return true;
|
||||
}
|
||||
|
||||
MIB_IF_ROW2 entry;
|
||||
DWORD error;
|
||||
entry.InterfaceLuid.Value = m_luid;
|
||||
error = GetIfEntry2(&entry);
|
||||
if (error == ERROR_FILE_NOT_FOUND) {
|
||||
// If the interface no longer exists, there is nothing to restore.
|
||||
return true;
|
||||
}
|
||||
if (error != NO_ERROR) {
|
||||
logger.error() << "Failed to resolve interface entry:" << error;
|
||||
return false;
|
||||
}
|
||||
|
||||
QList<QHostAddress> empty;
|
||||
if (m_setInterfaceDnsSettingsProcAddr == nullptr) {
|
||||
return updateResolversNetsh(empty);
|
||||
return updateResolversNetsh(entry.InterfaceIndex, empty);
|
||||
}
|
||||
return updateResolversWin32(empty);
|
||||
return updateResolversWin32(entry.InterfaceGuid, empty);
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ class DnsUtilsWindows final : public DnsUtils {
|
||||
quint64 m_luid = 0;
|
||||
DWORD (*m_setInterfaceDnsSettingsProcAddr)(GUID, const void*) = nullptr;
|
||||
|
||||
bool updateResolversWin32(const QList<QHostAddress>& resolvers);
|
||||
bool updateResolversNetsh(const QList<QHostAddress>& resolvers);
|
||||
bool updateResolversWin32(GUID, const QList<QHostAddress>& resolvers);
|
||||
bool updateResolversNetsh(int ifindex, const QList<QHostAddress>& resolvers);
|
||||
};
|
||||
|
||||
#endif // DNSUTILSWINDOWS_H
|
||||
|
||||
@@ -38,7 +38,6 @@ class WindowsDaemon final : public Daemon {
|
||||
Inactive,
|
||||
};
|
||||
|
||||
State m_state = Inactive;
|
||||
int m_inetAdapterIndex = -1;
|
||||
|
||||
WireguardUtilsWindows* m_wgutils = nullptr;
|
||||
|
||||
@@ -114,7 +114,7 @@ void WindowsRouteMonitor::updateValidInterfaces(int family) {
|
||||
void WindowsRouteMonitor::updateExclusionRoute(MIB_IPFORWARD_ROW2* data,
|
||||
void* ptable) {
|
||||
PMIB_IPFORWARD_TABLE2 table = reinterpret_cast<PMIB_IPFORWARD_TABLE2>(ptable);
|
||||
SOCKADDR_INET nexthop = {0};
|
||||
SOCKADDR_INET nexthop = {};
|
||||
quint64 bestLuid = 0;
|
||||
int bestMatch = -1;
|
||||
ULONG bestMetric = ULONG_MAX;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <QFileInfo>
|
||||
#include <QNetworkInterface>
|
||||
#include <QScopeGuard>
|
||||
#include <QThread>
|
||||
|
||||
namespace {
|
||||
Logger logger("WindowsSplitTunnel");
|
||||
@@ -29,6 +30,9 @@ WindowsSplitTunnel::WindowsSplitTunnel(QObject* parent) : QObject(parent) {
|
||||
uninstallDriver();
|
||||
return;
|
||||
}
|
||||
|
||||
m_tries = 0;
|
||||
|
||||
if (!isInstalled()) {
|
||||
logger.debug() << "Driver is not Installed, doing so";
|
||||
auto handle = installDriver();
|
||||
@@ -59,10 +63,10 @@ void WindowsSplitTunnel::initDriver() {
|
||||
m_driver = CreateFileW(DRIVER_SYMLINK, GENERIC_READ | GENERIC_WRITE, 0,
|
||||
nullptr, OPEN_EXISTING, 0, nullptr);
|
||||
;
|
||||
|
||||
if (m_driver == INVALID_HANDLE_VALUE) {
|
||||
if (m_driver == INVALID_HANDLE_VALUE && m_tries < 500) {
|
||||
WindowsUtils::windowsLog("Failed to open Driver: ");
|
||||
|
||||
m_tries++;
|
||||
Sleep(100);
|
||||
// If the handle is not present, try again after the serivce has started;
|
||||
auto driver_manager = WindowsServiceManager(DRIVER_SERVICE_NAME);
|
||||
QObject::connect(&driver_manager, &WindowsServiceManager::serviceStarted,
|
||||
|
||||
@@ -158,6 +158,7 @@ class WindowsSplitTunnel final : public QObject {
|
||||
constexpr static const auto MV_SERVICE_NAME = L"MullvadVPN";
|
||||
DRIVER_STATE getState();
|
||||
|
||||
int m_tries;
|
||||
// Initializes the WFP Sublayer
|
||||
bool initSublayer();
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ Logger logger("tunnel.dll");
|
||||
|
||||
WindowsTunnelLogger::WindowsTunnelLogger(const QString& filename,
|
||||
QObject* parent)
|
||||
: QObject(parent), m_logfile(filename, this), m_timer(this) {
|
||||
: QObject(parent), m_timer(this), m_logfile(filename, this) {
|
||||
MZ_COUNT_CTOR(WindowsTunnelLogger);
|
||||
|
||||
m_startTime = QDateTime::currentMSecsSinceEpoch() * 1000000;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "WindowsTunnelService.h"
|
||||
#include "windowstunnelservice.h"
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
@@ -30,12 +30,22 @@ static bool waitForServiceStatus(SC_HANDLE service, DWORD expectedStatus);
|
||||
|
||||
WindowsTunnelService::WindowsTunnelService(QObject* parent) : QObject(parent) {
|
||||
MZ_COUNT_CTOR(WindowsTunnelService);
|
||||
logger.debug() << "WindowsTunnelService created.";
|
||||
|
||||
m_scm = OpenSCManager(nullptr, nullptr, SC_MANAGER_ALL_ACCESS);
|
||||
if (m_scm == nullptr) {
|
||||
WindowsUtils::windowsLog("Failed to open SCManager");
|
||||
}
|
||||
|
||||
// Is the service already running? Terminate it.
|
||||
SC_HANDLE service =
|
||||
OpenService((SC_HANDLE)m_scm, TUNNEL_SERVICE_NAME, SERVICE_ALL_ACCESS);
|
||||
if (service != nullptr) {
|
||||
logger.info() << "Tunnel already exists. Terminating it.";
|
||||
stopAndDeleteTunnelService(service);
|
||||
CloseServiceHandle(service);
|
||||
}
|
||||
|
||||
connect(&m_timer, &QTimer::timeout, this, &WindowsTunnelService::timeout);
|
||||
}
|
||||
|
||||
@@ -138,7 +148,7 @@ bool WindowsTunnelService::start(const QString& configData) {
|
||||
|
||||
logger.debug() << "Service:" << qApp->applicationFilePath();
|
||||
|
||||
service = CreateService(scm, TUNNEL_SERVICE_NAME, L"Amezia VPN (tunnel)",
|
||||
service = CreateService(scm, TUNNEL_SERVICE_NAME, L"Amnezia VPN (tunnel)",
|
||||
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
|
||||
SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
|
||||
(const wchar_t*)serviceCmdline.utf16(), nullptr, 0,
|
||||
|
||||
@@ -116,10 +116,12 @@ bool WireguardUtilsWindows::addInterface(const InterfaceConfig& config) {
|
||||
m_luid = luid.Value;
|
||||
m_routeMonitor.setLuid(luid.Value);
|
||||
|
||||
// Enable the windows firewall
|
||||
NET_IFINDEX ifindex;
|
||||
ConvertInterfaceLuidToIndex(&luid, &ifindex);
|
||||
WindowsFirewall::instance()->enableKillSwitch(ifindex);
|
||||
if (config.m_killSwitchEnabled) {
|
||||
// Enable the windows firewall
|
||||
NET_IFINDEX ifindex;
|
||||
ConvertInterfaceLuidToIndex(&luid, &ifindex);
|
||||
WindowsFirewall::instance()->enableKillSwitch(ifindex);
|
||||
}
|
||||
|
||||
logger.debug() << "Registration completed";
|
||||
return true;
|
||||
@@ -137,9 +139,10 @@ bool WireguardUtilsWindows::updatePeer(const InterfaceConfig& config) {
|
||||
QByteArray pskKey =
|
||||
QByteArray::fromBase64(qPrintable(config.m_serverPskKey));
|
||||
|
||||
// Enable the windows firewall for this peer.
|
||||
WindowsFirewall::instance()->enablePeerTraffic(config);
|
||||
|
||||
if (config.m_killSwitchEnabled) {
|
||||
// Enable the windows firewall for this peer.
|
||||
WindowsFirewall::instance()->enablePeerTraffic(config);
|
||||
}
|
||||
logger.debug() << "Configuring peer" << publicKey.toHex()
|
||||
<< "via" << config.m_serverIpv4AddrIn;
|
||||
|
||||
@@ -148,7 +151,9 @@ bool WireguardUtilsWindows::updatePeer(const InterfaceConfig& config) {
|
||||
QTextStream out(&message);
|
||||
out << "set=1\n";
|
||||
out << "public_key=" << QString(publicKey.toHex()) << "\n";
|
||||
out << "preshared_key=" << QString(pskKey.toHex()) << "\n";
|
||||
if (!config.m_serverPskKey.isNull()) {
|
||||
out << "preshared_key=" << QString(pskKey.toHex()) << "\n";
|
||||
}
|
||||
if (!config.m_serverIpv4AddrIn.isNull()) {
|
||||
out << "endpoint=" << config.m_serverIpv4AddrIn << ":";
|
||||
} else if (!config.m_serverIpv6AddrIn.isNull()) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <d3d11.h>
|
||||
#include <dxgi.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <shlobj_core.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QHostAddress>
|
||||
@@ -19,9 +20,9 @@
|
||||
#include "logger.h"
|
||||
#include "platforms/windows/windowsutils.h"
|
||||
|
||||
#define TUNNEL_SERVICE_NAME L"WireGuardTunnel$amnvpn"
|
||||
|
||||
constexpr const char* VPN_NAME = "AmneziaVPN";
|
||||
constexpr const char* WIREGUARD_DIR = "WireGuard";
|
||||
constexpr const char* DATA_DIR = "Data";
|
||||
|
||||
namespace {
|
||||
Logger logger("WindowsCommons");
|
||||
@@ -67,27 +68,67 @@ QString WindowsCommons::tunnelConfigFile() {
|
||||
return QString();
|
||||
}
|
||||
|
||||
// static
|
||||
QString WindowsCommons::tunnelLogFile() {
|
||||
QStringList paths =
|
||||
QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
|
||||
static QString tunnelLogFilePath = getTunnelLogFilePath();
|
||||
return tunnelLogFilePath;
|
||||
}
|
||||
|
||||
for (const QString& path : paths) {
|
||||
QDir dir(path);
|
||||
if (!dir.exists()) {
|
||||
continue;
|
||||
// static
|
||||
QString WindowsCommons::getProgramFilesPath() {
|
||||
wchar_t* path = nullptr;
|
||||
|
||||
if (SUCCEEDED(
|
||||
SHGetKnownFolderPath(FOLDERID_ProgramFiles, 0, nullptr, &path))) {
|
||||
auto guard = qScopeGuard([&] { CoTaskMemFree(path); });
|
||||
return QString::fromWCharArray(path);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
// static
|
||||
QString WindowsCommons::getTunnelLogFilePath() {
|
||||
// Return WireGuard's log file path, "\Program Files\WireGuard\Data\log.bin",
|
||||
// if the directory path exists
|
||||
auto programFilesPath = getProgramFilesPath();
|
||||
if (!programFilesPath.isEmpty()) {
|
||||
QDir programFilesDir(programFilesPath);
|
||||
|
||||
if (programFilesDir.exists()) {
|
||||
QDir wireGuardDir(programFilesDir.filePath(WIREGUARD_DIR));
|
||||
|
||||
if (wireGuardDir.exists()) {
|
||||
QDir wireGuardDataDir(wireGuardDir.filePath(DATA_DIR));
|
||||
|
||||
if (wireGuardDataDir.exists()) {
|
||||
return wireGuardDataDir.filePath("log.bin");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QDir vpnDir(dir.filePath(VPN_NAME));
|
||||
if (!vpnDir.exists()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return vpnDir.filePath("log.bin");
|
||||
}
|
||||
|
||||
logger.debug() << "Failed to find WireGuard Tunnel log file";
|
||||
return QString();
|
||||
}
|
||||
|
||||
// static
|
||||
int WindowsCommons::AdapterIndexTo(const QHostAddress& dst) {
|
||||
logger.debug() << "Getting Current Internet Adapter that routes to"
|
||||
<< logger.sensitive(dst.toString());
|
||||
quint32_be ipBigEndian;
|
||||
quint32 ip = dst.toIPv4Address();
|
||||
qToBigEndian(ip, &ipBigEndian);
|
||||
_MIB_IPFORWARDROW routeInfo;
|
||||
auto result = GetBestRoute(ipBigEndian, 0, &routeInfo);
|
||||
if (result != NO_ERROR) {
|
||||
return -1;
|
||||
}
|
||||
auto adapter =
|
||||
QNetworkInterface::interfaceFromIndex(routeInfo.dwForwardIfIndex);
|
||||
logger.debug() << "Internet Adapter:" << adapter.name();
|
||||
return routeInfo.dwForwardIfIndex;
|
||||
}
|
||||
|
||||
// static
|
||||
int WindowsCommons::VPNAdapterIndex() {
|
||||
// For someReason QNetworkInterface::fromName(MozillaVPN) does not work >:(
|
||||
@@ -102,7 +143,7 @@ int WindowsCommons::VPNAdapterIndex() {
|
||||
|
||||
// Static
|
||||
QString WindowsCommons::getCurrentPath() {
|
||||
QByteArray buffer(2048, 0xFF);
|
||||
QByteArray buffer(2048, 0xFFu);
|
||||
auto ok = GetModuleFileNameA(NULL, buffer.data(), buffer.size());
|
||||
|
||||
if (ok == ERROR_INSUFFICIENT_BUFFER) {
|
||||
|
||||
@@ -19,9 +19,14 @@ class WindowsCommons final {
|
||||
|
||||
// Returns the Interface Index of the VPN Adapter
|
||||
static int VPNAdapterIndex();
|
||||
|
||||
// Returns the Interface Index that could Route to dst
|
||||
static int AdapterIndexTo(const QHostAddress& dst);
|
||||
// Returns the Path of the Current process
|
||||
static QString getCurrentPath();
|
||||
|
||||
private:
|
||||
static QString getTunnelLogFilePath();
|
||||
static QString getProgramFilesPath();
|
||||
};
|
||||
|
||||
#endif // WINDOWSCOMMONS_H
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "windowsnetworkwatcher.h"
|
||||
|
||||
#include <QNetworkInformation>
|
||||
#include <QScopeGuard>
|
||||
|
||||
#include "leakdetector.h"
|
||||
@@ -136,9 +137,4 @@ void WindowsNetworkWatcher::processWlan(PWLAN_NOTIFICATION_DATA data) {
|
||||
logger.debug() << "Unsecure network:" << logger.sensitive(ssid)
|
||||
<< "id:" << logger.sensitive(bssid);
|
||||
emit unsecuredNetwork(ssid, bssid);
|
||||
}
|
||||
|
||||
NetworkWatcherImpl::TransportType WindowsNetworkWatcher::getTransportType() {
|
||||
// TODO: Implement this once we update to Qt6.3 (VPN-3511)
|
||||
return TransportType_Other;
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,6 @@ class WindowsNetworkWatcher final : public NetworkWatcherImpl {
|
||||
|
||||
void initialize() override;
|
||||
|
||||
NetworkWatcherImpl::TransportType getTransportType() override;
|
||||
|
||||
private:
|
||||
static void wlanCallback(PWLAN_NOTIFICATION_DATA data, PVOID context);
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <WS2tcpip.h>
|
||||
#include <Windows.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <winternl.h>
|
||||
|
||||
// Note: This important must come after the previous three.
|
||||
// clang-format off
|
||||
#include <IcmpAPI.h>
|
||||
@@ -16,17 +18,58 @@
|
||||
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "windowscommons.h"
|
||||
#include "platforms/windows/windowsutils.h"
|
||||
#include "windowscommons.h"
|
||||
|
||||
#pragma comment(lib, "Ws2_32")
|
||||
|
||||
/*
|
||||
* On 64 Bit systems we need to use another struct.
|
||||
*/
|
||||
#ifdef _WIN64
|
||||
using MZ_ICMP_ECHO_REPLY = ICMP_ECHO_REPLY32;
|
||||
#else
|
||||
using MZ_ICMP_ECHO_REPLY = ICMP_ECHO_REPLY;
|
||||
#endif
|
||||
|
||||
constexpr WORD WindowsPingPayloadSize = sizeof(quint16);
|
||||
constexpr size_t ICMP_ERR_SIZE = 8;
|
||||
/*
|
||||
* IcmpSendEcho2 expects us to provide a Buffer that is
|
||||
* at least this size
|
||||
*/
|
||||
constexpr size_t MinimumReplyBufferSize =
|
||||
sizeof(ICMP_ECHO_REPLY) + WindowsPingPayloadSize + ICMP_ERR_SIZE +
|
||||
sizeof(IO_STATUS_BLOCK);
|
||||
/**
|
||||
* ICMP_ECHO_REPLY32 is smaller than ICMP_ECHO_REPLY, so if we use that due to
|
||||
* binary compat Windows will add some padding.
|
||||
*/
|
||||
constexpr auto reply_padding =
|
||||
sizeof(ICMP_ECHO_REPLY) - sizeof(MZ_ICMP_ECHO_REPLY);
|
||||
|
||||
// Disable Packing, so the compiler does not add padding in this struct between
|
||||
// different sized types.
|
||||
#pragma pack(push, 1)
|
||||
struct ICMP_ECHO_REPLY_BUFFER {
|
||||
MZ_ICMP_ECHO_REPLY reply;
|
||||
std::array<uint8_t, reply_padding> padding;
|
||||
quint16 payload;
|
||||
std::array<char8_t, ICMP_ERR_SIZE> icmp_error;
|
||||
IO_STATUS_BLOCK status;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
// If the Size is not the MinimumReplyBufferSize, the compiler added
|
||||
// padding, so the fields will not be properly aligned with
|
||||
// what IcmpSendEcho2 will write.
|
||||
static_assert(sizeof(ICMP_ECHO_REPLY_BUFFER) == MinimumReplyBufferSize,
|
||||
"Fulfills the size requirements");
|
||||
|
||||
struct WindowsPingSenderPrivate {
|
||||
HANDLE m_handle;
|
||||
HANDLE m_event;
|
||||
unsigned char m_buffer[sizeof(ICMP_ECHO_REPLY) + WindowsPingPayloadSize + 8];
|
||||
ICMP_ECHO_REPLY_BUFFER m_replyBuffer;
|
||||
};
|
||||
|
||||
namespace {
|
||||
@@ -58,7 +101,7 @@ WindowsPingSender::WindowsPingSender(const QHostAddress& source,
|
||||
QObject::connect(m_notifier, &QWinEventNotifier::activated, this,
|
||||
&WindowsPingSender::pingEventReady);
|
||||
|
||||
memset(m_private->m_buffer, 0, sizeof(m_private->m_buffer));
|
||||
m_private->m_replyBuffer = {};
|
||||
}
|
||||
|
||||
WindowsPingSender::~WindowsPingSender() {
|
||||
@@ -86,16 +129,33 @@ void WindowsPingSender::sendPing(const QHostAddress& dest, quint16 sequence) {
|
||||
|
||||
quint32 v4dst = dest.toIPv4Address();
|
||||
if (m_source.isNull()) {
|
||||
IcmpSendEcho2(m_private->m_handle, m_private->m_event, nullptr, nullptr,
|
||||
qToBigEndian<quint32>(v4dst), &sequence, sizeof(sequence),
|
||||
nullptr, m_private->m_buffer, sizeof(m_private->m_buffer),
|
||||
10000);
|
||||
IcmpSendEcho2(m_private->m_handle, // IcmpHandle,
|
||||
m_private->m_event, // Event
|
||||
nullptr, // ApcRoutine
|
||||
nullptr, // ApcContext
|
||||
qToBigEndian<quint32>(v4dst), // DestinationAddress
|
||||
&sequence, // RequestData
|
||||
sizeof(sequence), // RequestSize
|
||||
nullptr, // RequestOptions
|
||||
&m_private->m_replyBuffer, // [OUT] ReplyBuffer
|
||||
sizeof(m_private->m_replyBuffer), // ReplySize
|
||||
10000 // Timeout
|
||||
);
|
||||
} else {
|
||||
quint32 v4src = m_source.toIPv4Address();
|
||||
IcmpSendEcho2Ex(m_private->m_handle, m_private->m_event, nullptr, nullptr,
|
||||
qToBigEndian<quint32>(v4src), qToBigEndian<quint32>(v4dst),
|
||||
&sequence, sizeof(sequence), nullptr, m_private->m_buffer,
|
||||
sizeof(m_private->m_buffer), 10000);
|
||||
IcmpSendEcho2Ex(m_private->m_handle, // IcmpHandle
|
||||
m_private->m_event, // Event
|
||||
nullptr, // ApcRoutine
|
||||
nullptr, // ApcContext
|
||||
qToBigEndian<quint32>(v4src), // SourceAddress
|
||||
qToBigEndian<quint32>(v4dst), // DestinationAddress
|
||||
&sequence, // RequestData
|
||||
sizeof(sequence), // RequestSize
|
||||
nullptr, // RequestOptions
|
||||
&m_private->m_replyBuffer, // [OUT] ReplyBuffer
|
||||
sizeof(m_private->m_replyBuffer), // ReplySize
|
||||
10000 // Timeout
|
||||
);
|
||||
}
|
||||
|
||||
DWORD status = GetLastError();
|
||||
@@ -108,8 +168,11 @@ void WindowsPingSender::sendPing(const QHostAddress& dest, quint16 sequence) {
|
||||
}
|
||||
|
||||
void WindowsPingSender::pingEventReady() {
|
||||
DWORD replyCount =
|
||||
IcmpParseReplies(m_private->m_buffer, sizeof(m_private->m_buffer));
|
||||
// Cleanup all data once we're done with m_replyBuffer.
|
||||
const auto guard = qScopeGuard([this]() { m_private->m_replyBuffer = {}; });
|
||||
|
||||
DWORD replyCount = IcmpParseReplies(&m_private->m_replyBuffer,
|
||||
sizeof(m_private->m_replyBuffer));
|
||||
if (replyCount == 0) {
|
||||
DWORD error = GetLastError();
|
||||
if (error == IP_REQ_TIMED_OUT) {
|
||||
@@ -120,14 +183,25 @@ void WindowsPingSender::pingEventReady() {
|
||||
<< " Message: " << errmsg;
|
||||
return;
|
||||
}
|
||||
|
||||
const ICMP_ECHO_REPLY* replies = (const ICMP_ECHO_REPLY*)m_private->m_buffer;
|
||||
for (DWORD i = 0; i < replyCount; i++) {
|
||||
if (replies[i].DataSize < sizeof(quint16)) {
|
||||
continue;
|
||||
}
|
||||
quint16 sequence;
|
||||
memcpy(&sequence, replies[i].Data, sizeof(quint16));
|
||||
emit recvPing(sequence);
|
||||
// We only allocated for one reply, so more should be impossible.
|
||||
if (replyCount != 1) {
|
||||
logger.error() << "Invalid amount of responses recieved";
|
||||
return;
|
||||
}
|
||||
if (m_private->m_replyBuffer.reply.Data == nullptr) {
|
||||
logger.error() << "Did get a ping response without payload";
|
||||
return;
|
||||
}
|
||||
// Assert that the (void*) pointer of Data is pointing
|
||||
// to our ReplyBuffer payload.
|
||||
if (m_private->m_replyBuffer.reply.Data == nullptr) {
|
||||
logger.error() << "Did get a ping response without payload";
|
||||
return;
|
||||
}
|
||||
// Assert that the (void*) pointer of Data is pointing
|
||||
// to our ReplyBuffer payload.
|
||||
assert(m_private->m_replyBuffer.reply.Data ==
|
||||
static_cast<PVOID>(&m_private->m_replyBuffer.payload));
|
||||
|
||||
emit recvPing(m_private->m_replyBuffer.payload);
|
||||
}
|
||||
|
||||
@@ -336,8 +336,11 @@ void OpenVpnProtocol::updateVpnGateway(const QString &line)
|
||||
for (int i = 0; i < netInterfaces.size(); i++) {
|
||||
for (int j=0; j < netInterfaces.at(i).addressEntries().size(); j++)
|
||||
{
|
||||
// killSwitch toggle
|
||||
if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) {
|
||||
IpcClient::Interface()->enableKillSwitch(QJsonObject(), netInterfaces.at(i).index());
|
||||
if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
|
||||
IpcClient::Interface()->enableKillSwitch(QJsonObject(), netInterfaces.at(i).index());
|
||||
}
|
||||
m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index());
|
||||
m_configData.insert("vpnGateway", m_vpnGateway);
|
||||
m_configData.insert("vpnServer", m_configData.value(amnezia::config_key::hostName).toString());
|
||||
@@ -347,7 +350,10 @@ void OpenVpnProtocol::updateVpnGateway(const QString &line)
|
||||
}
|
||||
#endif
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
|
||||
IpcClient::Interface()->enableKillSwitch(m_configData, 0);
|
||||
// killSwitch toggle
|
||||
if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
|
||||
IpcClient::Interface()->enableKillSwitch(m_configData, 0);
|
||||
}
|
||||
#endif
|
||||
qDebug() << QString("Set vpn local address %1, gw %2").arg(m_vpnLocalAddress).arg(vpnGateway());
|
||||
}
|
||||
|
||||
@@ -92,6 +92,8 @@ namespace amnezia
|
||||
constexpr char splitTunnelApps[] = "splitTunnelApps";
|
||||
constexpr char appSplitTunnelType[] = "appSplitTunnelType";
|
||||
|
||||
constexpr char killSwitchOption[] = "killSwitchOption";
|
||||
|
||||
constexpr char crc[] = "crc";
|
||||
|
||||
constexpr char clientId[] = "clientId";
|
||||
|
||||
@@ -142,7 +142,6 @@ ErrorCode XrayProtocol::startTun2Sock()
|
||||
QThread::msleep(5000);
|
||||
IpcClient::Interface()->createTun("utun22", amnezia::protocols::xray::defaultLocalAddr);
|
||||
IpcClient::Interface()->updateResolvers("utun22", dnsAddr);
|
||||
IpcClient::Interface()->enableKillSwitch(m_configData, 0);
|
||||
#endif
|
||||
#ifdef Q_OS_WINDOWS
|
||||
QThread::msleep(15000);
|
||||
@@ -151,7 +150,12 @@ ErrorCode XrayProtocol::startTun2Sock()
|
||||
QThread::msleep(1000);
|
||||
IpcClient::Interface()->createTun("tun2", amnezia::protocols::xray::defaultLocalAddr);
|
||||
IpcClient::Interface()->updateResolvers("tun2", dnsAddr);
|
||||
IpcClient::Interface()->enableKillSwitch(m_configData, 0);
|
||||
#endif
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
|
||||
// killSwitch toggle
|
||||
if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
|
||||
IpcClient::Interface()->enableKillSwitch(m_configData, 0);
|
||||
}
|
||||
#endif
|
||||
if (m_routeMode == 0) {
|
||||
IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "0.0.0.0/1");
|
||||
@@ -165,8 +169,11 @@ ErrorCode XrayProtocol::startTun2Sock()
|
||||
for (int i = 0; i < netInterfaces.size(); i++) {
|
||||
for (int j=0; j < netInterfaces.at(i).addressEntries().size(); j++)
|
||||
{
|
||||
// killSwitch toggle
|
||||
if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) {
|
||||
IpcClient::Interface()->enableKillSwitch(QJsonObject(), netInterfaces.at(i).index());
|
||||
if (QVariant(m_configData.value(config_key::killSwitchOption).toString()).toBool()) {
|
||||
IpcClient::Interface()->enableKillSwitch(QJsonObject(), netInterfaces.at(i).index());
|
||||
}
|
||||
m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index());
|
||||
m_configData.insert("vpnGateway", m_vpnGateway);
|
||||
m_configData.insert("vpnServer", m_remoteAddress);
|
||||
@@ -200,6 +207,7 @@ void XrayProtocol::stop()
|
||||
{
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
|
||||
IpcClient::Interface()->disableKillSwitch();
|
||||
IpcClient::Interface()->StartRoutingIpv6();
|
||||
#endif
|
||||
qDebug() << "XrayProtocol::stop()";
|
||||
m_xrayProcess.terminate();
|
||||
|
||||
@@ -256,7 +256,7 @@ Settings::RouteMode Settings::routeMode() const
|
||||
return static_cast<RouteMode>(value("Conf/routeMode", 0).toInt());
|
||||
}
|
||||
|
||||
bool Settings::getSitesSplitTunnelingEnabled() const
|
||||
bool Settings::isSitesSplitTunnelingEnabled() const
|
||||
{
|
||||
return value("Conf/sitesSplitTunnelingEnabled", false).toBool();
|
||||
}
|
||||
@@ -415,7 +415,7 @@ void Settings::setVpnApps(AppsRouteMode mode, const QVector<InstalledAppInfo> &a
|
||||
m_settings.sync();
|
||||
}
|
||||
|
||||
bool Settings::getAppsSplitTunnelingEnabled() const
|
||||
bool Settings::isAppsSplitTunnelingEnabled() const
|
||||
{
|
||||
return value("Conf/appsSplitTunnelingEnabled", false).toBool();
|
||||
}
|
||||
@@ -425,6 +425,16 @@ void Settings::setAppsSplitTunnelingEnabled(bool enabled)
|
||||
setValue("Conf/appsSplitTunnelingEnabled", enabled);
|
||||
}
|
||||
|
||||
bool Settings::isKillSwitchEnabled() const
|
||||
{
|
||||
return value("Conf/killSwitchEnabled", true).toBool();
|
||||
}
|
||||
|
||||
void Settings::setKillSwitchEnabled(bool enabled)
|
||||
{
|
||||
setValue("Conf/killSwitchEnabled", enabled);
|
||||
}
|
||||
|
||||
QString Settings::getInstallationUuid(const bool needCreate)
|
||||
{
|
||||
auto uuid = value("Conf/installationUuid", "").toString();
|
||||
|
||||
@@ -115,7 +115,7 @@ public:
|
||||
RouteMode routeMode() const;
|
||||
void setRouteMode(RouteMode mode) { setValue("Conf/routeMode", mode); }
|
||||
|
||||
bool getSitesSplitTunnelingEnabled() const;
|
||||
bool isSitesSplitTunnelingEnabled() const;
|
||||
void setSitesSplitTunnelingEnabled(bool enabled);
|
||||
|
||||
QVariantMap vpnSites(RouteMode mode) const
|
||||
@@ -211,9 +211,11 @@ public:
|
||||
QVector<InstalledAppInfo> getVpnApps(AppsRouteMode mode) const;
|
||||
void setVpnApps(AppsRouteMode mode, const QVector<InstalledAppInfo> &apps);
|
||||
|
||||
bool getAppsSplitTunnelingEnabled() const;
|
||||
bool isAppsSplitTunnelingEnabled() const;
|
||||
void setAppsSplitTunnelingEnabled(bool enabled);
|
||||
|
||||
bool isKillSwitchEnabled() const;
|
||||
void setKillSwitchEnabled(bool enabled);
|
||||
QString getInstallationUuid(const bool needCreate);
|
||||
|
||||
signals:
|
||||
|
||||
@@ -613,7 +613,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="384"/>
|
||||
<source>Remove OpenVpn from server?</source>
|
||||
<source>Remove OpenVPN from server?</source>
|
||||
<translation>احذف OpenVPN من الخادم?</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2104,8 +2104,8 @@ It's okay as long as it's from someone you trust.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="116"/>
|
||||
<source>OpenVpn native format</source>
|
||||
<translation>تنسيق OpenVpn الاصلي</translation>
|
||||
<source>OpenVPN native format</source>
|
||||
<translation>تنسيق OpenVPN الاصلي</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="121"/>
|
||||
@@ -2716,7 +2716,7 @@ It's okay as long as it's from someone you trust.</source>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="35"/>
|
||||
<source>OpenVPN config missing</source>
|
||||
<translation>OpenVpn تكوين مفقود</translation>
|
||||
<translation>OpenVPN تكوين مفقود</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="32"/>
|
||||
@@ -2726,12 +2726,12 @@ It's okay as long as it's from someone you trust.</source>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="36"/>
|
||||
<source>OpenVPN management server error</source>
|
||||
<translation>OpenVpn خطأ في إدارة الخادم</translation>
|
||||
<translation>OpenVPN خطأ في إدارة الخادم</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="39"/>
|
||||
<source>OpenVPN executable missing</source>
|
||||
<translation>OpenVpn executeable مفقود</translation>
|
||||
<translation>OpenVPN executable مفقود</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="40"/>
|
||||
@@ -2968,7 +2968,7 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
|
||||
* Recognised by DPI analysis systems and therefore susceptible to blocking
|
||||
* Can operate over both TCP and UDP network protocols.</source>
|
||||
<translation>يبقا OpenVPN كأحد اشهر بروتوكولات VPN و التي تم اختبارها عبر الزمن.
|
||||
ينشأ بروتوكول امان مميز, يستفيد من SSL/TLS للتشفير و تغير المفاتيح. واكثر من ذلك, OpenVpn يدعم تعدد طرق المصادقة يجعلة متعدد الاستخدامات وقابلة للتكيف, تلبية مجموعة واسعة من الأجهزة وأنظمة التشغيل. بسبب طبيعتة مفتوحة المصدر, يستفيد OpenVPN من التدقيق الشامل من قبل المجتمع العالمي, مما يعزز أمنها باستمرار. مع توازن قوي بين الأداء والأمان والتوافق, يظل OpenVPN الخيار الأفضل للأفراد والشركات المهتمين بالخصوصية على حدٍ سواء.
|
||||
ينشأ بروتوكول امان مميز, يستفيد من SSL/TLS للتشفير و تغير المفاتيح. واكثر من ذلك, OpenVPN يدعم تعدد طرق المصادقة يجعلة متعدد الاستخدامات وقابلة للتكيف, تلبية مجموعة واسعة من الأجهزة وأنظمة التشغيل. بسبب طبيعتة مفتوحة المصدر, يستفيد OpenVPN من التدقيق الشامل من قبل المجتمع العالمي, مما يعزز أمنها باستمرار. مع توازن قوي بين الأداء والأمان والتوافق, يظل OpenVPN الخيار الأفضل للأفراد والشركات المهتمين بالخصوصية على حدٍ سواء.
|
||||
|
||||
* مٌتاح في AmneziaVPN عبر جميع المنصات
|
||||
* استهلاك طاقة عادي علي اجهزة المحمول
|
||||
@@ -3330,7 +3330,7 @@ While it offers a blend of security, stability, and speed, it's essential t
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="313"/>
|
||||
<source>Medium or High</source>
|
||||
<source>High</source>
|
||||
<translation>متوسط او عالي</translation>
|
||||
</message>
|
||||
<message>
|
||||
|
||||
@@ -589,7 +589,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="384"/>
|
||||
<source>Remove OpenVpn from server?</source>
|
||||
<source>Remove OpenVPN from server?</source>
|
||||
<translation>آیا میخواهید OpenVPN از سرور حذف شود؟</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -1986,7 +1986,7 @@ It's okay as long as it's from someone you trust.</source>
|
||||
<name>PageShare</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="116"/>
|
||||
<source>OpenVpn native format</source>
|
||||
<source>OpenVPN native format</source>
|
||||
<translation>فرمت OpenVPN</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -3234,7 +3234,7 @@ For more detailed information, you can
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="313"/>
|
||||
<source>Medium or High</source>
|
||||
<source>High</source>
|
||||
<translation>متوسط یا بالا</translation>
|
||||
</message>
|
||||
<message>
|
||||
|
||||
3427
client/translations/amneziavpn_hi_IN.ts
Normal file
3427
client/translations/amneziavpn_hi_IN.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -589,7 +589,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="384"/>
|
||||
<source>Remove OpenVpn from server?</source>
|
||||
<source>Remove OpenVPN from server?</source>
|
||||
<translation>AmneziaWG ကို ဆာဗာမှ ဖယ်ရှားမည်လား?</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -1987,8 +1987,8 @@ It's okay as long as it's from someone you trust.</source>
|
||||
<name>PageShare</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="116"/>
|
||||
<source>OpenVpn native format</source>
|
||||
<translation>OpenVpn မူရင်းဖောမတ်</translation>
|
||||
<source>OpenVPN native format</source>
|
||||
<translation>OpenVPN မူရင်းဖောမတ်</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="121"/>
|
||||
@@ -3232,7 +3232,7 @@ For more detailed information, you can
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="313"/>
|
||||
<source>Medium or High</source>
|
||||
<source>High</source>
|
||||
<translation>Medium သို့မဟုတ် High</translation>
|
||||
</message>
|
||||
<message>
|
||||
|
||||
@@ -589,8 +589,8 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="384"/>
|
||||
<source>Remove OpenVpn from server?</source>
|
||||
<translation>Удалить OpenVpn с сервера?</translation>
|
||||
<source>Remove OpenVPN from server?</source>
|
||||
<translation>Удалить OpenVPN с сервера?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="385"/>
|
||||
@@ -1724,6 +1724,14 @@ Already installed containers were found on the server. All installed containers
|
||||
<translation>Добавить импортированные сайты к существующим</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PageSettingsAppSplitTunneling</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="26"/>
|
||||
<source>Cannot change split tunneling settings during active connection</source>
|
||||
<translation>Невозможно изменить настройки раздельного туннелирования при включенном VPN</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PageSetupWizardConfigSource</name>
|
||||
<message>
|
||||
@@ -2087,7 +2095,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
<name>PageShare</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="116"/>
|
||||
<source>OpenVpn native format</source>
|
||||
<source>OpenVPN native format</source>
|
||||
<translation>OpenVPN нативный формат</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2876,7 +2884,23 @@ If there is a extreme level of Internet censorship in your region, we advise you
|
||||
* Not recognised by DPI analysis systems
|
||||
* Works over TCP network protocol, 443 port.
|
||||
</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Это связка протокола OpenVPN и плагина Cloak, разработанная специально для защиты от блокировки.
|
||||
|
||||
OpenVPN обеспечивает безопасное VPN-соединение, шифруя весь интернет-трафик между клиентом и сервером.
|
||||
|
||||
Cloak защищает OpenVPN от обнаружения и блокировки.
|
||||
|
||||
Cloak изменяет метаданные пакетов таким образом, что полностью маскирует VPN-трафик под обычный веб-трафик, а также защищает VPN от обнаружения с помощью Active Probing. Это делает его очень защищенным от обнаружения
|
||||
|
||||
Сразу после получения первого пакета данных Cloak устанавливает подлинность входящего соединения. Если аутентификация не проходит, плагин маскирует сервер под фальшивый сайт, и ваш VPN становится невидимым для систем анализа.
|
||||
|
||||
Если в вашем регионе наблюдается жесткая интернет-цензура, мы советуем вам уже при первом подключении использовать только OpenVPN через Cloak.
|
||||
|
||||
* Доступен в AmneziaVPN на всех платформах
|
||||
* Высокое энергопотребление на мобильных устройствах
|
||||
* Гибкие настройки
|
||||
* Не распознается системами DPI-анализа
|
||||
* Работает по сетевому протоколу TCP, порт 443.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="174"/>
|
||||
@@ -2889,7 +2913,15 @@ WireGuard is very susceptible to blocking due to its distinct packet signatures.
|
||||
* Minimum number of settings
|
||||
* Easily recognised by DPI analysis systems, susceptible to blocking
|
||||
* Works over UDP network protocol.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Относительно новый и популярный VPN-протокол с простой архитектурой.
|
||||
WireGuard обеспечивает стабильное VPN-соединение и высокую производительность на всех устройствах. Он использует строго заданные настройки шифрования. WireGuard по сравнению с OpenVPN имеет меньшую задержку и лучшую пропускную способность при передаче данных.
|
||||
WireGuard очень уязвим для блокировки из-за характерных сигнатур пакетов. В отличие от некоторых других VPN-протоколов, использующих методы обфускации, последовательные сигнатуры пакетов WireGuard легче идентифицируются и, следовательно, могут блокироваться современными Deep Packet Inspection (DPI) системами и другими инструментами для сетевого мониторинга.
|
||||
|
||||
* Доступен в AmneziaVPN на всех платформах
|
||||
* Низкое энергопотребление
|
||||
* Минимальная конфигурация
|
||||
* Легко распознается системами DPI-анализа, поддается блокировке
|
||||
* Работает по сетевому протоколу UDP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="203"/>
|
||||
@@ -2950,16 +2982,21 @@ While it offers a blend of security, stability, and speed, it's essential t
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="115"/>
|
||||
<source>XRay with REALITY - Suitable for countries with the highest level of internet censorship. Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods.</source>
|
||||
<translation>XRay с REALITY - подойдет для стран с самым высоким уровнем цензуры в Интернете. Маскировка трафика под веб-трафик на уровне TLS и защита от обнаружения активными методами прослушивания.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="121"/>
|
||||
<source>IKEv2 - Modern stable protocol, a bit faster than others, restores connection after signal loss. It has native support on the latest versions of Android and iOS.</source>
|
||||
<translation>IKEv2 Современный стабильный протокол, немного быстрее других восстанавливает соединение после потери сигнала. Имеет нативную поддержку последних версиий Android и iOS.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="118"/>
|
||||
<location filename="../containers/containers_defs.cpp" line="124"/>
|
||||
<source>Deploy a WordPress site on the Tor network in two clicks.</source>
|
||||
<translation>Разверните сайт на WordPress в сети Tor в два клика.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="120"/>
|
||||
<location filename="../containers/containers_defs.cpp" line="126"/>
|
||||
<source>Replace the current DNS server with your own. This will increase your privacy level.</source>
|
||||
<translation>Замените DNS-сервер на Amnezia DNS. Это повысит уровень конфиденциальности.</translation>
|
||||
</message>
|
||||
@@ -2998,6 +3035,17 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
|
||||
* Настраиваемый протокол шифрования
|
||||
* Обнаруживается некоторыми DPI-системами
|
||||
* Работает по сетевому протоколу TCP.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="203"/>
|
||||
<source>The REALITY protocol, a pioneering development by the creators of XRay, is specifically designed to counteract the highest levels of internet censorship through its novel approach to evasion.
|
||||
It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, thus presenting an authentic TLS certificate and data.
|
||||
This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, legitimate sites without the need for specific configurations.
|
||||
Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, REALITY's innovative "friend or foe" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship.</source>
|
||||
<translation>Протокол REALITY, новаторская разработка создателей XRay, специально разработан для противодействия самым строгим мерам интернет-цензуры благодаря новому подходу к обходу.
|
||||
Он уникальным образом идентифицирует цензоров на этапе TLS рукопожатия, беспрепятственно работая в качестве прокси для реальных клиентов и перенаправляя цензоров на сайты, такие как google.com, тем самым представляя подлинный TLS сертификат и данные.
|
||||
Эта передовая способность отличает REALITY от аналогичных технологий благодаря способности маскировать веб-трафик так, как будто он поступает со случайных, легитимных сайтов, без необходимости специальной настройки.
|
||||
В отличие от более старых протоколов, таких как VMess, VLESS и XTLS-Vision, технология распознавания REALITY "друг или враг" на этапе рукопожатия TLS повышает надежность и обходит обнаружение сложными системами DPI, которые используют методы активного прослушивания. Это делает REALITY эффективным решением для поддержания свободы интернета в странах с жесткой цензурой.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="217"/>
|
||||
@@ -3395,8 +3443,8 @@ This means that AmneziaWG keeps the fast performance of the original while addin
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="313"/>
|
||||
<source>Medium or High</source>
|
||||
<translation>Средний или Высокий</translation>
|
||||
<source>High</source>
|
||||
<translation>Высокий</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="314"/>
|
||||
|
||||
@@ -692,7 +692,7 @@ Already installed containers were found on the server. All installed containers
|
||||
<translation type="vanished">Видалити OpenVPN</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove OpenVpn from server?</source>
|
||||
<source>Remove OpenVPN from server?</source>
|
||||
<translation type="vanished">Видалити OpenVPN з серверу?</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -1176,12 +1176,12 @@ Already installed containers were found on the server. All installed containers
|
||||
<name>PageSettingsAppSplitTunneling</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="37"/>
|
||||
<source>Only the Apps listed here will be accessed through the VPN</source>
|
||||
<source>Only the apps from the list should have access via VPN</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppSplitTunneling.qml" line="42"/>
|
||||
<source>Apps from the list should not be accessed via VPN</source>
|
||||
<source>Apps from the list should not have access via VPN</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2301,7 +2301,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
<name>PageShare</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="125"/>
|
||||
<source>OpenVpn native format</source>
|
||||
<source>OpenVPN native format</source>
|
||||
<translation>OpenVPN нативний формат</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -3627,8 +3627,8 @@ This means that AmneziaWG keeps the fast performance of the original while addin
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="333"/>
|
||||
<source>Medium or High</source>
|
||||
<translation>Середній або високий</translation>
|
||||
<source>High</source>
|
||||
<translation>Високий</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="334"/>
|
||||
|
||||
3335
client/translations/amneziavpn_ur_PK.ts
Normal file
3335
client/translations/amneziavpn_ur_PK.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -611,7 +611,7 @@ Already installed containers were found on the server. All installed containers
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolOpenVpnSettings.qml" line="384"/>
|
||||
<source>Remove OpenVpn from server?</source>
|
||||
<source>Remove OpenVPN from server?</source>
|
||||
<translation>从服务器移除OpenVPN吗?</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -2182,7 +2182,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="116"/>
|
||||
<source>OpenVpn native format</source>
|
||||
<source>OpenVPN native format</source>
|
||||
<translation>OpenVPN原生格式</translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -3216,12 +3216,12 @@ While it offers a blend of security, stability, and speed, it's essential t
|
||||
<translation type="vanished">OpenVPN容器</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Container with OpenVpn and ShadowSocks</source>
|
||||
<translation type="vanished">含 OpenVpn 和 ShadowSocks 的容器</translation>
|
||||
<source>Container with OpenVPN and ShadowSocks</source>
|
||||
<translation type="vanished">含 OpenVPN 和 ShadowSocks 的容器</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Container with OpenVpn and ShadowSocks protocols configured with traffic masking by Cloak plugin</source>
|
||||
<translation type="vanished">含 OpenVpn 和 ShadowSocks 协议的容器,通过 Cloak 插件配置混淆流量</translation>
|
||||
<source>Container with OpenVPN and ShadowSocks protocols configured with traffic masking by Cloak plugin</source>
|
||||
<translation type="vanished">含 OpenVPN 和 ShadowSocks 协议的容器,通过 Cloak 插件配置混淆流量</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>WireGuard container</source>
|
||||
@@ -3529,7 +3529,7 @@ While it offers a blend of security, stability, and speed, it's essential t
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../containers/containers_defs.cpp" line="313"/>
|
||||
<source>Medium or High</source>
|
||||
<source>High</source>
|
||||
<translation>中或高</translation>
|
||||
</message>
|
||||
<message>
|
||||
|
||||
@@ -29,7 +29,6 @@ void AppSplitTunnelingController::addApp(const QString &appPath)
|
||||
|
||||
void AppSplitTunnelingController::addApps(QVector<QPair<QString, QString>> apps)
|
||||
{
|
||||
qDebug() << apps;
|
||||
for (const auto &app : apps) {
|
||||
InstalledAppInfo appInfo { app.first, app.second, "" };
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QQuickItem>
|
||||
#include <QStandardPaths>
|
||||
#include <QRandomGenerator>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include "core/errorstrings.h"
|
||||
#ifdef Q_OS_ANDROID
|
||||
@@ -103,7 +103,11 @@ bool ImportController::extractConfigFromData(QString data)
|
||||
switch (m_configType) {
|
||||
case ConfigTypes::OpenVpn: {
|
||||
m_config = extractOpenVpnConfig(config);
|
||||
return m_config.empty() ? false : true;
|
||||
if (!m_config.empty()) {
|
||||
checkForMaliciousStrings(m_config);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case ConfigTypes::Awg:
|
||||
case ConfigTypes::WireGuard: {
|
||||
@@ -116,7 +120,11 @@ bool ImportController::extractConfigFromData(QString data)
|
||||
}
|
||||
case ConfigTypes::Amnezia: {
|
||||
m_config = QJsonDocument::fromJson(config.toUtf8()).object();
|
||||
return m_config.empty() ? false : true;
|
||||
if (!m_config.empty()) {
|
||||
checkForMaliciousStrings(m_config);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case ConfigTypes::Backup: {
|
||||
if (!m_serversModel->getServersCount()) {
|
||||
@@ -161,6 +169,11 @@ QString ImportController::getConfigFileName()
|
||||
return m_configFileName;
|
||||
}
|
||||
|
||||
QString ImportController::getMaliciousWarningText()
|
||||
{
|
||||
return m_maliciousWarningText;
|
||||
}
|
||||
|
||||
bool ImportController::isNativeWireGuardConfig()
|
||||
{
|
||||
return m_configType == ConfigTypes::WireGuard;
|
||||
@@ -223,6 +236,7 @@ void ImportController::importConfig()
|
||||
|
||||
m_config = {};
|
||||
m_configFileName.clear();
|
||||
m_maliciousWarningText.clear();
|
||||
}
|
||||
|
||||
QJsonObject ImportController::extractOpenVpnConfig(const QString &data)
|
||||
@@ -526,3 +540,43 @@ QString ImportController::getQrCodeScanProgressString()
|
||||
return tr("Scanned %1 of %2.").arg(m_receivedQrCodeChunksCount).arg(m_totalQrCodeChunksCount);
|
||||
}
|
||||
#endif
|
||||
|
||||
void ImportController::checkForMaliciousStrings(const QJsonObject &serverConfig)
|
||||
{
|
||||
const QJsonArray &containers = serverConfig[config_key::containers].toArray();
|
||||
for (const QJsonValue &container : containers) {
|
||||
auto containerConfig = container.toObject();
|
||||
auto containerName = containerConfig[config_key::container].toString();
|
||||
if ((containerName == ContainerProps::containerToString(DockerContainer::OpenVpn))
|
||||
|| (containerName == ContainerProps::containerToString(DockerContainer::Cloak))
|
||||
|| (containerName == ContainerProps::containerToString(DockerContainer::ShadowSocks))) {
|
||||
QString protocolConfig =
|
||||
containerConfig[ProtocolProps::protoToString(Proto::OpenVpn)].toObject()[config_key::last_config].toString();
|
||||
QString protocolConfigJson = QJsonDocument::fromJson(protocolConfig.toUtf8()).object()[config_key::config].toString();
|
||||
|
||||
const QRegularExpression regExp { "(\\w+-\\w+|\\w+)" };
|
||||
const size_t dangerousTagsMaxCount = 3;
|
||||
|
||||
// https://github.com/OpenVPN/openvpn/blob/master/doc/man-sections/script-options.rst
|
||||
QStringList dangerousTags {
|
||||
"up", "tls-verify", "ipchange", "client-connect", "route-up", "route-pre-down", "client-disconnect", "down", "learn-address", "auth-user-pass-verify"
|
||||
};
|
||||
|
||||
QStringList maliciousStrings;
|
||||
QStringList lines = protocolConfigJson.replace("\r", "").split("\n");
|
||||
for (const QString &l : lines) {
|
||||
QRegularExpressionMatch match = regExp.match(l);
|
||||
if (dangerousTags.contains(match.captured(0))) {
|
||||
maliciousStrings << l;
|
||||
}
|
||||
}
|
||||
|
||||
if (maliciousStrings.size() >= dangerousTagsMaxCount) {
|
||||
m_maliciousWarningText = tr("In the imported configuration, potentially dangerous lines were found:");
|
||||
for (const auto &string : maliciousStrings) {
|
||||
m_maliciousWarningText.push_back(QString("<br><i>%1</i>").arg(string));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ public slots:
|
||||
bool extractConfigFromQr(const QByteArray &data);
|
||||
QString getConfig();
|
||||
QString getConfigFileName();
|
||||
QString getMaliciousWarningText();
|
||||
|
||||
#if defined Q_OS_ANDROID || defined Q_OS_IOS
|
||||
void startDecodingQr();
|
||||
@@ -63,6 +64,8 @@ private:
|
||||
QJsonObject extractWireGuardConfig(const QString &data);
|
||||
QJsonObject extractXrayConfig(const QString &data);
|
||||
|
||||
void checkForMaliciousStrings(const QJsonObject &protocolConfig);
|
||||
|
||||
#if defined Q_OS_ANDROID || defined Q_OS_IOS
|
||||
void stopDecodingQr();
|
||||
#endif
|
||||
@@ -74,6 +77,7 @@ private:
|
||||
QJsonObject m_config;
|
||||
QString m_configFileName;
|
||||
ConfigTypes m_configType;
|
||||
QString m_maliciousWarningText;
|
||||
|
||||
#if defined Q_OS_ANDROID || defined Q_OS_IOS
|
||||
QMap<int, QByteArray> m_qrCodeChunks;
|
||||
|
||||
@@ -77,8 +77,6 @@ void SettingsController::toggleLogging(bool enable)
|
||||
AmneziaVPN::toggleLogging(enable);
|
||||
#endif
|
||||
if (enable == true) {
|
||||
checkIfNeedDisableLogs();
|
||||
|
||||
qInfo().noquote() << QString("Logging has enabled on %1 version %2 %3").arg(APPLICATION_NAME, APP_VERSION, GIT_COMMIT_HASH);
|
||||
qInfo().noquote() << QString("%1 (%2)").arg(QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture());
|
||||
}
|
||||
@@ -216,10 +214,22 @@ bool SettingsController::isCameraPresent()
|
||||
|
||||
void SettingsController::checkIfNeedDisableLogs()
|
||||
{
|
||||
m_loggingDisableDate = m_settings->getLogEnableDate().addDays(14);
|
||||
if (m_loggingDisableDate <= QDateTime::currentDateTime()) {
|
||||
toggleLogging(false);
|
||||
clearLogs();
|
||||
emit loggingDisableByWatcher();
|
||||
if (m_settings->isSaveLogs()) {
|
||||
m_loggingDisableDate = m_settings->getLogEnableDate().addDays(14);
|
||||
if (m_loggingDisableDate <= QDateTime::currentDateTime()) {
|
||||
toggleLogging(false);
|
||||
clearLogs();
|
||||
emit loggingDisableByWatcher();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SettingsController::isKillSwitchEnabled()
|
||||
{
|
||||
return m_settings->isKillSwitchEnabled();
|
||||
}
|
||||
|
||||
void SettingsController::toggleKillSwitch(bool enable)
|
||||
{
|
||||
m_settings->setKillSwitchEnabled(enable);
|
||||
}
|
||||
|
||||
@@ -63,6 +63,9 @@ public slots:
|
||||
|
||||
bool isCameraPresent();
|
||||
|
||||
bool isKillSwitchEnabled();
|
||||
void toggleKillSwitch(bool enable);
|
||||
|
||||
signals:
|
||||
void primaryDnsChanged();
|
||||
void secondaryDnsChanged();
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
AppSplitTunnelingModel::AppSplitTunnelingModel(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: QAbstractListModel(parent), m_settings(settings)
|
||||
{
|
||||
m_isSplitTunnelingEnabled = m_settings->getAppsSplitTunnelingEnabled();
|
||||
m_isSplitTunnelingEnabled = m_settings->isAppsSplitTunnelingEnabled();
|
||||
m_currentRouteMode = m_settings->getAppsRouteMode();
|
||||
if (m_currentRouteMode == Settings::VpnAllApps) { // for old split tunneling configs
|
||||
m_settings->setAppsRouteMode(static_cast<Settings::AppsRouteMode>(Settings::VpnAllExceptApps));
|
||||
@@ -48,7 +48,6 @@ bool AppSplitTunnelingModel::addApp(const InstalledAppInfo &appInfo)
|
||||
m_settings->setVpnApps(m_currentRouteMode, m_apps);
|
||||
endInsertRows();
|
||||
|
||||
qDebug() << "app added " << appInfo.appName;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@ namespace
|
||||
constexpr char container[] = "container";
|
||||
constexpr char userData[] = "userData";
|
||||
constexpr char creationDate[] = "creationDate";
|
||||
constexpr char latestHandshake[] = "latestHandshake";
|
||||
constexpr char dataReceived[] = "dataReceived";
|
||||
constexpr char dataSent[] = "dataSent";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +46,9 @@ QVariant ClientManagementModel::data(const QModelIndex &index, int role) const
|
||||
switch (role) {
|
||||
case ClientNameRole: return userData.value(configKey::clientName).toString();
|
||||
case CreationDateRole: return userData.value(configKey::creationDate).toString();
|
||||
case LatestHandshakeRole: return userData.value(configKey::latestHandshake).toString();
|
||||
case DataReceivedRole: return userData.value(configKey::dataReceived).toString();
|
||||
case DataSentRole: return userData.value(configKey::dataSent).toString();
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
@@ -112,6 +118,38 @@ ErrorCode ClientManagementModel::updateModel(const DockerContainer container, co
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<WgShowData> data;
|
||||
wgShow(container, credentials, serverController, data);
|
||||
|
||||
for (const auto &client : data) {
|
||||
int i = 0;
|
||||
for (const auto &it : std::as_const(m_clientsTable)) {
|
||||
if (it.isObject()) {
|
||||
QJsonObject obj = it.toObject();
|
||||
if (obj.contains(configKey::clientId) && obj[configKey::clientId].toString() == client.clientId) {
|
||||
QJsonObject userData = obj[configKey::userData].toObject();
|
||||
|
||||
if (!client.latestHandshake.isEmpty()) {
|
||||
userData[configKey::latestHandshake] = client.latestHandshake;
|
||||
}
|
||||
|
||||
if (!client.dataReceived.isEmpty()) {
|
||||
userData[configKey::dataReceived] = client.dataReceived;
|
||||
}
|
||||
|
||||
if (!client.dataSent.isEmpty()) {
|
||||
userData[configKey::dataSent] = client.dataSent;
|
||||
}
|
||||
|
||||
obj[configKey::userData] = userData;
|
||||
m_clientsTable.replace(i, obj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
return error;
|
||||
}
|
||||
@@ -195,6 +233,71 @@ ErrorCode ClientManagementModel::getWireGuardClients(const DockerContainer conta
|
||||
return error;
|
||||
}
|
||||
|
||||
ErrorCode ClientManagementModel::wgShow(const DockerContainer container, const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController, std::vector<WgShowData> &data)
|
||||
{
|
||||
if (container != DockerContainer::WireGuard && container != DockerContainer::Awg) {
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
ErrorCode error = ErrorCode::NoError;
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||
stdOut += data + "\n";
|
||||
return ErrorCode::NoError;
|
||||
};
|
||||
|
||||
const QString command = QString("sudo docker exec -i $CONTAINER_NAME bash -c '%1'").arg("wg show all");
|
||||
|
||||
QString script = serverController->replaceVars(command, serverController->genVarsForScript(credentials, container));
|
||||
error = serverController->runScript(credentials, script, cbReadStdOut);
|
||||
if (error != ErrorCode::NoError) {
|
||||
logger.error() << "Failed to execute wg show command";
|
||||
return error;
|
||||
}
|
||||
|
||||
if (stdOut.isEmpty()) {
|
||||
return error;
|
||||
}
|
||||
|
||||
const auto getStrValue = [](const auto str) { return str.mid(str.indexOf(":") + 1).trimmed(); };
|
||||
|
||||
const auto parts = stdOut.split('\n');
|
||||
const auto peerList = parts.filter("peer:");
|
||||
const auto latestHandshakeList = parts.filter("latest handshake:");
|
||||
const auto transferredDataList = parts.filter("transfer:");
|
||||
|
||||
if (latestHandshakeList.isEmpty() || transferredDataList.isEmpty() || peerList.isEmpty()) {
|
||||
return error;
|
||||
}
|
||||
|
||||
const auto changeHandshakeFormat = [](QString &latestHandshake) {
|
||||
const std::vector<std::pair<QString, QString>> replaceMap = { { " days", "d" }, { " hours", "h" }, { " minutes", "m" },
|
||||
{ " seconds", "s" }, { " day", "d" }, { " hour", "h" },
|
||||
{ " minute", "m" }, { " second", "s" } };
|
||||
|
||||
for (const auto &item : replaceMap) {
|
||||
latestHandshake.replace(item.first, item.second);
|
||||
}
|
||||
};
|
||||
|
||||
for (int i = 0; i < peerList.size() && i < transferredDataList.size(); ++i) {
|
||||
const auto transferredData = getStrValue(transferredDataList[i]).split(",");
|
||||
auto latestHandshake = getStrValue(latestHandshakeList[i]);
|
||||
auto serverBytesReceived = transferredData.front().trimmed();
|
||||
auto serverBytesSent = transferredData.back().trimmed();
|
||||
|
||||
changeHandshakeFormat(latestHandshake);
|
||||
|
||||
serverBytesReceived.chop(QStringLiteral(" received").length());
|
||||
serverBytesSent.chop(QStringLiteral(" sent").length());
|
||||
|
||||
data.push_back({ getStrValue(peerList[i]), latestHandshake, serverBytesSent, serverBytesReceived });
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
bool ClientManagementModel::isClientExists(const QString &clientId)
|
||||
{
|
||||
for (const QJsonValue &value : std::as_const(m_clientsTable)) {
|
||||
@@ -486,5 +589,8 @@ QHash<int, QByteArray> ClientManagementModel::roleNames() const
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[ClientNameRole] = "clientName";
|
||||
roles[CreationDateRole] = "creationDate";
|
||||
roles[LatestHandshakeRole] = "latestHandshake";
|
||||
roles[DataReceivedRole] = "dataReceived";
|
||||
roles[DataSentRole] = "dataSent";
|
||||
return roles;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,18 @@ class ClientManagementModel : public QAbstractListModel
|
||||
public:
|
||||
enum Roles {
|
||||
ClientNameRole = Qt::UserRole + 1,
|
||||
CreationDateRole
|
||||
CreationDateRole,
|
||||
LatestHandshakeRole,
|
||||
DataReceivedRole,
|
||||
DataSentRole
|
||||
};
|
||||
|
||||
struct WgShowData
|
||||
{
|
||||
QString clientId;
|
||||
QString latestHandshake;
|
||||
QString dataReceived;
|
||||
QString dataSent;
|
||||
};
|
||||
|
||||
ClientManagementModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||
@@ -57,6 +68,9 @@ private:
|
||||
ErrorCode getWireGuardClients(const DockerContainer container, const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController, int &count);
|
||||
|
||||
ErrorCode wgShow(const DockerContainer container, const ServerCredentials &credentials,
|
||||
const QSharedPointer<ServerController> &serverController, std::vector<WgShowData> &data);
|
||||
|
||||
QJsonArray m_clientsTable;
|
||||
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
|
||||
@@ -48,6 +48,8 @@ QString LanguageModel::getLocalLanguageName(const LanguageSettings::AvailableLan
|
||||
case LanguageSettings::AvailableLanguageEnum::Persian: strLanguage = "فارسی"; break;
|
||||
case LanguageSettings::AvailableLanguageEnum::Arabic: strLanguage = "العربية"; break;
|
||||
case LanguageSettings::AvailableLanguageEnum::Burmese: strLanguage = "မြန်မာဘာသာ"; break;
|
||||
case LanguageSettings::AvailableLanguageEnum::Urdu: strLanguage = "اُرْدُوْ"; break;
|
||||
case LanguageSettings::AvailableLanguageEnum::Hindi: strLanguage = "हिन्दी"; break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -65,6 +67,8 @@ void LanguageModel::changeLanguage(const LanguageSettings::AvailableLanguageEnum
|
||||
case LanguageSettings::AvailableLanguageEnum::Persian: emit updateTranslations(QLocale::Persian); break;
|
||||
case LanguageSettings::AvailableLanguageEnum::Arabic: emit updateTranslations(QLocale::Arabic); break;
|
||||
case LanguageSettings::AvailableLanguageEnum::Burmese: emit updateTranslations(QLocale::Burmese); break;
|
||||
case LanguageSettings::AvailableLanguageEnum::Urdu: emit updateTranslations(QLocale::Urdu); break;
|
||||
case LanguageSettings::AvailableLanguageEnum::Hindi: emit updateTranslations(QLocale::Hindi); break;
|
||||
default: emit updateTranslations(QLocale::English); break;
|
||||
}
|
||||
}
|
||||
@@ -80,6 +84,8 @@ int LanguageModel::getCurrentLanguageIndex()
|
||||
case QLocale::Persian: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Persian); break;
|
||||
case QLocale::Arabic: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Arabic); break;
|
||||
case QLocale::Burmese: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Burmese); break;
|
||||
case QLocale::Urdu: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Urdu); break;
|
||||
case QLocale::Hindi: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::Hindi); break;
|
||||
default: return static_cast<int>(LanguageSettings::AvailableLanguageEnum::English); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@ namespace LanguageSettings
|
||||
Ukrainian,
|
||||
Persian,
|
||||
Arabic,
|
||||
Burmese
|
||||
Burmese,
|
||||
Urdu,
|
||||
Hindi
|
||||
};
|
||||
Q_ENUM_NS(AvailableLanguageEnum)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
SitesModel::SitesModel(std::shared_ptr<Settings> settings, QObject *parent)
|
||||
: QAbstractListModel(parent), m_settings(settings)
|
||||
{
|
||||
m_isSplitTunnelingEnabled = m_settings->getSitesSplitTunnelingEnabled();
|
||||
m_isSplitTunnelingEnabled = m_settings->isSitesSplitTunnelingEnabled();
|
||||
m_currentRouteMode = m_settings->routeMode();
|
||||
if (m_currentRouteMode == Settings::VpnAllSites) { // for old split tunneling configs
|
||||
m_settings->setRouteMode(static_cast<Settings::RouteMode>(Settings::VpnOnlyForwardSites));
|
||||
|
||||
@@ -15,11 +15,13 @@ Item {
|
||||
|
||||
property var clickedFunction
|
||||
|
||||
property string buttonImageSource
|
||||
property string rightImageSource
|
||||
property string leftImageSource
|
||||
property bool isLeftImageHoverEnabled: true //todo separete this qml file to 3
|
||||
|
||||
property alias rightButton: rightImage
|
||||
property alias eyeButton: eyeImage
|
||||
property FlickableType parentFlickable
|
||||
|
||||
property string textColor: "#d7d8db"
|
||||
@@ -34,6 +36,7 @@ Item {
|
||||
property string rightImageColor: "#d7d8db"
|
||||
|
||||
property bool descriptionOnTop: false
|
||||
property bool hideDescription: true
|
||||
|
||||
implicitWidth: content.implicitWidth + content.anchors.topMargin + content.anchors.bottomMargin
|
||||
implicitHeight: content.implicitHeight + content.anchors.leftMargin + content.anchors.rightMargin
|
||||
@@ -57,6 +60,45 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: root.enabled
|
||||
|
||||
onEntered: {
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = rightImage.hoveredColor
|
||||
} else if (leftImageSource) {
|
||||
leftImageBackground.color = rightImage.hoveredColor
|
||||
}
|
||||
root.textOpacity = 0.8
|
||||
}
|
||||
|
||||
onExited: {
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = rightImage.defaultColor
|
||||
} else if (leftImageSource) {
|
||||
leftImageBackground.color = rightImage.defaultColor
|
||||
}
|
||||
root.textOpacity = 1
|
||||
}
|
||||
|
||||
onPressedChanged: {
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
|
||||
} else if (leftImageSource) {
|
||||
leftImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
|
||||
}
|
||||
root.textOpacity = 0.7
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (clickedFunction && typeof clickedFunction === "function") {
|
||||
clickedFunction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: content
|
||||
anchors.fill: parent
|
||||
@@ -129,7 +171,7 @@ Item {
|
||||
CaptionTextType {
|
||||
id: description
|
||||
|
||||
text: root.descriptionText
|
||||
text: (eyeImage.visible && hideDescription) ? replaceWithAsterisks(root.descriptionText) : root.descriptionText
|
||||
color: {
|
||||
if (root.enabled) {
|
||||
return root.descriptionOnTop ? root.textColor : root.descriptionColor
|
||||
@@ -154,6 +196,47 @@ Item {
|
||||
Behavior on opacity {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
function replaceWithAsterisks(input) {
|
||||
return '*'.repeat(input.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: eyeImage
|
||||
visible: buttonImageSource !== ""
|
||||
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
hoverEnabled: true
|
||||
image: buttonImageSource
|
||||
imageColor: rightImageColor
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
Rectangle {
|
||||
id: eyeImageBackground
|
||||
anchors.fill: parent
|
||||
radius: 12
|
||||
color: "transparent"
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
hideDescription = !hideDescription
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
clicked()
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
clicked()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,6 +263,11 @@ Item {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
if (clickedFunction && typeof clickedFunction === "function") {
|
||||
clickedFunction()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,45 +285,6 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: root.enabled
|
||||
|
||||
onEntered: {
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = rightImage.hoveredColor
|
||||
} else if (leftImageSource) {
|
||||
leftImageBackground.color = rightImage.hoveredColor
|
||||
}
|
||||
root.textOpacity = 0.8
|
||||
}
|
||||
|
||||
onExited: {
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = rightImage.defaultColor
|
||||
} else if (leftImageSource) {
|
||||
leftImageBackground.color = rightImage.defaultColor
|
||||
}
|
||||
root.textOpacity = 1
|
||||
}
|
||||
|
||||
onPressedChanged: {
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
|
||||
} else if (leftImageSource) {
|
||||
leftImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
|
||||
}
|
||||
root.textOpacity = 0.7
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (clickedFunction && typeof clickedFunction === "function") {
|
||||
clickedFunction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
if (clickedFunction && typeof clickedFunction === "function") {
|
||||
clickedFunction()
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property string textColor: "#D7D8DB"
|
||||
property string backGroundColor: "#1C1D21"
|
||||
property string imageColor: "#D7D8DB"
|
||||
property string textString
|
||||
property int textFormat: Text.PlainText
|
||||
|
||||
property string iconPath
|
||||
property real iconWidth: 16
|
||||
@@ -36,6 +41,13 @@ Rectangle {
|
||||
height: iconHeight
|
||||
|
||||
source: iconPath
|
||||
|
||||
layer {
|
||||
enabled: true
|
||||
effect: ColorOverlay {
|
||||
color: imageColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CaptionTextType {
|
||||
@@ -45,6 +57,7 @@ Rectangle {
|
||||
Layout.leftMargin: 8
|
||||
|
||||
text: textString
|
||||
textFormat: root.textFormat
|
||||
color: textColor
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ PageType {
|
||||
descriptionOnTop: true
|
||||
|
||||
parentFlickable: fl
|
||||
KeyNavigation.tab: passwordLabel.rightButton
|
||||
KeyNavigation.tab: passwordLabel.eyeButton
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: "#D7D8DB"
|
||||
@@ -183,7 +183,8 @@ PageType {
|
||||
descriptionOnTop: true
|
||||
|
||||
parentFlickable: fl
|
||||
Keys.onTabPressed: {
|
||||
eyeButton.KeyNavigation.tab: passwordLabel.rightButton
|
||||
rightButton.Keys.onTabPressed: {
|
||||
if (mountButton.visible) {
|
||||
mountButton.forceActiveFocus()
|
||||
} else {
|
||||
@@ -194,6 +195,8 @@ PageType {
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: "#D7D8DB"
|
||||
|
||||
buttonImageSource: hideDescription ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
|
||||
@@ -47,12 +47,12 @@ PageType {
|
||||
|
||||
QtObject {
|
||||
id: onlyForwardApps
|
||||
property string name: qsTr("Only the Apps listed here will be accessed through the VPN")
|
||||
property string name: qsTr("Only the apps from the list should have access via VPN")
|
||||
property int type: routeMode.onlyForwardApps
|
||||
}
|
||||
QtObject {
|
||||
id: allExceptApps
|
||||
property string name: qsTr("Apps from the list should not be accessed via VPN")
|
||||
property string name: qsTr("Apps from the list should not have access via VPN")
|
||||
property int type: routeMode.allExceptApps
|
||||
}
|
||||
|
||||
|
||||
@@ -15,43 +15,9 @@ PageType {
|
||||
|
||||
defaultActiveFocusItem: focusItem
|
||||
|
||||
function getNextComponentInFocusChain(componentId) {
|
||||
const componentsList = [focusItem,
|
||||
backButton,
|
||||
switcher,
|
||||
switcherAutoStart,
|
||||
switcherAutoConnect,
|
||||
switcherStartMinimized,
|
||||
labelWithButtonLanguage,
|
||||
labelWithButtonLogging,
|
||||
labelWithButtonReset,
|
||||
]
|
||||
|
||||
const idx = componentsList.indexOf(componentId)
|
||||
|
||||
if (idx === -1) {
|
||||
return null
|
||||
}
|
||||
|
||||
let nextIndex = idx + 1
|
||||
if (nextIndex >= componentsList.length) {
|
||||
nextIndex = 0
|
||||
}
|
||||
|
||||
if (componentsList[nextIndex].visible) {
|
||||
if ((nextIndex) >= 6) {
|
||||
return componentsList[nextIndex].rightButton
|
||||
} else {
|
||||
return componentsList[nextIndex]
|
||||
}
|
||||
} else {
|
||||
return getNextComponentInFocusChain(componentsList[nextIndex])
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: focusItem
|
||||
KeyNavigation.tab: root.getNextComponentInFocusChain(focusItem)
|
||||
KeyNavigation.tab: backButton
|
||||
|
||||
onFocusChanged: {
|
||||
if (focusItem.activeFocus) {
|
||||
@@ -68,7 +34,7 @@ PageType {
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20
|
||||
|
||||
KeyNavigation.tab: root.getNextComponentInFocusChain(backButton)
|
||||
KeyNavigation.tab: GC.isMobile() ? switcher : switcherAutoStart
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
@@ -108,7 +74,7 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
KeyNavigation.tab: root.getNextComponentInFocusChain(switcher)
|
||||
KeyNavigation.tab: labelWithButtonLanguage.rightButton
|
||||
parentFlickable: fl
|
||||
}
|
||||
|
||||
@@ -126,7 +92,7 @@ PageType {
|
||||
text: qsTr("Auto start")
|
||||
descriptionText: qsTr("Launch the application every time the device is starts")
|
||||
|
||||
KeyNavigation.tab: root.getNextComponentInFocusChain(switcherAutoStart)
|
||||
KeyNavigation.tab: switcherAutoConnect
|
||||
parentFlickable: fl
|
||||
|
||||
checked: SettingsController.isAutoStartEnabled()
|
||||
@@ -151,7 +117,7 @@ PageType {
|
||||
text: qsTr("Auto connect")
|
||||
descriptionText: qsTr("Connect to VPN on app start")
|
||||
|
||||
KeyNavigation.tab: root.getNextComponentInFocusChain(switcherAutoConnect)
|
||||
KeyNavigation.tab: switcherStartMinimized
|
||||
parentFlickable: fl
|
||||
|
||||
checked: SettingsController.isAutoConnectEnabled()
|
||||
@@ -176,7 +142,7 @@ PageType {
|
||||
text: qsTr("Start minimized")
|
||||
descriptionText: qsTr("Launch application minimized")
|
||||
|
||||
KeyNavigation.tab: root.getNextComponentInFocusChain(switcherStartMinimized)
|
||||
KeyNavigation.tab: labelWithButtonLanguage.rightButton
|
||||
parentFlickable: fl
|
||||
|
||||
checked: SettingsController.isStartMinimizedEnabled()
|
||||
@@ -199,7 +165,7 @@ PageType {
|
||||
descriptionText: LanguageModel.currentLanguageName
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
KeyNavigation.tab: root.getNextComponentInFocusChain(labelWithButtonLanguage)
|
||||
KeyNavigation.tab: labelWithButtonLogging.rightButton
|
||||
parentFlickable: fl
|
||||
|
||||
clickedFunction: function() {
|
||||
@@ -218,7 +184,7 @@ PageType {
|
||||
descriptionText: SettingsController.isLoggingEnabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
KeyNavigation.tab: root.getNextComponentInFocusChain(labelWithButtonLogging)
|
||||
KeyNavigation.tab: labelWithButtonReset.rightButton
|
||||
parentFlickable: fl
|
||||
|
||||
clickedFunction: function() {
|
||||
|
||||
@@ -149,10 +149,9 @@ PageType {
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
|
||||
if (ConnectionController.isConnected) {
|
||||
PageController.showNotificationMessage(qsTr("Cannot restore backup settings during active connection"))
|
||||
} else
|
||||
{
|
||||
} else {
|
||||
PageController.showBusyIndicator(true)
|
||||
SettingsController.restoreAppConfig(filePath)
|
||||
PageController.showBusyIndicator(false)
|
||||
|
||||
@@ -101,9 +101,15 @@ PageType {
|
||||
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
|
||||
}
|
||||
|
||||
Keys.onTabPressed: splitTunnelingButton2.visible ?
|
||||
splitTunnelingButton2.forceActiveFocus() :
|
||||
lastItemTabClicked()
|
||||
Keys.onTabPressed: {
|
||||
if (splitTunnelingButton2.visible) {
|
||||
return splitTunnelingButton2.rightButton.forceActiveFocus()
|
||||
} else if (killSwitchSwitcher.visible) {
|
||||
return killSwitchSwitcher.forceActiveFocus()
|
||||
} else {
|
||||
lastItemTabClicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
@@ -124,12 +130,48 @@ PageType {
|
||||
PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling)
|
||||
}
|
||||
|
||||
Keys.onTabPressed: lastItemTabClicked()
|
||||
Keys.onTabPressed: {
|
||||
if (killSwitchSwitcher.visible) {
|
||||
return killSwitchSwitcher.forceActiveFocus()
|
||||
} else {
|
||||
lastItemTabClicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: root.isAppSplitTinnelingEnabled
|
||||
}
|
||||
|
||||
SwitcherType {
|
||||
id: killSwitchSwitcher
|
||||
visible: !GC.isMobile()
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
text: qsTr("KillSwitch")
|
||||
descriptionText: qsTr("Disables your internet if your encrypted VPN connection drops out for any reason.")
|
||||
|
||||
checked: SettingsController.isKillSwitchEnabled()
|
||||
checkable: !ConnectionController.isConnected
|
||||
onCheckedChanged: {
|
||||
if (checked !== SettingsController.isKillSwitchEnabled()) {
|
||||
SettingsController.toggleKillSwitch(checked)
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
if (!checkable) {
|
||||
PageController.showNotificationMessage(qsTr("Cannot change killSwitch settings during active connection"))
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onTabPressed: lastItemTabClicked()
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: GC.isDesktop()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +125,20 @@ PageType {
|
||||
text: qsTr("Enable WireGuard obfuscation. It may be useful if WireGuard is blocked on your provider.")
|
||||
}
|
||||
|
||||
WarningType {
|
||||
Layout.topMargin: 16
|
||||
Layout.fillWidth: true
|
||||
|
||||
textString: ImportController.getMaliciousWarningText()
|
||||
textFormat: Qt.RichText
|
||||
visible: textString !== ""
|
||||
|
||||
iconPath: "qrc:/images/controls/alert-circle.svg"
|
||||
|
||||
textColor: "#EB5757"
|
||||
imageColor: "#EB5757"
|
||||
}
|
||||
|
||||
WarningType {
|
||||
Layout.topMargin: 16
|
||||
Layout.fillWidth: true
|
||||
@@ -136,7 +150,7 @@ PageType {
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 16
|
||||
Layout.bottomMargin: 48
|
||||
|
||||
implicitHeight: configContent.implicitHeight
|
||||
|
||||
@@ -151,13 +165,23 @@ PageType {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 16
|
||||
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
text: ImportController.getConfig()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: columnContent
|
||||
anchors.bottomMargin: -24
|
||||
color: "#0E0E11"
|
||||
opacity: 0.8
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: columnContent
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
@@ -122,7 +122,7 @@ PageType {
|
||||
}
|
||||
QtObject {
|
||||
id: openVpnConnectionFormat
|
||||
property string name: qsTr("OpenVpn native format")
|
||||
property string name: qsTr("OpenVPN native format")
|
||||
property var type: PageShare.ConfigType.OpenVpn
|
||||
}
|
||||
QtObject {
|
||||
@@ -766,9 +766,9 @@ PageType {
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: root.height * 0.5
|
||||
|
||||
expandedContent: ColumnLayout {
|
||||
id: expandedContent
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
@@ -778,6 +778,10 @@ PageType {
|
||||
|
||||
spacing: 8
|
||||
|
||||
onImplicitHeightChanged: {
|
||||
clientInfoDrawer.expandedHeight = expandedContent.implicitHeight + 32
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: clientInfoDrawer
|
||||
enabled: !GC.isMobile()
|
||||
@@ -788,10 +792,47 @@ PageType {
|
||||
|
||||
Header2Type {
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
headerText: clientName
|
||||
descriptionText: qsTr("Creation date: ") + creationDate
|
||||
}
|
||||
|
||||
ColumnLayout
|
||||
{
|
||||
id: textColumn
|
||||
property string textColor: "#878B91"
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
ParagraphTextType {
|
||||
color: textColumn.textColor
|
||||
visible: creationDate
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Creation date: %1").arg(creationDate)
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
color: textColumn.textColor
|
||||
visible: latestHandshake
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Latest handshake: %1").arg(latestHandshake)
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
color: textColumn.textColor
|
||||
visible: dataReceived
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Data received: %1").arg(dataReceived)
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
color: textColumn.textColor
|
||||
visible: dataSent
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Data sent: %1").arg(dataSent)
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
|
||||
@@ -112,7 +112,7 @@ PageType {
|
||||
|
||||
shareConnectionDrawer.headerText = qsTr("Accessing ") + serverSelector.text
|
||||
shareConnectionDrawer.configContentHeaderText = qsTr("File with accessing settings to ") + serverSelector.text
|
||||
// serverSelector.close()
|
||||
serverSelector.close()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
|
||||
@@ -70,7 +70,7 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
|
||||
|
||||
IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << dns1 << dns2);
|
||||
|
||||
if (m_settings->getSitesSplitTunnelingEnabled()) {
|
||||
if (m_settings->isSitesSplitTunnelingEnabled()) {
|
||||
IpcClient::Interface()->routeDeleteList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0");
|
||||
// qDebug() << "VpnConnection::onConnectionStateChanged :: adding custom routes, count:" << forwardIps.size();
|
||||
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
|
||||
@@ -89,7 +89,7 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
|
||||
} else if (state == Vpn::ConnectionState::Error) {
|
||||
IpcClient::Interface()->flushDns();
|
||||
|
||||
if (m_settings->getSitesSplitTunnelingEnabled()) {
|
||||
if (m_settings->isSitesSplitTunnelingEnabled()) {
|
||||
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
|
||||
IpcClient::Interface()->clearSavedRoutes();
|
||||
}
|
||||
@@ -237,15 +237,17 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede
|
||||
m_remoteAddress = credentials.hostName;
|
||||
emit connectionStateChanged(Vpn::ConnectionState::Connecting);
|
||||
|
||||
m_vpnConfiguration = vpnConfiguration;
|
||||
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
if (m_vpnProtocol) {
|
||||
disconnect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
|
||||
m_vpnProtocol->stop();
|
||||
m_vpnProtocol.reset();
|
||||
}
|
||||
appendKillSwitchConfig();
|
||||
#endif
|
||||
|
||||
m_vpnConfiguration = vpnConfiguration;
|
||||
appendSplitTunnelingConfig();
|
||||
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
@@ -282,6 +284,11 @@ void VpnConnection::createProtocolConnections()
|
||||
connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64)));
|
||||
}
|
||||
|
||||
void VpnConnection::appendKillSwitchConfig()
|
||||
{
|
||||
m_vpnConfiguration.insert(config_key::killSwitchOption, QVariant(m_settings->isKillSwitchEnabled()).toString());
|
||||
}
|
||||
|
||||
void VpnConnection::appendSplitTunnelingConfig()
|
||||
{
|
||||
if (m_vpnConfiguration.value(config_key::configVersion).toInt()) {
|
||||
@@ -297,35 +304,33 @@ void VpnConnection::appendSplitTunnelingConfig()
|
||||
|
||||
m_vpnConfiguration.insert(config_key::splitTunnelType, Settings::RouteMode::VpnOnlyForwardSites);
|
||||
m_vpnConfiguration.insert(config_key::splitTunnelSites, allowedIpsJsonArray);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Settings::RouteMode routeMode = Settings::RouteMode::VpnAllSites;
|
||||
QJsonArray sitesJsonArray;
|
||||
if (m_settings->isSitesSplitTunnelingEnabled()) {
|
||||
routeMode = m_settings->routeMode();
|
||||
|
||||
Settings::RouteMode routeMode = Settings::RouteMode::VpnAllSites;
|
||||
QJsonArray sitesJsonArray;
|
||||
if (m_settings->getSitesSplitTunnelingEnabled()) {
|
||||
routeMode = m_settings->routeMode();
|
||||
auto sites = m_settings->getVpnIps(routeMode);
|
||||
for (const auto &site : sites) {
|
||||
sitesJsonArray.append(site);
|
||||
}
|
||||
|
||||
auto sites = m_settings->getVpnIps(routeMode);
|
||||
for (const auto &site : sites) {
|
||||
sitesJsonArray.append(site);
|
||||
// Allow traffic to Amnezia DNS
|
||||
if (routeMode == Settings::VpnOnlyForwardSites) {
|
||||
sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns1).toString());
|
||||
sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns2).toString());
|
||||
}
|
||||
}
|
||||
|
||||
// Allow traffic to Amnezia DNS
|
||||
if (routeMode == Settings::VpnOnlyForwardSites) {
|
||||
sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns1).toString());
|
||||
sitesJsonArray.append(m_vpnConfiguration.value(config_key::dns2).toString());
|
||||
}
|
||||
m_vpnConfiguration.insert(config_key::splitTunnelType, routeMode);
|
||||
m_vpnConfiguration.insert(config_key::splitTunnelSites, sitesJsonArray);
|
||||
}
|
||||
|
||||
m_vpnConfiguration.insert(config_key::splitTunnelType, routeMode);
|
||||
m_vpnConfiguration.insert(config_key::splitTunnelSites, sitesJsonArray);
|
||||
|
||||
Settings::AppsRouteMode appsRouteMode = Settings::AppsRouteMode::VpnAllApps;
|
||||
QJsonArray appsJsonArray;
|
||||
if (m_settings->getAppsSplitTunnelingEnabled()) {
|
||||
if (m_settings->isAppsSplitTunnelingEnabled()) {
|
||||
appsRouteMode = m_settings->getAppsRouteMode();
|
||||
|
||||
auto apps = m_settings->getVpnApps(appsRouteMode);
|
||||
|
||||
@@ -94,6 +94,7 @@ private:
|
||||
void createProtocolConnections();
|
||||
|
||||
void appendSplitTunnelingConfig();
|
||||
void appendKillSwitchConfig();
|
||||
};
|
||||
|
||||
#endif // VPNCONNECTION_H
|
||||
|
||||
@@ -360,7 +360,11 @@ bool IpcServer::enablePeerTraffic(const QJsonObject &configStr)
|
||||
config.m_vpnDisabledApps.append(i.toString());
|
||||
}
|
||||
|
||||
WindowsFirewall::instance()->enablePeerTraffic(config);
|
||||
// killSwitch toggle
|
||||
if (QVariant(configStr.value(amnezia::config_key::killSwitchOption).toString()).toBool()) {
|
||||
WindowsFirewall::instance()->enablePeerTraffic(config);
|
||||
}
|
||||
|
||||
WindowsDaemon::instance()->prepareActivation(config, inetAdapterIndex);
|
||||
WindowsDaemon::instance()->activateSplitTunnel(config, vpnAdapterIndex);
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user