Compare commits

...

9 Commits

Author SHA1 Message Date
Mykola Baibuz
f3b3e937e9 Update binaries 2024-09-11 00:29:07 +03:00
Mykola Baibuz
92492952e9 Update prebuilt 2024-09-10 21:45:46 +03:00
vladimir.kuznetsov
3d152adf5f chore: removed unused header 2024-09-10 22:25:06 +04:00
vladimir.kuznetsov
9be3221cbe Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into feature/goodbyedpi 2024-09-10 21:53:14 +04:00
vladimir.kuznetsov
d47e37e04f limited goodbyedpi to the windows platform 2024-09-10 21:40:46 +04:00
vladimir.kuznetsov
5e27f0c8f0 feature: added a ui with the feature to configure goodbye dpi
- added configuration file selection
- added modset selection
2024-09-10 17:31:44 +04:00
Mykola Baibuz
c7a48aeabc Update GoodByeDPI description 2024-09-05 15:55:08 +03:00
Mykola Baibuz
ca1bf0d6cd Setup correct blacklist path 2024-09-04 23:51:23 +03:00
Mykola Baibuz
96f26d5a01 GoodByeDPI support initial 2024-09-04 23:23:52 +03:00
31 changed files with 630 additions and 50 deletions

View File

@@ -241,8 +241,14 @@ file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS
${CMAKE_CURRENT_LIST_DIR}/ui/models/services/*.cpp
)
file(GLOB UI_CONTROLLERS_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/controllers/*.h)
file(GLOB UI_CONTROLLERS_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/controllers/*.cpp)
file(GLOB UI_CONTROLLERS_H CONFIGURE_DEPENDS
${CMAKE_CURRENT_LIST_DIR}/ui/controllers/*.h
${CMAKE_CURRENT_LIST_DIR}/ui/models/localServices/*.h
)
file(GLOB UI_CONTROLLERS_CPP CONFIGURE_DEPENDS
${CMAKE_CURRENT_LIST_DIR}/ui/controllers/*.cpp
${CMAKE_CURRENT_LIST_DIR}/ui/models/localServices/*.cpp
)
set(HEADERS ${HEADERS}
${COMMON_FILES_H}
@@ -267,10 +273,12 @@ if(WIN32)
set(HEADERS ${HEADERS}
${CMAKE_CURRENT_LIST_DIR}/protocols/ikev2_vpn_protocol_windows.h
${CMAKE_CURRENT_LIST_DIR}/localServices/goodByeDpi.h
)
set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/protocols/ikev2_vpn_protocol_windows.cpp
${CMAKE_CURRENT_LIST_DIR}/localServices/goodByeDpi.cpp
)
set(RESOURCES ${RESOURCES}

View File

@@ -235,6 +235,7 @@ void AmneziaApplication::registerTypes()
Vpn::declareQmlVpnConnectionStateEnum();
PageLoader::declareQmlPageEnum();
PageLoader::declareQmlFolderEnum();
}
void AmneziaApplication::loadFonts()
@@ -455,4 +456,13 @@ void AmneziaApplication::initControllers()
m_systemController.reset(new SystemController(m_settings));
m_engine->rootContext()->setContextProperty("SystemController", m_systemController.get());
m_localServicesController.reset(new LocalServicesController(m_serversModel, m_settings));
m_engine->rootContext()->setContextProperty("LocalServicesController", m_localServicesController.get());
connect(m_connectionController.get(), &ConnectionController::startLocalService, m_localServicesController.get(),
&LocalServicesController::start);
connect(m_connectionController.get(), &ConnectionController::stopLocalService, m_localServicesController.get(),
&LocalServicesController::stop);
connect(m_localServicesController.get(), &LocalServicesController::serviceStateChanged, m_connectionController.get(),
&ConnectionController::connectionStateChanged);
}

View File

@@ -24,6 +24,7 @@
#include "ui/controllers/sitesController.h"
#include "ui/controllers/systemController.h"
#include "ui/controllers/appSplitTunnelingController.h"
#include "ui/controllers/localServicesController.h"
#include "ui/models/containers_model.h"
#include "ui/models/languageModel.h"
#include "ui/models/protocols/cloakConfigModel.h"
@@ -136,6 +137,7 @@ private:
QScopedPointer<SitesController> m_sitesController;
QScopedPointer<SystemController> m_systemController;
QScopedPointer<AppSplitTunnelingController> m_appSplitTunnelingController;
QScopedPointer<LocalServicesController> m_localServicesController;
QNetworkAccessManager *m_nam;

View File

@@ -1,7 +1,7 @@
#include "containers_defs.h"
#include "QJsonObject"
#include "QJsonDocument"
#include "QJsonObject"
QDebug operator<<(QDebug debug, const amnezia::DockerContainer &c)
{
@@ -96,7 +96,7 @@ QMap<DockerContainer, QString> ContainerProps::containerHumanNames()
{ DockerContainer::Awg, "AmneziaWG" },
{ DockerContainer::Xray, "XRay" },
{ DockerContainer::Ipsec, QObject::tr("IPsec") },
{ DockerContainer::SSXray, "Shadowsocks"},
{ DockerContainer::SSXray, "Shadowsocks" },
{ DockerContainer::TorWebSite, QObject::tr("Website in Tor network") },
{ DockerContainer::Dns, QObject::tr("AmneziaDNS") },
@@ -124,27 +124,24 @@ QMap<DockerContainer, QString> ContainerProps::containerDescriptions()
"but very resistant to blockages. "
"Recommended for regions with high levels of censorship.") },
{ DockerContainer::Xray,
QObject::tr("XRay with REALITY - Suitable for countries with the highest level of internet censorship. "
"Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods.") },
QObject::tr(
"XRay with REALITY - Suitable for countries with the highest level of internet censorship. "
"Traffic masking as web traffic at the TLS level, and protection against detection by active probing methods.") },
{ DockerContainer::Ipsec,
QObject::tr("IKEv2/IPsec - Modern stable protocol, a bit faster than others, restores connection after "
"signal loss. It has native support on the latest versions of Android and iOS.") },
{ DockerContainer::TorWebSite, QObject::tr("Deploy a WordPress site on the Tor network in two clicks.") },
{ DockerContainer::Dns,
QObject::tr("Replace the current DNS server with your own. This will increase your privacy level.") },
{ DockerContainer::Sftp,
QObject::tr("Create a file vault on your server to securely store and transfer files.") },
{ DockerContainer::Socks5Proxy,
QObject::tr("") } };
{ DockerContainer::Dns, QObject::tr("Replace the current DNS server with your own. This will increase your privacy level.") },
{ DockerContainer::Sftp, QObject::tr("Create a file vault on your server to securely store and transfer files.") },
{ DockerContainer::Socks5Proxy, QObject::tr("") } };
}
QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
{
return {
{ DockerContainer::OpenVpn,
QObject::tr(
"OpenVPN stands as one of the most popular and time-tested VPN protocols available.\n"
QObject::tr("OpenVPN stands as one of the most popular and time-tested VPN protocols available.\n"
"It employs its unique security protocol, "
"leveraging the strength of SSL/TLS for encryption and key exchange. "
"Furthermore, OpenVPN's support for a multitude of authentication methods makes it versatile and adaptable, "
@@ -160,7 +157,8 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
"* Can operate over both TCP and UDP network protocols.") },
{ DockerContainer::ShadowSocks,
QObject::tr("Shadowsocks, inspired by the SOCKS5 protocol, safeguards the connection using the AEAD cipher. "
"Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS connection."
"Although Shadowsocks is designed to be discreet and challenging to identify, it isn't identical to a standard HTTPS "
"connection."
"However, certain traffic analysis systems might still detect a Shadowsocks connection. "
"Due to limited support in Amnezia, it's recommended to use AmneziaWG protocol.\n\n"
"* Available in the AmneziaVPN only on desktop platforms\n"
@@ -217,15 +215,18 @@ QMap<DockerContainer, QString> ContainerProps::containerDetailedDescriptions()
"* Works over UDP network protocol.") },
{ DockerContainer::Xray,
QObject::tr("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.\n"
"It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate clients while diverting censors to genuine websites like google.com, "
"is specifically designed to counteract the highest levels of internet censorship through its novel approach to "
"evasion.\n"
"It uniquely identifies censors during the TLS handshake phase, seamlessly operating as a proxy for legitimate "
"clients while diverting censors to genuine websites like google.com, "
"thus presenting an authentic TLS certificate and data. \n"
"This advanced capability differentiates REALITY from similar technologies by its ability to disguise web traffic as coming from random, "
"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. \n"
"Unlike older protocols such as VMess, VLESS, and the XTLS-Vision transport, "
"REALITY's innovative \"friend or foe\" recognition at the TLS handshake enhances security and circumvents detection by sophisticated DPI systems employing active probing techniques. "
"This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship.")
},
"REALITY's innovative \"friend or foe\" recognition at the TLS handshake enhances security and circumvents detection "
"by sophisticated DPI systems employing active probing techniques. "
"This makes REALITY a robust solution for maintaining internet freedom in environments with stringent censorship.") },
{ DockerContainer::Ipsec,
QObject::tr("IKEv2, paired with the IPSec encryption layer, stands as a modern and stable VPN protocol.\n"
"One of its distinguishing features is its ability to swiftly switch between networks and devices, "
@@ -287,7 +288,8 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
case DockerContainer::Awg: return true;
case DockerContainer::Xray: return true;
case DockerContainer::Cloak: return true;
case DockerContainer::SSXray: return true;
case DockerContainer::SSXray:
return true;
// case DockerContainer::ShadowSocks: return true;
default: return false;
}
@@ -383,10 +385,8 @@ bool ContainerProps::isShareable(DockerContainer container)
QJsonObject ContainerProps::getProtocolConfigFromContainer(const Proto protocol, const QJsonObject &containerConfig)
{
QString protocolConfigString = containerConfig.value(ProtocolProps::protoToString(protocol))
.toObject()
.value(config_key::last_config)
.toString();
QString protocolConfigString =
containerConfig.value(ProtocolProps::protoToString(protocol)).toObject().value(config_key::last_config).toString();
return QJsonDocument::fromJson(protocolConfigString.toUtf8()).object();
}

View File

@@ -80,6 +80,7 @@ namespace amnezia
ExecutableMissing = 604,
XrayExecutableMissing = 605,
Tun2SockExecutableMissing = 606,
GoodByeDPIExecutableMissing = 607,
// VPN errors
OpenVpnAdaptersInUseError = 700,

View File

@@ -43,6 +43,7 @@ QString errorString(ErrorCode code) {
case (ErrorCode::CloakExecutableMissing): errorMessage = QObject::tr("Cloak (ck-client) executable missing"); break;
case (ErrorCode::AmneziaServiceConnectionFailed): errorMessage = QObject::tr("Amnezia helper service error"); break;
case (ErrorCode::OpenSslFailed): errorMessage = QObject::tr("OpenSSL failed"); break;
case (ErrorCode::GoodByeDPIExecutableMissing): errorMessage = QObject::tr("GoodbyeDPI executable missing"); break;
// VPN errors
case (ErrorCode::OpenVpnAdaptersInUseError): errorMessage = QObject::tr("Can't connect: another VPN connection is active"); break;

View File

@@ -0,0 +1,62 @@
#include "goodByeDpi.h"
#include "core/ipcclient.h"
#include "utilities.h"
GoodByeDpi::GoodByeDpi(QObject *parent) : QObject { parent }
{
}
amnezia::ErrorCode GoodByeDpi::start(const QString &blackListFile, const int modset)
{
if (!QFileInfo::exists(Utils::goodbyedpiPath())) {
return amnezia::ErrorCode::GoodByeDPIExecutableMissing;
}
m_goodbyeDPIProcess = IpcClient::CreatePrivilegedProcess();
if (!m_goodbyeDPIProcess) {
return amnezia::ErrorCode::AmneziaServiceConnectionFailed;
}
m_goodbyeDPIProcess->waitForSource(1000);
if (!m_goodbyeDPIProcess->isInitialized()) {
qWarning() << "IpcProcess replica is not connected!";
return amnezia::ErrorCode::AmneziaServiceConnectionFailed;
}
m_goodbyeDPIProcess->setProgram(amnezia::PermittedProcess::GoodbyeDPI);
QStringList arguments;
arguments << QString("-%1").arg(modset);
arguments << QString("--blacklist %1").arg(blackListFile);
m_goodbyeDPIProcess->setArguments(arguments);
qDebug() << arguments.join(" ");
connect(m_goodbyeDPIProcess.data(), &PrivilegedProcess::errorOccurred,
[&](QProcess::ProcessError error) { qDebug() << "PrivilegedProcess errorOccurred" << error; });
connect(m_goodbyeDPIProcess.data(), &PrivilegedProcess::stateChanged, [&](QProcess::ProcessState newState) {
qDebug() << "PrivilegedProcess stateChanged" << newState;
if (newState == QProcess::Running) {
qDebug() << "PrivilegedProcess running";
emit serviceStateChanged(Vpn::ConnectionState::Connected);
}
});
connect(m_goodbyeDPIProcess.data(), &PrivilegedProcess::finished, this, [&]() {
qDebug() << "PrivilegedProcess finished";
emit serviceStateChanged(Vpn::ConnectionState::Disconnected);
});
m_goodbyeDPIProcess->start();
return amnezia::ErrorCode::NoError;
}
void GoodByeDpi::stop()
{
if (m_goodbyeDPIProcess) {
m_goodbyeDPIProcess->close();
}
}

View File

@@ -0,0 +1,25 @@
#ifndef GOODBYEDPI_H
#define GOODBYEDPI_H
#include <QObject>
#include "core/defs.h"
#include "core/privileged_process.h"
#include "protocols/vpnprotocol.h"
class GoodByeDpi : public QObject
{
Q_OBJECT
public:
explicit GoodByeDpi(QObject *parent = nullptr);
amnezia::ErrorCode start(const QString &blackListFile, const int modset);
void stop();
private:
QSharedPointer<PrivilegedProcess> m_goodbyeDPIProcess;
signals:
void serviceStateChanged(Vpn::ConnectionState state);
};
#endif // GOODBYEDPI_H

View File

@@ -100,6 +100,8 @@ namespace amnezia
constexpr char clientId[] = "clientId";
constexpr char isGoodbyeDpi[] = "is_goodbye_dpi";
}
namespace protocols
@@ -254,7 +256,8 @@ namespace amnezia
TorWebSite,
Dns,
Sftp,
Socks5Proxy
Socks5Proxy,
GoodyeDPI
};
Q_ENUM_NS(Proto)

View File

@@ -218,6 +218,7 @@
<file>ui/qml/Pages2/PageSettingsApiLanguageList.qml</file>
<file>images/controls/archive-restore.svg</file>
<file>images/controls/help-circle.svg</file>
<file>ui/qml/Pages2/LocalServices/PageGoodByeDpiSettings.qml</file>
</qresource>
<qresource prefix="/countriesFlags">
<file>images/flagKit/ZW.svg</file>

View File

@@ -538,3 +538,33 @@ void Settings::toggleDevGatewayEnv(bool enabled)
{
m_isDevGatewayEnv = enabled;
}
void Settings::setGoodbyeDpiBlackListFile(const QString &file)
{
setValue("Conf/goodbyeDpiBlackListFile", file);
}
QString Settings::getGoodbyeDpiBlackListFile() const
{
return value("Conf/goodbyeDpiBlackListFile").toString();
}
void Settings::toggleGoodbyeDpi(bool enable)
{
setValue("Conf/isGoodbyeDpiEnabled", enable);
}
bool Settings::isGoodbyeDpiEnabled() const
{
return value("Conf/isGoodbyeDpiEnabled", false).toBool();
}
void Settings::setGoodbyeDpiModset(const int modset)
{
setValue("Conf/goodbyeDpiModset", modset);
}
int Settings::getGoodbyeDpiModset() const
{
return value("Conf/goodbyeDpiModset", 9).toInt();
}

View File

@@ -113,7 +113,10 @@ public:
QString routeModeString(RouteMode mode) const;
RouteMode routeMode() const;
void setRouteMode(RouteMode mode) { setValue("Conf/routeMode", mode); }
void setRouteMode(RouteMode mode)
{
setValue("Conf/routeMode", mode);
}
bool isSitesSplitTunnelingEnabled() const;
void setSitesSplitTunnelingEnabled(bool enabled);
@@ -222,6 +225,15 @@ public:
bool isDevGatewayEnv();
void toggleDevGatewayEnv(bool enabled);
void setGoodbyeDpiBlackListFile(const QString &file);
QString getGoodbyeDpiBlackListFile() const;
void toggleGoodbyeDpi(bool enable);
bool isGoodbyeDpiEnabled() const;
void setGoodbyeDpiModset(const int modset);
int getGoodbyeDpiModset() const;
signals:
void saveLogsChanged(bool enabled);
void screenshotsEnabledChanged(bool enabled);

View File

@@ -44,6 +44,13 @@ void ConnectionController::openConnection()
int serverIndex = m_serversModel->getDefaultServerIndex();
QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex);
const auto isGoodbyeDpi = serverConfig.value(config_key::isGoodbyeDpi).toBool(false);
if (isGoodbyeDpi) {
emit startLocalService();
return;
}
auto configVersion = serverConfig.value(config_key::configVersion).toInt();
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Preparing);
@@ -65,6 +72,15 @@ void ConnectionController::openConnection()
void ConnectionController::closeConnection()
{
int serverIndex = m_serversModel->getDefaultServerIndex();
QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex);
const auto isGoodbyeDpi = serverConfig.value(config_key::isGoodbyeDpi).toBool(false);
if (isGoodbyeDpi) {
emit stopLocalService();
return;
}
emit disconnectFromVpn();
}

View File

@@ -61,6 +61,9 @@ signals:
void updateApiConfigFromTelegram();
void configFromApiUpdated();
void startLocalService();
void stopLocalService();
private:
Vpn::ConnectionState getCurrentConnectionState();
bool isProtocolConfigExists(const QJsonObject &containerConfig, const DockerContainer container);

View File

@@ -0,0 +1,99 @@
#include "localServicesController.h"
namespace
{
// Logger logger("ServerController");
}
LocalServicesController::LocalServicesController(const QSharedPointer<ServersModel> &serversModel,
const std::shared_ptr<Settings> &settings, QObject *parent)
: QObject(parent), m_serversModel(serversModel), m_settings(settings)
{
#ifdef Q_OS_WINDOWS
connect(&m_goodbyeDpiService, &GoodByeDpi::serviceStateChanged, this, &LocalServicesController::serviceStateChanged);
#endif
}
LocalServicesController::~LocalServicesController()
{
#ifdef Q_OS_WINDOWS
m_goodbyeDpiService.stop();
#endif
}
void LocalServicesController::toggleGoodbyeDpi(bool enable)
{
if (enable) {
QJsonObject server;
server.insert(config_key::isGoodbyeDpi, true);
server.insert(config_key::description, "GoodbyeDPI service");
server.insert(config_key::name, "GoodbyeDPI");
m_serversModel->addServer(server);
m_serversModel->setDefaultServerIndex(m_serversModel->getServersCount() - 1);
m_settings->toggleGoodbyeDpi(true);
emit toggleGoodbyeDpiFinished(tr("GoodbyeDPI added to home page"));
} else {
for (int i = 0; i < m_serversModel->getServersCount(); i++) {
if (m_serversModel->getServerConfig(i).value(config_key::isGoodbyeDpi).toBool(false)) {
m_serversModel->setProcessedServerIndex(i);
m_serversModel->removeServer();
break;
}
}
m_settings->toggleGoodbyeDpi(false);
emit toggleGoodbyeDpiFinished("GoodbyeDPI removed from home page");
}
}
bool LocalServicesController::isGoodbyeDpiEnabled()
{
return m_settings->isGoodbyeDpiEnabled();
}
void LocalServicesController::setGoodbyeDpiBlackListFile(const QString &file)
{
m_settings->setGoodbyeDpiBlackListFile(file);
}
QString LocalServicesController::getGoodbyeDpiBlackListFile()
{
auto file = m_settings->getGoodbyeDpiBlackListFile();
if (file.isEmpty()) {
return m_defaultBlackListFile;
}
return file;
}
void LocalServicesController::resetGoodbyeDpiBlackListFile()
{
m_settings->setGoodbyeDpiBlackListFile(m_defaultBlackListFile);
}
void LocalServicesController::setGoodbyeDpiModset(const int modset)
{
m_settings->setGoodbyeDpiModset(modset);
}
int LocalServicesController::getGoodbyeDpiModset()
{
return m_settings->getGoodbyeDpiModset();
}
void LocalServicesController::start()
{
#ifdef Q_OS_WINDOWS
auto errorCode = m_goodbyeDpiService.start(getGoodbyeDpiBlackListFile(), getGoodbyeDpiModset());
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
}
#endif
}
void LocalServicesController::stop()
{
#ifdef Q_OS_WINDOWS
m_goodbyeDpiService.stop();
#endif
}

View File

@@ -0,0 +1,58 @@
#ifndef LOCALSERVICESCONTROLLER_H
#define LOCALSERVICESCONTROLLER_H
#include <QObject>
#ifdef Q_OS_WINDOWS
#include "localServices/goodByeDpi.h"
#endif
#include "protocols/vpnprotocol.h"
#include "settings.h"
#include "ui/models/servers_model.h"
class LocalServicesController : public QObject
{
Q_OBJECT
public:
LocalServicesController(const QSharedPointer<ServersModel> &serversModel, const std::shared_ptr<Settings> &settings,
QObject *parent = nullptr);
~LocalServicesController();
Q_PROPERTY(bool isGoodbyeDpiEnabled READ isGoodbyeDpiEnabled NOTIFY toggleGoodbyeDpiFinished)
public slots:
void toggleGoodbyeDpi(bool enable);
bool isGoodbyeDpiEnabled();
void setGoodbyeDpiBlackListFile(const QString &file);
QString getGoodbyeDpiBlackListFile();
void resetGoodbyeDpiBlackListFile();
void setGoodbyeDpiModset(const int modset);
int getGoodbyeDpiModset();
void start();
void stop();
signals:
void errorOccurred(ErrorCode errorCode);
void toggleGoodbyeDpiFinished(const QString &message);
void serviceStateChanged(Vpn::ConnectionState state);
private:
std::shared_ptr<Settings> m_settings;
QSharedPointer<ServersModel> m_serversModel;
#ifdef Q_OS_WINDOWS
GoodByeDpi m_goodbyeDpiService;
#endif
bool m_isGoodbyeDpiServiceEnabled = false;
#ifdef Q_OS_WINDOWS
QString m_defaultBlackListFile = QCoreApplication::applicationDirPath() + "/goodbyedpi/blacklist.txt";
#else
QString m_defaultBlackListFile;
#endif
};
#endif // LOCALSERVICESCONTROLLER_H

View File

@@ -47,11 +47,19 @@ bool PageController::isStartPageVisible()
}
}
QString PageController::getPagePath(PageLoader::PageEnum page)
QString PageController::getPagePath(PageLoader::PageEnum page, PageLoader::FolderEnum folder)
{
QMetaEnum metaEnum = QMetaEnum::fromType<PageLoader::PageEnum>();
QString pageName = metaEnum.valueToKey(static_cast<int>(page));
return "qrc:/ui/qml/Pages2/" + pageName + ".qml";
metaEnum = QMetaEnum::fromType<PageLoader::FolderEnum>();
QString folderName = "";
if (metaEnum.value(static_cast<int>(folder)) != static_cast<int>(PageLoader::FolderEnum::Root)) {
folderName = metaEnum.valueToKey(static_cast<int>(folder));
folderName += "/";
}
return "qrc:/ui/qml/Pages2/" + folderName + pageName + ".qml";
}
void PageController::closeWindow()

View File

@@ -61,7 +61,9 @@ namespace PageLoader
PageShareFullAccess,
PageDevMenu
PageDevMenu,
PageGoodByeDpiSettings
};
Q_ENUM_NS(PageEnum)
@@ -69,6 +71,19 @@ namespace PageLoader
{
qmlRegisterUncreatableMetaObject(PageLoader::staticMetaObject, "PageEnum", 1, 0, "PageEnum", "Error: only enums");
}
Q_NAMESPACE
enum class FolderEnum {
Root = 0,
LocalServices
};
Q_ENUM_NS(FolderEnum)
static void declareQmlFolderEnum()
{
qmlRegisterUncreatableMetaObject(PageLoader::staticMetaObject, "FolderEnum", 1, 0, "FolderEnum", "Error: only enums");
}
}
class PageController : public QObject
@@ -80,7 +95,7 @@ public:
public slots:
bool isStartPageVisible();
QString getPagePath(PageLoader::PageEnum page);
QString getPagePath(PageLoader::PageEnum page, PageLoader::FolderEnum folder = PageLoader::FolderEnum::Root);
void closeWindow();
void hideWindow();
@@ -103,7 +118,7 @@ public slots:
void onShowErrorMessage(amnezia::ErrorCode errorCode);
signals:
void goToPage(PageLoader::PageEnum page, bool slide = true);
void goToPage(PageLoader::PageEnum page, PageLoader::FolderEnum folder = PageLoader::FolderEnum::Root, bool slide = true);
void goToStartPage();
void goToPageHome();
void goToPageSettings();

View File

@@ -16,8 +16,7 @@
SettingsController::SettingsController(const QSharedPointer<ServersModel> &serversModel,
const QSharedPointer<ContainersModel> &containersModel,
const QSharedPointer<LanguageModel> &languageModel,
const QSharedPointer<SitesModel> &sitesModel,
const QSharedPointer<LanguageModel> &languageModel, const QSharedPointer<SitesModel> &sitesModel,
const QSharedPointer<AppSplitTunnelingModel> &appSplitTunnelingModel,
const std::shared_ptr<Settings> &settings, QObject *parent)
: QObject(parent),
@@ -31,7 +30,8 @@ SettingsController::SettingsController(const QSharedPointer<ServersModel> &serve
m_appVersion = QString("%1 (%2, %3)").arg(QString(APP_VERSION), __DATE__, GIT_COMMIT_HASH);
checkIfNeedDisableLogs();
#ifdef Q_OS_ANDROID
connect(AndroidController::instance(), &AndroidController::notificationStateChanged, this, &SettingsController::onNotificationStateChanged);
connect(AndroidController::instance(), &AndroidController::notificationStateChanged, this,
&SettingsController::onNotificationStateChanged);
#endif
}
@@ -145,8 +145,7 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data)
bool ok = m_settings->restoreAppConfig(data);
if (ok) {
m_serversModel->resetModel();
m_languageModel->changeLanguage(
static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
m_languageModel->changeLanguage(static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
emit restoreBackupFinished();
} else {
emit changeSettingsErrorOccurred(tr("Backup file is corrupted"));
@@ -162,8 +161,7 @@ void SettingsController::clearSettings()
{
m_settings->clearSettings();
m_serversModel->resetModel();
m_languageModel->changeLanguage(
static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
m_languageModel->changeLanguage(static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
m_sitesModel->setRouteMode(Settings::RouteMode::VpnOnlyForwardSites);
m_sitesModel->toggleSplitTunneling(false);

View File

@@ -88,9 +88,10 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const
const QJsonObject server = m_servers.at(index.row()).toObject();
const auto apiConfig = server.value(configKey::apiConfig).toObject();
const auto configVersion = server.value(config_key::configVersion).toInt();
const auto isGoodbyeDpi = server.value(config_key::isGoodbyeDpi).toBool(false);
switch (role) {
case NameRole: {
if (configVersion) {
if (configVersion || isGoodbyeDpi) {
return server.value(config_key::name).toString();
}
auto name = server.value(config_key::description).toString();
@@ -100,6 +101,10 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const
return name;
}
case ServerDescriptionRole: {
if (isGoodbyeDpi) {
return server.value(config_key::description).toString();
}
auto description = getServerDescription(server, index.row());
return configVersion ? description : description + server.value(config_key::hostName).toString();
}
@@ -144,6 +149,9 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const
QString primaryDns = server.value(config_key::dns1).toString();
return primaryDns == protocols::dns::amneziaDnsIp;
}
case IsGoodByeDpiRole: {
return isGoodbyeDpi;
}
}
return QVariant();
@@ -208,6 +216,12 @@ QString ServersModel::getServerDescription(const QJsonObject &server, const int
const QString ServersModel::getDefaultServerDescriptionCollapsed()
{
const QJsonObject server = m_servers.at(m_defaultServerIndex).toObject();
const auto isGoodbyeDpi = server.value(config_key::isGoodbyeDpi).toBool(false);
if (isGoodbyeDpi) {
return server.value(config_key::description).toString();
}
const auto configVersion = server.value(config_key::configVersion).toInt();
auto description = getServerDescription(server, m_defaultServerIndex);
if (configVersion) {
@@ -222,6 +236,12 @@ const QString ServersModel::getDefaultServerDescriptionCollapsed()
const QString ServersModel::getDefaultServerDescriptionExpanded()
{
const QJsonObject server = m_servers.at(m_defaultServerIndex).toObject();
const auto isGoodbyeDpi = server.value(config_key::isGoodbyeDpi).toBool(false);
if (isGoodbyeDpi) {
return server.value(config_key::description).toString();
}
const auto configVersion = server.value(config_key::configVersion).toInt();
auto description = getServerDescription(server, m_defaultServerIndex);
if (configVersion) {
@@ -370,6 +390,8 @@ QHash<int, QByteArray> ServersModel::roleNames() const
roles[IsCountrySelectionAvailableRole] = "isCountrySelectionAvailable";
roles[ApiAvailableCountriesRole] = "apiAvailableCountries";
roles[ApiServerCountryCodeRole] = "apiServerCountryCode";
roles[IsGoodByeDpiRole] = "isGoodbyeDpi";
return roles;
}

View File

@@ -38,7 +38,9 @@ public:
ApiAvailableCountriesRole,
ApiServerCountryCodeRole,
HasAmneziaDns
HasAmneziaDns,
IsGoodByeDpiRole
};
ServersModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);

View File

@@ -0,0 +1,159 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import PageEnum 1.0
import Style 1.0
import "./"
import "../../Controls2"
import "../../Config"
import "../../Controls2/TextTypes"
import "../../Components"
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
property bool isGoodbyeDpiEnabled: LocalServicesController.isGoodbyeDpiEnabled
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 16
HeaderType {
Layout.fillWidth: true
headerText: qsTr("GoodbyeDPI settings")
descriptionText: qsTr("Deep Packet Inspection circumvention utility")
}
SwitcherType {
Layout.fillWidth: true
text: qsTr("Enable GoodbyeDPI")
checked: LocalServicesController.isGoodbyeDpiEnabled
onCheckedChanged: {
if (checked !== LocalServicesController.isGoodbyeDpiEnabled) {
LocalServicesController.toggleGoodbyeDpi(checked)
}
}
}
RowLayout {
Layout.fillWidth: true
Layout.topMargin: 16
enabled: !content.isGoodbyeDpiEnabled
ListItemTitleType {
Layout.fillWidth: true
text: LocalServicesController.getGoodbyeDpiBlackListFile()
}
ImageButtonType {
image: "qrc:/images/controls/folder-search-2.svg"
imageColor: AmneziaStyle.color.paleGray
onClicked: function() {
var fileName = SystemController.getFileName(qsTr("Open black list file"),
qsTr("Text files (*.txt)"))
LocalServicesController.setGoodbyeDpiBlackListFile(fileName)
}
}
ImageButtonType {
image: "qrc:/images/controls/trash.svg"
imageColor: AmneziaStyle.color.paleGray
onClicked: function() {
LocalServicesController.resetGoodbyeDpiBlackListFile()
}
}
}
DropDownType {
id: modsetDropDown
Layout.fillWidth: true
descriptionText: qsTr("Setup templates")
headerText: qsTr("Modset")
drawerParent: root
enabled: !content.isGoodbyeDpiEnabled
listView: ListViewWithRadioButtonType {
id: modsetListView
rootWidth: root.width
model: ListModel {
ListElement { name : "-p -r -s -f 2 -k 2 -n -e 2" }
ListElement { name : "-p -r -s -f 2 -k 2 -n -e 40" }
ListElement { name : "-p -r -s -e 40" }
ListElement { name : "-p -r -s" }
ListElement { name : "-f 2 -e 2 --auto-ttl --reverse-frag --max-payload" }
ListElement { name : "-f 2 -e 2 --wrong-seq --reverse-frag --max-payload" }
ListElement { name : "-f 2 -e 2 --wrong-chksum --reverse-frag --max-payload" }
ListElement { name : "-f 2 -e 2 --wrong-seq --wrong-chksum --reverse-frag --max-payload" }
ListElement { name : "-f 2 -e 2 --wrong-seq --wrong-chksum --reverse-frag --max-payload -q" }
}
clickedFunction: function() {
modsetDropDown.text = selectedText
LocalServicesController.setGoodbyeDpiModset(currentIndex + 1)
modsetDropDown.close()
}
Component.onCompleted: {
modsetListView.currentIndex = (LocalServicesController.getGoodbyeDpiModset() - 1)
modsetListView.triggerCurrentItem()
}
}
}
BasicButtonType {
id: detailedInstructionsButton
implicitHeight: 32
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.goldenApricot
text: qsTr("Description of options")
clickedFunc: function() {
Qt.openUrlExternally("https://github.com/ValdikSS/GoodbyeDPI?tab=readme-ov-file#how-to-use")
}
}
}
}
}

View File

@@ -105,6 +105,8 @@ PageType {
buttonTextLabel.font.pixelSize: 14
buttonTextLabel.font.weight: 500
visible: !ServersModel.getDefaultServerData("isGoodbyeDpi")
property bool isSplitTunnelingEnabled: SitesModel.isTunnelingEnabled || AppSplitTunnelingModel.isTunnelingEnabled ||
ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
@@ -304,7 +306,7 @@ PageType {
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
spacing: 8
visible: !ServersModel.isDefaultServerFromApi
visible: !ServersModel.isDefaultServerFromApi && !ServersModel.getDefaultServerData("isGoodbyeDpi")
Item {
id: focusItem1
@@ -535,8 +537,12 @@ PageType {
Keys.onReturnPressed: serverInfoButton.clicked()
onClicked: function() {
ServersModel.processedIndex = index
PageController.goToPage(PageEnum.PageSettingsServerInfo)
if (ServersModel.getDefaultServerData("isGoodbyeDpi")) {
PageController.goToPage(PageEnum.PageGoodByeDpiSettings, PageEnum.LocalServices)
} else {
ServersModel.processedIndex = index
PageController.goToPage(PageEnum.PageSettingsServerInfo)
}
drawer.close()
}
}

View File

@@ -95,6 +95,22 @@ PageType {
DividerType {}
LabelWithButtonType {
Layout.fillWidth: true
visible: Qt.platform.os === "windows"
text: qsTr("Local bypass services")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/app.svg"
clickedFunction: function() {
PageController.goToPage(PageEnum.PageGoodByeDpiSettings, PageEnum.LocalServices)
}
}
DividerType {}
LabelWithButtonType {
id: backup
Layout.fillWidth: true

View File

@@ -111,6 +111,8 @@ PageType {
id: server
Layout.fillWidth: true
visible: !isGoodbyeDpi
text: name
parentFlickable: fl
descriptionText: {

View File

@@ -54,7 +54,7 @@ PageType {
function onServerAlreadyExists(serverIndex) {
PageController.goToStartPage()
ServersModel.processedIndex = serverIndex
PageController.goToPage(PageEnum.PageSettingsServerInfo, false)
PageController.goToPage(PageEnum.PageSettingsServerInfo, PageEnum.LocalServices, false)
PageController.showErrorMessage(qsTr("The server has already been added to the application"))
}

View File

@@ -60,8 +60,8 @@ PageType {
tabBarStackView.pop()
}
function onGoToPage(page, slide) {
var pagePath = PageController.getPagePath(page)
function onGoToPage(page, folder, slide) {
var pagePath = PageController.getPagePath(page, folder)
if (slide) {
tabBarStackView.push(pagePath, { "objectName" : pagePath }, StackView.PushTransition)
@@ -212,6 +212,14 @@ PageType {
}
}
Connections {
target: LocalServicesController
function onErrorOccurred(error) {
PageController.showErrorMessage(error)
}
}
StackViewType {
id: tabBarStackView

View File

@@ -198,6 +198,15 @@ QString Utils::certUtilPath()
#endif
}
QString Utils::goodbyedpiPath()
{
#ifdef Q_OS_WIN
return Utils::executable("goodbyedpi/goodbyedpi", true);
#else
return "";
#endif
}
QString Utils::tun2socksPath()
{
#ifdef Q_OS_WIN

View File

@@ -33,6 +33,7 @@ public:
static QString wireguardExecPath();
static QString certUtilPath();
static QString tun2socksPath();
static QString goodbyedpiPath();
#ifdef Q_OS_WIN
static bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent);

View File

@@ -14,7 +14,8 @@ enum PermittedProcess {
OpenVPN,
Wireguard,
Tun2Socks,
CertUtil
CertUtil,
GoodbyeDPI
};
inline QString permittedProcessPath(PermittedProcess pid)
@@ -27,6 +28,8 @@ inline QString permittedProcessPath(PermittedProcess pid)
return Utils::certUtilPath();
} else if (pid == PermittedProcess::Tun2Socks) {
return Utils::tun2socksPath();
} else if (pid == PermittedProcess::GoodbyeDPI){
return Utils::goodbyedpiPath();
}
return "";
}