Compare commits

..

12 Commits

Author SHA1 Message Date
lunardunno
adaea28627 without check dir
Exclude checking for availability of the user's home directory.
Direct use of the $HOME variable.
2025-03-16 12:06:04 +04:00
Yaroslav
9fbea76b74 There's a common issue of building iOS apps on Qt 6.8 because of new introduced ffmpeg dependency in multimedia Qt package (#1414)
ref: https://community.esri.com/t5/qt-maps-sdk-questions/build-failure-on-ios-with-qt-6-8/m-p/1548701#M5339
2025-03-14 20:40:27 +07:00
lunardunno
b3ff120bcf Checking server user permissions to use sudo (#1442)
* Username if whoami returns an error

Сommand to use home directory name if whoami returns error or is missing for prepare_host.sh.

* Update check_user_in_sudo.sh

Сommand to use home directory name if whoami returns error or is missing for check_user_in_sudo.sh.
Checking server user permissions to use sudo using a package manager or using uname.
Сhecking and redefining the system language.
Checking requirements for sudo users or root in script.

* Cases have been changed and added.

Changed description of the “Server User Not In Sudo” case.
Corrected the name and description of the "ServerPacketManagerError" case. Packet to Package.
Adding a "SudoPackageIsNotPreinstalled" case.
Adding a "ServerUserNotAllowedInSudoers" case.
Adding a "ServerUserPasswordRequired" case.

* Serves errors have been changed and added.

Corrected the name of the "ServerPacketManagerError" error to "ServerPackageManagerError".
Adding a "SudoPackageIsNotPreinstalled" error.
Adding a "ServerUserNotAllowedInSudoers" error.
Adding a "ServerUserPasswordRequired" error.

* Return ServerPacketManagerError

Return to the name "ServerPacketManagerError".

* Added errors handling 

Added new errors' handling to serverController.cpp.
Permission checks are also performed for the root user.

* Update translations

Updating translations for two existing server errors.

* Myanmar translation update

* Update for my_MM.ts

* checking for not allowed

Checking for "not allowed" in stdOut

* Removed "not allowed"

Removed check for "not allowed" in stdOut

* Removed nested launch

Removed nested launch via sudo

* Returned nested launch

Returned nested launch via sudo

* All checks with sudo

Both checks with sudo always run.

* Moved removing timestamp sudo

Removing the sudo timestamp is done every time.

* Checking the user directory

Checking the accessibility of the user's home directory

* Polishing

Изменение порядка обработки ошибок.

* changing detection order 

change the order of detection of inconsistencies:
1. sudo not preinstalled. (if user != root)
2. user not in sudo or wheel group. (if user != root)
3. user's directory is not accessible. (for all)
4. user not allowed in sudoers. (for all)
5. user password required. (for all)

* Packet to Package

* chore: bump version (#1463)

* fix for sh (#1462)

Fix for servers where sh is used as default shell.

* Username if whoami returns an error

Сommand to use home directory name if whoami returns error or is missing for prepare_host.sh.

* Update check_user_in_sudo.sh

Сommand to use home directory name if whoami returns error or is missing for check_user_in_sudo.sh.
Checking server user permissions to use sudo using a package manager or using uname.
Сhecking and redefining the system language.
Checking requirements for sudo users or root in script.

* Cases have been changed and added.

Changed description of the “Server User Not In Sudo” case.
Corrected the name and description of the "ServerPacketManagerError" case. Packet to Package.
Adding a "SudoPackageIsNotPreinstalled" case.
Adding a "ServerUserNotAllowedInSudoers" case.
Adding a "ServerUserPasswordRequired" case.

* Serves errors have been changed and added.

Corrected the name of the "ServerPacketManagerError" error to "ServerPackageManagerError".
Adding a "SudoPackageIsNotPreinstalled" error.
Adding a "ServerUserNotAllowedInSudoers" error.
Adding a "ServerUserPasswordRequired" error.

* Return ServerPacketManagerError

Return to the name "ServerPacketManagerError".

* Update translations

Updating translations for two existing server errors.

* Added errors handling 

Added new errors' handling to serverController.cpp.
Permission checks are also performed for the root user.

* Myanmar translation update

* Update for my_MM.ts

* checking for not allowed

Checking for "not allowed" in stdOut

* Removed "not allowed"

Removed check for "not allowed" in stdOut

* Removed nested launch

Removed nested launch via sudo

* Returned nested launch

Returned nested launch via sudo

* All checks with sudo

Both checks with sudo always run.

* Moved removing timestamp sudo

Removing the sudo timestamp is done every time.

* Checking the user directory

Checking the accessibility of the user's home directory

* Polishing

Изменение порядка обработки ошибок.

* changing detection order 

change the order of detection of inconsistencies:
1. sudo not preinstalled. (if user != root)
2. user not in sudo or wheel group. (if user != root)
3. user's directory is not accessible. (for all)
4. user not allowed in sudoers. (for all)
5. user password required. (for all)

* Undoing unintended changes

Undoing unintended changes.

* Undoing unintended change

Undoing unintended change.

* not allowed to use sudo

The user is not allowed to use sudo on this server.

* Capital letters in the error

Capital letters in the error description.

---------

Co-authored-by: albexk <albexk@proton.me>
2025-03-14 20:39:58 +07:00
paldeflex
9dea98f020 chore: README typo fixes (#1467) 2025-03-10 23:22:09 +07:00
Mykola Baibuz
c4701d4e7a Update XRay for Desktops (#1459)
version 25.3.6
2025-03-10 15:11:26 +07:00
Nethius
48903ca3a1 chore: fixed proxyStorageUrl typo (#1466) 2025-03-09 13:36:21 +07:00
Nethius
0c9fd4aef4 feature: added multiply proxy storage support (#1465) 2025-03-09 13:07:08 +07:00
lunardunno
b2af2e46ac fix for sh (#1462)
Fix for servers where sh is used as default shell.
2025-03-09 12:34:00 +07:00
albexk
efc76a0683 chore: bump version (#1463) 2025-03-09 10:30:43 +07:00
Nethius
c4a553c166 chore: error body processing (#1458) 2025-03-07 10:39:12 +07:00
Cyril Anisimov
69a00b0252 feature: remove the limit of ip addresses = 254 (#1438) 2025-03-06 21:43:47 +07:00
KsZnak
4257c08b43 Update amneziavpn_ru_RU.ts (#1457) 2025-03-06 21:38:42 +07:00
104 changed files with 755 additions and 1869 deletions

View File

@@ -6,11 +6,11 @@
[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
### [English](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README.md) | Русский
[AmneziaVPN](https://amnezia.org) — это open sourse VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
[AmneziaVPN](https://amnezia.org) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
[![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org)
### [Сайт](https://amnezia.org) | [Зеркало на сайт](https://storage.googleapis.com/amnezia/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
### [Сайт](https://amnezia.org) | [Зеркало сайта](https://storage.googleapis.com/amnezia/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
> [!TIP]
> Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org).
@@ -30,7 +30,7 @@
- Классические VPN-протоколы: OpenVPN, WireGuard и IKEv2.
- Протоколы с маскировкой трафика (обфускацией): OpenVPN с плагином [Cloak](https://github.com/cbeuw/Cloak), Shadowsocks (OpenVPN over Shadowsocks), [AmneziaWG](https://docs.amnezia.org/documentation/amnezia-wg/) and XRay.
- Поддержка Split Tunneling — добавляйте любые сайты или приложения в список, чтобы включить VPN только для них.
- Поддерживает платформы: Windows, MacOS, Linux, Android, iOS.
- Поддерживает платформы: Windows, macOS, Linux, Android, iOS.
- Поддержка конфигурации протокола AmneziaWG на [бета-прошивке Keenetic](https://docs.keenetic.com/ua/air/kn-1611/en/6319-latest-development-release.html#UUID-186c4108-5afd-c10b-f38a-cdff6c17fab3_section-idm33192196168192-improved).
## Ссылки
@@ -38,10 +38,10 @@
- [https://amnezia.org](https://amnezia.org) - Веб-сайт проекта | [Альтернативная ссылка (зеркало)](https://storage.googleapis.com/kldscp/amnezia.org)
- [https://docs.amnezia.org](https://docs.amnezia.org) - Документация
- [https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit
- [https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Канал поддржки в Telegram (Английский)
- [https://t.me/amnezia_vpn_ir](https://t.me/amnezia_vpn_ir) - Канал поддржки в Telegram (Фарси)
- [https://t.me/amnezia_vpn_mm](https://t.me/amnezia_vpn_mm) - Канал поддржки в Telegram (Мьянма)
- [https://t.me/amnezia_vpn](https://t.me/amnezia_vpn) - Канал поддржки в Telegram (Русский)
- [https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Канал поддержки в Telegram (Английский)
- [https://t.me/amnezia_vpn_ir](https://t.me/amnezia_vpn_ir) - Канал поддержки в Telegram (Фарси)
- [https://t.me/amnezia_vpn_mm](https://t.me/amnezia_vpn_mm) - Канал поддержки в Telegram (Мьянма)
- [https://t.me/amnezia_vpn](https://t.me/amnezia_vpn) - Канал поддержки в Telegram (Русский)
- [https://vpnpay.io/en/amnezia-premium/](https://vpnpay.io/en/amnezia-premium/) - Amnezia Premium | [Зеркало](https://storage.googleapis.com/kldscp/vpnpay.io/ru/amnezia-premium\)
## Технологии
@@ -80,8 +80,8 @@ git submodule update --init --recursive
Проверьте папку deploy для скриптов сборки.
### Как собрать iOS-приложение из исходного кода на MacOS
1. Убедитесь, что у вас установлен XCode версии 14 или выше.
2. Для генерации проекта XCode используется QT. Требуется версия QT 6.6.2. Установите QT для MacOS здесь или через QT Online Installer. Необходимые модули:
1. Убедитесь, что у вас установлен Xcode версии 14 или выше.
2. Для генерации проекта Xcode используется QT. Требуется версия QT 6.6.2. Установите QT для MacOS здесь или через QT Online Installer. Необходимые модули:
- MacOS
- iOS
- Модуль совместимости с Qt 5
@@ -117,7 +117,7 @@ $QT_IOS_BIN/qt-cmake . -B build-ios -GXcode -DQT_HOST_PATH=$QT_MACOS_ROOT_DIR
export PATH=$(PATH):/path/to/GOPATH/bin
```
6. Откройте проект в XCode. Теперь вы можете тестировать, архивировать или публиковать приложение.
6. Откройте проект в Xcode. Теперь вы можете тестировать, архивировать или публиковать приложение.
Если сборка завершится с ошибкой:
```

View File

@@ -31,10 +31,6 @@ add_definitions(-DDEV_AGW_PUBLIC_KEY="$ENV{DEV_AGW_PUBLIC_KEY}")
add_definitions(-DDEV_AGW_ENDPOINT="$ENV{DEV_AGW_ENDPOINT}")
add_definitions(-DDEV_S3_ENDPOINT="$ENV{DEV_S3_ENDPOINT}")
if(IOS)
set(PACKAGES ${PACKAGES} Multimedia)
endif()
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
set(PACKAGES ${PACKAGES} Widgets)
endif()
@@ -48,10 +44,6 @@ set(LIBS ${LIBS}
Qt6::Core5Compat Qt6::Concurrent
)
if(IOS)
set(LIBS ${LIBS} Qt6::Multimedia)
endif()
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
set(LIBS ${LIBS} Qt6::Widgets)
endif()

View File

@@ -3,6 +3,7 @@
#include <QDebug>
#include <QJsonDocument>
#include <QProcess>
#include <QRegularExpression>
#include <QString>
#include <QTemporaryDir>
#include <QTemporaryFile>
@@ -19,13 +20,17 @@
#include "settings.h"
#include "utilities.h"
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
bool isAwg, QObject *parent)
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings,
const QSharedPointer<ServerController> &serverController, bool isAwg,
QObject *parent)
: ConfiguratorBase(settings, serverController, parent), m_isAwg(isAwg)
{
m_serverConfigPath = m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath;
m_serverPublicKeyPath = m_isAwg ? amnezia::protocols::awg::serverPublicKeyPath : amnezia::protocols::wireguard::serverPublicKeyPath;
m_serverPskKeyPath = m_isAwg ? amnezia::protocols::awg::serverPskKeyPath : amnezia::protocols::wireguard::serverPskKeyPath;
m_serverConfigPath =
m_isAwg ? amnezia::protocols::awg::serverConfigPath : amnezia::protocols::wireguard::serverConfigPath;
m_serverPublicKeyPath =
m_isAwg ? amnezia::protocols::awg::serverPublicKeyPath : amnezia::protocols::wireguard::serverPublicKeyPath;
m_serverPskKeyPath =
m_isAwg ? amnezia::protocols::awg::serverPskKeyPath : amnezia::protocols::wireguard::serverPskKeyPath;
m_configTemplate = m_isAwg ? ProtocolScriptType::awg_template : ProtocolScriptType::wireguard_template;
m_protocolName = m_isAwg ? config_key::awg : config_key::wireguard;
@@ -63,9 +68,31 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys()
return connData;
}
QList<QHostAddress> WireguardConfigurator::getIpsFromConf(const QString &input)
{
QRegularExpression regex("AllowedIPs = (\\d+\\.\\d+\\.\\d+\\.\\d+)");
QRegularExpressionMatchIterator matchIterator = regex.globalMatch(input);
QList<QHostAddress> ips;
while (matchIterator.hasNext()) {
QRegularExpressionMatch match = matchIterator.next();
const QString address_string { match.captured(1) };
const QHostAddress address { address_string };
if (address.isNull()) {
qWarning() << "Couldn't recognize the ip address: " << address_string;
} else {
ips << address;
}
}
return ips;
}
WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardConfig(const ServerCredentials &credentials,
DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode)
const QJsonObject &containerConfig,
ErrorCode &errorCode)
{
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
connData.host = credentials.hostName;
@@ -76,65 +103,45 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
return connData;
}
// Get list of already created clients (only IP addresses)
QString nextIpNumber;
{
QString script = QString("cat %1 | grep AllowedIPs").arg(m_serverConfigPath);
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
QString getIpsScript = QString("cat %1 | grep AllowedIPs").arg(m_serverConfigPath);
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
return ErrorCode::NoError;
};
errorCode = m_serverController->runContainerScript(credentials, container, script, cbReadStdOut);
if (errorCode != ErrorCode::NoError) {
return connData;
}
errorCode = m_serverController->runContainerScript(credentials, container, getIpsScript, cbReadStdOut);
if (errorCode != ErrorCode::NoError) {
return connData;
}
auto ips = getIpsFromConf(stdOut);
stdOut.replace("AllowedIPs = ", "");
stdOut.replace("/32", "");
QStringList ips = stdOut.split("\n", Qt::SkipEmptyParts);
// remove extra IPs from each line for case when user manually edited the wg0.conf
// and added there more IPs for route his itnernal networks, like:
// ...
// AllowedIPs = 10.8.1.6/32, 192.168.1.0/24, 192.168.2.0/24, ...
// ...
// without this code - next IP would be 1 if last item in 'ips' has format above
QStringList vpnIps;
for (const auto &ip : ips) {
vpnIps.append(ip.split(",", Qt::SkipEmptyParts).first().trimmed());
}
ips = vpnIps;
// Calc next IP address
if (ips.isEmpty()) {
nextIpNumber = "2";
QHostAddress nextIp = [&] {
QHostAddress result;
QHostAddress lastIp;
if (ips.empty()) {
lastIp.setAddress(containerConfig.value(m_protocolName)
.toObject()
.value(config_key::subnet_address)
.toString(protocols::wireguard::defaultSubnetAddress));
} else {
int next = ips.last().split(".").last().toInt() + 1;
if (next > 254) {
errorCode = ErrorCode::AddressPoolError;
return connData;
}
nextIpNumber = QString::number(next);
lastIp = ips.last();
}
}
QString subnetIp = containerConfig.value(m_protocolName).toObject().value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
{
QStringList l = subnetIp.split(".", Qt::SkipEmptyParts);
if (l.isEmpty()) {
errorCode = ErrorCode::AddressPoolError;
return connData;
quint8 lastOctet = static_cast<quint8>(lastIp.toIPv4Address());
switch (lastOctet) {
case 254: result.setAddress(lastIp.toIPv4Address() + 3); break;
case 255: result.setAddress(lastIp.toIPv4Address() + 2); break;
default: result.setAddress(lastIp.toIPv4Address() + 1); break;
}
l.removeLast();
l.append(nextIpNumber);
connData.clientIP = l.join(".");
}
return result;
}();
connData.clientIP = nextIp.toString();
// Get keys
connData.serverPubKey = m_serverController->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
connData.serverPubKey =
m_serverController->getTextFileFromContainer(container, credentials, m_serverPublicKeyPath, errorCode);
connData.serverPubKey.replace("\n", "");
if (errorCode != ErrorCode::NoError) {
return connData;
@@ -161,10 +168,12 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
return connData;
}
QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'").arg(m_serverConfigPath);
QString script = QString("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip %1)'")
.arg(m_serverConfigPath);
errorCode = m_serverController->runScript(
credentials, m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container)));
credentials,
m_serverController->replaceVars(script, m_serverController->genVarsForScript(credentials, container)));
return connData;
}
@@ -173,8 +182,8 @@ QString WireguardConfigurator::createConfig(const ServerCredentials &credentials
const QJsonObject &containerConfig, ErrorCode &errorCode)
{
QString scriptData = amnezia::scriptData(m_configTemplate, container);
QString config =
m_serverController->replaceVars(scriptData, m_serverController->genVarsForScript(credentials, container, containerConfig));
QString config = m_serverController->replaceVars(
scriptData, m_serverController->genVarsForScript(credentials, container, containerConfig));
ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode);
if (errorCode != ErrorCode::NoError) {
@@ -208,16 +217,16 @@ QString WireguardConfigurator::createConfig(const ServerCredentials &credentials
return QJsonDocument(jConfig).toJson();
}
QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString)
QString WireguardConfigurator::processConfigWithLocalSettings(const QPair<QString, QString> &dns,
const bool isApiConfig, QString &protocolConfigString)
{
processConfigWithDnsSettings(dns, protocolConfigString);
return protocolConfigString;
}
QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString)
QString WireguardConfigurator::processConfigWithExportSettings(const QPair<QString, QString> &dns,
const bool isApiConfig, QString &protocolConfigString)
{
processConfigWithDnsSettings(dns, protocolConfigString);

View File

@@ -1,6 +1,7 @@
#ifndef WIREGUARD_CONFIGURATOR_H
#define WIREGUARD_CONFIGURATOR_H
#include <QHostAddress>
#include <QObject>
#include <QProcessEnvironment>
@@ -12,8 +13,8 @@ class WireguardConfigurator : public ConfiguratorBase
{
Q_OBJECT
public:
WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController, bool isAwg,
QObject *parent = nullptr);
WireguardConfigurator(std::shared_ptr<Settings> settings, const QSharedPointer<ServerController> &serverController,
bool isAwg, QObject *parent = nullptr);
struct ConnectionData
{
@@ -26,15 +27,18 @@ public:
QString port;
};
QString createConfig(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig,
ErrorCode &errorCode);
QString createConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode);
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString);
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig, QString &protocolConfigString);
QString processConfigWithLocalSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString);
QString processConfigWithExportSettings(const QPair<QString, QString> &dns, const bool isApiConfig,
QString &protocolConfigString);
static ConnectionData genClientKeys();
private:
QList<QHostAddress> getIpsFromConf(const QString &input);
ConnectionData prepareWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode &errorCode);

View File

@@ -47,9 +47,6 @@ void CoreController::initModels()
m_sitesModel.reset(new SitesModel(m_settings, this));
m_engine->rootContext()->setContextProperty("SitesModel", m_sitesModel.get());
m_allowedDnsModel.reset(new AllowedDnsModel(m_settings, this));
m_engine->rootContext()->setContextProperty("AllowedDnsModel", m_allowedDnsModel.get());
m_appSplitTunnelingModel.reset(new AppSplitTunnelingModel(m_settings, this));
m_engine->rootContext()->setContextProperty("AppSplitTunnelingModel", m_appSplitTunnelingModel.get());
@@ -132,9 +129,6 @@ void CoreController::initControllers()
m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
m_allowedDnsController.reset(new AllowedDnsController(m_settings, m_allowedDnsModel));
m_engine->rootContext()->setContextProperty("AllowedDnsController", m_allowedDnsController.get());
m_appSplitTunnelingController.reset(new AppSplitTunnelingController(m_settings, m_appSplitTunnelingModel));
m_engine->rootContext()->setContextProperty("AppSplitTunnelingController", m_appSplitTunnelingController.get());
@@ -219,7 +213,6 @@ void CoreController::initSignalHandlers()
initAutoConnectHandler();
initAmneziaDnsToggledHandler();
initPrepareConfigHandler();
initStrictKillSwitchHandler();
}
void CoreController::initNotificationHandler()
@@ -346,12 +339,6 @@ void CoreController::initPrepareConfigHandler()
});
}
void CoreController::initStrictKillSwitchHandler()
{
connect(m_settingsController.get(), &SettingsController::strictKillSwitchEnabledChanged,
m_vpnConnection.get(), &VpnConnection::onKillSwitchModeChanged);
}
QSharedPointer<PageController> CoreController::pageController() const
{
return m_pageController;

View File

@@ -8,7 +8,6 @@
#include "ui/controllers/api/apiConfigsController.h"
#include "ui/controllers/api/apiSettingsController.h"
#include "ui/controllers/appSplitTunnelingController.h"
#include "ui/controllers/allowedDnsController.h"
#include "ui/controllers/connectionController.h"
#include "ui/controllers/exportController.h"
#include "ui/controllers/focusController.h"
@@ -19,7 +18,6 @@
#include "ui/controllers/sitesController.h"
#include "ui/controllers/systemController.h"
#include "ui/models/allowed_dns_model.h"
#include "ui/models/containers_model.h"
#include "ui/models/languageModel.h"
#include "ui/models/protocols/cloakConfigModel.h"
@@ -82,7 +80,6 @@ private:
void initAutoConnectHandler();
void initAmneziaDnsToggledHandler();
void initPrepareConfigHandler();
void initStrictKillSwitchHandler();
QQmlApplicationEngine *m_engine {}; // TODO use parent child system here?
std::shared_ptr<Settings> m_settings;
@@ -105,7 +102,6 @@ private:
QScopedPointer<SitesController> m_sitesController;
QScopedPointer<SystemController> m_systemController;
QScopedPointer<AppSplitTunnelingController> m_appSplitTunnelingController;
QScopedPointer<AllowedDnsController> m_allowedDnsController;
QScopedPointer<ApiSettingsController> m_apiSettingsController;
QScopedPointer<ApiConfigsController> m_apiConfigsController;
@@ -116,7 +112,6 @@ private:
QSharedPointer<LanguageModel> m_languageModel;
QSharedPointer<ProtocolsModel> m_protocolsModel;
QSharedPointer<SitesModel> m_sitesModel;
QSharedPointer<AllowedDnsModel> m_allowedDnsModel;
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
QSharedPointer<ClientManagementModel> m_clientManagementModel;

View File

@@ -26,6 +26,10 @@ namespace
constexpr char apiPayload[] = "api_payload";
constexpr char keyPayload[] = "key_payload";
}
constexpr QLatin1String errorResponsePattern1("No active configuration found for");
constexpr QLatin1String errorResponsePattern2("No non-revoked public key found for");
constexpr QLatin1String errorResponsePattern3("Account not found.");
}
GatewayController::GatewayController(const QString &gatewayEndpoint, bool isDevEnvironment, int requestTimeoutMsecs, QObject *parent)
@@ -194,16 +198,16 @@ QStringList GatewayController::getProxyUrls()
QList<QSslError> sslErrors;
QNetworkReply *reply;
QStringList proxyStorageUrl;
QStringList proxyStorageUrls;
if (m_isDevEnvironment) {
proxyStorageUrl = QStringList { DEV_S3_ENDPOINT };
proxyStorageUrls = QString(DEV_S3_ENDPOINT).split(", ");
} else {
proxyStorageUrl = QStringList { PROD_S3_ENDPOINT };
proxyStorageUrls = QString(PROD_S3_ENDPOINT).split(", ");
}
QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
for (const auto &proxyStorageUrl : proxyStorageUrl) {
for (const auto &proxyStorageUrl : proxyStorageUrls) {
request.setUrl(proxyStorageUrl);
reply = amnApp->networkManager()->get(request);
@@ -262,7 +266,16 @@ bool GatewayController::shouldBypassProxy(QNetworkReply *reply, const QByteArray
} else if (responseBody.contains("html")) {
qDebug() << "The response contains an html tag";
return true;
} else if (reply->error() == QNetworkReply::NetworkError::NoError && checkEncryption) {
} else if (reply->error() == QNetworkReply::NetworkError::ContentNotFoundError) {
if (responseBody.contains(errorResponsePattern1) || responseBody.contains(errorResponsePattern2)
|| responseBody.contains(errorResponsePattern3)) {
return false;
} else {
return true;
}
} else if (reply->error() != QNetworkReply::NetworkError::NoError) {
return true;
} else if (checkEncryption) {
try {
QSimpleCrypto::QBlockCipher blockCipher;
static_cast<void>(blockCipher.decryptAesBlockCipher(responseBody, key, iv, "", salt));

View File

@@ -757,10 +757,6 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, DockerContainer container)
{
if (credentials.userName == "root") {
return ErrorCode::NoError;
}
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
@@ -774,8 +770,16 @@ ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, D
const QString scriptData = amnezia::scriptData(SharedScriptType::check_user_in_sudo);
ErrorCode error = runScript(credentials, replaceVars(scriptData, genVarsForScript(credentials)), cbReadStdOut, cbReadStdErr);
if (!stdOut.contains("sudo"))
if (credentials.userName != "root" && stdOut.contains("sudo:") && !stdOut.contains("uname:") && stdOut.contains("not found"))
return ErrorCode::SudoPackageIsNotPreinstalled;
if (credentials.userName != "root" && !stdOut.contains("sudo") && !stdOut.contains("wheel"))
return ErrorCode::ServerUserNotInSudo;
if (stdOut.contains("can't cd to") || stdOut.contains("Permission denied") || stdOut.contains("No such file or directory"))
return ErrorCode::ServerUserDirectoryNotAccessible;
if (stdOut.contains("sudoers") || stdOut.contains("is not allowed to run sudo on"))
return ErrorCode::ServerUserNotAllowedInSudoers;
if (stdOut.contains("password is required"))
return ErrorCode::ServerUserPasswordRequired;
return error;
}

View File

@@ -54,6 +54,10 @@ namespace amnezia
ServerCancelInstallation = 204,
ServerUserNotInSudo = 205,
ServerPacketManagerError = 206,
SudoPackageIsNotPreinstalled = 207,
ServerUserDirectoryNotAccessible = 208,
ServerUserNotAllowedInSudoers = 209,
ServerUserPasswordRequired = 210,
// Ssh connection errors
SshRequestDeniedError = 300,

View File

@@ -20,8 +20,12 @@ QString errorString(ErrorCode code) {
case(ErrorCode::ServerContainerMissingError): errorMessage = QObject::tr("Server error: Docker container missing"); break;
case(ErrorCode::ServerDockerFailedError): errorMessage = QObject::tr("Server error: Docker failed"); break;
case(ErrorCode::ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); break;
case(ErrorCode::ServerUserNotInSudo): errorMessage = QObject::tr("The user does not have permission to use sudo"); break;
case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Packet manager error"); break;
case(ErrorCode::ServerUserNotInSudo): errorMessage = QObject::tr("The user is not a member of the sudo group"); break;
case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Package manager error"); break;
case(ErrorCode::SudoPackageIsNotPreinstalled): errorMessage = QObject::tr("The sudo package is not pre-installed"); break;
case(ErrorCode::ServerUserDirectoryNotAccessible): errorMessage = QObject::tr("The server user's home directory is not accessible"); break;
case(ErrorCode::ServerUserNotAllowedInSudoers): errorMessage = QObject::tr("Action not allowed in sudoers"); break;
case(ErrorCode::ServerUserPasswordRequired): errorMessage = QObject::tr("The user's password is required"); break;
// Libssh errors
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;

View File

@@ -371,9 +371,6 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
if (!parseStringList(obj, "vpnDisabledApps", config.m_vpnDisabledApps)) {
return false;
}
if (!parseStringList(obj, "allowedDnsServers", config.m_allowedDnsServers)) {
return false;
}
config.m_killSwitchEnabled = QVariant(obj.value("killSwitchOption").toString()).toBool();

View File

@@ -48,13 +48,6 @@ QJsonObject InterfaceConfig::toJson() const {
}
json.insert("excludedAddresses", jsExcludedAddresses);
QJsonArray jsAllowedDnsServers;
for (const QString& i : m_allowedDnsServers) {
jsAllowedDnsServers.append(QJsonValue(i));
}
json.insert("allowedDnsServers", jsAllowedDnsServers);
QJsonArray disabledApps;
for (const QString& i : m_vpnDisabledApps) {
disabledApps.append(QJsonValue(i));

View File

@@ -37,7 +37,6 @@ class InterfaceConfig {
QList<IPAddress> m_allowedIPAddressRanges;
QStringList m_excludedAddresses;
QStringList m_vpnDisabledApps;
QStringList m_allowedDnsServers;
bool m_killSwitchEnabled;
#if defined(MZ_ANDROID) || defined(MZ_IOS)
QString m_installationId;

View File

@@ -123,7 +123,6 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
int appSplitTunnelType = rawConfig.value(amnezia::config_key::appSplitTunnelType).toInt();
QJsonArray splitTunnelApps = rawConfig.value(amnezia::config_key::splitTunnelApps).toArray();
QJsonArray allowedDns = rawConfig.value(amnezia::config_key::allowedDnsServers).toArray();
QJsonObject wgConfig = rawConfig.value(protocolName + "_config_data").toObject();
@@ -227,8 +226,6 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
json.insert("vpnDisabledApps", splitTunnelApps);
json.insert("allowedDnsServers", allowedDns);
json.insert(amnezia::config_key::killSwitchOption, rawConfig.value(amnezia::config_key::killSwitchOption));
if (protocolName == amnezia::config_key::awg) {

View File

@@ -17,8 +17,6 @@
#include "leakdetector.h"
#include "logger.h"
#include "killswitch.h"
constexpr const int WG_TUN_PROC_TIMEOUT = 5000;
constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg";
@@ -184,7 +182,7 @@ bool WireguardUtilsLinux::deleteInterface() {
QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name"));
// double-check + ensure our firewall is installed and enabled
KillSwitch::instance()->disableKillSwitch();
LinuxFirewall::uninstall();
return true;
}

View File

@@ -16,8 +16,6 @@
#include "leakdetector.h"
#include "logger.h"
#include "killswitch.h"
constexpr const int WG_TUN_PROC_TIMEOUT = 5000;
constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg";
@@ -182,7 +180,7 @@ bool WireguardUtilsMacos::deleteInterface() {
QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name"));
// double-check + ensure our firewall is installed and enabled
KillSwitch::instance()->disableKillSwitch();
MacOSFirewall::uninstall();
return true;
}

View File

@@ -29,8 +29,6 @@
#include "logger.h"
#include "platforms/windows/windowsutils.h"
#include "killswitch.h"
#define IPV6_ADDRESS_SIZE 16
// ID for the Firewall Sublayer
@@ -182,24 +180,11 @@ bool WindowsFirewall::enableInterface(int vpnAdapterIndex) {
} \
}
logger.info() << "Enabling Killswitch Using Adapter:" << vpnAdapterIndex;
if (vpnAdapterIndex < 0)
{
IPAddress allv4("0.0.0.0/0");
if (!blockTrafficTo(allv4, MED_WEIGHT,
"Block Internet", "killswitch")) {
return false;
}
IPAddress allv6("::/0");
if (!blockTrafficTo(allv6, MED_WEIGHT,
"Block Internet", "killswitch")) {
return false;
}
} else
logger.info() << "Enabling firewall Using Adapter:" << vpnAdapterIndex;
FW_OK(allowTrafficOfAdapter(vpnAdapterIndex, MED_WEIGHT,
"Allow usage of VPN Adapter"));
"Allow usage of VPN Adapter"));
FW_OK(allowDHCPTraffic(MED_WEIGHT, "Allow DHCP Traffic"));
FW_OK(allowHyperVTraffic(MAX_WEIGHT, "Allow Hyper-V Traffic"));
FW_OK(allowHyperVTraffic(MED_WEIGHT, "Allow Hyper-V Traffic"));
FW_OK(allowTrafficForAppOnAll(getCurrentPath(), MAX_WEIGHT,
"Allow all for AmneziaVPN.exe"));
FW_OK(blockTrafficOnPort(53, MED_WEIGHT, "Block all DNS"));
@@ -277,14 +262,6 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
}
}
for (const QString& dns : config.m_allowedDnsServers) {
logger.debug() << "Allow DNS: " << dns;
if (!allowTrafficTo(QHostAddress(dns), 53, HIGH_WEIGHT,
"Allow DNS-Server", config.m_serverPublicKey)) {
return false;
}
}
if (!config.m_excludedAddresses.empty()) {
for (const QString& i : config.m_excludedAddresses) {
logger.debug() << "excludedAddresses range: " << i;
@@ -336,41 +313,37 @@ bool WindowsFirewall::disablePeerTraffic(const QString& pubkey) {
}
bool WindowsFirewall::disableKillSwitch() {
return KillSwitch::instance()->disableKillSwitch();
}
bool WindowsFirewall::allowAllTraffic() {
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
auto cleanup = qScopeGuard([&] {
if (result != ERROR_SUCCESS) {
FwpmTransactionAbort0(m_sessionHandle);
}
});
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
auto cleanup = qScopeGuard([&] {
if (result != ERROR_SUCCESS) {
logger.error() << "FwpmTransactionBegin0 failed. Return value:.\n"
<< result;
return false;
FwpmTransactionAbort0(m_sessionHandle);
}
});
if (result != ERROR_SUCCESS) {
logger.error() << "FwpmTransactionBegin0 failed. Return value:.\n"
<< result;
return false;
}
for (const auto& filterID : m_peerRules.values()) {
FwpmFilterDeleteById0(m_sessionHandle, filterID);
}
for (const auto& filterID : m_peerRules.values()) {
FwpmFilterDeleteById0(m_sessionHandle, filterID);
}
for (const auto& filterID : qAsConst(m_activeRules)) {
FwpmFilterDeleteById0(m_sessionHandle, filterID);
}
for (const auto& filterID : qAsConst(m_activeRules)) {
FwpmFilterDeleteById0(m_sessionHandle, filterID);
}
// Commit!
result = FwpmTransactionCommit0(m_sessionHandle);
if (result != ERROR_SUCCESS) {
logger.error() << "FwpmTransactionCommit0 failed. Return value:.\n"
<< result;
return false;
}
m_peerRules.clear();
m_activeRules.clear();
logger.debug() << "Firewall Disabled!";
return true;
// Commit!
result = FwpmTransactionCommit0(m_sessionHandle);
if (result != ERROR_SUCCESS) {
logger.error() << "FwpmTransactionCommit0 failed. Return value:.\n"
<< result;
return false;
}
m_peerRules.clear();
m_activeRules.clear();
logger.debug() << "Firewall Disabled!";
return true;
}
bool WindowsFirewall::allowTrafficForAppOnAll(const QString& exePath,

View File

@@ -43,7 +43,6 @@ class WindowsFirewall final : public QObject {
bool enablePeerTraffic(const InterfaceConfig& config);
bool disablePeerTraffic(const QString& pubkey);
bool disableKillSwitch();
bool allowAllTraffic();
private:
static bool initSublayer();

View File

@@ -171,11 +171,6 @@ ErrorCode OpenVpnProtocol::start()
return lastError();
}
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
IpcClient::Interface()->allowTrafficTo(QStringList(NetworkUtilities::getIPAddress(
m_configData.value(amnezia::config_key::hostName).toString())));
#endif
// Detect default gateway
#ifdef Q_OS_MAC
QProcess p;

View File

@@ -95,8 +95,6 @@ namespace amnezia
constexpr char splitTunnelApps[] = "splitTunnelApps";
constexpr char appSplitTunnelType[] = "appSplitTunnelType";
constexpr char allowedDnsServers[] = "allowedDnsServers";
constexpr char killSwitchOption[] = "killSwitchOption";
constexpr char crc[] = "crc";

View File

@@ -129,7 +129,6 @@
<file>ui/qml/Components/SettingsContainersListView.qml</file>
<file>ui/qml/Components/ShareConnectionDrawer.qml</file>
<file>ui/qml/Components/TransportProtoSelector.qml</file>
<file>ui/qml/Components/AddSitePanel.qml</file>
<file>ui/qml/Config/GlobalConfig.qml</file>
<file>ui/qml/Config/qmldir</file>
<file>ui/qml/Controls2/BackButtonType.qml</file>
@@ -144,9 +143,7 @@
<file>ui/qml/Controls2/DropDownType.qml</file>
<file>ui/qml/Controls2/FlickableType.qml</file>
<file>ui/qml/Controls2/Header2Type.qml</file>
<file>ui/qml/Controls2/BaseHeaderType.qml</file>
<file>ui/qml/Controls2/HeaderTypeWithButton.qml</file>
<file>ui/qml/Controls2/HeaderTypeWithSwitcher.qml</file>
<file>ui/qml/Controls2/HeaderType.qml</file>
<file>ui/qml/Controls2/HorizontalRadioButton.qml</file>
<file>ui/qml/Controls2/ImageButtonType.qml</file>
<file>ui/qml/Controls2/LabelWithButtonType.qml</file>
@@ -202,8 +199,6 @@
<file>ui/qml/Pages2/PageSettingsBackup.qml</file>
<file>ui/qml/Pages2/PageSettingsConnection.qml</file>
<file>ui/qml/Pages2/PageSettingsDns.qml</file>
<file>ui/qml/Pages2/PageSettingsKillSwitch.qml</file>
<file>ui/qml/Pages2/PageSettingsKillSwitchExceptions.qml</file>
<file>ui/qml/Pages2/PageSettingsLogging.qml</file>
<file>ui/qml/Pages2/PageSettingsServerData.qml</file>
<file>ui/qml/Pages2/PageSettingsServerInfo.qml</file>

View File

@@ -1,7 +1,7 @@
#include "secure_qsettings.h"
#include "../client/3rd/QSimpleCrypto/src/include/QAead.h"
#include "../client/3rd/QSimpleCrypto/src/include/QBlockCipher.h"
#include "QAead.h"
#include "QBlockCipher.h"
#include "utilities.h"
#include <QDataStream>
#include <QDebug>

View File

@@ -6,7 +6,7 @@
#include <QObject>
#include <QSettings>
#include "../client/3rd/qtkeychain/qtkeychain/keychain.h"
#include "keychain.h"
class SecureQSettings : public QObject
{

View File

@@ -1,2 +1,12 @@
CUR_USER=$(whoami);\
groups $CUR_USER
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); opt="--version";\
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); opt="--version";\
elif which yum > /dev/null 2>&1; then pm=$(which yum); opt="--version";\
elif which pacman > /dev/null 2>&1; then pm=$(which pacman); opt="--version";\
else pm="uname"; opt="-a";\
fi;\
CUR_USER=$(whoami 2>/dev/null || echo $HOME | sed 's/.*\///');\
echo $LANG | grep -qE '^(en_US.UTF-8|C.UTF-8|C)$' || export LC_ALL=C;\
sudo -K;\
if [ "$CUR_USER" = "root" ] || ( groups "$CUR_USER" | grep -E '\<(sudo|wheel)\>' ); then \
sudo -nu $CUR_USER $pm $opt > /dev/null; sudo -n $pm $opt > /dev/null;\
fi

View File

@@ -1,4 +1,4 @@
CUR_USER=$(whoami);\
CUR_USER=$(whoami 2>/dev/null || echo ~ | sed 's/.*\///');\
sudo mkdir -p $DOCKERFILE_FOLDER;\
sudo chown $CUR_USER $DOCKERFILE_FOLDER;\
if ! sudo docker network ls | grep -q amnezia-dns-net; then sudo docker network create \

View File

@@ -443,16 +443,6 @@ void Settings::setKillSwitchEnabled(bool enabled)
setValue("Conf/killSwitchEnabled", enabled);
}
bool Settings::isStrictKillSwitchEnabled() const
{
return value("Conf/strictKillSwitchEnabled", false).toBool();
}
void Settings::setStrictKillSwitchEnabled(bool enabled)
{
setValue("Conf/strictKillSwitchEnabled", enabled);
}
QString Settings::getInstallationUuid(const bool needCreate)
{
auto uuid = value("Conf/installationUuid", "").toString();
@@ -558,13 +548,3 @@ void Settings::disableHomeAdLabel()
{
setValue("Conf/homeAdLabelVisible", false);
}
QStringList Settings::allowedDnsServers() const
{
return value("Conf/allowedDnsServers").toStringList();
}
void Settings::setAllowedDnsServers(const QStringList &servers)
{
setValue("Conf/allowedDnsServers", servers);
}

View File

@@ -213,10 +213,6 @@ public:
bool isKillSwitchEnabled() const;
void setKillSwitchEnabled(bool enabled);
bool isStrictKillSwitchEnabled() const;
void setStrictKillSwitchEnabled(bool enabled);
QString getInstallationUuid(const bool needCreate);
void resetGatewayEndpoint();
@@ -229,9 +225,6 @@ public:
bool isHomeAdLabelVisible();
void disableHomeAdLabel();
QStringList allowedDnsServers() const;
void setAllowedDnsServers(const QStringList &servers);
signals:
void saveLogsChanged(bool enabled);
void screenshotsEnabledChanged(bool enabled);

View File

@@ -3334,8 +3334,8 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../core/errorstrings.cpp" line="22"/>
<source>The user does not have permission to use sudo</source>
<translation>ليس لدي المستخدم الصلحيات لأستخدام sudo</translation>
<source>The user is not a member of the sudo group</source>
<translation>المستخدم ليس عضوًا في مجموعة sudo</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="26"/>
@@ -3399,7 +3399,7 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../core/errorstrings.cpp" line="23"/>
<source>Server error: Packet manager error</source>
<source>Server error: Package manager error</source>
<translation>خطأ في الخادم: خطأ في مدير الحزم</translation>
</message>
<message>

View File

@@ -3468,8 +3468,8 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="22"/>
<source>The user does not have permission to use sudo</source>
<translation>The user does not have permission to use sudo</translation>
<source>The user is not a member of the sudo group</source>
<translation>کاربر عضو گروه sudo نیست</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="26"/>
@@ -3590,8 +3590,8 @@ It&apos;s okay as long as it&apos;s from someone you trust.</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="23"/>
<source>Server error: Packet manager error</source>
<translation>Server error: Packet manager error</translation>
<source>Server error: Package manager error</source>
<translation>خطای سرور: خطای مدیر بسته</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="34"/>

View File

@@ -3434,13 +3434,13 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../core/errorstrings.cpp" line="22"/>
<source>The user does not have permission to use sudo</source>
<translation> sudo ि </translation>
<source>The user is not a member of the sudo group</source>
<translation> sudo </translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="23"/>
<source>Server error: Packet manager error</source>
<translation> ि: ि</translation>
<source>Server error: Package manager error</source>
<translation> ि: ि</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="26"/>

View File

@@ -3330,8 +3330,8 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../core/errorstrings.cpp" line="22"/>
<source>The user does not have permission to use sudo</source>
<translation> sudo ကက</translation>
<source>The user is not a member of the sudo group</source>
<translation> sudo </translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="26"/>
@@ -3395,8 +3395,8 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../core/errorstrings.cpp" line="23"/>
<source>Server error: Packet manager error</source>
<translation> မှု: Packet Manager </translation>
<source>Server error: Package manager error</source>
<translation> - Package manager </translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="34"/>

View File

@@ -6,7 +6,7 @@
<message>
<location filename="../ui/qml/Components/AdLabel.qml" line="57"/>
<source>Amnezia Premium - for access to any website</source>
<translation>Amnezia Premium - для доступа к любым сайтам </translation>
<translation>Amnezia Premium - для доступа к любым сайтам</translation>
</message>
</context>
<context>
@@ -15,37 +15,37 @@
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="31"/>
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="34"/>
<source>Active</source>
<translation type="unfinished"></translation>
<translation>Активна</translation>
</message>
<message>
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="34"/>
<source>Inactive</source>
<translation type="unfinished"></translation>
<translation>Не активна</translation>
</message>
<message>
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="47"/>
<source>%1 out of %2</source>
<translation type="unfinished"></translation>
<translation>%1 из %2</translation>
</message>
<message>
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="51"/>
<source>Classic VPN for seamless work, downloading large files, and watching videos. Access all websites and online resources. Speeds up to 200 Mbps</source>
<translation type="unfinished"></translation>
<translation>Классический VPN для комфортной работы, загрузки больших файлов и просмотра видео. Доступ ко всем сайтам и онлайн-ресурсам. Скорость до 200 Мбит/с</translation>
</message>
<message>
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="54"/>
<source>Free unlimited access to a basic set of websites such as Facebook, Instagram, Twitter (X), Discord, Telegram and more. YouTube is not included in the free plan.</source>
<translation type="unfinished"></translation>
<translation>Бесплатный неограниченный доступ к базовому набору сайтов и приложений, таким как Facebook, Instagram, Twitter (X), Discord, Telegram и другим. YouTube не включен в бесплатный тариф.</translation>
</message>
<message>
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="125"/>
<source>amnezia_free_support_bot</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/models/api/apiAccountInfoModel.cpp" line="127"/>
<source>amnezia_premium_support_bot</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
</context>
<context>
@@ -53,17 +53,17 @@
<message>
<location filename="../ui/controllers/api/apiConfigsController.cpp" line="203"/>
<source>%1 installed successfully.</source>
<translation type="unfinished">%1 успешно установлен.</translation>
<translation>%1 успешно установлен.</translation>
</message>
<message>
<location filename="../ui/controllers/api/apiConfigsController.cpp" line="258"/>
<source>API config reloaded</source>
<translation type="unfinished">Конфигурация API перезагружена</translation>
<translation>Конфигурация API перезагружена</translation>
</message>
<message>
<location filename="../ui/controllers/api/apiConfigsController.cpp" line="262"/>
<source>Successfully changed the country of connection to %1</source>
<translation type="unfinished">Изменение страны подключения на %1</translation>
<translation>Страна подключения изменена на %1</translation>
</message>
</context>
<context>
@@ -92,18 +92,18 @@
<message>
<location filename="../ui/models/api/apiServicesModel.cpp" line="68"/>
<source>Amnezia Premium is VPN for comfortable work, downloading large files and watching videos in 8K resolution. Works for any sites with no restrictions. Speed up to %1 MBit/s. Unlimited traffic.</source>
<translation type="unfinished"></translation>
<translation>Amnezia Premium VPN для комфортной работы, скачивания больших файлов и просмотра видео в высоком разрешении. Скорость до %1 Мбит/с. Безлимитный трафик.</translation>
</message>
<message>
<location filename="../ui/models/api/apiServicesModel.cpp" line="72"/>
<location filename="../ui/models/api/apiServicesModel.cpp" line="85"/>
<source>AmneziaFree provides free unlimited access to a basic set of web sites, such as Facebook, Instagram, Twitter (X), Discord, Telegram, and others. YouTube is not included in the free plan.</source>
<translation type="unfinished"></translation>
<translation>AmneziaFree предоставляет бесплатный неограниченный доступ к базовому набору сайтов и приложений, таким как Facebook, Instagram, Twitter (X), Discord, Telegram и другим. YouTube не включен в бесплатный тариф.</translation>
</message>
<message>
<location filename="../ui/models/api/apiServicesModel.cpp" line="82"/>
<source>Amnezia Premium is VPN for comfortable work, downloading large files and watching videos in 8K resolution. Works for any sites with no restrictions.</source>
<translation type="unfinished"></translation>
<translation>Amnezia Premium VPN для комфортной работы, скачивания больших файлов и просмотра видео в высоком разрешении. Работает для любых сайтов без ограничений.</translation>
</message>
<message>
<location filename="../ui/models/api/apiServicesModel.cpp" line="97"/>
@@ -562,12 +562,12 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="97"/>
<source>AmneziaWG settings</source>
<translation type="unfinished">Настройки AmneziaWG</translation>
<translation>Настройки AmneziaWG</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="105"/>
<source>MTU</source>
<translation type="unfinished">MTU</translation>
<translation>MTU</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="181"/>
@@ -577,7 +577,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="191"/>
<source>Port</source>
<translation type="unfinished">Порт</translation>
<translation>Порт</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="278"/>
@@ -592,7 +592,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="289"/>
<source>Only the settings for this device will be changed</source>
<translation>Будут изменены настройки только для этого устройства.</translation>
<translation>Будут изменены настройки только для этого устройства</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="290"/>
@@ -1036,12 +1036,12 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml" line="91"/>
<source>WG settings</source>
<translation type="unfinished">Настройки WG</translation>
<translation>Настройки WG</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml" line="99"/>
<source>MTU</source>
<translation type="unfinished">MTU</translation>
<translation>MTU</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml" line="116"/>
@@ -1051,12 +1051,12 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml" line="126"/>
<source>Port</source>
<translation type="unfinished">Порт</translation>
<translation>Порт</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml" line="149"/>
<source>Save</source>
<translation type="unfinished">Сохранить</translation>
<translation>Сохранить</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml" line="153"/>
@@ -1508,7 +1508,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="46"/>
<source>support@amnezia.org</source>
<translation type="unfinished"></translation>
<translation>support@amnezia.org</translation>
</message>
<message>
<source>Mail</source>
@@ -1526,7 +1526,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="50"/>
<source>mailto:support@amnezia.org</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="57"/>
@@ -1536,7 +1536,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="58"/>
<source>Discover the source code</source>
<translation type="unfinished"></translation>
<translation>Посмотреть исходный код</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="61"/>
@@ -1551,7 +1551,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="69"/>
<source>Visit official website</source>
<translation type="unfinished"></translation>
<translation>Посетить официальный сайт</translation>
</message>
<message>
<source>https://amnezia.org</source>
@@ -1578,12 +1578,12 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiAvailableCountries.qml" line="84"/>
<source>Location for connection</source>
<translation type="unfinished"></translation>
<translation>Страны для подключения</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiAvailableCountries.qml" line="123"/>
<source>Unable change server location while there is an active connection</source>
<translation type="unfinished">Невозможно изменить локацию во время активного соединения</translation>
<translation>Невозможно изменить локацию во время активного соединения</translation>
</message>
</context>
<context>
@@ -1591,57 +1591,57 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="45"/>
<source>Active devices</source>
<translation type="unfinished"></translation>
<translation>Активные устройства</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="46"/>
<source>Manage currently connected devices</source>
<translation type="unfinished"></translation>
<translation>Управление подключенными устройствами</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="55"/>
<source>You can find the identifier on the Support tab or, for older versions of the app, by tapping &apos;+&apos; and then the three dots at the top of the page.</source>
<translation type="unfinished"></translation>
<translation>Вы можете найти support tag во вкладке «Поддержка» или, в более ранних версиях приложения, нажав «+» на нижней панели, а затем три точки вверху страницы.</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="69"/>
<source> (current device)</source>
<translation type="unfinished"></translation>
<translation> (текущее устройство)</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="70"/>
<source>Support tag: </source>
<translation type="unfinished"></translation>
<translation>Support tag: </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="70"/>
<source>Last updated: </source>
<translation type="unfinished"></translation>
<translation>Последнее обновление: </translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="75"/>
<source>Cannot unlink device during active connection</source>
<translation type="unfinished"></translation>
<translation>Невозможно отвязать устройство во время активного соединения</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="79"/>
<source>Are you sure you want to unlink this device?</source>
<translation type="unfinished"></translation>
<translation>Вы уверены, что хотите отвязать это устройство?</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="80"/>
<source>This will unlink the device from your subscription. You can reconnect it anytime by pressing&#xa0;Connect.</source>
<translation type="unfinished"></translation>
<translation>Это устройство будет отвязано от вашей подписки. Вы можете подключить его снова в любой момент, нажав кнопку &quot;Подключиться&quot;.</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="81"/>
<source>Continue</source>
<translation type="unfinished">Продолжить</translation>
<translation>Продолжить</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="82"/>
<source>Cancel</source>
<translation type="unfinished">Отменить</translation>
<translation>Отменить</translation>
</message>
</context>
<context>
@@ -1649,82 +1649,82 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="22"/>
<source>Windows</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="23"/>
<source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#windows</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="29"/>
<source>macOS</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="30"/>
<source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#macos</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="36"/>
<source>Android</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="37"/>
<source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#android</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="43"/>
<source>AndroidTV</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="44"/>
<source>https://docs.amnezia.org/ru/documentation/instructions/android_tv_connect/</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="50"/>
<source>iOS</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="51"/>
<source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#ios</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="57"/>
<source>Linux</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="58"/>
<source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#linux</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="64"/>
<source>Routers</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="65"/>
<source>https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#routers</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="101"/>
<source>How to connect on another device</source>
<translation type="unfinished"></translation>
<translation>Как подключить другие устройства</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="102"/>
<source>Setup guides on the Amnezia website</source>
<translation type="unfinished"></translation>
<translation>Инструкции по настройке</translation>
</message>
</context>
<context>
@@ -1739,82 +1739,82 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="23"/>
<source>Save AmneziaVPN config</source>
<translation type="unfinished">Сохранить конфигурацию AmneziaVPN</translation>
<translation>Сохранить конфигурацию AmneziaVPN</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="48"/>
<source>Configuration files</source>
<translation type="unfinished"></translation>
<translation>Файл конфигурации</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="49"/>
<source>For router setup or the AmneziaWG app</source>
<translation type="unfinished"></translation>
<translation>Для настройки роутера или приложения AmneziaWG</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="61"/>
<source>The configuration needs to be reissued</source>
<translation type="unfinished"></translation>
<translation>Необходимо заново скачать конфигурацию и добавить ее в приложение</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="126"/>
<source> configuration file</source>
<translation type="unfinished"></translation>
<translation> файл конфигурации</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="132"/>
<source>Generate a new configuration file</source>
<translation type="unfinished"></translation>
<translation>Создать новый файл конфигурации</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="133"/>
<source>The previously created one will stop working</source>
<translation type="unfinished"></translation>
<translation>Ранее созданный файл перестанет работать</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="144"/>
<source>Revoke the current configuration file</source>
<translation type="unfinished"></translation>
<translation>Отозвать текущий файл конфигурации</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="177"/>
<source>Config file saved</source>
<translation type="unfinished"></translation>
<translation>Файл конфигурации сохранен</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="191"/>
<source>The config has been revoked</source>
<translation type="unfinished"></translation>
<translation>Конфигурация была отозвана</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="198"/>
<source>Generate a new %1 configuration file?</source>
<translation type="unfinished"></translation>
<translation>Создать новый %1 файл конфигурации?</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="200"/>
<source>Revoke the current %1 configuration file?</source>
<translation type="unfinished"></translation>
<translation>Отозвать текущий %1 файл конфигурации?</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="203"/>
<source>Your previous configuration file will no longer work, and it will not be possible to connect using it</source>
<translation type="unfinished"></translation>
<translation>Ваш предыдущий файл конфигурации не будет работать, и вы больше не сможете использовать его для подключения</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="204"/>
<source>Download</source>
<translation type="unfinished"></translation>
<translation>Скачать</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="204"/>
<source>Continue</source>
<translation type="unfinished">Продолжить</translation>
<translation>Продолжить</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="205"/>
<source>Cancel</source>
<translation type="unfinished">Отменить</translation>
<translation>Отменить</translation>
</message>
</context>
<context>
@@ -1834,7 +1834,7 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="37"/>
<source>Valid until</source>
<translation type="unfinished"></translation>
<translation>Действует до</translation>
</message>
<message>
<source>Speed</source>
@@ -1847,67 +1847,67 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="29"/>
<source>Subscription status</source>
<translation type="unfinished"></translation>
<translation>Статус подписки</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="45"/>
<source>Active connections</source>
<translation type="unfinished"></translation>
<translation>Активные соединения</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="171"/>
<source>Configurations have been updated for some countries. Download and install the updated configuration files</source>
<translation type="unfinished"></translation>
<translation>Сетевые адреса одного или нескольких серверов были обновлены. Пожалуйста, удалите старые конфигурацию и загрузите новые файлы</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="186"/>
<source>Subscription key</source>
<translation type="unfinished"></translation>
<translation>Ключ для подключения</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="190"/>
<source>Amnezia Premium subscription key</source>
<translation type="unfinished"></translation>
<translation>Ключ подписки Amnezia Premium</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="194"/>
<source>Save VPN key to file</source>
<translation type="unfinished"></translation>
<translation>Сохранить VPN-ключ в файле</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="195"/>
<source>Copy VPN key</source>
<translation type="unfinished"></translation>
<translation>Скопировать VPN ключ</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="216"/>
<source>Configuration files</source>
<translation type="unfinished"></translation>
<translation>Файл конфигурации</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="218"/>
<source>Manage configuration files</source>
<translation type="unfinished"></translation>
<translation>Управление файлами конфигурации</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="236"/>
<source>Active devices</source>
<translation type="unfinished"></translation>
<translation>Активные устройства</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="238"/>
<source>Manage currently connected devices</source>
<translation type="unfinished"></translation>
<translation>Управление подключенными устройствами</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="255"/>
<source>Support</source>
<translation type="unfinished"></translation>
<translation>Поддержка</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="270"/>
<source>How to connect on another device</source>
<translation type="unfinished"></translation>
<translation>Как подключить другие устройства</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="295"/>
@@ -1941,22 +1941,22 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="332"/>
<source>Unlink this device</source>
<translation type="unfinished"></translation>
<translation>Отвязать это устройство</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="335"/>
<source>Are you sure you want to unlink this device?</source>
<translation type="unfinished"></translation>
<translation>Вы уверены, что хотите отвязать это устройство?</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="336"/>
<source>This will unlink the device from your subscription. You can reconnect it anytime by pressing&#xa0;Connect.</source>
<translation type="unfinished"></translation>
<translation>Это устройство будет отвязано от вашей подписки. Вы можете подключить его снова в любой момент, нажав кнопку Подключиться.</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="342"/>
<source>Cannot unlink device during active connection</source>
<translation type="unfinished"></translation>
<translation>Невозможно отвязать устройство во время активного соединения</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiServerInfo.qml" line="370"/>
@@ -1979,57 +1979,57 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="22"/>
<source>Telegram</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="30"/>
<source>Email Support</source>
<translation type="unfinished"></translation>
<translation>Email</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="31"/>
<source>support@amnezia.org</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="38"/>
<source>Email Billing &amp; Orders</source>
<translation type="unfinished"></translation>
<translation>По вопросам оплаты</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="39"/>
<source>help@vpnpay.io</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="46"/>
<source>Website</source>
<translation type="unfinished">Веб-сайт</translation>
<translation>Сайт</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="47"/>
<source>amnezia.org</source>
<translation type="unfinished"></translation>
<translation>amnezia.org</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="81"/>
<source>Support</source>
<translation type="unfinished"></translation>
<translation>Поддержка</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="82"/>
<source>Our technical support specialists are available to assist you at any time</source>
<translation type="unfinished"></translation>
<translation>Наши специалисты технической поддержки всегда готовы помочь вам.</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="109"/>
<source>Support tag</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="119"/>
<source>Copied</source>
<translation type="unfinished">Скопировано</translation>
<translation>Скопировано</translation>
</message>
</context>
<context>
@@ -2730,27 +2730,27 @@ Already installed containers were found on the server. All installed containers
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="98"/>
<source> connection settings</source>
<translation type="unfinished"></translation>
<translation> настройки подключения</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="110"/>
<source>Click the &quot;connect&quot; button to create a connection configuration</source>
<translation type="unfinished"></translation>
<translation>Нажмите кнопку «Подключиться», чтобы создать конфигурацию</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="130"/>
<source> server settings</source>
<translation type="unfinished"> настройки сервера</translation>
<translation> настройки сервера</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="172"/>
<source>Clear profile</source>
<translation type="unfinished">Очистить профиль</translation>
<translation>Очистить профиль</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="176"/>
<source>The connection configuration will be deleted for this device only</source>
<translation type="unfinished">Конфигурация подключения будет удалена только для этого устройства</translation>
<translation>Конфигурация подключения будет удалена только на этом устройстве</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSettingsServerProtocol.qml" line="182"/>
@@ -3201,17 +3201,17 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardEasy.qml" line="68"/>
<source>Choose Installation Type</source>
<translation type="unfinished"></translation>
<translation>Выберите тип установки</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardEasy.qml" line="142"/>
<source>Manual</source>
<translation type="unfinished"></translation>
<translation>Ручная</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardEasy.qml" line="143"/>
<source>Choose a VPN protocol</source>
<translation>Выберите VPN-протокол</translation>
<translation>Выбрать VPN-протокол</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageSetupWizardEasy.qml" line="200"/>
@@ -3711,7 +3711,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<message>
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="134"/>
<source>Access error!</source>
<translation type="unfinished">Ошибка доступа!</translation>
<translation>Ошибка доступа!</translation>
</message>
<message>
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="140"/>
@@ -4009,7 +4009,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<message>
<location filename="../core/errorstrings.cpp" line="15"/>
<source>The selected protocol is not supported on the current platform</source>
<translation type="unfinished">Выбранный протокол не поддерживается на данном устройстве</translation>
<translation>Выбранный протокол не поддерживается на данном устройстве</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="18"/>
@@ -4038,13 +4038,13 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="23"/>
<source>The user does not have permission to use sudo</source>
<translation>У пользователя нет прав на использование sudo</translation>
<source>The user is not a member of the sudo group</source>
<translation>Пользователь не входит в группу sudo</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="24"/>
<source>Server error: Packet manager error</source>
<translation>Ошибка сервера: ошибка менеджера пакетов</translation>
<source>Server error: Package manager error</source>
<translation>Ошибка сервера: Ошибка менеджера пакетов</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="27"/>
@@ -4217,7 +4217,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<location filename="../core/errorstrings.cpp" line="55"/>
<source>VPN Protocols is not installed.
Please install VPN container at first</source>
<translation type="unfinished">VPN-протоколы не установлены.
<translation>VPN-протоколы не установлены.
Пожалуйста, установите протокол</translation>
</message>
<message>
@@ -4248,7 +4248,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<message>
<location filename="../core/errorstrings.cpp" line="67"/>
<source>Failed to decrypt response payload</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="68"/>
@@ -4258,7 +4258,7 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
<message>
<location filename="../core/errorstrings.cpp" line="69"/>
<source>The limit of allowed configurations per subscription has been exceeded</source>
<translation type="unfinished"></translation>
<translation>Превышен лимит разрешенных конфигураций для одной подписки</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="73"/>
@@ -4435,27 +4435,27 @@ While it offers a blend of security, stability, and speed, it&apos;s essential t
<message>
<location filename="../containers/containers_defs.cpp" line="113"/>
<source>Shadowsocks masks VPN traffic, making it resemble normal web traffic, but it may still be detected by certain analysis systems.</source>
<translation type="unfinished"></translation>
<translation>Shadowsocks маскирует VPN-трафик, делая его похожим на обычный веб-трафик, но он все равно может быть обнаружен некоторыми системами анализа.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="115"/>
<source>OpenVPN over Cloak - OpenVPN with VPN masquerading as web traffic and protection against active-probing detection. It is very resistant to detection, but offers low speed.</source>
<translation type="unfinished"></translation>
<translation>OpenVPN over Cloak OpenVPN с маскировкой под веб-трафик , а также с защитой от обнаружения и систем анализа трафика. Он очень устойчив к обнаружению, но имеет низкую скорость работы в сравнении с другими похожими протоколами.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="118"/>
<source>WireGuard - popular VPN protocol with high performance, high speed and low power consumption.</source>
<translation type="unfinished"></translation>
<translation>WireGuard популярный VPN-протокол с высокой производительностью, высокой скоростью и низким энергопотреблением.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="121"/>
<source>AmneziaWG is a special protocol from Amnezia based on WireGuard. It provides high connection speed and ensures stable operation even in the most challenging network conditions.</source>
<translation type="unfinished"></translation>
<translation>AmneziaWG специальный протокол от Amnezia, основанный на WireGuard. Он обеспечивает высокую скорость соединения и гарантирует стабильную работу даже в самых сложных условиях.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="124"/>
<source>XRay with REALITY masks VPN traffic as web traffic and protects against active probing. It is highly resistant to detection and offers high speed.</source>
<translation type="unfinished"></translation>
<translation>XRay с REALITY маскирует VPN-трафик под веб-трафик. Обладает высокой устойчивостью к обнаружению и обеспечивает высокую скорость соединения.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="143"/>
@@ -4467,7 +4467,12 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
* Flexible customisation to suit user needs to work with different operating systems and devices
* Recognised by DPI systems and therefore susceptible to blocking
* Can operate over both TCP and UDP network protocols.</source>
<translation type="unfinished"></translation>
<translation>OpenVPN является одним из самых популярных и проверенных временем VPN-протоколов. Он использует собственный протокол безопасности, и криптографические протоколы SSL/TLS для шифрования и обмена ключами. Более того, поддержка множества методов аутентификации делает OpenVPN универсальным, адаптируемым и подходящим для широкого спектра устройств и операционных систем. Благодаря своему открытому коду, OpenVPN подвергается тщательной проверке со стороны мирового сообщества, что постоянно укрепляет его безопасность. Имея отличный баланс между производительностью, безопасностью и совместимостью OpenVPN остается лучшим выбором для людей и компаний, заботящихся о конфиденциальности, однако OpenVPN легко распознается современными системами анализа трафика.
Доступен в AmneziaVPN на всех платформах
Нормальное энергопотребление на мобильных устройствах
Гибкая настройка полезная при работе с различными операционными системами и устройствами
Распознается системами DPI и, следовательно, уязвим к блокировкам
Может работать как по TCP, так и по UDP протоколу.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="168"/>
@@ -4487,7 +4492,22 @@ Immediately after receiving the first data packet, Cloak authenticates the incom
* Not recognised by detection 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 становится невидимым для систем анализа. Имеет низкую скорость работы в сравнении с другими похожими протоколами.
* Доступно в AmneziaVPN на всех платформах.
* Высокое энергопотребление на мобильных устройствах
* Гибкие настройки
* Не распознается системами обнаружения.
* Работает по сетевому протоколу TCP, порт 443.
</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="185"/>
@@ -4500,7 +4520,15 @@ WireGuard is very susceptible to detection and blocking due to its distinct pack
* 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 легко идентифицируются системами анализа трафика.
* Доступно в AmneziaVPN на всех платформах.
* Низкое энергопотребление
* Минимальное количество настроек
* Легко распознается системами анализа DPI, подвержен блокировке.
* Работает по сетевому протоколу UDP.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="198"/>
@@ -4513,7 +4541,15 @@ This means that AmneziaWG keeps the fast performance of the original while addin
* Minimum number of settings
* Not recognised by traffic analysis systems
* Works over UDP network protocol.</source>
<translation type="unfinished"></translation>
<translation>AmneziaWG это современная версия популярного VPN протокола, основанная на базе WireGuard, сохранившая упрощенную архитектуру и высокопроизводительные возможности на всех устройствах.
Хотя WireGuard известен своей эффективностью, обнаружить его довольно легко из-за различных сигнатур пакетов. AmneziaWG решает эту проблему, используя более совершенные методы работы, смешивая свой трафик с обычным интернет-трафиком.
Это означает, что AmneziaWG сохраняет высокую производительность оригинала, добавляя при этом дополнительный уровень скрытности, что делает его отличным выбором для тех, кому нужно быстрое и незаметное VPN-соединение.
* Доступно в AmneziaVPN на всех платформах.
* Низкое энергопотребление
* Минимальное количество настроек
* Не распознается системами анализа трафика.
* Работает по сетевому протоколу UDP.</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="214"/>
@@ -4521,7 +4557,10 @@ This means that AmneziaWG keeps the fast performance of the original while addin
It uniquely identifies attackers during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting attackers to genuine websites, 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. This makes REALITY a robust solution for maintaining internet freedom.</source>
<translation type="unfinished"></translation>
<translation>Протокол REALITY, современная разработка от создателей XRay. Призван обеспечить высочайший уровень защиты от обнаружения благодаря инновационному подходу к безопасности и конфиденциальности.
Он безошибочно идентифицирует злоумышленников на этапе установления связи TLS, беспрепятственно работая в качестве прокси-сервера для оригинального клиента и перенаправляя злоумышленников на подлинные веб-сайты, предоставляя тем самым подлинный сертификат TLS и данные.
Эта расширенная возможность отличает REALITY от аналогичных технологий тем, что способна маскироваться под случайный веб-трафик без использования специальных настроек.
В отличие от старых протоколов, таких как VMess, VLESS и транспорт XTLS-Vision, REALITY имеет инновационную технологию распознавания «свой-чужой».Это делает REALITY надежным решением для обеспечения доступа к свободному интернету.</translation>
</message>
<message>
<source>WireGuard - New popular VPN protocol with high performance, high speed and low power consumption. Recommended for regions with low levels of censorship.</source>
@@ -4575,12 +4614,12 @@ It employs its unique security protocol, leveraging the strength of SSL/TLS for
* Configurable encryption protocol
* Detectable by some DPI systems
* Works over TCP network protocol.</source>
<translation>Shadowsocks создан на основе протокола SOCKS5, защищает соединение с помощью шифра AEAD. Несмотря на то, что протокол Shadowsocks разработан таким образом, чтобы быть незаметным и сложным для идентификации, он не идентичен стандартному HTTPS-соединению. Поэтому некоторые системы анализа трафика всё же могут обнаружить соединение Shadowsocks. В связи с ограниченной поддержкой в Amnezia рекомендуется использовать протокол AmneziaWG.
<translation>Shadowsocks создан на основе протокола SOCKS5, защищает соединение с помощью шифра AEAD. Несмотря на то, что протокол Shadowsocks разработан таким образом, чтобы быть незаметным и сложным для идентификации, он не идентичен стандартному HTTPS-соединению, поэтому некоторые системы анализа трафика всё же могут обнаружить соединение Shadowsocks. В связи с ограниченной поддержкой в Amnezia рекомендуется использовать протокол AmneziaWG.
* Доступен в AmneziaVPN только для ПК и ноутбуков
* Настраиваемый протокол шифрования
* Распознается некоторыми системами DPI-анализа
* Работает по сетевому протоколу TCP</translation>
* Работает по сетевому протоколу TCP.</translation>
</message>
<message>
<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.
@@ -4911,12 +4950,12 @@ This means that AmneziaWG keeps the fast performance of the original while addin
<message>
<location filename="../ui/qml/Components/RenameServerDrawer.qml" line="30"/>
<source>Server name</source>
<translation type="unfinished">Имя сервера</translation>
<translation>Имя сервера</translation>
</message>
<message>
<location filename="../ui/qml/Components/RenameServerDrawer.qml" line="41"/>
<source>Save</source>
<translation type="unfinished">Сохранить</translation>
<translation>Сохранить</translation>
</message>
</context>
<context>
@@ -4932,7 +4971,7 @@ This means that AmneziaWG keeps the fast performance of the original while addin
<message>
<location filename="../ui/qml/Components/ServersListView.qml" line="86"/>
<source>Unable change server while there is an active connection</source>
<translation type="unfinished">Невозможно изменить сервер во время активного соединения</translation>
<translation>Невозможно изменить сервер во время активного соединения</translation>
</message>
</context>
<context>
@@ -5186,12 +5225,12 @@ This means that AmneziaWG keeps the fast performance of the original while addin
<message>
<location filename="../containers/containers_defs.cpp" line="338"/>
<source>Automatic</source>
<translation type="unfinished"></translation>
<translation>Автоматическая</translation>
</message>
<message>
<location filename="../containers/containers_defs.cpp" line="346"/>
<source>AmneziaWG protocol will be installed. It provides high connection speed and ensures stable operation even in the most challenging network conditions.</source>
<translation type="unfinished"></translation>
<translation>Будет установлен протокол AmneziaWG. Он обеспечивает высокую скорость соединения и гарантирует стабильную работу даже в самых сложных условиях.</translation>
</message>
</context>
<context>

View File

@@ -3700,13 +3700,13 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="22"/>
<source>The user does not have permission to use sudo</source>
<translation>The user does not have permission to use sudo</translation>
<source>The user is not a member of the sudo group</source>
<translation>Користувач не входить до групи sudo</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="23"/>
<source>Server error: Packet manager error</source>
<translation type="unfinished"></translation>
<source>Server error: Package manager error</source>
<translation>Помилка сервера: Помилка менеджера пакетів</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="26"/>

View File

@@ -3433,8 +3433,8 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../core/errorstrings.cpp" line="22"/>
<source>The user does not have permission to use sudo</source>
<translation>صارف کو sudo استعمال کرنے کی اجازت نہیں ہے</translation>
<source>The user is not a member of the sudo group</source>
<translation>صارف sudo گروپ کا رکن نہیں ہے</translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="26"/>
@@ -3498,7 +3498,7 @@ Already installed containers were found on the server. All installed containers
</message>
<message>
<location filename="../core/errorstrings.cpp" line="23"/>
<source>Server error: Packet manager error</source>
<source>Server error: Package manager error</source>
<translation>سرور خطا: پیکیج منیجر خطا</translation>
</message>
<message>

View File

@@ -3675,13 +3675,13 @@ and will not be shared or disclosed to the Amnezia or any third parties</source>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="22"/>
<source>The user does not have permission to use sudo</source>
<translation>root权限</translation>
<source>The user is not a member of the sudo group</source>
<translation> sudo </translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="23"/>
<source>Server error: Packet manager error</source>
<translation type="unfinished"></translation>
<source>Server error: Package manager error</source>
<translation></translation>
</message>
<message>
<location filename="../core/errorstrings.cpp" line="26"/>

View File

@@ -1,101 +0,0 @@
#include "allowedDnsController.h"
#include <QFile>
#include <QStandardPaths>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include "systemController.h"
#include "core/networkUtilities.h"
#include "core/defs.h"
AllowedDnsController::AllowedDnsController(const std::shared_ptr<Settings> &settings,
const QSharedPointer<AllowedDnsModel> &allowedDnsModel,
QObject *parent)
: QObject(parent), m_settings(settings), m_allowedDnsModel(allowedDnsModel)
{
}
void AllowedDnsController::addDns(QString ip)
{
if (ip.isEmpty()) {
return;
}
if (!NetworkUtilities::ipAddressRegExp().match(ip).hasMatch()) {
emit errorOccurred(tr("The address does not look like a valid IP address"));
return;
}
if (m_allowedDnsModel->addDns(ip)) {
emit finished(tr("New DNS server added: %1").arg(ip));
} else {
emit errorOccurred(tr("DNS server already exists: %1").arg(ip));
}
}
void AllowedDnsController::removeDns(int index)
{
auto modelIndex = m_allowedDnsModel->index(index);
auto ip = m_allowedDnsModel->data(modelIndex, AllowedDnsModel::Roles::IpRole).toString();
m_allowedDnsModel->removeDns(modelIndex);
emit finished(tr("DNS server removed: %1").arg(ip));
}
void AllowedDnsController::importDns(const QString &fileName, bool replaceExisting)
{
QByteArray jsonData;
if (!SystemController::readFile(fileName, jsonData)) {
emit errorOccurred(tr("Can't open file: %1").arg(fileName));
return;
}
QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonData);
if (jsonDocument.isNull()) {
emit errorOccurred(tr("Failed to parse JSON data from file: %1").arg(fileName));
return;
}
if (!jsonDocument.isArray()) {
emit errorOccurred(tr("The JSON data is not an array in file: %1").arg(fileName));
return;
}
auto jsonArray = jsonDocument.array();
QStringList dnsServers;
for (auto jsonValue : jsonArray) {
auto ip = jsonValue.toString();
if (!NetworkUtilities::ipAddressRegExp().match(ip).hasMatch()) {
qDebug() << ip << " is not a valid IP address";
continue;
}
dnsServers.append(ip);
}
m_allowedDnsModel->addDnsList(dnsServers, replaceExisting);
emit finished(tr("Import completed"));
}
void AllowedDnsController::exportDns(const QString &fileName)
{
auto dnsServers = m_allowedDnsModel->getCurrentDnsServers();
QJsonArray jsonArray;
for (const auto &ip : dnsServers) {
jsonArray.append(ip);
}
QJsonDocument jsonDocument(jsonArray);
QByteArray jsonData = jsonDocument.toJson();
SystemController::saveFile(fileName, jsonData);
emit finished(tr("Export completed"));
}

View File

@@ -1,35 +0,0 @@
#ifndef ALLOWEDDNSCONTROLLER_H
#define ALLOWEDDNSCONTROLLER_H
#include <QObject>
#include "settings.h"
#include "ui/models/allowed_dns_model.h"
class AllowedDnsController : public QObject
{
Q_OBJECT
public:
explicit AllowedDnsController(const std::shared_ptr<Settings> &settings,
const QSharedPointer<AllowedDnsModel> &allowedDnsModel,
QObject *parent = nullptr);
public slots:
void addDns(QString ip);
void removeDns(int index);
void importDns(const QString &fileName, bool replaceExisting);
void exportDns(const QString &fileName);
signals:
void errorOccurred(const QString &errorMessage);
void finished(const QString &message);
void saveFile(const QString &fileName, const QString &data);
private:
std::shared_ptr<Settings> m_settings;
QSharedPointer<AllowedDnsModel> m_allowedDnsModel;
};
#endif // ALLOWEDDNSCONTROLLER_H

View File

@@ -31,15 +31,13 @@ namespace PageLoader
PageSettingsLogging,
PageSettingsSplitTunneling,
PageSettingsAppSplitTunneling,
PageSettingsKillSwitch,
PageSettingsApiServerInfo,
PageSettingsApiAvailableCountries,
PageSettingsApiSupport,
PageSettingsApiInstructions,
PageSettingsApiNativeConfigs,
PageSettingsApiDevices,
PageSettingsKillSwitchExceptions,
PageServiceSftpSettings,
PageServiceTorWebsiteSettings,
PageServiceDnsSettings,

View File

@@ -245,24 +245,6 @@ bool SettingsController::isKillSwitchEnabled()
void SettingsController::toggleKillSwitch(bool enable)
{
m_settings->setKillSwitchEnabled(enable);
emit killSwitchEnabledChanged();
if (enable == false) {
emit strictKillSwitchEnabledChanged(false);
} else {
emit strictKillSwitchEnabledChanged(isStrictKillSwitchEnabled());
}
}
bool SettingsController::isStrictKillSwitchEnabled()
{
return m_settings->isStrictKillSwitchEnabled();
}
void SettingsController::toggleStrictKillSwitch(bool enable)
{
m_settings->setStrictKillSwitchEnabled(enable);
emit strictKillSwitchEnabledChanged(enable);
}
bool SettingsController::isNotificationPermissionGranted()

View File

@@ -24,8 +24,6 @@ public:
Q_PROPERTY(QString secondaryDns READ getSecondaryDns WRITE setSecondaryDns NOTIFY secondaryDnsChanged)
Q_PROPERTY(bool isLoggingEnabled READ isLoggingEnabled WRITE toggleLogging NOTIFY loggingStateChanged)
Q_PROPERTY(bool isNotificationPermissionGranted READ isNotificationPermissionGranted NOTIFY onNotificationStateChanged)
Q_PROPERTY(bool isKillSwitchEnabled READ isKillSwitchEnabled WRITE toggleKillSwitch NOTIFY killSwitchEnabledChanged)
Q_PROPERTY(bool strictKillSwitchEnabled READ isStrictKillSwitchEnabled WRITE toggleStrictKillSwitch NOTIFY strictKillSwitchEnabledChanged)
Q_PROPERTY(bool isDevModeEnabled READ isDevModeEnabled NOTIFY devModeEnabled)
Q_PROPERTY(QString gatewayEndpoint READ getGatewayEndpoint WRITE setGatewayEndpoint NOTIFY gatewayEndpointChanged)
@@ -77,9 +75,6 @@ public slots:
bool isKillSwitchEnabled();
void toggleKillSwitch(bool enable);
bool isStrictKillSwitchEnabled();
void toggleStrictKillSwitch(bool enable);
bool isNotificationPermissionGranted();
void requestNotificationPermission();
@@ -103,8 +98,6 @@ signals:
void primaryDnsChanged();
void secondaryDnsChanged();
void loggingStateChanged();
void killSwitchEnabledChanged();
void strictKillSwitchEnabledChanged(bool enabled);
void restoreBackupFinished();
void changeSettingsFinished(const QString &finishedMessage);

View File

@@ -1,86 +0,0 @@
#include "allowed_dns_model.h"
AllowedDnsModel::AllowedDnsModel(std::shared_ptr<Settings> settings, QObject *parent)
: QAbstractListModel(parent), m_settings(settings)
{
fillDnsServers();
}
int AllowedDnsModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_dnsServers.size();
}
QVariant AllowedDnsModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() < 0 || index.row() >= static_cast<int>(rowCount()))
return QVariant();
switch (role) {
case IpRole:
return m_dnsServers.at(index.row());
default:
return QVariant();
}
}
bool AllowedDnsModel::addDns(const QString &ip)
{
if (m_dnsServers.contains(ip)) {
return false;
}
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_dnsServers.append(ip);
m_settings->setAllowedDnsServers(m_dnsServers);
endInsertRows();
return true;
}
void AllowedDnsModel::addDnsList(const QStringList &dnsServers, bool replaceExisting)
{
beginResetModel();
if (replaceExisting) {
m_dnsServers.clear();
}
for (const QString &ip : dnsServers) {
if (!m_dnsServers.contains(ip)) {
m_dnsServers.append(ip);
}
}
m_settings->setAllowedDnsServers(m_dnsServers);
endResetModel();
}
void AllowedDnsModel::removeDns(QModelIndex index)
{
if (!index.isValid() || index.row() >= m_dnsServers.size()) {
return;
}
beginRemoveRows(QModelIndex(), index.row(), index.row());
m_dnsServers.removeAt(index.row());
m_settings->setAllowedDnsServers(m_dnsServers);
endRemoveRows();
}
QStringList AllowedDnsModel::getCurrentDnsServers()
{
return m_dnsServers;
}
QHash<int, QByteArray> AllowedDnsModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[IpRole] = "ip";
return roles;
}
void AllowedDnsModel::fillDnsServers()
{
m_dnsServers = m_settings->allowedDnsServers();
}

View File

@@ -1,37 +0,0 @@
#ifndef ALLOWEDDNSMODEL_H
#define ALLOWEDDNSMODEL_H
#include <QAbstractListModel>
#include "settings.h"
class AllowedDnsModel : public QAbstractListModel
{
Q_OBJECT
public:
enum Roles {
IpRole = Qt::UserRole + 1
};
explicit AllowedDnsModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
public slots:
bool addDns(const QString &ip);
void addDnsList(const QStringList &dnsServers, bool replaceExisting);
void removeDns(QModelIndex index);
QStringList getCurrentDnsServers();
protected:
QHash<int, QByteArray> roleNames() const override;
private:
void fillDnsServers();
std::shared_ptr<Settings> m_settings;
QStringList m_dnsServers;
};
#endif // ALLOWEDDNSMODEL_H

View File

@@ -1,73 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Style 1.0
import "../Controls2"
import "../Controls2/TextTypes"
Item {
id: root
property bool enabled: true
property string placeholderText: ""
property alias textField: searchField.textField
signal addClicked(string text)
signal moreClicked()
implicitWidth: 360
implicitHeight: 96
Rectangle {
id: background
anchors.fill: parent
color: "#0E0F12"
opacity: 0.85
z: -1
}
RowLayout {
id: addSiteButton
enabled: root.enabled
spacing: 2
anchors {
fill: parent
topMargin: 16
leftMargin: 16
rightMargin: 16
bottomMargin: 24
}
TextFieldWithHeaderType {
id: searchField
Layout.fillWidth: true
rightButtonClickedOnEnter: true
textField.placeholderText: root.placeholderText
buttonImageSource: "qrc:/images/controls/plus.svg"
clickedFunc: function() {
root.addClicked(textField.text)
textField.text = ""
}
}
ImageButtonType {
id: addSiteButtonImage
implicitWidth: 56
implicitHeight: 56
image: "qrc:/images/controls/more-vertical.svg"
imageColor: AmneziaStyle.color.paleGray
onClicked: root.moreClicked()
Keys.onReturnPressed: addSiteButtonImage.clicked()
Keys.onEnterPressed: addSiteButtonImage.clicked()
}
}
}

View File

@@ -1,45 +0,0 @@
import QtQuick
import QtQuick.Layouts
import Style 1.0
import "TextTypes"
Item {
id: root
property string headerText
property int headerTextMaximumLineCount: 2
property int headerTextElide: Qt.ElideRight
property string descriptionText
property alias headerRow: headerRow
implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight
ColumnLayout {
id: content
anchors.fill: parent
RowLayout {
id: headerRow
Header1TextType {
id: header
Layout.fillWidth: true
text: root.headerText
maximumLineCount: root.headerTextMaximumLineCount
elide: root.headerTextElide
}
}
ParagraphTextType {
id: description
Layout.topMargin: 16
Layout.fillWidth: true
text: root.descriptionText
color: AmneziaStyle.color.mutedGray
visible: root.descriptionText !== ""
}
}
}

View File

@@ -0,0 +1,86 @@
import QtQuick
import QtQuick.Layouts
import Style 1.0
import "TextTypes"
Item {
id: root
property string actionButtonImage
property var actionButtonFunction
property alias actionButton: headerActionButton
property string headerText
property int headerTextMaximumLineCount: 2
property int headerTextElide: Qt.ElideRight
property string descriptionText
implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight
ColumnLayout {
id: content
anchors.fill: parent
RowLayout {
Header1TextType {
id: header
Layout.fillWidth: true
text: root.headerText
maximumLineCount: root.headerTextMaximumLineCount
elide: root.headerTextElide
}
ImageButtonType {
id: headerActionButton
implicitWidth: 40
implicitHeight: 40
Layout.alignment: Qt.AlignRight
image: root.actionButtonImage
imageColor: AmneziaStyle.color.paleGray
visible: image ? true : false
onClicked: {
if (actionButtonFunction && typeof actionButtonFunction === "function") {
actionButtonFunction()
}
}
}
}
ParagraphTextType {
id: description
Layout.topMargin: 16
Layout.fillWidth: true
text: root.descriptionText
color: AmneziaStyle.color.mutedGray
visible: root.descriptionText !== ""
}
}
Keys.onEnterPressed: {
if (actionButtonFunction && typeof actionButtonFunction === "function") {
actionButtonFunction()
}
}
Keys.onReturnPressed: {
if (actionButtonFunction && typeof actionButtonFunction === "function") {
actionButtonFunction()
}
}
}

View File

@@ -1,44 +0,0 @@
import QtQuick
import QtQuick.Layouts
import Style 1.0
BaseHeaderType {
id: root
property string actionButtonImage
property var actionButtonFunction
property alias actionButton: headerActionButton
Component.onCompleted: {
headerRow.children.push(headerActionButton)
}
ImageButtonType {
id: headerActionButton
implicitWidth: 40
implicitHeight: 40
Layout.alignment: Qt.AlignRight
image: root.actionButtonImage
imageColor: AmneziaStyle.color.paleGray
visible: image ? true : false
onClicked: {
if (actionButtonFunction && typeof actionButtonFunction === "function") {
actionButtonFunction()
}
}
}
Keys.onEnterPressed: {
if (actionButtonFunction && typeof actionButtonFunction === "function") {
actionButtonFunction()
}
}
Keys.onReturnPressed: {
if (actionButtonFunction && typeof actionButtonFunction === "function") {
actionButtonFunction()
}
}
}

View File

@@ -1,28 +0,0 @@
import QtQuick
import QtQuick.Layouts
import Style 1.0
BaseHeaderType {
id: root
property var switcherFunction
property bool showSwitcher: false
property alias switcher: headerSwitcher
Component.onCompleted: {
headerRow.children.push(headerSwitcher)
}
SwitcherType {
id: headerSwitcher
Layout.alignment: Qt.AlignRight
visible: root.showSwitcher
onToggled: {
if (switcherFunction && typeof switcherFunction === "function") {
switcherFunction(checked)
}
}
}
}

View File

@@ -20,11 +20,7 @@ RadioButton {
property string selectedColor: AmneziaStyle.color.transparent
property string textColor: AmneziaStyle.color.paleGray
property string textDisabledColor: AmneziaStyle.color.mutedGray
property string selectedTextColor: AmneziaStyle.color.goldenApricot
property string selectedTextDisabledColor: AmneziaStyle.color.burntOrange
property string descriptionColor: AmneziaStyle.color.mutedGray
property string descriptionDisabledColor: AmneziaStyle.color.charcoalGray
property string borderFocusedColor: AmneziaStyle.color.paleGray
property int borderFocusedWidth: 1
@@ -34,12 +30,6 @@ RadioButton {
property bool isFocusable: true
property string radioButtonInnerCirclePressedSource: "qrc:/images/controls/radio-button-inner-circle-pressed.png"
property string radioButtonInnerCircleSource: "qrc:/images/controls/radio-button-inner-circle.png"
property string radioButtonPressedSource: "qrc:/images/controls/radio-button-pressed.svg"
property string radioButtonDefaultSource: "qrc:/images/controls/radio-button.svg"
Keys.onTabPressed: {
FocusController.nextKeyTabItem()
}
@@ -104,15 +94,14 @@ RadioButton {
if (showImage) {
return imageSource
} else if (root.pressed) {
return root.radioButtonInnerCirclePressedSource
return "qrc:/images/controls/radio-button-inner-circle-pressed.png"
} else if (root.checked) {
return root.radioButtonInnerCircleSource
return "qrc:/images/controls/radio-button-inner-circle.png"
}
return ""
}
opacity: root.enabled ? 1.0 : 0.3
anchors.centerIn: parent
width: 24
@@ -124,13 +113,12 @@ RadioButton {
if (showImage) {
return ""
} else if (root.pressed || root.checked) {
return root.radioButtonPressedSource
return "qrc:/images/controls/radio-button-pressed.svg"
} else {
return root.radioButtonDefaultSource
return "qrc:/images/controls/radio-button.svg"
}
}
opacity: root.enabled ? 1.0 : 0.3
anchors.centerIn: parent
width: 24
@@ -160,11 +148,10 @@ RadioButton {
elide: root.textElide
color: {
if (root.enabled) {
return root.checked ? selectedTextColor : textColor
} else {
return root.checked ? selectedTextDisabledColor : textDisabledColor
if (root.checked) {
return selectedTextColor
}
return textColor
}
Layout.fillWidth: true
@@ -177,7 +164,7 @@ RadioButton {
CaptionTextType {
id: description
color: root.enabled ? root.descriptionColor : root.descriptionDisabledColor
color: AmneziaStyle.color.mutedGray
text: root.descriptionText
visible: root.descriptionText !== ""

View File

@@ -56,7 +56,7 @@ PageType {
anchors.rightMargin: 16
anchors.leftMargin: 16
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
Layout.topMargin: 20

View File

@@ -39,7 +39,7 @@ PageType {
header: ColumnLayout {
width: listView.width
BaseHeaderType {
HeaderType {
id: header
Layout.fillWidth: true

View File

@@ -91,7 +91,7 @@ PageType {
spacing: 0
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
headerText: qsTr("AmneziaWG settings")

View File

@@ -91,7 +91,7 @@ PageType {
spacing: 0
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
headerText: qsTr("AmneziaWG settings")

View File

@@ -76,7 +76,7 @@ PageType {
spacing: 0
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
headerText: qsTr("Cloak settings")

View File

@@ -75,7 +75,7 @@ PageType {
spacing: 0
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
headerText: qsTr("OpenVPN settings")

View File

@@ -32,7 +32,7 @@ PageType {
id: backButton
}
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16

View File

@@ -78,7 +78,7 @@ PageType {
spacing: 0
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
headerText: qsTr("Shadowsocks settings")

View File

@@ -85,7 +85,7 @@ PageType {
spacing: 0
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
headerText: qsTr("WG settings")

View File

@@ -77,7 +77,7 @@ PageType {
spacing: 0
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
headerText: qsTr("WG settings")
}

View File

@@ -75,7 +75,7 @@ PageType {
spacing: 0
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
headerText: qsTr("XRay settings")
}

View File

@@ -43,7 +43,7 @@ PageType {
anchors.left: parent.left
anchors.right: parent.right
BaseHeaderType {
HeaderType {
id: header
Layout.fillWidth: true

View File

@@ -85,7 +85,7 @@ PageType {
spacing: 0
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16

View File

@@ -77,7 +77,7 @@ PageType {
spacing: 0
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
@@ -217,7 +217,7 @@ PageType {
}
}
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
headerText: qsTr("SOCKS5 settings")

View File

@@ -54,7 +54,7 @@ PageType {
spacing: 0
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16

View File

@@ -29,7 +29,7 @@ PageType {
spacing: 0
BaseHeaderType {
HeaderType {
id: header
Layout.fillWidth: true
Layout.topMargin: 24

View File

@@ -69,7 +69,7 @@ PageType {
Layout.topMargin: 20
}
HeaderTypeWithButton {
HeaderType {
id: headerContent
objectName: "headerContent"

View File

@@ -35,7 +35,7 @@ PageType {
id: backButton
}
BaseHeaderType {
HeaderType {
id: header
Layout.fillWidth: true

View File

@@ -91,7 +91,7 @@ PageType {
id: backButton
}
BaseHeaderType {
HeaderType {
id: header
Layout.fillWidth: true

View File

@@ -38,7 +38,7 @@ PageType {
id: backButton
}
BaseHeaderType {
HeaderType {
id: header
Layout.fillWidth: true

View File

@@ -93,7 +93,7 @@ PageType {
Layout.topMargin: 20
}
HeaderTypeWithButton {
HeaderType {
id: headerContent
objectName: "headerContent"

View File

@@ -71,7 +71,7 @@ PageType {
id: backButton
}
BaseHeaderType {
HeaderType {
id: header
Layout.fillWidth: true

View File

@@ -79,22 +79,29 @@ PageType {
id: backButton
}
HeaderTypeWithSwitcher {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
RowLayout {
HeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
headerText: qsTr("App split tunneling")
headerText: qsTr("App split tunneling")
enabled: root.pageEnabled
showSwitcher: true
switcher {
checked: AppSplitTunnelingModel.isTunnelingEnabled
enabled: root.pageEnabled
}
switcherFunction: function(checked) {
AppSplitTunnelingModel.toggleSplitTunneling(checked)
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
SwitcherType {
id: switcher
Layout.fillWidth: true
Layout.rightMargin: 16
enabled: root.pageEnabled
checked: AppSplitTunnelingModel.isTunnelingEnabled
onToggled: {
AppSplitTunnelingModel.toggleSplitTunneling(checked)
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
}
}
}

View File

@@ -38,7 +38,7 @@ PageType {
spacing: 0
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16

View File

@@ -60,7 +60,7 @@ PageType {
spacing: 16
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
headerText: qsTr("Back up your configuration")

View File

@@ -36,7 +36,7 @@ PageType {
anchors.left: parent.left
anchors.right: parent.right
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
@@ -94,7 +94,9 @@ PageType {
}
}
DividerType {}
DividerType {
visible: root.isAppSplitTinnelingEnabled
}
LabelWithButtonType {
id: splitTunnelingButton2
@@ -117,20 +119,29 @@ PageType {
visible: root.isAppSplitTinnelingEnabled
}
LabelWithButtonType {
id: killSwitchButton
SwitcherType {
id: killSwitchSwitcher
visible: !GC.isMobile()
Layout.fillWidth: true
Layout.margins: 16
text: qsTr("KillSwitch")
descriptionText: qsTr("Blocks network connections without VPN")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
descriptionText: qsTr("Disables your internet if your encrypted VPN connection drops out for any reason.")
parentFlickable: fl
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsKillSwitch)
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"))
}
}
}

View File

@@ -50,7 +50,7 @@ PageType {
spacing: 16
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
headerText: qsTr("DNS servers")

View File

@@ -1,112 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import PageEnum 1.0
import Style 1.0
import "./"
import "../Controls2"
import "../Config"
PageType {
id: root
BackButtonType {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
}
FlickableType {
id: fl
anchors.top: backButton.bottom
anchors.bottom: parent.bottom
contentHeight: content.height
ColumnLayout {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
HeaderTypeWithSwitcher {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Kill Switch")
descriptionText: qsTr("Enable to ensure network traffic goes through a secure VPN tunnel, preventing accidental exposure of your IP and DNS queries if the connection drops")
showSwitcher: true
switcher {
checked: SettingsController.isKillSwitchEnabled
enabled: !ConnectionController.isConnected
}
switcherFunction: function(checked) {
if (!ConnectionController.isConnected) {
SettingsController.isKillSwitchEnabled = checked
} else {
PageController.showNotificationMessage(qsTr("Cannot change killSwitch settings during active connection"))
switcher.checked = SettingsController.isKillSwitchEnabled
}
}
}
VerticalRadioButton {
id: softKillSwitch
Layout.fillWidth: true
Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected
checked: !SettingsController.strictKillSwitchEnabled
text: qsTr("Soft Kill Switch")
descriptionText: qsTr("Internet connection is blocked if VPN connection drops accidentally")
onClicked: {
SettingsController.strictKillSwitchEnabled = false
}
}
DividerType {}
VerticalRadioButton {
id: strictKillSwitch
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: SettingsController.isKillSwitchEnabled && !ConnectionController.isConnected
checked: SettingsController.strictKillSwitchEnabled
text: qsTr("Strict Kill Switch")
descriptionText: qsTr("Internet connection is blocked even if VPN was turned off manually or not started")
onClicked: {
SettingsController.strictKillSwitchEnabled = true
}
}
DividerType {}
LabelWithButtonType {
Layout.topMargin: 32
Layout.fillWidth: true
enabled: true
text: qsTr("DNS Exceptions")
descriptionText: qsTr("DNS servers from the list will remain accessible when Kill Switch is triggered")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsKillSwitchExceptions)
}
}
}
}
}

View File

@@ -1,296 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Dialogs
import QtCore
import SortFilterProxyModel 0.2
import PageEnum 1.0
import ProtocolEnum 1.0
import ContainerProps 1.0
import Style 1.0
import "./"
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
import "../Components"
PageType {
id: root
property bool pageEnabled: true
ColumnLayout {
id: header
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
BackButtonType {
id: backButton
}
BaseHeaderType {
enabled: root.pageEnabled
Layout.fillWidth: true
Layout.leftMargin: 16
headerText: qsTr("DNS Exceptions")
descriptionText: qsTr("DNS servers from the list will remain accessible when Kill Switch is triggered")
}
}
ListView {
id: listView
anchors.top: header.bottom
anchors.topMargin: 16
anchors.bottom: parent.bottom
width: parent.width
enabled: root.pageEnabled
property bool isFocusable: true
cacheBuffer: 200
displayMarginBeginning: 40
displayMarginEnd: 40
ScrollBar.vertical: ScrollBarType { }
footer: AddSitePanel {
id: addSitePanel
width: listView.width
z: 10
enabled: root.pageEnabled
placeholderText: qsTr("IPv4 address")
onAddClicked: function(text) {
PageController.showBusyIndicator(true)
AllowedDnsController.addDns(text)
PageController.showBusyIndicator(false)
}
onMoreClicked: {
moreActionsDrawer.openTriggered()
}
}
footerPositioning: ListView.OverlayFooter
model: SortFilterProxyModel {
id: dnsFilterModel
sourceModel: AllowedDnsModel
filters: [
RegExpFilter {
roleName: "ip"
pattern: ".*" + addSitePanel.textField.text + ".*"
caseSensitivity: Qt.CaseInsensitive
}
]
}
clip: true
reuseItems: true
delegate: ColumnLayout {
id: delegateContent
width: listView.width
LabelWithButtonType {
id: site
Layout.fillWidth: true
text: ip
rightImageSource: "qrc:/images/controls/trash.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
var headerText = qsTr("Delete ") + ip + "?"
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
AllowedDnsController.removeDns(dnsFilterModel.mapToSource(index))
if (!GC.isMobile()) {
site.rightButton.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
site.rightButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
DividerType {}
}
}
DrawerType2 {
id: moreActionsDrawer
anchors.fill: parent
expandedHeight: parent.height * 0.4375
expandedStateContent: ColumnLayout {
id: moreActionsDrawerContent
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
Header2Type {
Layout.fillWidth: true
Layout.margins: 16
headerText: qsTr("Import / Export addresses")
}
LabelWithButtonType {
id: importSitesButton
Layout.fillWidth: true
text: qsTr("Import")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
importSitesDrawer.openTriggered()
}
}
DividerType {}
LabelWithButtonType {
id: exportSitesButton
Layout.fillWidth: true
text: qsTr("Save address list")
clickedFunction: function() {
var fileName = ""
if (GC.isMobile()) {
fileName = "amnezia_killswitch_exceptions.json"
} else {
fileName = SystemController.getFileName(qsTr("Save addresses"),
qsTr("Address files (*.json)"),
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/amnezia_killswitch_exceptions",
true,
".json")
}
if (fileName !== "") {
PageController.showBusyIndicator(true)
AllowedDnsController.exportDns(fileName)
moreActionsDrawer.closeTriggered()
PageController.showBusyIndicator(false)
}
}
}
DividerType {}
}
}
DrawerType2 {
id: importSitesDrawer
anchors.fill: parent
expandedHeight: parent.height * 0.4375
expandedStateContent: Item {
implicitHeight: importSitesDrawer.expandedHeight
BackButtonType {
id: importSitesDrawerBackButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
backButtonFunction: function() {
importSitesDrawer.closeTriggered()
}
}
FlickableType {
anchors.top: importSitesDrawerBackButton.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
contentHeight: importSitesDrawerContent.height
ColumnLayout {
id: importSitesDrawerContent
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
Header2Type {
Layout.fillWidth: true
Layout.margins: 16
headerText: qsTr("Import address list")
}
LabelWithButtonType {
id: importSitesButton2
Layout.fillWidth: true
text: qsTr("Replace address list")
clickedFunction: function() {
var fileName = SystemController.getFileName(qsTr("Open address file"),
qsTr("Address files (*.json)"))
if (fileName !== "") {
importSitesDrawerContent.importSites(fileName, true)
}
}
}
DividerType {}
LabelWithButtonType {
id: importSitesButton3
Layout.fillWidth: true
text: qsTr("Add imported addresses to existing ones")
clickedFunction: function() {
var fileName = SystemController.getFileName(qsTr("Open address file"),
qsTr("Address files (*.json)"))
if (fileName !== "") {
importSitesDrawerContent.importSites(fileName, false)
}
}
}
function importSites(fileName, replaceExistingSites) {
PageController.showBusyIndicator(true)
AllowedDnsController.importDns(fileName, replaceExistingSites)
PageController.showBusyIndicator(false)
importSitesDrawer.closeTriggered()
moreActionsDrawer.closeTriggered()
}
DividerType {}
}
}
}
}
}

View File

@@ -40,7 +40,7 @@ PageType {
header: ColumnLayout {
width: listView.width
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16

View File

@@ -71,7 +71,7 @@ PageType {
objectName: "backButton"
}
HeaderTypeWithButton {
HeaderType {
id: headerContent
objectName: "headerContent"

View File

@@ -34,7 +34,7 @@ PageType {
id: backButton
}
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16

View File

@@ -31,7 +31,7 @@ PageType {
id: backButton
}
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16

View File

@@ -94,22 +94,33 @@ PageType {
id: backButton
}
HeaderTypeWithSwitcher {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Split tunneling")
enabled: root.pageEnabled
showSwitcher: true
switcher {
checked: SitesModel.isTunnelingEnabled
RowLayout {
HeaderType {
enabled: root.pageEnabled
Layout.fillWidth: true
Layout.leftMargin: 16
headerText: qsTr("Split tunneling")
}
switcherFunction: function(checked) {
SitesModel.toggleSplitTunneling(checked)
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
SwitcherType {
id: switcher
enabled: root.pageEnabled
Layout.fillWidth: true
Layout.rightMargin: 16
function onToggledFunc() {
SitesModel.toggleSplitTunneling(this.checked)
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
}
checked: SitesModel.isTunnelingEnabled
onToggled: { onToggledFunc() }
Keys.onEnterPressed: { onToggledFunc() }
Keys.onReturnPressed: { onToggledFunc() }
}
}

View File

@@ -35,7 +35,7 @@ PageType {
Layout.topMargin: 20
}
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
Layout.topMargin: 8
Layout.rightMargin: 16

View File

@@ -28,7 +28,7 @@ PageType {
Layout.topMargin: 20
}
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
Layout.topMargin: 8
Layout.rightMargin: 16

View File

@@ -43,7 +43,7 @@ PageType {
header: ColumnLayout {
width: listView.width
HeaderTypeWithButton {
HeaderType {
id: moreButton
property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible()
@@ -74,7 +74,7 @@ PageType {
anchors.right: parent.right
spacing: 0
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
Layout.topMargin: 32
Layout.leftMargin: 16

View File

@@ -66,7 +66,7 @@ PageType {
header: ColumnLayout {
width: listView.width
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16

View File

@@ -59,7 +59,7 @@ PageType {
spacing: 16
BaseHeaderType {
HeaderType {
id: header
implicitWidth: parent.width

View File

@@ -118,7 +118,7 @@ PageType {
anchors.rightMargin: 16
anchors.leftMargin: 16
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
Layout.topMargin: 20

View File

@@ -96,7 +96,7 @@ PageType {
Layout.leftMargin: -16
}
BaseHeaderType {
HeaderType {
id: header
Layout.fillWidth: true

View File

@@ -58,7 +58,7 @@ PageType {
header: ColumnLayout {
width: listView.width
BaseHeaderType {
HeaderType {
id: header
Layout.fillWidth: true

View File

@@ -33,7 +33,7 @@ PageType {
Layout.topMargin: 20
}
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16

View File

@@ -61,7 +61,7 @@ PageType {
anchors.rightMargin: 16
anchors.leftMargin: 16
BaseHeaderType {
HeaderType {
headerText: qsTr("New connection")
}

View File

@@ -169,7 +169,7 @@ PageType {
spacing: 0
HeaderTypeWithButton {
HeaderType {
id: header
Layout.fillWidth: true
Layout.topMargin: 24

View File

@@ -44,7 +44,7 @@ PageType {
spacing: 0
BaseHeaderType {
HeaderType {
Layout.fillWidth: true
Layout.topMargin: 24

View File

@@ -52,28 +52,6 @@ void VpnConnection::onBytesChanged(quint64 receivedBytes, quint64 sentBytes)
emit bytesChanged(receivedBytes, sentBytes);
}
void VpnConnection::onKillSwitchModeChanged(bool enabled)
{
#ifdef AMNEZIA_DESKTOP
if (!m_IpcClient) {
m_IpcClient = new IpcClient(this);
}
if (!m_IpcClient->isSocketConnected()) {
if (!IpcClient::init(m_IpcClient)) {
qWarning() << "Error occurred when init IPC client";
emit serviceIsNotReady();
return;
}
}
if (IpcClient::Interface()) {
qDebug() << "Set KillSwitch Strict mode enabled " << enabled;
IpcClient::Interface()->refreshKillSwitch(enabled);
}
#endif
}
void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
{
@@ -308,7 +286,6 @@ void VpnConnection::createProtocolConnections()
void VpnConnection::appendKillSwitchConfig()
{
m_vpnConfiguration.insert(config_key::killSwitchOption, QVariant(m_settings->isKillSwitchEnabled()).toString());
m_vpnConfiguration.insert(config_key::allowedDnsServers, QVariant(m_settings->allowedDnsServers()).toJsonValue());
}
void VpnConnection::appendSplitTunnelingConfig()

View File

@@ -48,14 +48,14 @@ public:
public slots:
void connectToVpn(int serverIndex,
const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration);
const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration);
void disconnectFromVpn();
void addRoutes(const QStringList &ips);
void deleteRoutes(const QStringList &ips);
void flushDns();
void onKillSwitchModeChanged(bool enabled);
signals:
void bytesChanged(quint64 receivedBytes, quint64 sentBytes);

View File

@@ -29,9 +29,6 @@ class IpcInterface
SLOT( void StopRoutingIpv6() );
SLOT( bool disableKillSwitch() );
SLOT( bool disableAllTraffic() );
SLOT( bool refreshKillSwitch( bool enabled ) );
SLOT( bool allowTrafficTo( const QStringList ranges ) );
SLOT( bool enablePeerTraffic( const QJsonObject &configStr) );
SLOT( bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex) );
SLOT( bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) );

View File

@@ -8,12 +8,21 @@
#include "logger.h"
#include "router.h"
#include "killswitch.h"
#include "../core/networkUtilities.h"
#include "../client/protocols/protocols_defs.h"
#ifdef Q_OS_WIN
#include "../client/platforms/windows/daemon/windowsdaemon.h"
#include "../client/platforms/windows/daemon/windowsfirewall.h"
#include "tapcontroller_win.h"
#endif
#ifdef Q_OS_LINUX
#include "../client/platforms/linux/daemon/linuxfirewall.h"
#endif
#ifdef Q_OS_MACOS
#include "../client/platforms/macos/daemon/macosfirewall.h"
#endif
IpcServer::IpcServer(QObject *parent) : IpcInterfaceSource(parent)
@@ -179,32 +188,174 @@ void IpcServer::setLogsEnabled(bool enabled)
}
}
bool IpcServer::allowTrafficTo(QStringList ranges)
{
return KillSwitch::instance()->allowTrafficTo(ranges);
}
bool IpcServer::disableAllTraffic()
{
return KillSwitch::instance()->disableAllTraffic();
}
bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex)
{
return KillSwitch::instance()->enableKillSwitch(configStr, vpnAdapterIndex);
#ifdef Q_OS_WIN
auto firewallManager = WindowsFirewall::create(this);
Q_ASSERT(firewallManager != nullptr);
return firewallManager->enableInterface(vpnAdapterIndex);
#endif
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
int splitTunnelType = configStr.value("splitTunnelType").toInt();
QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray();
bool blockAll = 0;
bool allowNets = 0;
bool blockNets = 0;
QStringList allownets;
QStringList blocknets;
if (splitTunnelType == 0) {
blockAll = true;
allowNets = true;
allownets.append(configStr.value("vpnServer").toString());
} else if (splitTunnelType == 1) {
blockNets = true;
for (auto v : splitTunnelSites) {
blocknets.append(v.toString());
}
} else if (splitTunnelType == 2) {
blockAll = true;
allowNets = true;
allownets.append(configStr.value("vpnServer").toString());
for (auto v : splitTunnelSites) {
allownets.append(v.toString());
}
}
#endif
#ifdef Q_OS_LINUX
// double-check + ensure our firewall is installed and enabled
if (!LinuxFirewall::isInstalled())
LinuxFirewall::install();
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), blockAll);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), allowNets);
LinuxFirewall::updateAllowNets(allownets);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), blockAll);
LinuxFirewall::updateBlockNets(blocknets);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), true);
QStringList dnsServers;
dnsServers.append(configStr.value(amnezia::config_key::dns1).toString());
dnsServers.append(configStr.value(amnezia::config_key::dns2).toString());
dnsServers.append("127.0.0.1");
dnsServers.append("127.0.0.53");
LinuxFirewall::updateDNSServers(dnsServers);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true);
#endif
#ifdef Q_OS_MACOS
// double-check + ensure our firewall is installed and enabled. This is necessary as
// other software may disable pfctl before re-enabling with their own rules (e.g other VPNs)
if (!MacOSFirewall::isInstalled())
MacOSFirewall::install();
MacOSFirewall::ensureRootAnchorPriority();
MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), blockAll);
MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), allowNets);
MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), allowNets, QStringLiteral("allownets"), allownets);
MacOSFirewall::setAnchorEnabled(QStringLiteral("120.blockNets"), blockNets);
MacOSFirewall::setAnchorTable(QStringLiteral("120.blockNets"), blockNets, QStringLiteral("blocknets"), blocknets);
MacOSFirewall::setAnchorEnabled(QStringLiteral("200.allowVPN"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), true);
QStringList dnsServers;
dnsServers.append(configStr.value(amnezia::config_key::dns1).toString());
dnsServers.append(configStr.value(amnezia::config_key::dns2).toString());
MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true);
MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), dnsServers);
#endif
return true;
}
bool IpcServer::disableKillSwitch()
{
return KillSwitch::instance()->disableKillSwitch();
#ifdef Q_OS_WIN
auto firewallManager = WindowsFirewall::create(this);
Q_ASSERT(firewallManager != nullptr);
return firewallManager->disableKillSwitch();
#endif
#ifdef Q_OS_LINUX
LinuxFirewall::uninstall();
#endif
#ifdef Q_OS_MACOS
MacOSFirewall::uninstall();
#endif
return true;
}
bool IpcServer::enablePeerTraffic(const QJsonObject &configStr)
{
return KillSwitch::instance()->enablePeerTraffic(configStr);
}
#ifdef Q_OS_WIN
InterfaceConfig config;
config.m_dnsServer = configStr.value(amnezia::config_key::dns1).toString();
config.m_serverPublicKey = "openvpn";
config.m_serverIpv4Gateway = configStr.value("vpnGateway").toString();
config.m_serverIpv4AddrIn = configStr.value("vpnServer").toString();
int vpnAdapterIndex = configStr.value("vpnAdapterIndex").toInt();
int inetAdapterIndex = configStr.value("inetAdapterIndex").toInt();
bool IpcServer::refreshKillSwitch(bool enabled)
{
return KillSwitch::instance()->refresh(enabled);
int splitTunnelType = configStr.value("splitTunnelType").toInt();
QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray();
QStringList AllowedIPAddesses;
// Use APP split tunnel
if (splitTunnelType == 0 || splitTunnelType == 2) {
config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("0.0.0.0"), 0));
config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("::"), 0));
}
if (splitTunnelType == 1) {
for (auto v : splitTunnelSites) {
QString ipRange = v.toString();
if (ipRange.split('/').size() > 1) {
config.m_allowedIPAddressRanges.append(
IPAddress(QHostAddress(ipRange.split('/')[0]), atoi(ipRange.split('/')[1].toLocal8Bit())));
} else {
config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress(ipRange), 32));
}
}
}
config.m_excludedAddresses.append(configStr.value("vpnServer").toString());
if (splitTunnelType == 2) {
for (auto v : splitTunnelSites) {
QString ipRange = v.toString();
config.m_excludedAddresses.append(ipRange);
}
}
for (const QJsonValue &i : configStr.value(amnezia::config_key::splitTunnelApps).toArray()) {
if (!i.isString()) {
break;
}
config.m_vpnDisabledApps.append(i.toString());
}
// killSwitch toggle
if (QVariant(configStr.value(amnezia::config_key::killSwitchOption).toString()).toBool()) {
auto firewallManager = WindowsFirewall::create(this);
Q_ASSERT(firewallManager != nullptr);
firewallManager->enablePeerTraffic(config);
}
WindowsDaemon::instance()->prepareActivation(config, inetAdapterIndex);
WindowsDaemon::instance()->activateSplitTunnel(config, vpnAdapterIndex);
#endif
return true;
}

View File

@@ -34,12 +34,9 @@ public:
virtual bool deleteTun(const QString &dev) override;
virtual void StartRoutingIpv6() override;
virtual void StopRoutingIpv6() override;
virtual bool disableAllTraffic() override;
virtual bool allowTrafficTo(QStringList ranges) override;
virtual bool enablePeerTraffic(const QJsonObject &configStr) override;
virtual bool enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) override;
virtual bool disableKillSwitch() override;
virtual bool refreshKillSwitch( bool enabled ) override;
virtual bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) override;
private:

Some files were not shown because too many files have changed in this diff Show More