Compare commits

...

40 Commits

Author SHA1 Message Date
Mykola Baibuz
b03acc95c4 Merge branch 'dev' into feature/update-mozilla-upstream 2024-05-08 21:38:39 +03:00
pokamest
24759c92ad Merge pull request #791 from amnezia-vpn/feature/prevent-log-spam
Prevent service log spam on Windows
2024-05-07 14:45:26 -07:00
Vladyslav Miachkov
9e92ee020e Add connect button background (#785)
Add connect button background
2024-05-03 01:12:22 +01:00
pokamest
7a4f6b628b Merge pull request #789 from amnezia-vpn/bugfix/page-application-settings-warnings
fixed qml warnings
2024-05-02 17:11:23 -07:00
Mykola Baibuz
7e2f223d7f Prevent service log spam on Windows 2024-04-30 22:17:50 +03:00
pokamest
eb48e4b668 Merge pull request #772 from amnezia-vpn/feature/check-openvpn-config
added checking for dangerous strings in openvpn configuration files
2024-04-30 10:26:12 -07:00
Mykola Baibuz
0b0766ce8b Some rebranding 2024-04-30 16:51:09 +03:00
Mykola Baibuz
26825c2898 Update Mozilla upstream for MacOS 2024-04-30 15:13:17 +03:00
pokamest
9ace09a604 Merge pull request #788 from amnezia-vpn/bugfix/wgshow-invert-transfer-data 2024-04-30 02:34:10 -07:00
vladimir.kuznetsov
702735c2ca fixed qml warnings 2024-04-30 14:32:30 +05:00
Andrey Zaharow
174f2ac3db Censorship levels translation update (#770)
Censorship levels translation update
2024-04-29 22:36:18 +01:00
pokamest
e3b5b4a9d9 Merge pull request #768 from amnezia-vpn/feature/remove-middle-lvl-of-censorship
Remove middle level of censorship
2024-04-29 14:35:45 -07:00
Andrey Zaharow
72ba012765 Minor text corrections (#771)
Minor text corrections
2024-04-29 22:33:35 +01:00
pokamest
0f9bbcd060 Merge pull request #787 from amnezia-vpn/translation/Hindi-Language
Add Hindi language
2024-04-29 14:28:43 -07:00
Vladyslav Miachkov
a9d038d8bf Invert received/sent data for client info 2024-04-29 22:40:37 +03:00
Mykola Baibuz
bf67e3491e Update Mozilla upstream for Linux 2024-04-29 22:17:43 +03:00
Mykola Baibuz
856bc23b5d Update Mozilla upstream for Windows 2024-04-29 21:48:53 +03:00
Shehab Ahmed
54a6845315 Add Hindi language 2024-04-29 19:52:57 +03:00
pokamest
0c7059a476 Merge pull request #786 from amnezia-vpn/bugfix/killswitch-switcher-mobile
hide killswitch switcher for mobile platforms
2024-04-29 04:50:11 -07:00
pokamest
5bed92ab0b WindowsTunnelService typo fix 2024-04-29 11:12:27 +01:00
vladimir.kuznetsov
49a14785c6 hide killswitch switcher for mobile platforms 2024-04-29 13:36:23 +05:00
pokamest
2c78c06dda Merge pull request #780 from amnezia-vpn/bugfix/api-server-app-split-tunneling
fixed appSplitTunneling for api servers
2024-04-28 06:04:17 -07:00
Vladyslav Miachkov
cf8a0efd0d Get data from wg show command (#764)
Get data from wg show command
2024-04-28 14:03:41 +01:00
Andrey Zaharow
5211cdd4c0 Add hide password on SFTP page feature (#719)
Hide password on SFTP page feature
2024-04-28 12:48:38 +01:00
vladimir.kuznetsov
d10aa43d8b fixed appSplitTunneling for api servers 2024-04-26 18:45:25 +05:00
pokamest
6b0f1ed429 Merge pull request #779 from amnezia-vpn/bugfix/macos-runner
bump xcode-version for macos build
2024-04-26 04:33:43 -07:00
vladimir.kuznetsov
4bde1ccb44 bump xcode-version for macos build 2024-04-26 14:21:04 +05:00
pokamest
03c18c44e2 Merge pull request #774 from amnezia-vpn/fix/remove_appname_log
Remove logging of application and package names
2024-04-25 07:53:53 -07:00
Shehab Ahmed
72ffc7ce6a Translation/urdu language (#773)
* add Urdu translation
2024-04-25 15:30:31 +01:00
Nethius
87b738ef16 added killSwitch switcher (#746)
* added killSwitch switcher
* KillSwitch toggle for OpenVPN and XRay
* killSwitch toggle for AWG/WG protocol
* Some fixes for killSwitch
2024-04-25 14:01:00 +01:00
albexk
b868831bcb Remove logging of application and package names, as this is personal user data 2024-04-22 16:56:27 +03:00
pokamest
477d7214c5 Version bump 4.5.3.0 2024-04-21 16:02:16 +01:00
vladimir.kuznetsov
f3cd3d4f06 added checking for dangerous strings in openvpn configuration files 2024-04-21 17:58:57 +05:00
Andrey Zaharow
aea4cc2389 Remove middle level of censorship 2024-04-21 02:14:22 +02:00
pokamest
245aa8eb8c Merge pull request #767 from amnezia-vpn/fix/logging 2024-04-20 11:04:28 -07:00
albexk
f14a2add0f Fix clearing logs on Android and checking if logs need to be deleted 2024-04-20 17:51:33 +03:00
pokamest
89703ba58f Merge pull request #766 from amnezia-vpn/feature/native-wg-psk
Add support for native WG configs without PSK parameter
2024-04-20 03:03:06 -07:00
Mykola Baibuz
23715fca8b Add support for native WG configs without PSK parameter 2024-04-19 22:14:06 +03:00
pokamest
d90685600e Merge pull request #763 from amnezia-vpn/bugfix/full-access-share-drawer
fixed drawer closing on full access share screen
2024-04-19 06:37:05 -07:00
vladimir.kuznetsov
f007e5eb5c fixed drawer closing on full access share screen 2024-04-19 18:04:19 +05:00
75 changed files with 7775 additions and 419 deletions

View File

@@ -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

View File

@@ -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")

View File

@@ -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)

View File

@@ -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)
}

View File

@@ -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)
}
}

View File

@@ -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()
}
}
}

View File

@@ -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");

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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));

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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() {}

View File

@@ -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

View File

@@ -15,7 +15,6 @@ class IOSNetworkWatcher : public NetworkWatcherImpl {
~IOSNetworkWatcher();
void initialize() override;
NetworkWatcherImpl::TransportType getTransportType() override;
private:
NetworkWatcherImpl::TransportType toTransportType(nw_path_t path);

View File

@@ -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;

View File

@@ -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
View 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();

View File

@@ -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;

View File

@@ -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()) {

View File

@@ -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;
}

View File

@@ -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 {

View File

@@ -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);
}

View File

@@ -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

View File

@@ -38,7 +38,6 @@ class WindowsDaemon final : public Daemon {
Inactive,
};
State m_state = Inactive;
int m_inetAdapterIndex = -1;
WireguardUtilsWindows* m_wgutils = nullptr;

View File

@@ -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;

View File

@@ -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,

View File

@@ -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();

View File

@@ -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;

View File

@@ -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,

View File

@@ -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()) {

View File

@@ -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) {

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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());
}

View File

@@ -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";

View File

@@ -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();

View File

@@ -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();

View File

@@ -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:

View File

@@ -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&apos;s okay as long as it&apos;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&apos;s okay as long as it&apos;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&apos;s okay as long as it&apos;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&apos;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>

View File

@@ -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&apos;s okay as long as it&apos;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>

File diff suppressed because it is too large Load Diff

View File

@@ -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&apos;s okay as long as it&apos;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>

View File

@@ -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&apos;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&apos;s innovative &quot;friend or foe&quot; 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 &quot;друг или враг&quot; на этапе рукопожатия 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"/>

View File

@@ -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"/>

File diff suppressed because it is too large Load Diff

View File

@@ -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&apos;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&apos;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>

View File

@@ -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, "" };

View File

@@ -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));
}
}
}
}
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -63,6 +63,9 @@ public slots:
bool isCameraPresent();
bool isKillSwitchEnabled();
void toggleKillSwitch(bool enable);
signals:
void primaryDnsChanged();
void secondaryDnsChanged();

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -16,7 +16,9 @@ namespace LanguageSettings
Ukrainian,
Persian,
Arabic,
Burmese
Burmese,
Urdu,
Hindi
};
Q_ENUM_NS(AvailableLanguageEnum)

View File

@@ -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));

View File

@@ -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()

View File

@@ -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
}
}

View File

@@ -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"))

View File

@@ -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
}

View File

@@ -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() {

View File

@@ -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)

View File

@@ -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()
}
}
}
}

View File

@@ -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

View File

@@ -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 {

View File

@@ -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: {

View File

@@ -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);

View File

@@ -94,6 +94,7 @@ private:
void createProtocolConnections();
void appendSplitTunnelingConfig();
void appendKillSwitchConfig();
};
#endif // VPNCONNECTION_H

View File

@@ -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;