Compare commits

..

1 Commits

Author SHA1 Message Date
Mitternacht822
582f21b0b1 fix(linux): force Qt6 modules to link from bundled lib via rpath 2026-02-09 21:31:16 +04:00
69 changed files with 1361 additions and 1181 deletions

View File

@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN)
set(AMNEZIAVPN_VERSION 4.8.14.6)
set(AMNEZIAVPN_VERSION 4.8.13.0)
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
DESCRIPTION "AmneziaVPN"
@@ -12,7 +12,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
set(APP_ANDROID_VERSION_CODE 2118)
set(APP_ANDROID_VERSION_CODE 2106)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux")
@@ -61,9 +61,6 @@ if(WIN32 AND NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
set(CPACK_PACKAGE_VENDOR "AmneziaVPN")
set(CPACK_PACKAGE_VERSION ${AMNEZIAVPN_VERSION})
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "AmneziaVPN client")
set(AMNEZIA_LICENSE_TXT "${CMAKE_BINARY_DIR}/LICENSE.txt")
configure_file("${CMAKE_SOURCE_DIR}/LICENSE" "${AMNEZIA_LICENSE_TXT}" COPYONLY)
set(CPACK_RESOURCE_FILE_LICENSE "${AMNEZIA_LICENSE_TXT}")
set(CPACK_PACKAGE_INSTALL_DIRECTORY "AmneziaVPN")
set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}")
set(CPACK_PACKAGE_EXECUTABLES "AmneziaVPN" "AmneziaVPN")

View File

@@ -37,6 +37,10 @@ if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
set(PACKAGES ${PACKAGES} Widgets)
endif()
if(LINUX AND NOT ANDROID)
list(APPEND PACKAGES QuickTemplates2 QmlModels OpenGL)
endif()
find_package(Qt6 REQUIRED COMPONENTS ${PACKAGES})
set(LIBS ${LIBS}
@@ -52,6 +56,23 @@ endif()
qt_standard_project_setup()
qt_add_executable(${PROJECT} MANUAL_FINALIZATION)
if(LINUX AND NOT ANDROID)
target_link_options(${PROJECT} PRIVATE "-Wl,--no-as-needed")
target_link_options(${PROJECT} PRIVATE "LINKER:--disable-new-dtags")
set_target_properties(${PROJECT} PROPERTIES
BUILD_RPATH "\$ORIGIN/../lib"
INSTALL_RPATH "\$ORIGIN/../lib"
INSTALL_RPATH_USE_LINK_PATH FALSE
)
set_property(TARGET ${PROJECT} PROPERTY BUILD_WITH_INSTALL_RPATH TRUE)
target_link_libraries(${PROJECT} PRIVATE
Qt6::QuickTemplates2
Qt6::QmlModels
Qt6::OpenGL
)
endif()
target_include_directories(${PROJECT} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
)
@@ -59,6 +80,7 @@ target_include_directories(${PROJECT} PUBLIC
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep)
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_interface.rep)
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_tun2socks.rep)
endif()
qt6_add_resources(QRC ${QRC} ${CMAKE_CURRENT_LIST_DIR}/resources.qrc)
@@ -78,7 +100,6 @@ set(AMNEZIAVPN_TS_FILES
)
file(GLOB_RECURSE AMNEZIAVPN_TS_SOURCES *.qrc *.cpp *.h *.ui)
list(FILTER AMNEZIAVPN_TS_SOURCES EXCLUDE REGEX "qtgamepad/examples")
qt_create_translation(AMNEZIAVPN_QM_FILES ${AMNEZIAVPN_TS_SOURCES} ${AMNEZIAVPN_TS_FILES})
@@ -195,6 +216,17 @@ elseif(APPLE)
endif()
target_link_libraries(${PROJECT} PRIVATE ${LIBS})
if(LINUX AND NOT ANDROID)
target_link_libraries(${PROJECT} PRIVATE
"-Wl,--push-state,--no-as-needed"
Qt6::QuickTemplates2
Qt6::QmlModels
Qt6::OpenGL
"-Wl,--pop-state"
)
endif()
target_compile_definitions(${PROJECT} PRIVATE "MZ_$<UPPER_CASE:${MZ_PLATFORM_NAME}>")
# deploy artifacts required to run the application to the debug build folder

View File

@@ -75,8 +75,6 @@ private const val OPEN_FILE_ACTION_CODE = 3
private const val CHECK_NOTIFICATION_PERMISSION_ACTION_CODE = 4
private const val PREFS_NOTIFICATION_PERMISSION_ASKED = "NOTIFICATION_PERMISSION_ASKED"
private const val OPEN_FILE_AFTER_RESUME_DELAY_MS = 400L
private const val KEY_PENDING_OPEN_FILE_URI = "pending_open_file_uri"
class AmneziaActivity : QtActivity() {
@@ -93,12 +91,6 @@ class AmneziaActivity : QtActivity() {
private val actionResultHandlers = mutableMapOf<Int, ActivityResultHandler>()
private val permissionRequestHandlers = mutableMapOf<Int, PermissionRequestHandler>()
private var isActivityResumed = false
private var hasWindowFocus = false
private val resumeHandler = Handler(Looper.getMainLooper())
private var pendingOpenFileUri: String? = null
private var openFileDeliveryScheduled = false
private val vpnServiceEventHandler: Handler by lazy(NONE) {
object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
@@ -200,18 +192,11 @@ class AmneziaActivity : QtActivity() {
doBindService()
}
)
pendingOpenFileUri = savedInstanceState?.getString(KEY_PENDING_OPEN_FILE_URI)
openFileDeliveryScheduled = false
registerBroadcastReceivers()
intent?.let(::processIntent)
runBlocking { vpnProto = proto.await() }
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
pendingOpenFileUri?.let { outState.putString(KEY_PENDING_OPEN_FILE_URI, it) }
}
private fun loadLibs() {
listOf(
"rsapss",
@@ -277,11 +262,6 @@ class AmneziaActivity : QtActivity() {
}
override fun onStop() {
isActivityResumed = false
hasWindowFocus = false
// Cancel all pending operations when activity stops
resumeHandler.removeCallbacksAndMessages(null)
openFileDeliveryScheduled = false
Log.d(TAG, "Stop Amnezia activity")
doUnbindService()
mainScope.launch {
@@ -293,41 +273,39 @@ class AmneziaActivity : QtActivity() {
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
hasWindowFocus = hasFocus
Log.d(TAG, "Window focus changed: hasFocus=$hasFocus")
// Cancel pending operations if window loses focus
if (!hasFocus) {
resumeHandler.removeCallbacksAndMessages(null)
}
}
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
val deviceId = event.deviceId
val keyCode = event.keyCode
val pressed = event.action == KeyEvent.ACTION_DOWN
val source = event.source
when (keyCode) {
KeyEvent.KEYCODE_BUTTON_A,
KeyEvent.KEYCODE_BUTTON_B,
KeyEvent.KEYCODE_BUTTON_X,
KeyEvent.KEYCODE_BUTTON_Y,
KeyEvent.KEYCODE_BUTTON_START,
KeyEvent.KEYCODE_BUTTON_SELECT -> {
nativeGamepadKeyEvent(0, keyCode, pressed)
if (deviceId < 0 && pressed) {
when (keyCode) {
KeyEvent.KEYCODE_BUTTON_A,
KeyEvent.KEYCODE_BUTTON_B,
KeyEvent.KEYCODE_BUTTON_X,
KeyEvent.KEYCODE_BUTTON_Y,
KeyEvent.KEYCODE_BUTTON_START,
KeyEvent.KEYCODE_BUTTON_SELECT,
KeyEvent.KEYCODE_DPAD_CENTER -> {
nativeGamepadKeyEvent(0, keyCode, true)
nativeGamepadKeyEvent(0, keyCode, false)
return true
}
}
KeyEvent.KEYCODE_DPAD_CENTER,
KeyEvent.KEYCODE_DPAD_UP,
KeyEvent.KEYCODE_DPAD_DOWN,
KeyEvent.KEYCODE_DPAD_LEFT,
KeyEvent.KEYCODE_DPAD_RIGHT -> {
val syntheticKeyCode = if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) KeyEvent.KEYCODE_ENTER else keyCode
val synthetic = KeyEvent(
event.downTime, event.eventTime, event.action, syntheticKeyCode,
event.repeatCount, event.metaState, -1, event.scanCode,
event.flags, InputDevice.SOURCE_KEYBOARD
)
return super.dispatchKeyEvent(synthetic)
}
// Real gamepad events (deviceId >= 0)
if (deviceId >= 0) {
val isGamepad = (source and InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD
val isJoystick = (source and InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK
val isDpad = (source and InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD
if (isGamepad || isJoystick || isDpad) {
nativeGamepadKeyEvent(deviceId, keyCode, pressed)
return true
}
}
@@ -338,58 +316,30 @@ class AmneziaActivity : QtActivity() {
override fun onPause() {
super.onPause()
isActivityResumed = false
// Cancel all pending operations when activity pauses
resumeHandler.removeCallbacksAndMessages(null)
openFileDeliveryScheduled = false
Log.d(TAG, "Pause Amnezia activity")
}
override fun onResume() {
super.onResume()
isActivityResumed = true
Log.d(TAG, "Resume Amnezia activity")
if (pendingOpenFileUri != null && !openFileDeliveryScheduled) {
val uri = pendingOpenFileUri!!
openFileDeliveryScheduled = true
resumeHandler.postDelayed({
if (!isFinishing && !isDestroyed) {
pendingOpenFileUri = null
openFileDeliveryScheduled = false
mainScope.launch {
qtInitialized.await()
QtAndroidController.onFileOpened(uri)
}
}
}, OPEN_FILE_AFTER_RESUME_DELAY_MS)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
/* if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
window.decorView.apply {
invalidate()
resumeHandler.postDelayed({
// Check if activity is still resumed and has focus before executing
if (isActivityResumed && hasWindowFocus && !isFinishing && !isDestroyed) {
sendTouch(1f, 1f)
}
postDelayed({
sendTouch(1f, 1f)
}, 100)
resumeHandler.postDelayed({
if (isActivityResumed && hasWindowFocus && !isFinishing && !isDestroyed) {
sendTouch(2f, 2f)
}
postDelayed({
sendTouch(2f, 2f)
}, 200)
resumeHandler.postDelayed({
if (isActivityResumed && hasWindowFocus && !isFinishing && !isDestroyed) {
requestLayout()
invalidate()
}
postDelayed({
requestLayout()
invalidate()
}, 250)
}
}
} */
Log.d(TAG, "Resume Amnezia activity")
}
private fun configureWindowForEdgeToEdge() {
@@ -427,35 +377,31 @@ class AmneziaActivity : QtActivity() {
ViewCompat.setOnApplyWindowInsetsListener(window.decorView) { view, windowInsets ->
val imeInsets = windowInsets.getInsets(WindowInsetsCompat.Type.ime())
val imeVisible = windowInsets.isVisible(WindowInsetsCompat.Type.ime())
val imeHeight = if (imeVisible) imeInsets.bottom else 0
val density = resources.displayMetrics.density
val imeHeightDp = (imeHeight / density).toInt()
// Also track system bars (navigation bar, status bar) changes
val systemBarsInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val navBarHeight = systemBarsInsets.bottom
val navBarHeightDp = (navBarHeight / density).toInt()
val statusBarHeight = systemBarsInsets.top
val statusBarHeightDp = (statusBarHeight / density).toInt()
mainScope.launch {
qtInitialized.await()
QtAndroidController.onImeInsetsChanged(imeHeightDp)
QtAndroidController.onSystemBarsInsetsChanged(navBarHeightDp, statusBarHeightDp)
}
// Return windowInsets instead of CONSUMED to allow proper handling
windowInsets
}
}
override fun onDestroy() {
isActivityResumed = false
hasWindowFocus = false
// Cancel all pending operations when activity is destroyed
resumeHandler.removeCallbacksAndMessages(null)
Log.d(TAG, "Destroy Amnezia activity")
unregisterBroadcastReceiver(notificationStateReceiver)
notificationStateReceiver = null
@@ -781,13 +727,9 @@ class AmneziaActivity : QtActivity() {
grantUriPermission(packageName, this, Intent.FLAG_GRANT_READ_URI_PERMISSION)
}?.toString() ?: ""
Log.v(TAG, "Open file: $uri")
if (uri.isNotEmpty()) {
pendingOpenFileUri = uri
} else {
mainScope.launch {
qtInitialized.await()
QtAndroidController.onFileOpened(uri)
}
mainScope.launch {
qtInitialized.await()
QtAndroidController.onFileOpened(uri)
}
}
))

View File

@@ -33,10 +33,7 @@ class TvFilePicker : ComponentActivity() {
return intent
}
}) {
setResult(RESULT_OK, Intent().apply {
data = it
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
})
setResult(RESULT_OK, Intent().apply { data = it })
finish()
}

View File

@@ -163,7 +163,7 @@ add_custom_command(TARGET ${PROJECT} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory
$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks
COMMAND /usr/bin/find "$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks/OpenVPNAdapter.framework" -name "*.sha256" -delete
COMMAND /usr/bin/codesign --force --sign "Apple Distribution: Privacy Technologies OU"
COMMAND /usr/bin/codesign --force --sign "Apple Distribution"
"$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks/OpenVPNAdapter.framework/Versions/Current/OpenVPNAdapter"
COMMAND ${QT_BIN_DIR_DETECTED}/macdeployqt $<TARGET_BUNDLE_DIR:AmneziaVPN> -appstore-compliant -qmldir=${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Signing OpenVPNAdapter framework"

View File

@@ -181,6 +181,7 @@ if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
set(HEADERS ${HEADERS}
${CLIENT_ROOT_DIR}/core/ipcclient.h
${CLIENT_ROOT_DIR}/core/privileged_process.h
${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.h
${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.h
${CLIENT_ROOT_DIR}/protocols/openvpnovercloakprotocol.h
@@ -193,6 +194,7 @@ if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
set(SOURCES ${SOURCES}
${CLIENT_ROOT_DIR}/core/ipcclient.cpp
${CLIENT_ROOT_DIR}/core/privileged_process.cpp
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.cpp
${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.cpp

View File

@@ -135,7 +135,7 @@ void CoreController::initControllers()
new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_appSplitTunnelingModel, m_settings));
m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
m_sitesController.reset(new SitesController(m_settings, m_sitesModel));
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));
@@ -368,11 +368,7 @@ void CoreController::initPrepareConfigHandler()
return;
}
m_installController->validateConfig();
});
connect(m_installController.get(), &InstallController::configValidated, this, [this](bool isValid) {
if (!isValid) {
if (!m_installController->isConfigValid()) {
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
return;
}

View File

@@ -337,9 +337,6 @@ QStringList GatewayController::getProxyUrls(const QString &serviceType, const QS
} else {
baseUrls = QString(PROD_S3_ENDPOINT).split(", ");
}
std::random_device randomDevice;
std::mt19937 generator(randomDevice());
std::shuffle(baseUrls.begin(), baseUrls.end(), generator);
QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;

View File

@@ -7,6 +7,7 @@ IpcClient::IpcClient(QObject *parent) : QObject(parent)
{
m_node.connectToNode(QUrl("local:" + amnezia::getIpcServiceUrl()));
m_interface.reset(m_node.acquire<IpcInterfaceReplica>());
m_tun2socks.reset(m_node.acquire<IpcProcessTun2SocksReplica>());
}
IpcClient& IpcClient::Instance()
@@ -32,43 +33,68 @@ QSharedPointer<IpcInterfaceReplica> IpcClient::Interface()
return rep;
}
QSharedPointer<IpcProcessInterfaceReplica> IpcClient::CreatePrivilegedProcess()
QSharedPointer<IpcProcessTun2SocksReplica> IpcClient::InterfaceTun2Socks()
{
return withInterface([](QSharedPointer<IpcInterfaceReplica> &iface) -> QSharedPointer<IpcProcessInterfaceReplica> {
auto createPrivilegedProcess = iface->createPrivilegedProcess();
if (!createPrivilegedProcess.waitForFinished()) {
qCritical() << "Failed to create privileged process";
return nullptr;
}
const int pid = createPrivilegedProcess.returnValue();
auto* node = new QRemoteObjectNode();
node->connectToNode(QUrl(QString("local:%1").arg(amnezia::getIpcProcessUrl(pid))));
QSharedPointer<IpcProcessInterfaceReplica> rep(
node->acquire<IpcProcessInterfaceReplica>(),
[node] (IpcProcessInterfaceReplica *ptr) {
delete ptr;
node->deleteLater();
}
);
if (rep.isNull()) {
qCritical() << "IpcClient::CreatePrivilegedProcess(): Failed to acquire replica";
return nullptr;
}
if (!rep->waitForSource()) {
qCritical() << "IpcClient::CreatePrivilegedProcess(): Failed to initialize replica";
return nullptr;
}
if (!rep->isReplicaValid()) {
qCritical() << "IpcClient::CreatePrivilegedProcess(): Replica is invalid";
return nullptr;
}
return rep;
},
[]() -> QSharedPointer<IpcProcessInterfaceReplica> {
QSharedPointer<IpcProcessTun2SocksReplica> rep = Instance().m_tun2socks;
if (rep.isNull()) {
qCritical() << "IpcClient::InterfaceTun2Socks: Replica is undefined";
return nullptr;
});
}
if (!rep->waitForSource(1000)) {
qCritical() << "IpcClient::InterfaceTun2Socks: Failed to initialize replica";
return nullptr;
}
if (!rep->isReplicaValid()) {
qWarning() << "IpcClient::InterfaceTun2Socks(): Replica is invalid";
}
return rep;
}
QSharedPointer<PrivilegedProcess> IpcClient::CreatePrivilegedProcess()
{
QSharedPointer<IpcInterfaceReplica> rep = Interface();
if (!rep) {
qCritical() << "IpcClient::createPrivilegedProcess: Replica is invalid";
return nullptr;
}
QRemoteObjectPendingReply<int> pidReply = rep->createPrivilegedProcess();
if (!pidReply.waitForFinished(5000)){
qCritical() << "IpcClient::createPrivilegedProcess: Failed to execute RO createPrivilegedProcess call";
return nullptr;
}
int pid = pidReply.returnValue();
QSharedPointer<ProcessDescriptor> pd(new ProcessDescriptor());
pd->localSocket.reset(new QLocalSocket(pd->replicaNode.data()));
connect(pd->localSocket.data(), &QLocalSocket::connected, pd->replicaNode.data(), [pd]() {
pd->replicaNode->addClientSideConnection(pd->localSocket.data());
IpcProcessInterfaceReplica *repl = pd->replicaNode->acquire<IpcProcessInterfaceReplica>();
// TODO: rework the unsafe cast below
PrivilegedProcess *priv = static_cast<PrivilegedProcess *>(repl);
pd->ipcProcess.reset(priv);
if (!pd->ipcProcess) {
qWarning() << "Acquire PrivilegedProcess failed";
} else {
pd->ipcProcess->waitForSource(1000);
if (!pd->ipcProcess->isReplicaValid()) {
qWarning() << "PrivilegedProcess replica is not connected!";
}
QObject::connect(pd->ipcProcess.data(), &PrivilegedProcess::destroyed, pd->ipcProcess.data(),
[pd]() { pd->replicaNode->deleteLater(); });
}
});
pd->localSocket->connectToServer(amnezia::getIpcProcessUrl(pid));
if (!pd->localSocket->waitForConnected()) {
qCritical() << "IpcClient::createPrivilegedProcess: Failed to connect to process' socket";
return nullptr;
}
auto processReplica = QSharedPointer<PrivilegedProcess>(pd->ipcProcess);
return processReplica;
}

View File

@@ -5,7 +5,9 @@
#include <QObject>
#include "rep_ipc_interface_replica.h"
#include "rep_ipc_process_interface_replica.h"
#include "rep_ipc_process_tun2socks_replica.h"
#include "privileged_process.h"
class IpcClient : public QObject
{
@@ -16,7 +18,8 @@ public:
static IpcClient& Instance();
static QSharedPointer<IpcInterfaceReplica> Interface();
static QSharedPointer<IpcProcessInterfaceReplica> CreatePrivilegedProcess();
static QSharedPointer<IpcProcessTun2SocksReplica> InterfaceTun2Socks();
static QSharedPointer<PrivilegedProcess> CreatePrivilegedProcess();
template <typename Func>
static auto withInterface(Func func)
@@ -51,6 +54,18 @@ signals:
private:
QRemoteObjectNode m_node;
QSharedPointer<IpcInterfaceReplica> m_interface;
QSharedPointer<IpcProcessTun2SocksReplica> m_tun2socks;
struct ProcessDescriptor {
ProcessDescriptor () {
replicaNode = QSharedPointer<QRemoteObjectNode>(new QRemoteObjectNode());
ipcProcess = QSharedPointer<PrivilegedProcess>();
localSocket = QSharedPointer<QLocalSocket>();
}
QSharedPointer<PrivilegedProcess> ipcProcess;
QSharedPointer<QRemoteObjectNode> replicaNode;
QSharedPointer<QLocalSocket> localSocket;
};
};
#endif // IPCCLIENT_H

View File

@@ -0,0 +1,27 @@
#include "privileged_process.h"
PrivilegedProcess::PrivilegedProcess() :
IpcProcessInterfaceReplica()
{
}
PrivilegedProcess::~PrivilegedProcess()
{
qDebug() << "PrivilegedProcess::~PrivilegedProcess()";
}
void PrivilegedProcess::waitForFinished(int msecs)
{
QSharedPointer<QEventLoop> loop(new QEventLoop);
connect(this, &PrivilegedProcess::finished, this, [this, loop](int exitCode, QProcess::ExitStatus exitStatus) mutable{
loop->quit();
loop.clear();
});
QTimer::singleShot(msecs, this, [this, loop]() mutable {
loop->quit();
loop.clear();
});
loop->exec();
}

View File

@@ -0,0 +1,24 @@
#ifndef PRIVILEGED_PROCESS_H
#define PRIVILEGED_PROCESS_H
#include <QObject>
#include "rep_ipc_process_interface_replica.h"
// This class is dangerous - instance of this class casted from base class,
// so it support only functions
// Do not add any members into it
//
class PrivilegedProcess : public IpcProcessInterfaceReplica
{
Q_OBJECT
public:
PrivilegedProcess();
~PrivilegedProcess() override;
void waitForFinished(int msecs);
};
#endif // PRIVILEGED_PROCESS_H

View File

@@ -72,9 +72,9 @@ void NetworkWatcher::initialize() {
connect(m_impl, &NetworkWatcherImpl::unsecuredNetwork, this,
&NetworkWatcher::unsecuredNetwork);
connect(m_impl, &NetworkWatcherImpl::networkChanged, this,
&NetworkWatcher::networkChanged);
connect(m_impl, &NetworkWatcherImpl::wakeup, this,
&NetworkWatcher::wakeup);
&NetworkWatcher::networkChange);
connect(m_impl, &NetworkWatcherImpl::sleepMode, this,
&NetworkWatcher::onSleepMode);
m_impl->initialize();
// Enable sleep/wake monitoring for VPN auto-reconnection
@@ -97,6 +97,12 @@ void NetworkWatcher::settingsChanged() {
logger.debug() << "NetworkWatcher settings changed - keeping sleep monitoring active";
}
void NetworkWatcher::onSleepMode()
{
logger.debug() << "Resumed from sleep mode";
emit sleepMode();
}
void NetworkWatcher::unsecuredNetwork(const QString& networkName,
const QString& networkId) {
logger.debug() << "Unsecured network:" << logger.sensitive(networkName)

View File

@@ -29,11 +29,13 @@ public:
// false to restore.
void simulateDisconnection(bool simulatedDisconnection);
void onSleepMode();
QNetworkInformation::Reachability getReachability();
signals:
void networkChanged();
void wakeup();
void networkChange();
void sleepMode();
private:
void settingsChanged();

View File

@@ -41,7 +41,7 @@ signals:
// TODO: Only windows-networkwatcher has this, the other plattforms should
// too.
void networkChanged(QString newBSSID);
void wakeup();
void sleepMode();
private:

View File

@@ -41,8 +41,8 @@ void LinuxNetworkWatcher::initialize() {
connect(m_worker, &LinuxNetworkWatcherWorker::unsecuredNetwork, this,
&LinuxNetworkWatcher::unsecuredNetwork);
connect(m_worker, &LinuxNetworkWatcherWorker::wakeup, this,
&NetworkWatcherImpl::wakeup);
connect(m_worker, &LinuxNetworkWatcherWorker::sleepMode, this,
&NetworkWatcherImpl::sleepMode);
// Let's wait a few seconds to allow the UI to be fully loaded and shown.
// This is not strictly needed, but it's better for user experience because

View File

@@ -200,7 +200,7 @@ void LinuxNetworkWatcherWorker::checkDevices() {
void LinuxNetworkWatcherWorker::NMStateChanged(quint32 state)
{
if (state == NM_STATE_ASLEEP) {
emit wakeup();
emit sleepMode();
}
logger.debug() << "NMStateChanged " << state;

View File

@@ -23,7 +23,7 @@ class LinuxNetworkWatcherWorker final : public QObject {
signals:
void unsecuredNetwork(const QString& networkName, const QString& networkId);
void wakeup();
void sleepMode();
public slots:
void initialize();

View File

@@ -173,10 +173,10 @@ void PowerNotificationsListener::sleepWakeupCallBack(void *refParam, io_service_
case kIOMessageSystemHasPoweredOn:
/* Announces that the system and its devices have woken up. */
logger.debug() << "System has powered on - emitting wakeup signal from dedicated CFRunLoop thread";
logger.debug() << "System has powered on - emitting sleepMode signal from dedicated CFRunLoop thread";
if (listener->m_watcher) {
// Use QMetaObject::invokeMethod for thread-safe signal emission
QMetaObject::invokeMethod(listener->m_watcher, "wakeup", Qt::QueuedConnection);
QMetaObject::invokeMethod(listener->m_watcher, "sleepMode", Qt::QueuedConnection);
}
break;

View File

@@ -62,9 +62,6 @@ void WindowsDaemon::prepareActivation(const InterfaceConfig& config, int inetAda
}
void WindowsDaemon::activateSplitTunnel(const InterfaceConfig& config, int vpnAdapterIndex) {
if (m_splitTunnelManager == nullptr)
return;
if (config.m_vpnDisabledApps.length() > 0) {
m_splitTunnelManager->start(m_inetAdapterIndex, vpnAdapterIndex);
m_splitTunnelManager->excludeApps(config.m_vpnDisabledApps);

View File

@@ -41,7 +41,7 @@ LRESULT WindowsNetworkWatcher::PowerWndProcCallback(HWND hwnd, UINT uMsg, WPARAM
switch (uMsg) {
case WM_POWERBROADCAST:
if (wParam == PBT_APMRESUMESUSPEND) {
emit obj->wakeup();
emit obj->sleepMode();
}
break;
default:

View File

@@ -232,6 +232,12 @@ ErrorCode OpenVpnProtocol::start()
return ErrorCode::AmneziaServiceConnectionFailed;
}
m_openVpnProcess->waitForSource(5000);
if (!m_openVpnProcess->isInitialized()) {
qWarning() << "IpcProcess replica is not connected!";
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return ErrorCode::AmneziaServiceConnectionFailed;
}
m_openVpnProcess->setProgram(PermittedProcess::OpenVPN);
QStringList arguments({
"--config", configPath(), "--management", m_managementHost, QString::number(mgmtPort),
@@ -240,13 +246,13 @@ ErrorCode OpenVpnProtocol::start()
m_openVpnProcess->setArguments(arguments);
qDebug() << arguments.join(" ");
connect(m_openVpnProcess.data(), &IpcProcessInterfaceReplica::errorOccurred,
connect(m_openVpnProcess.data(), &PrivilegedProcess::errorOccurred,
[&](QProcess::ProcessError error) { qDebug() << "PrivilegedProcess errorOccurred" << error; });
connect(m_openVpnProcess.data(), &IpcProcessInterfaceReplica::stateChanged,
connect(m_openVpnProcess.data(), &PrivilegedProcess::stateChanged,
[&](QProcess::ProcessState newState) { qDebug() << "PrivilegedProcess stateChanged" << newState; });
connect(m_openVpnProcess.data(), &IpcProcessInterfaceReplica::finished, this,
connect(m_openVpnProcess.data(), &PrivilegedProcess::finished, this,
[&]() { setConnectionState(Vpn::ConnectionState::Disconnected); });
m_openVpnProcess->start();

View File

@@ -53,7 +53,7 @@ private:
void updateRouteGateway(QString line);
void updateVpnGateway(const QString &line);
QSharedPointer<IpcProcessInterfaceReplica> m_openVpnProcess;
QSharedPointer<PrivilegedProcess> m_openVpnProcess;
};
#endif // OPENVPNPROTOCOL_H

View File

@@ -233,7 +233,7 @@ namespace amnezia
constexpr char defaultResponsePacketMagicHeader[] = "3288052141";
constexpr char defaultTransportPacketMagicHeader[] = "2528465083";
constexpr char defaultUnderloadPacketMagicHeader[] = "1766607858";
constexpr char defaultSpecialJunk1[] = "<r 2><b 0x858000010001000000000669636c6f756403636f6d0000010001c00c000100010000105a00044d583737>";
constexpr char defaultSpecialJunk1[] = "<b 0x084481800001000300000000077469636b65747306776964676574096b696e6f706f69736b0272750000010001c00c0005000100000039001806776964676574077469636b6574730679616e646578c025c0390005000100000039002b1765787465726e616c2d7469636b6574732d776964676574066166697368610679616e646578036e657400c05d000100010000001c000457fafe25>";
constexpr char defaultSpecialJunk2[] = "";
constexpr char defaultSpecialJunk3[] = "";
constexpr char defaultSpecialJunk4[] = "";

View File

@@ -15,7 +15,7 @@ WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject *
m_impl.reset(new LocalSocketController());
connect(m_impl.get(), &ControllerImpl::connected, this,
[this](const QString &pubkey, const QDateTime &connectionTimestamp) {
setConnectionState(Vpn::ConnectionState::Connected);
emit connectionStateChanged(Vpn::ConnectionState::Connected);
});
connect(m_impl.get(), &ControllerImpl::statusUpdated, this,
[this](const QString& serverIpv4Gateway,
@@ -38,7 +38,7 @@ WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject *
});
connect(m_impl.get(), &ControllerImpl::disconnected, this,
[this]() { setConnectionState(Vpn::ConnectionState::Disconnected); });
[this]() { emit connectionStateChanged(Vpn::ConnectionState::Disconnected); });
m_impl->initialize(nullptr, nullptr);
}

View File

@@ -1,7 +1,6 @@
#include "xrayprotocol.h"
#include "core/ipcclient.h"
#include "ipc.h"
#include "utilities.h"
#include "core/networkUtilities.h"
@@ -10,37 +9,14 @@
#include <QJsonObject>
#include <QNetworkInterface>
#include <QJsonDocument>
#include <QtCore/qlogging.h>
#include <QtCore/qobjectdefs.h>
#include <QtCore/qprocess.h>
#ifdef Q_OS_MACOS
static const QString tunName = "utun22";
#else
static const QString tunName = "tun2";
#endif
XrayProtocol::XrayProtocol(const QJsonObject &configuration, QObject *parent) : VpnProtocol(configuration, parent)
{
readXrayConfiguration(configuration);
m_routeGateway = NetworkUtilities::getGatewayAndIface().first;
m_vpnGateway = amnezia::protocols::xray::defaultLocalAddr;
m_vpnLocalAddress = amnezia::protocols::xray::defaultLocalAddr;
m_routeGateway = NetworkUtilities::getGatewayAndIface().first;
m_routeMode = static_cast<Settings::RouteMode>(configuration.value(amnezia::config_key::splitTunnelType).toInt());
m_remoteAddress = NetworkUtilities::getIPAddress(m_rawConfig.value(amnezia::config_key::hostName).toString());
const QString primaryDns = configuration.value(amnezia::config_key::dns1).toString();
m_dnsServers.push_back(QHostAddress(primaryDns));
if (primaryDns != amnezia::protocols::dns::amneziaDnsIp) {
const QString secondaryDns = configuration.value(amnezia::config_key::dns2).toString();
m_dnsServers.push_back(QHostAddress(secondaryDns));
}
QJsonObject xrayConfiguration = configuration.value(ProtocolProps::key_proto_config_data(Proto::Xray)).toObject();
if (xrayConfiguration.isEmpty()) {
xrayConfiguration = configuration.value(ProtocolProps::key_proto_config_data(Proto::SSXray)).toObject();
}
m_xrayConfig = xrayConfiguration;
m_t2sProcess = IpcClient::InterfaceTun2Socks();
}
XrayProtocol::~XrayProtocol()
@@ -53,190 +29,70 @@ ErrorCode XrayProtocol::start()
{
qDebug() << "XrayProtocol::start()";
return IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
auto xrayStart = iface->xrayStart(QJsonDocument(m_xrayConfig).toJson());
if (!xrayStart.waitForFinished() || !xrayStart.returnValue()) {
qCritical() << "Failed to start xray";
return ErrorCode::XrayExecutableCrashed;
}
return startTun2Socks();
const ErrorCode err = IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
iface->xrayStart(QJsonDocument(m_xrayConfig).toJson());
return ErrorCode::NoError;
}, [] () {
return ErrorCode::AmneziaServiceConnectionFailed;
});
}
if (err != ErrorCode::NoError)
return err;
void XrayProtocol::stop()
{
qDebug() << "XrayProtocol::stop()";
IpcClient::withInterface([](QSharedPointer<IpcInterfaceReplica> iface) {
auto disableKillSwitch = iface->disableKillSwitch();
if (!disableKillSwitch.waitForFinished() || !disableKillSwitch.returnValue())
qWarning() << "Failed to disable killswitch";
auto StartRoutingIpv6 = iface->StartRoutingIpv6();
if (!StartRoutingIpv6.waitForFinished() || !StartRoutingIpv6.returnValue())
qWarning() << "Failed to start routing ipv6";
auto restoreResolvers = iface->restoreResolvers();
if (!restoreResolvers.waitForFinished() || !restoreResolvers.returnValue())
qWarning() << "Failed to restore resolvers";
auto deleteTun = iface->deleteTun(tunName);
if (!deleteTun.waitForFinished() || !deleteTun.returnValue())
qWarning() << "Failed to delete tun";
auto xrayStop = iface->xrayStop();
if (!xrayStop.waitForFinished() || !xrayStop.returnValue())
qWarning() << "Failed to stop xray";
});
if (m_tun2socksProcess) {
m_tun2socksProcess->blockSignals(true);
#ifndef Q_OS_WIN
m_tun2socksProcess->terminate();
auto waitForFinished = m_tun2socksProcess->waitForFinished(1000);
if (!waitForFinished.waitForFinished() || !waitForFinished.returnValue()) {
qWarning() << "Failed to terminate tun2socks. Killing the process...";
m_tun2socksProcess->kill();
}
#else
// terminate does not do anything useful on Windows
// so just kill the process
m_tun2socksProcess->kill();
#endif
m_tun2socksProcess->close();
m_tun2socksProcess.reset();
}
setConnectionState(Vpn::ConnectionState::Disconnected);
}
ErrorCode XrayProtocol::startTun2Socks()
{
m_tun2socksProcess = IpcClient::CreatePrivilegedProcess();
if (!m_tun2socksProcess->waitForSource()) {
return ErrorCode::AmneziaServiceConnectionFailed;
}
m_tun2socksProcess->setProgram(PermittedProcess::Tun2Socks);
m_tun2socksProcess->setArguments({"-device", QString("tun://%1").arg(tunName), "-proxy", "socks5://127.0.0.1:10808" });
connect(m_tun2socksProcess.data(), &IpcProcessInterfaceReplica::readyReadStandardOutput, this, [this]() {
auto readAllStandardOutput = m_tun2socksProcess->readAllStandardOutput();
if (!readAllStandardOutput.waitForFinished()) {
qWarning() << "Failed to read output from tun2socks";
return;
}
const QString line = readAllStandardOutput.returnValue();
if (!line.contains("[TCP]") && !line.contains("[UDP]"))
qDebug() << "[tun2socks]:" << line;
if (line.contains("[STACK] tun://") && line.contains("<-> socks5://127.0.0.1")) {
disconnect(m_tun2socksProcess.data(), &IpcProcessInterfaceReplica::readyReadStandardOutput, this, nullptr);
if (ErrorCode res = setupRouting(); res != ErrorCode::NoError) {
stop();
setLastError(res);
} else {
setConnectionState(Vpn::ConnectionState::Connected);
}
}
}, Qt::QueuedConnection);
connect(m_tun2socksProcess.data(), &IpcProcessInterfaceReplica::finished, this, [this](int exitCode, QProcess::ExitStatus exitStatus) {
if (exitStatus == QProcess::ExitStatus::CrashExit) {
qCritical() << "Tun2socks process crashed!";
} else {
qCritical() << QString("Tun2socks process was closed with %1 exit code").arg(exitCode);
}
stop();
setLastError(ErrorCode::Tun2SockExecutableCrashed);
}, Qt::QueuedConnection);
m_tun2socksProcess->start();
return ErrorCode::NoError;
setConnectionState(Vpn::ConnectionState::Connecting);
return startTun2Sock();
}
ErrorCode XrayProtocol::setupRouting() {
return IpcClient::withInterface([this](QSharedPointer<IpcInterfaceReplica> iface) -> ErrorCode {
#ifdef Q_OS_WIN
const int inetAdapterIndex = NetworkUtilities::AdapterIndexTo(QHostAddress(m_remoteAddress));
#endif
QList<QHostAddress> dnsAddr;
dnsAddr.push_back(QHostAddress(m_primaryDNS));
// We don't use secondary DNS if primary DNS is AmneziaDNS
if (!m_primaryDNS.contains(amnezia::protocols::dns::amneziaDnsIp)) {
dnsAddr.push_back(QHostAddress(m_secondaryDNS));
}
#ifdef AMNEZIA_DESKTOP
#ifdef Q_OS_MACOS
const QString tunName = "utun22";
#else
const QString tunName = "tun2";
#endif
auto createTun = iface->createTun(tunName, amnezia::protocols::xray::defaultLocalAddr);
if (!createTun.waitForFinished() || !createTun.returnValue()) {
qCritical() << "Failed to assign IP address for TUN";
if (!createTun.waitForFinished(1000) || !createTun.returnValue()) {
qWarning() << "Failed to assign IP address for TUN";
return ErrorCode::InternalError;
}
auto updateResolvers = iface->updateResolvers(tunName, m_dnsServers);
if (!updateResolvers.waitForFinished() || !updateResolvers.returnValue()) {
qCritical() << "Failed to set DNS resolvers for TUN";
auto updateResolvers = iface->updateResolvers(tunName, dnsAddr);
if (!updateResolvers.waitForFinished(1000) || !updateResolvers.returnValue()) {
qWarning() << "Failed to set DNS resolvers for TUN";
return ErrorCode::InternalError;
}
#ifdef Q_OS_WIN
int vpnAdapterIndex = -1;
QList<QNetworkInterface> netInterfaces = QNetworkInterface::allInterfaces();
for (auto& netInterface : netInterfaces) {
for (auto& address : netInterface.addressEntries()) {
if (m_vpnLocalAddress == address.ip().toString())
vpnAdapterIndex = netInterface.index();
}
}
#else
static const int vpnAdapterIndex = 0;
#endif
const bool killSwitchEnabled = QVariant(m_rawConfig.value(config_key::killSwitchOption).toString()).toBool();
if (killSwitchEnabled) {
if (vpnAdapterIndex != -1) {
QJsonObject config = m_rawConfig;
config.insert("vpnServer", m_remoteAddress);
auto enableKillSwitch = IpcClient::Interface()->enableKillSwitch(config, vpnAdapterIndex);
if (!enableKillSwitch.waitForFinished() || !enableKillSwitch.returnValue()) {
qCritical() << "Failed to enable killswitch";
return ErrorCode::InternalError;
}
} else
qWarning() << "Failed to get vpnAdapterIndex. Killswitch disabled";
}
if (m_routeMode == Settings::RouteMode::VpnAllSites) {
static const QStringList subnets = { "1.0.0.0/8", "2.0.0.0/7", "4.0.0.0/6", "8.0.0.0/5", "16.0.0.0/4", "32.0.0.0/3", "64.0.0.0/2", "128.0.0.0/1" };
auto routeAddList = iface->routeAddList(m_vpnGateway, subnets);
if (!routeAddList.waitForFinished() || routeAddList.returnValue() != subnets.count()) {
qCritical() << "Failed to set routes for TUN";
if (!routeAddList.waitForFinished(1000) || routeAddList.returnValue() != subnets.count()) {
qWarning() << "Failed to set routes for TUN";
return ErrorCode::InternalError;
}
}
auto StopRoutingIpv6 = iface->StopRoutingIpv6();
if (!StopRoutingIpv6.waitForFinished() || !StopRoutingIpv6.returnValue()) {
qCritical() << "Failed to disable IPv6 routing";
if (!StopRoutingIpv6.waitForFinished(1000) || !StopRoutingIpv6.returnValue()) {
qWarning() << "Failed to disable IPv6 routing";
return ErrorCode::InternalError;
}
#ifdef Q_OS_WIN
if (inetAdapterIndex != -1 && vpnAdapterIndex != -1) {
QJsonObject config = m_rawConfig;
config.insert("inetAdapterIndex", inetAdapterIndex);
config.insert("vpnAdapterIndex", vpnAdapterIndex);
config.insert("vpnGateway", m_vpnGateway);
config.insert("vpnServer", m_remoteAddress);
auto enablePeerTraffic = iface->enablePeerTraffic(config);
if (!enablePeerTraffic.waitForFinished() || !enablePeerTraffic.returnValue()) {
qCritical() << "Failed to enable peer traffic";
return ErrorCode::InternalError;
}
} else
qWarning() << "Failed to get adapter indexes. Split-tunneling disabled";
auto enablePeerTraffic = iface->enablePeerTraffic(m_xrayConfig);
if (!enablePeerTraffic.waitForFinished(5000) || !enablePeerTraffic.returnValue()) {
qWarning() << "Failed to enable peer traffic";
return ErrorCode::InternalError;
}
#endif
return ErrorCode::NoError;
},
@@ -244,3 +100,79 @@ ErrorCode XrayProtocol::setupRouting() {
return ErrorCode::AmneziaServiceConnectionFailed;
});
}
ErrorCode XrayProtocol::startTun2Sock()
{
m_t2sProcess->start();
connect(m_t2sProcess.data(), &IpcProcessTun2SocksReplica::stateChanged, this,
[&](QProcess::ProcessState newState) { qDebug() << "PrivilegedProcess stateChanged" << newState; });
connect(m_t2sProcess.data(), &IpcProcessTun2SocksReplica::setConnectionState, this, [&](int vpnState) {
QMetaObject::invokeMethod(this, [this, vpnState]() {
qDebug() << "PrivilegedProcess setConnectionState " << vpnState;
if (vpnState == Vpn::ConnectionState::Connected) {
setConnectionState(Vpn::ConnectionState::Connecting);
if (ErrorCode res = setupRouting(); res != ErrorCode::NoError) {
stop();
setLastError(res);
} else
setConnectionState(Vpn::ConnectionState::Connected);
}
if (vpnState == Vpn::ConnectionState::Disconnected)
stop();
}, Qt::QueuedConnection);
});
return ErrorCode::NoError;
}
void XrayProtocol::stop()
{
qDebug() << "XrayProtocol::stop()";
IpcClient::withInterface([](QSharedPointer<IpcInterfaceReplica> iface) {
#ifdef AMNEZIA_DESKTOP
auto StartRoutingIpv6 = iface->StartRoutingIpv6();
if (!StartRoutingIpv6.waitForFinished(1000) || !StartRoutingIpv6.returnValue()) {
qWarning() << "XrayProtocol::stop(): Failed to start routing ipv6";
}
auto restoreResolvers = iface->restoreResolvers();
if (!restoreResolvers.waitForFinished(1000) || !restoreResolvers.returnValue()) {
qWarning() << "XrayProtocol::stop(): Failed to restore resolvers";
}
#if !defined(Q_OS_MACOS)
auto deleteTun = iface->deleteTun("tun2");
if (!deleteTun.waitForFinished(1000) || !deleteTun.returnValue()) {
qWarning() << "XrayProtocol::stop(): Failed to delete tun";
}
#endif
#endif
iface->xrayStop();
});
if (m_t2sProcess) {
m_t2sProcess->stop();
QThread::msleep(200);
}
setConnectionState(Vpn::ConnectionState::Disconnected);
}
void XrayProtocol::readXrayConfiguration(const QJsonObject &configuration)
{
QJsonObject xrayConfiguration = configuration.value(ProtocolProps::key_proto_config_data(Proto::Xray)).toObject();
if (xrayConfiguration.isEmpty()) {
xrayConfiguration = configuration.value(ProtocolProps::key_proto_config_data(Proto::SSXray)).toObject();
}
m_xrayConfig = xrayConfiguration;
m_routeMode = static_cast<Settings::RouteMode>(configuration.value(amnezia::config_key::splitTunnelType).toInt());
m_primaryDNS = configuration.value(amnezia::config_key::dns1).toString();
m_secondaryDNS = configuration.value(amnezia::config_key::dns2).toString();
}

View File

@@ -6,7 +6,6 @@
#include "core/ipcclient.h"
#include "vpnprotocol.h"
#include "settings.h"
#include <QtCore/qsharedpointer.h>
class XrayProtocol : public VpnProtocol
{
@@ -19,14 +18,16 @@ public:
private:
ErrorCode setupRouting();
ErrorCode startTun2Socks();
ErrorCode startTun2Sock();
void readXrayConfiguration(const QJsonObject &configuration);
QJsonObject m_xrayConfig;
Settings::RouteMode m_routeMode;
QList<QHostAddress> m_dnsServers;
QString m_remoteAddress;
QSharedPointer<IpcProcessInterfaceReplica> m_tun2socksProcess;
QString m_primaryDNS;
QString m_secondaryDNS;
#ifndef Q_OS_IOS
QSharedPointer<IpcProcessTun2SocksReplica> m_t2sProcess;
#endif
};
#endif // XRAYPROTOCOL_H

View File

@@ -35,12 +35,13 @@ SecureQSettings::SecureQSettings(const QString &organization, const QString &app
}
}
m_settings.setValue("Conf/encrypted", true);
m_settings.sync();
}
}
QVariant SecureQSettings::value(const QString &key, const QVariant &defaultValue) const
{
QMutexLocker locker(&m_mutex);
QMutexLocker locker(&mutex);
if (m_cache.contains(key)) {
return m_cache.value(key);
@@ -84,7 +85,7 @@ QVariant SecureQSettings::value(const QString &key, const QVariant &defaultValue
void SecureQSettings::setValue(const QString &key, const QVariant &value)
{
QMutexLocker locker(&m_mutex);
QMutexLocker locker(&mutex);
if (encryptionRequired() && encryptedKeys.contains(key)) {
if (!getEncKey().isEmpty() && !getEncIv().isEmpty()) {
@@ -106,20 +107,26 @@ void SecureQSettings::setValue(const QString &key, const QVariant &value)
}
m_cache.insert(key, value);
sync();
}
void SecureQSettings::remove(const QString &key)
{
QMutexLocker locker(&m_mutex);
QMutexLocker locker(&mutex);
m_settings.remove(key);
m_cache.remove(key);
sync();
}
void SecureQSettings::sync()
{
m_settings.sync();
}
QByteArray SecureQSettings::backupAppConfig() const
{
QMutexLocker locker(&m_mutex);
QJsonObject cfg;
const auto needToBackup = [this](const auto &key) {
@@ -154,8 +161,6 @@ QByteArray SecureQSettings::backupAppConfig() const
bool SecureQSettings::restoreAppConfig(const QByteArray &json)
{
QMutexLocker locker(&m_mutex);
QJsonObject cfg = QJsonDocument::fromJson(json).object();
if (cfg.isEmpty())
return false;
@@ -168,16 +173,10 @@ bool SecureQSettings::restoreAppConfig(const QByteArray &json)
setValue(key, cfg.value(key).toVariant());
}
sync();
return true;
}
void SecureQSettings::clearSettings()
{
QMutexLocker locker(&m_mutex);
m_settings.clear();
m_cache.clear();
}
QByteArray SecureQSettings::encryptText(const QByteArray &value) const
{
QSimpleCrypto::QBlockCipher cipher;
@@ -295,3 +294,11 @@ void SecureQSettings::setSecTag(const QString &tag, const QByteArray &data)
qCritical() << "SecureQSettings::setSecTag Error:" << job->errorString();
}
}
void SecureQSettings::clearSettings()
{
QMutexLocker locker(&mutex);
m_settings.clear();
m_cache.clear();
sync();
}

View File

@@ -16,16 +16,14 @@ public:
explicit SecureQSettings(const QString &organization, const QString &application = QString(),
QObject *parent = nullptr);
QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
void setValue(const QString &key, const QVariant &value);
Q_INVOKABLE QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
Q_INVOKABLE void setValue(const QString &key, const QVariant &value);
void remove(const QString &key);
void sync();
QByteArray backupAppConfig() const;
bool restoreAppConfig(const QByteArray &json);
void clearSettings();
private:
QByteArray encryptText(const QByteArray &value) const;
QByteArray decryptText(const QByteArray &ba) const;
@@ -37,6 +35,9 @@ private:
static QByteArray getSecTag(const QString &tag);
static void setSecTag(const QString &tag, const QByteArray &data);
void clearSettings();
private:
QSettings m_settings;
mutable QHash<QString, QVariant> m_cache;
@@ -52,7 +53,7 @@ private:
const QByteArray magicString { "EncData" }; // Magic keyword used for mark encrypted QByteArray
mutable QRecursiveMutex m_mutex;
mutable QMutex mutex;
};
#endif // SECUREQSETTINGS_H

View File

@@ -21,10 +21,10 @@ Settings::Settings(QObject *parent) : QObject(parent), m_settings(ORGANIZATION_N
{
// Import old settings
if (serversCount() == 0) {
QString user = m_settings.value("Server/userName").toString();
QString password = m_settings.value("Server/password").toString();
QString serverName = m_settings.value("Server/serverName").toString();
int port = m_settings.value("Server/serverPort").toInt();
QString user = value("Server/userName").toString();
QString password = value("Server/password").toString();
QString serverName = value("Server/serverName").toString();
int port = value("Server/serverPort").toInt();
if (!user.isEmpty() && !password.isEmpty() && !serverName.isEmpty()) {
QJsonObject server;
@@ -222,7 +222,7 @@ QString Settings::nextAvailableServerName() const
void Settings::setSaveLogs(bool enabled)
{
m_settings.setValue("Conf/saveLogs", enabled);
setValue("Conf/saveLogs", enabled);
#ifndef Q_OS_ANDROID
if (!isSaveLogs()) {
Logger::deInit();
@@ -242,12 +242,12 @@ void Settings::setSaveLogs(bool enabled)
QDateTime Settings::getLogEnableDate()
{
return m_settings.value("Conf/logEnableDate").toDateTime();
return value("Conf/logEnableDate").toDateTime();
}
void Settings::setLogEnableDate(QDateTime date)
{
m_settings.setValue("Conf/logEnableDate", date);
setValue("Conf/logEnableDate", date);
}
QString Settings::routeModeString(RouteMode mode) const
@@ -261,17 +261,17 @@ QString Settings::routeModeString(RouteMode mode) const
Settings::RouteMode Settings::routeMode() const
{
return static_cast<RouteMode>(m_settings.value("Conf/routeMode", 0).toInt());
return static_cast<RouteMode>(value("Conf/routeMode", 0).toInt());
}
bool Settings::isSitesSplitTunnelingEnabled() const
{
return m_settings.value("Conf/sitesSplitTunnelingEnabled", false).toBool();
return value("Conf/sitesSplitTunnelingEnabled", false).toBool();
}
void Settings::setSitesSplitTunnelingEnabled(bool enabled)
{
m_settings.setValue("Conf/sitesSplitTunnelingEnabled", enabled);
setValue("Conf/sitesSplitTunnelingEnabled", enabled);
}
bool Settings::addVpnSite(RouteMode mode, const QString &site, const QString &ip)
@@ -359,12 +359,12 @@ void Settings::removeAllVpnSites(RouteMode mode)
QString Settings::primaryDns() const
{
return m_settings.value("Conf/primaryDns", cloudFlareNs1).toString();
return value("Conf/primaryDns", cloudFlareNs1).toString();
}
QString Settings::secondaryDns() const
{
return m_settings.value("Conf/secondaryDns", cloudFlareNs2).toString();
return value("Conf/secondaryDns", cloudFlareNs2).toString();
}
void Settings::clearSettings()
@@ -386,18 +386,18 @@ QString Settings::appsRouteModeString(AppsRouteMode mode) const
Settings::AppsRouteMode Settings::getAppsRouteMode() const
{
return static_cast<AppsRouteMode>(m_settings.value("Conf/appsRouteMode", 0).toInt());
return static_cast<AppsRouteMode>(value("Conf/appsRouteMode", 0).toInt());
}
void Settings::setAppsRouteMode(AppsRouteMode mode)
{
m_settings.setValue("Conf/appsRouteMode", mode);
setValue("Conf/appsRouteMode", mode);
}
QVector<InstalledAppInfo> Settings::getVpnApps(AppsRouteMode mode) const
{
QVector<InstalledAppInfo> apps;
auto appsArray = m_settings.value("Conf/" + appsRouteModeString(mode)).toJsonArray();
auto appsArray = value("Conf/" + appsRouteModeString(mode)).toJsonArray();
for (const auto &app : appsArray) {
InstalledAppInfo appInfo;
appInfo.appName = app.toObject().value("appName").toString();
@@ -419,42 +419,43 @@ void Settings::setVpnApps(AppsRouteMode mode, const QVector<InstalledAppInfo> &a
appInfo.insert("appPath", app.appPath);
appsArray.push_back(appInfo);
}
m_settings.setValue("Conf/" + appsRouteModeString(mode), appsArray);
setValue("Conf/" + appsRouteModeString(mode), appsArray);
m_settings.sync();
}
bool Settings::isAppsSplitTunnelingEnabled() const
{
return m_settings.value("Conf/appsSplitTunnelingEnabled", false).toBool();
return value("Conf/appsSplitTunnelingEnabled", false).toBool();
}
void Settings::setAppsSplitTunnelingEnabled(bool enabled)
{
m_settings.setValue("Conf/appsSplitTunnelingEnabled", enabled);
setValue("Conf/appsSplitTunnelingEnabled", enabled);
}
bool Settings::isKillSwitchEnabled() const
{
return m_settings.value("Conf/killSwitchEnabled", true).toBool();
return value("Conf/killSwitchEnabled", true).toBool();
}
void Settings::setKillSwitchEnabled(bool enabled)
{
m_settings.setValue("Conf/killSwitchEnabled", enabled);
setValue("Conf/killSwitchEnabled", enabled);
}
bool Settings::isStrictKillSwitchEnabled() const
{
return m_settings.value("Conf/strictKillSwitchEnabled", false).toBool();
return value("Conf/strictKillSwitchEnabled", false).toBool();
}
void Settings::setStrictKillSwitchEnabled(bool enabled)
{
m_settings.setValue("Conf/strictKillSwitchEnabled", enabled);
setValue("Conf/strictKillSwitchEnabled", enabled);
}
QString Settings::getInstallationUuid(const bool needCreate)
{
auto uuid = m_settings.value("Conf/installationUuid", "").toString();
auto uuid = value("Conf/installationUuid", "").toString();
if (needCreate && uuid.isEmpty()) {
uuid = QUuid::createUuid().toString();
@@ -475,7 +476,7 @@ QString Settings::getInstallationUuid(const bool needCreate)
void Settings::setInstallationUuid(const QString &uuid)
{
m_settings.setValue("Conf/installationUuid", uuid);
setValue("Conf/installationUuid", uuid);
}
ServerCredentials Settings::defaultServerCredentials() const
@@ -496,6 +497,28 @@ ServerCredentials Settings::serverCredentials(int index) const
return credentials;
}
QVariant Settings::value(const QString &key, const QVariant &defaultValue) const
{
QVariant returnValue;
if (QThread::currentThread() == QCoreApplication::instance()->thread()) {
returnValue = m_settings.value(key, defaultValue);
} else {
QMetaObject::invokeMethod(&m_settings, "value", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVariant, returnValue),
Q_ARG(const QString &, key), Q_ARG(const QVariant &, defaultValue));
}
return returnValue;
}
void Settings::setValue(const QString &key, const QVariant &value)
{
if (QThread::currentThread() == QCoreApplication::instance()->thread()) {
m_settings.setValue(key, value);
} else {
QMetaObject::invokeMethod(&m_settings, "setValue", Qt::BlockingQueuedConnection, Q_ARG(const QString &, key),
Q_ARG(const QVariant &, value));
}
}
void Settings::resetGatewayEndpoint()
{
m_gatewayEndpoint = gatewayEndpoint;
@@ -518,50 +541,50 @@ QString Settings::getGatewayEndpoint(bool isTestPurchase)
bool Settings::isDevGatewayEnv(bool isTestPurchase)
{
return isTestPurchase ? true : m_settings.value("Conf/devGatewayEnv", false).toBool();
return isTestPurchase ? true : value("Conf/devGatewayEnv", false).toBool();
}
void Settings::toggleDevGatewayEnv(bool enabled)
{
m_settings.setValue("Conf/devGatewayEnv", enabled);
setValue("Conf/devGatewayEnv", enabled);
}
bool Settings::isHomeAdLabelVisible()
{
return m_settings.value("Conf/homeAdLabelVisible", true).toBool();
return value("Conf/homeAdLabelVisible", true).toBool();
}
void Settings::disableHomeAdLabel()
{
m_settings.setValue("Conf/homeAdLabelVisible", false);
setValue("Conf/homeAdLabelVisible", false);
}
bool Settings::isPremV1MigrationReminderActive()
{
return m_settings.value("Conf/premV1MigrationReminderActive", true).toBool();
return value("Conf/premV1MigrationReminderActive", true).toBool();
}
void Settings::disablePremV1MigrationReminder()
{
m_settings.setValue("Conf/premV1MigrationReminderActive", false);
setValue("Conf/premV1MigrationReminderActive", false);
}
QStringList Settings::allowedDnsServers() const
{
return m_settings.value("Conf/allowedDnsServers").toStringList();
return value("Conf/allowedDnsServers").toStringList();
}
void Settings::setAllowedDnsServers(const QStringList &servers)
{
m_settings.setValue("Conf/allowedDnsServers", servers);
setValue("Conf/allowedDnsServers", servers);
}
QStringList Settings::readNewsIds() const
{
return m_settings.value("News/readIds").toStringList();
return value("News/readIds").toStringList();
}
void Settings::setReadNewsIds(const QStringList &ids)
{
m_settings.setValue("News/readIds", ids);
setValue("News/readIds", ids);
}

View File

@@ -29,11 +29,11 @@ public:
QJsonArray serversArray() const
{
return QJsonDocument::fromJson(m_settings.value("Servers/serversList").toByteArray()).array();
return QJsonDocument::fromJson(value("Servers/serversList").toByteArray()).array();
}
void setServersArray(const QJsonArray &servers)
{
m_settings.setValue("Servers/serversList", QJsonDocument(servers).toJson());
setValue("Servers/serversList", QJsonDocument(servers).toJson());
}
// Servers section
@@ -45,11 +45,11 @@ public:
int defaultServerIndex() const
{
return m_settings.value("Servers/defaultServerIndex", 0).toInt();
return value("Servers/defaultServerIndex", 0).toInt();
}
void setDefaultServer(int index)
{
m_settings.setValue("Servers/defaultServerIndex", index);
setValue("Servers/defaultServerIndex", index);
}
QJsonObject defaultServer() const
{
@@ -78,34 +78,34 @@ public:
// App settings section
bool isAutoConnect() const
{
return m_settings.value("Conf/autoConnect", false).toBool();
return value("Conf/autoConnect", false).toBool();
}
void setAutoConnect(bool enabled)
{
m_settings.setValue("Conf/autoConnect", enabled);
setValue("Conf/autoConnect", enabled);
}
bool isStartMinimized() const
{
return m_settings.value("Conf/startMinimized", false).toBool();
return value("Conf/startMinimized", false).toBool();
}
void setStartMinimized(bool enabled)
{
m_settings.setValue("Conf/startMinimized", enabled);
setValue("Conf/startMinimized", enabled);
}
bool isNewsNotifications() const
{
return m_settings.value("Conf/newsNotifications", true).toBool();
return value("Conf/newsNotifications", true).toBool();
}
void setNewsNotifications(bool enabled)
{
m_settings.setValue("Conf/newsNotifications", enabled);
setValue("Conf/newsNotifications", enabled);
}
bool isSaveLogs() const
{
return m_settings.value("Conf/saveLogs", false).toBool();
return value("Conf/saveLogs", false).toBool();
}
void setSaveLogs(bool enabled);
@@ -122,18 +122,19 @@ public:
QString routeModeString(RouteMode mode) const;
RouteMode routeMode() const;
void setRouteMode(RouteMode mode) { m_settings.setValue("Conf/routeMode", mode); }
void setRouteMode(RouteMode mode) { setValue("Conf/routeMode", mode); }
bool isSitesSplitTunnelingEnabled() const;
void setSitesSplitTunnelingEnabled(bool enabled);
QVariantMap vpnSites(RouteMode mode) const
{
return m_settings.value("Conf/" + routeModeString(mode)).toMap();
return value("Conf/" + routeModeString(mode)).toMap();
}
void setVpnSites(RouteMode mode, const QVariantMap &sites)
{
m_settings.setValue("Conf/" + routeModeString(mode), sites);
setValue("Conf/" + routeModeString(mode), sites);
m_settings.sync();
}
bool addVpnSite(RouteMode mode, const QString &site, const QString &ip = "");
void addVpnSites(RouteMode mode, const QMap<QString, QString> &sites); // map <site, ip>
@@ -146,11 +147,11 @@ public:
bool useAmneziaDns() const
{
return m_settings.value("Conf/useAmneziaDns", true).toBool();
return value("Conf/useAmneziaDns", true).toBool();
}
void setUseAmneziaDns(bool enabled)
{
m_settings.setValue("Conf/useAmneziaDns", enabled);
setValue("Conf/useAmneziaDns", enabled);
}
QString primaryDns() const;
@@ -159,13 +160,13 @@ public:
// QString primaryDns() const { return m_primaryDns; }
void setPrimaryDns(const QString &primaryDns)
{
m_settings.setValue("Conf/primaryDns", primaryDns);
setValue("Conf/primaryDns", primaryDns);
}
// QString secondaryDns() const { return m_secondaryDns; }
void setSecondaryDns(const QString &secondaryDns)
{
m_settings.setValue("Conf/secondaryDns", secondaryDns);
setValue("Conf/secondaryDns", secondaryDns);
}
// static constexpr char openNicNs5[] = "94.103.153.176";
@@ -187,16 +188,16 @@ public:
};
void setAppLanguage(QLocale locale)
{
m_settings.setValue("Conf/appLanguage", locale.name());
setValue("Conf/appLanguage", locale.name());
};
bool isScreenshotsEnabled() const
{
return m_settings.value("Conf/screenshotsEnabled", true).toBool();
return value("Conf/screenshotsEnabled", true).toBool();
}
void setScreenshotsEnabled(bool enabled)
{
m_settings.setValue("Conf/screenshotsEnabled", enabled);
setValue("Conf/screenshotsEnabled", enabled);
emit screenshotsEnabledChanged(enabled);
}
@@ -254,6 +255,9 @@ signals:
void settingsCleared();
private:
QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
void setValue(const QString &key, const QVariant &value);
void setInstallationUuid(const QString &uuid);
mutable SecureQSettings m_settings;

File diff suppressed because it is too large Load Diff

View File

@@ -7,6 +7,7 @@
#include <QFileInfo>
#include <QImage>
#include <QStandardPaths>
#include "core/controllers/vpnConfigurationController.h"
#include "core/qrCodeUtils.h"
#include "core/serialization/serialization.h"
@@ -169,7 +170,8 @@ void ExportController::generateWireGuardConfig(const QString &clientName)
m_config.append(line + "\n");
}
m_qrCodes = qrCodeUtils::generateQrCodeImageSeries(m_config.toUtf8());
auto qr = qrCodeUtils::generateQrCode(m_config.toUtf8());
m_qrCodes << qrCodeUtils::svgToBase64(QString::fromStdString(toSvgString(qr, 1)));
emit exportConfigChanged();
}
@@ -189,7 +191,8 @@ void ExportController::generateAwgConfig(const QString &clientName)
m_config.append(line + "\n");
}
m_qrCodes = qrCodeUtils::generateQrCodeImageSeries(m_config.toUtf8());
auto qr = qrCodeUtils::generateQrCode(m_config.toUtf8());
m_qrCodes << qrCodeUtils::svgToBase64(QString::fromStdString(toSvgString(qr, 1)));
emit exportConfigChanged();
}

View File

@@ -291,8 +291,6 @@ void ImportController::processNativeWireGuardConfig()
clientProtocolConfig[config_key::cookieReplyPacketJunkSize] = "0";
clientProtocolConfig[config_key::transportPacketJunkSize] = "0";
clientProtocolConfig[config_key::specialJunk1] = protocols::awg::defaultSpecialJunk1;
clientProtocolConfig[config_key::isObfuscationEnabled] = true;
serverProtocolConfig[config_key::last_config] = QString(QJsonDocument(clientProtocolConfig).toJson());

View File

@@ -83,8 +83,8 @@ void InstallController::install(DockerContainer container, int port, TransportPr
int s1 = QRandomGenerator::global()->bounded(15, 150);
int s2 = QRandomGenerator::global()->bounded(15, 150);
int s3 = QRandomGenerator::global()->bounded(1, 64);
int s4 = QRandomGenerator::global()->bounded(1, 20);
int s3 = QRandomGenerator::global()->bounded(0, 64);
int s4 = QRandomGenerator::global()->bounded(0, 20);
// Ensure all values are unique and don't create equal packet sizes
QSet<int> usedValues;
@@ -97,12 +97,12 @@ void InstallController::install(DockerContainer container, int port, TransportPr
while (usedValues.contains(s3) || s1 + AwgConstant::messageInitiationSize == s3 + AwgConstant::messageCookieReplySize
|| s2 + AwgConstant::messageResponseSize == s3 + AwgConstant::messageCookieReplySize) {
s3 = QRandomGenerator::global()->bounded(1, 64);
s3 = QRandomGenerator::global()->bounded(0, 64);
}
usedValues.insert(s3);
while (usedValues.contains(s4)) {
s4 = QRandomGenerator::global()->bounded(1, 20);
s4 = QRandomGenerator::global()->bounded(0, 20);
}
QString initPacketJunkSize = QString::number(s1);
@@ -987,94 +987,79 @@ void InstallController::addEmptyServer()
emit installServerFinished(tr("Server added successfully"));
}
void InstallController::validateConfig()
bool InstallController::isConfigValid()
{
int serverIndex = m_serversModel->getDefaultServerIndex();
QJsonObject serverConfigObject = m_serversModel->getServerConfig(serverIndex);
if (apiUtils::isServerFromApi(serverConfigObject)) {
emit configValidated(true);
return;
return true;
}
if (!m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
emit noInstalledContainers();
emit configValidated(false);
return;
return false;
}
DockerContainer container = qvariant_cast<DockerContainer>(m_serversModel->data(serverIndex, ServersModel::Roles::DefaultContainerRole));
if (container == DockerContainer::None) {
emit installationErrorOccurred(ErrorCode::NoInstalledContainersError);
emit configValidated(false);
return;
return false;
}
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
auto isProtocolConfigExists = [](const QJsonObject &containerConfig, const DockerContainer container) {
for (Proto protocol : ContainerProps::protocolsForContainer(container)) {
QString protocolConfig =
containerConfig.value(ProtocolProps::protoToString(protocol)).toObject().value(config_key::last_config).toString();
QFutureWatcher<ErrorCode> watcher;
if (protocolConfig.isEmpty()) {
return false;
QFuture<ErrorCode> future = QtConcurrent::run([this, container, &credentials, &containerConfig, &serverController]() {
ErrorCode errorCode = ErrorCode::NoError;
auto isProtocolConfigExists = [](const QJsonObject &containerConfig, const DockerContainer container) {
for (Proto protocol : ContainerProps::protocolsForContainer(container)) {
QString protocolConfig =
containerConfig.value(ProtocolProps::protoToString(protocol)).toObject().value(config_key::last_config).toString();
if (protocolConfig.isEmpty()) {
return false;
}
}
return true;
};
if (!isProtocolConfigExists(containerConfig, container)) {
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
errorCode = vpnConfigurationController.createProtocolConfigForContainer(credentials, container, containerConfig);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
m_serversModel->updateContainerConfig(container, containerConfig);
errorCode = m_clientManagementModel->appendClient(container, credentials, containerConfig,
QString("Admin [%1]").arg(QSysInfo::prettyProductName()), serverController);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
}
return true;
};
return errorCode;
});
if (isProtocolConfigExists(containerConfig, container)) {
emit configValidated(true);
return;
QEventLoop wait;
connect(&watcher, &QFutureWatcher<ErrorCode>::finished, &wait, &QEventLoop::quit);
watcher.setFuture(future);
wait.exec();
ErrorCode errorCode = watcher.result();
if (errorCode != ErrorCode::NoError) {
emit installationErrorOccurred(errorCode);
return false;
}
struct ValidationResult {
ErrorCode errorCode = ErrorCode::NoError;
QJsonObject containerConfig;
};
QFuture<ValidationResult> future =
QtConcurrent::run([settings = m_settings, serverController, credentials, containerConfig, container]() mutable {
ValidationResult result;
result.containerConfig = containerConfig;
VpnConfigurationsController vpnConfigurationController(settings, serverController);
result.errorCode = vpnConfigurationController.createProtocolConfigForContainer(credentials, container,
result.containerConfig);
return result;
});
auto *watcher = new QFutureWatcher<ValidationResult>(this);
connect(watcher, &QFutureWatcher<ValidationResult>::finished, this,
[this, watcher, container, credentials, serverController]() {
auto result = watcher->result();
watcher->deleteLater();
if (result.errorCode != ErrorCode::NoError) {
emit installationErrorOccurred(result.errorCode);
emit configValidated(false);
return;
}
m_serversModel->updateContainerConfig(container, result.containerConfig);
ErrorCode appendError = m_clientManagementModel->appendClient(
container, credentials, result.containerConfig,
QString("Admin [%1]").arg(QSysInfo::prettyProductName()), serverController);
if (appendError != ErrorCode::NoError) {
emit installationErrorOccurred(appendError);
emit configValidated(false);
return;
}
emit configValidated(true);
});
watcher->setFuture(future);
return true;
}
bool InstallController::isUpdateDockerContainerRequired(const DockerContainer container, const QJsonObject &oldConfig,
@@ -1085,7 +1070,7 @@ bool InstallController::isUpdateDockerContainerRequired(const DockerContainer co
const QJsonObject &oldProtoConfig = oldConfig.value(ProtocolProps::protoToString(mainProto)).toObject();
const QJsonObject &newProtoConfig = newConfig.value(ProtocolProps::protoToString(mainProto)).toObject();
if (ContainerProps::isAwgContainer(container)) {
if (container == DockerContainer::Awg2) {
const AwgConfig oldConfig(oldProtoConfig);
const AwgConfig newConfig(newProtoConfig);

View File

@@ -50,10 +50,9 @@ public slots:
void addEmptyServer();
void validateConfig();
bool isConfigValid();
signals:
void configValidated(bool isValid);
void installContainerFinished(const QString &finishMessage, bool isServiceInstall);
void installServerFinished(const QString &finishMessage);

View File

@@ -178,11 +178,12 @@ void SettingsController::backupAppConfig(const QString &fileName)
void SettingsController::restoreAppConfig(const QString &fileName)
{
QByteArray data;
if (!SystemController::readFile(fileName, data)) {
emit changeSettingsErrorOccurred(tr("Can't open file: %1").arg(fileName));
return;
}
QFile file(fileName);
file.open(QIODevice::ReadOnly);
QByteArray data = file.readAll();
restoreAppConfigFromData(data);
}

View File

@@ -7,8 +7,10 @@
#include "systemController.h"
#include "core/networkUtilities.h"
SitesController::SitesController(const std::shared_ptr<Settings> &settings, const QSharedPointer<SitesModel> &sitesModel, QObject *parent)
: QObject(parent), m_settings(settings), m_sitesModel(sitesModel)
SitesController::SitesController(const std::shared_ptr<Settings> &settings,
const QSharedPointer<VpnConnection> &vpnConnection,
const QSharedPointer<SitesModel> &sitesModel, QObject *parent)
: QObject(parent), m_settings(settings), m_vpnConnection(vpnConnection), m_sitesModel(sitesModel)
{
}
@@ -32,20 +34,32 @@ void SitesController::addSite(QString hostname)
hostname = hostname.split("/", Qt::SkipEmptyParts).first();
}
const auto &resolveCallback = [this](const QHostInfo &hostInfo) {
const auto &processSite = [this](const QString &hostname, const QString &ip) {
m_sitesModel->addSite(hostname, ip);
if (!ip.isEmpty()) {
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection,
Q_ARG(QStringList, QStringList() << ip));
} else if (NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection,
Q_ARG(QStringList, QStringList() << hostname));
}
};
const auto &resolveCallback = [this, processSite](const QHostInfo &hostInfo) {
const QList<QHostAddress> &addresses = hostInfo.addresses();
for (const QHostAddress &addr : hostInfo.addresses()) {
if (addr.protocol() == QAbstractSocket::NetworkLayerProtocol::IPv4Protocol) {
m_sitesModel->addSite(hostInfo.hostName(), addr.toString());
processSite(hostInfo.hostName(), addr.toString());
break;
}
}
};
if (NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
m_sitesModel->addSite(hostname, "");
processSite(hostname, "");
} else {
m_sitesModel->addSite(hostname, "");
processSite(hostname, "");
QHostInfo::lookupHost(hostname, this, resolveCallback);
}
@@ -58,6 +72,9 @@ void SitesController::removeSite(int index)
auto hostname = m_sitesModel->data(modelIndex, SitesModel::Roles::UrlRole).toString();
m_sitesModel->removeSite(modelIndex);
QMetaObject::invokeMethod(m_vpnConnection.get(), "deleteRoutes", Qt::QueuedConnection,
Q_ARG(QStringList, QStringList() << hostname));
emit finished(tr("Site removed: %1").arg(hostname));
}
@@ -111,6 +128,8 @@ void SitesController::importSites(const QString &fileName, bool replaceExisting)
m_sitesModel->addSites(sites, replaceExisting);
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection, Q_ARG(QStringList, ips));
emit finished(tr("Import completed"));
}

View File

@@ -11,8 +11,9 @@ class SitesController : public QObject
{
Q_OBJECT
public:
explicit SitesController(const std::shared_ptr<Settings> &settings, const QSharedPointer<SitesModel> &sitesModel,
QObject *parent = nullptr);
explicit SitesController(const std::shared_ptr<Settings> &settings,
const QSharedPointer<VpnConnection> &vpnConnection,
const QSharedPointer<SitesModel> &sitesModel, QObject *parent = nullptr);
public slots:
void addSite(QString hostname);
@@ -30,6 +31,8 @@ signals:
private:
std::shared_ptr<Settings> m_settings;
QSharedPointer<VpnConnection> m_vpnConnection;
QSharedPointer<SitesModel> m_sitesModel;
};

View File

@@ -141,12 +141,10 @@ void AwgConfigModel::updateModel(const QJsonObject &config)
serverProtocolConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize);
m_serverProtocolConfig[config_key::responsePacketJunkSize] =
serverProtocolConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize);
if (protocolVersion == protocols::awg::awgV2) {
m_serverProtocolConfig[config_key::cookieReplyPacketJunkSize] =
serverProtocolConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize);
m_serverProtocolConfig[config_key::transportPacketJunkSize] =
serverProtocolConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize);
}
m_serverProtocolConfig[config_key::cookieReplyPacketJunkSize] =
serverProtocolConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize);
m_serverProtocolConfig[config_key::transportPacketJunkSize] =
serverProtocolConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize);
m_serverProtocolConfig[config_key::initPacketMagicHeader] =
serverProtocolConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader);
m_serverProtocolConfig[config_key::responsePacketMagicHeader] =

View File

@@ -8,10 +8,9 @@ Item {
id: root
property StackView stackView: StackView.view
property bool enableTimer: true
onVisibleChanged: {
if (visible && enableTimer) {
if (visible) {
timer.start()
}
}
@@ -25,6 +24,6 @@ Item {
FocusController.setFocusOnDefaultItem()
}
repeat: false // Stop the timer after one trigger
running: enableTimer // Start the timer
running: true // Start the timer
}
}

View File

@@ -330,8 +330,6 @@ PageType {
AwgTextField {
id: cookieReplyPacketJunkSizeTextField
visible: isAwg2
Layout.leftMargin: 16
Layout.rightMargin: 16
@@ -344,8 +342,6 @@ PageType {
AwgTextField {
id: transportPacketJunkSizeTextField
visible: isAwg2
Layout.leftMargin: 16
Layout.rightMargin: 16

View File

@@ -396,7 +396,9 @@ PageType {
PageController.showNotificationMessage(qsTr("Cannot remove server during active connection"))
} else {
PageController.showBusyIndicator(true)
InstallController.removeProcessedServer()
if (ApiConfigsController.deactivateDevice(true)) {
InstallController.removeProcessedServer()
}
PageController.showBusyIndicator(false)
}
}

View File

@@ -178,7 +178,7 @@ PageType {
Layout.margins: 16
text: qsTr("News Notification")
descriptionText: qsTr("Show a notification icon for unread news")
descriptionText: qsTr("Show notification icon when has unread news")
checked: SettingsController.isNewsNotificationsEnabled()
onToggled: function() {

View File

@@ -13,7 +13,6 @@ import "../Components"
PageType {
id: root
enableTimer: (SettingsController.isOnTv()) ? false : true
ColumnLayout {
id: content
@@ -46,22 +45,4 @@ PageType {
}
}
}
Timer {
interval: 250
running: SettingsController.isOnTv()
repeat: true
onTriggered: {
startButton.forceActiveFocus()
if (startButton.activeFocus) {
running = false
}
}
}
onVisibleChanged: {
if (visible && SettingsController.isOnTv()) {
startButton.forceActiveFocus()
}
}
}

View File

@@ -505,13 +505,7 @@ PageType {
exportTypeSelector.currentIndex = 0
}
selectedIndex = exportTypeSelector.currentIndex
if (model.length > 0 && model[selectedIndex] && model[selectedIndex].name !== undefined) {
exportTypeSelectorListView.selectedText = model[selectedIndex].name
exportTypeSelector.text = model[selectedIndex].name
} else {
exportTypeSelectorListView.selectedText = ""
exportTypeSelector.text = ""
}
exportTypeSelector.text = selectedText
}
rootWidth: root.width

View File

@@ -278,6 +278,7 @@ PageType {
}
Keys.onPressed: function(event) {
console.debug(">>>> ", event.key, " Event is caught by StartPage")
switch (event.key) {
case Qt.Key_Tab:
case Qt.Key_Down:
@@ -303,7 +304,7 @@ PageType {
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: parent.bottom
// Also adjust TabBar position when keyboard appears (Android 14+ workaround)
anchors.bottomMargin: SettingsController.imeHeight

View File

@@ -56,11 +56,6 @@ Window {
PageController.closeWindow()
}
onSceneGraphError: function(error, message) {
// Prevent qFatal crash on Android when EGL context is lost
console.warn("Scene graph error:", error, message)
}
title: "AmneziaVPN"
Item { // This item is needed for focus handling

View File

@@ -39,8 +39,9 @@ VpnConnection::VpnConnection(std::shared_ptr<Settings> settings, QObject *parent
{
#if defined(Q_OS_IOS) || defined(MACOS_NE)
m_checkTimer.setInterval(1000);
connect(IosController::Instance(), &IosController::connectionStateChanged, this, &VpnConnection::setConnectionState);
connect(IosController::Instance(), &IosController::connectionStateChanged, this, &VpnConnection::onConnectionStateChanged);
connect(IosController::Instance(), &IosController::bytesChanged, this, &VpnConnection::onBytesChanged);
#endif
}
@@ -58,7 +59,7 @@ void VpnConnection::onKillSwitchModeChanged(bool enabled)
#ifdef AMNEZIA_DESKTOP
IpcClient::withInterface([enabled](QSharedPointer<IpcInterfaceReplica> iface){
QRemoteObjectPendingReply<bool> reply = iface->refreshKillSwitch(enabled);
if (reply.waitForFinished() && reply.returnValue())
if (reply.waitForFinished(1000) && reply.returnValue())
qDebug() << "VpnConnection::onKillSwitchModeChanged: Killswitch refreshed";
else
qWarning() << "VpnConnection::onKillSwitchModeChanged: Failed to execute remote refreshKillSwitch call";
@@ -72,57 +73,60 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
auto container = m_settings->defaultContainer(m_settings->defaultServerIndex());
IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
switch (state) {
case Vpn::ConnectionState::Connected: {
iface->resetIpStack();
if (state == Vpn::ConnectionState::Connected) {
iface->resetIpStack();
iface->flushDns();
auto flushDns = iface->flushDns();
if (flushDns.waitForFinished() && flushDns.returnValue())
qDebug() << "VpnConnection::onConnectionStateChanged: Successfully flushed DNS";
else
qWarning() << "VpnConnection::onConnectionStateChanged: Failed to clear saved routes";
if (!ContainerProps::isAwgContainer(container) &&
container != DockerContainer::WireGuard) {
QString dns1 = m_vpnConfiguration.value(config_key::dns1).toString();
QString dns2 = m_vpnConfiguration.value(config_key::dns2).toString();
iface->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << dns1 << dns2);
if (!ContainerProps::isAwgContainer(container) &&
container != DockerContainer::WireGuard) {
QString dns1 = m_vpnConfiguration.value(config_key::dns1).toString();
QString dns2 = m_vpnConfiguration.value(config_key::dns2).toString();
if (m_settings->isSitesSplitTunnelingEnabled()) {
iface->routeDeleteList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0");
// qDebug() << "VpnConnection::onConnectionStateChanged :: adding custom routes, count:" << forwardIps.size();
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
QTimer::singleShot(1000, m_vpnProtocol.data(),
[this]() { addSitesRoutes(m_vpnProtocol->vpnGateway(), m_settings->routeMode()); });
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
iface->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0/1");
iface->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "128.0.0.0/1");
// TODO: add error code handling for all routeAddList (or rework the code below)
iface->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << dns1 << dns2);
if (m_settings->isSitesSplitTunnelingEnabled()) {
iface->routeDeleteList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0");
// qDebug() << "VpnConnection::onConnectionStateChanged :: adding custom routes, count:" << forwardIps.size();
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
QTimer::singleShot(1000, m_vpnProtocol.data(),
[this]() { addSitesRoutes(m_vpnProtocol->vpnGateway(), m_settings->routeMode()); });
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
iface->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0/1");
iface->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "128.0.0.0/1");
iface->routeAddList(m_vpnProtocol->routeGateway(), QStringList() << remoteAddress());
addSitesRoutes(m_vpnProtocol->routeGateway(), m_settings->routeMode());
}
iface->routeAddList(m_vpnProtocol->routeGateway(), QStringList() << remoteAddress());
addSitesRoutes(m_vpnProtocol->routeGateway(), m_settings->routeMode());
}
}
} break;
case Vpn::ConnectionState::Disconnected:
case Vpn::ConnectionState::Error: {
auto flushDns = iface->flushDns();
if (flushDns.waitForFinished() && flushDns.returnValue())
qDebug() << "VpnConnection::onConnectionStateChanged: Successfully flushed DNS";
else
qWarning() << "VpnConnection::onConnectionStateChanged: Failed to flush DNS";
}
auto clearSavedRoutes = iface->clearSavedRoutes();
if (clearSavedRoutes.waitForFinished() && clearSavedRoutes.returnValue())
qDebug() << "VpnConnection::onConnectionStateChanged: Successfully cleared saved routes";
else
qWarning() << "VpnConnection::onConnectionStateChanged: Failed to clear saved routes";
} break;
default:
break;
if (container != DockerContainer::Ipsec) {
if (startNetworkCheckIfReady()) {
m_pendingNetworkCheck = false;
} else {
m_pendingNetworkCheck = true;
qWarning() << "Deferring startNetworkCheck; missing gateway/local address"
<< m_vpnProtocol->vpnGateway() << m_vpnProtocol->vpnLocalAddress();
}
} else {
m_pendingNetworkCheck = false;
}
} else if (state == Vpn::ConnectionState::Error) {
m_pendingNetworkCheck = false;
iface->flushDns();
if (m_settings->isSitesSplitTunnelingEnabled()) {
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
iface->clearSavedRoutes();
}
}
} else if (state == Vpn::ConnectionState::Connecting) {
} else if (state == Vpn::ConnectionState::Disconnected) {
m_pendingNetworkCheck = false;
auto result = iface->stopNetworkCheck();
result.waitForFinished(3000);
}
});
#endif
@@ -136,6 +140,7 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
m_checkTimer.stop();
}
#endif
emit connectionStateChanged(state);
}
const QString &VpnConnection::remoteAddress() const
@@ -180,11 +185,7 @@ void VpnConnection::addSitesRoutes(const QString &gw, Settings::RouteMode mode)
});
m_settings->addVpnSite(mode, site, ip);
}
IpcClient::withInterface([](QSharedPointer<IpcInterfaceReplica> iface) {
auto reply = iface->flushDns();
if (reply.waitForFinished() || !reply.returnValue())
qWarning() << "VpnConnection::addSitesRoutes: Failed to flush DNS";
});
flushDns();
break;
}
}
@@ -199,6 +200,48 @@ QSharedPointer<VpnProtocol> VpnConnection::vpnProtocol() const
return m_vpnProtocol;
}
void VpnConnection::addRoutes(const QStringList &ips)
{
#ifdef AMNEZIA_DESKTOP
IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
if (connectionState() == Vpn::ConnectionState::Connected) {
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
iface->routeAddList(m_vpnProtocol->vpnGateway(), ips);
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
iface->routeAddList(m_vpnProtocol->routeGateway(), ips);
}
}
});
#endif
}
void VpnConnection::deleteRoutes(const QStringList &ips)
{
#ifdef AMNEZIA_DESKTOP
IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
if (connectionState() == Vpn::ConnectionState::Connected) {
if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) {
iface->routeDeleteList(vpnProtocol()->vpnGateway(), ips);
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
iface->routeDeleteList(m_vpnProtocol->routeGateway(), ips);
}
}
});
#endif
}
void VpnConnection::flushDns()
{
#ifdef AMNEZIA_DESKTOP
IpcClient::withInterface([](QSharedPointer<IpcInterfaceReplica> iface) {
auto reply = iface->flushDns();
if (reply.waitForFinished(1000) || !reply.returnValue()) {
qWarning() << "VpnConnection::flushDns(): Failed to flush DNS";
}
});
#endif
}
void VpnConnection::disconnectSlots()
{
if (m_vpnProtocol) {
@@ -222,15 +265,19 @@ ErrorCode VpnConnection::lastError() const
void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &vpnConfiguration)
{
qDebug() << QString("Trying to connect to VPN, server index is %1, container is %2, route mode is")
qDebug() << QString("ConnectToVpn, Server index is %1, container is %2, route mode is")
.arg(serverIndex)
.arg(ContainerProps::containerToString(container))
<< m_settings->routeMode();
m_remoteAddress = NetworkUtilities::getIPAddress(credentials.hostName);
setConnectionState(Vpn::ConnectionState::Connecting);
emit connectionStateChanged(Vpn::ConnectionState::Connecting);
m_pendingNetworkCheck = false;
m_vpnConfiguration = vpnConfiguration;
m_serverIndex = serverIndex;
m_serverCredentials = credentials;
m_dockerContainer = container;
#ifdef AMNEZIA_DESKTOP
if (m_vpnProtocol) {
@@ -246,7 +293,7 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
m_vpnProtocol.reset(VpnProtocol::factory(container, m_vpnConfiguration));
if (!m_vpnProtocol) {
setConnectionState(Vpn::ConnectionState::Error);
emit connectionStateChanged(Vpn::ConnectionState::Error);
return;
}
m_vpnProtocol->prepare();
@@ -264,23 +311,75 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede
createProtocolConnections();
if (ErrorCode err = m_vpnProtocol->start(); err != ErrorCode::NoError) {
setConnectionState(Vpn::ConnectionState::Error);
emit vpnProtocolError(err);
ErrorCode errorCode = m_vpnProtocol->start();
if (errorCode != ErrorCode::NoError)
emit connectionStateChanged(Vpn::ConnectionState::Error);
}
void VpnConnection::restartConnection()
{
// Only reconnect if VPN was connected before sleep/network change
if (!m_wasConnectedBeforeSleep) {
qDebug() << "VPN was not connected before sleep/network change, skipping reconnection";
return;
}
qDebug() << "VPN was connected before sleep/network change, attempting reconnection";
this->disconnectFromVpn();
#ifdef Q_OS_LINUX
QThread::msleep(5000);
#endif
this->connectToVpn(m_serverIndex, m_serverCredentials, m_dockerContainer, m_vpnConfiguration);
// Reset the flag after reconnection attempt
m_wasConnectedBeforeSleep = false;
}
void VpnConnection::createProtocolConnections()
{
connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
connect(m_vpnProtocol.data(), &VpnProtocol::connectionStateChanged, this, &VpnConnection::setConnectionState);
connect(m_vpnProtocol.data(), SIGNAL(connectionStateChanged(Vpn::ConnectionState)), this,
SLOT(onConnectionStateChanged(Vpn::ConnectionState)));
connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64)));
#ifdef AMNEZIA_DESKTOP
IpcClient::withInterface([this](QSharedPointer<IpcInterfaceReplica> rep) {
connect(rep.data(), &IpcInterfaceReplica::networkChanged, this, &VpnConnection::reconnectToVpn, Qt::QueuedConnection);
connect(rep.data(), &IpcInterfaceReplica::wakeup, this, &VpnConnection::reconnectToVpn, Qt::QueuedConnection);
});
if (m_connectionLoseHandle)
disconnect(m_connectionLoseHandle);
if (m_networkChangeHandle)
disconnect(m_networkChangeHandle);
m_connectionLoseHandle = QMetaObject::Connection();
m_networkChangeHandle = QMetaObject::Connection();
// TODO: replace unsafe IpcClient::Interface() calls
m_connectionLoseHandle = connect(IpcClient::Interface().data(), &IpcInterfaceReplica::connectionLose,
this, [this]() {
qDebug() << "Connection Lose";
auto result = IpcClient::Interface()->stopNetworkCheck();
result.waitForFinished(3000);
// Track VPN state before connection loss
m_wasConnectedBeforeSleep = isConnected();
qDebug() << "VPN was connected before connection loss:" << m_wasConnectedBeforeSleep;
this->restartConnection();
});
m_networkChangeHandle = connect(IpcClient::Interface().data(), &IpcInterfaceReplica::networkChange,
this, [this]() {
qDebug() << "Network change";
// Track VPN state before network change (including sleep/wake)
m_wasConnectedBeforeSleep = isConnected();
qDebug() << "VPN was connected before network change:" << m_wasConnectedBeforeSleep;
this->restartConnection();
});
connect(m_vpnProtocol.data(), &VpnProtocol::tunnelAddressesUpdated,
this, [this](const QString& gateway, const QString& localAddress) {
Q_UNUSED(gateway)
Q_UNUSED(localAddress)
if (connectionState() != Vpn::ConnectionState::Connected) {
return;
}
if (startNetworkCheckIfReady()) {
m_pendingNetworkCheck = false;
}
});
#endif
}
@@ -383,13 +482,28 @@ void VpnConnection::appendSplitTunnelingConfig()
m_vpnConfiguration.insert(config_key::appSplitTunnelType, appsRouteMode);
m_vpnConfiguration.insert(config_key::splitTunnelApps, appsJsonArray);
}
qDebug() << QString("Site split tunneling is %1, route mode is %2")
.arg(m_settings->isSitesSplitTunnelingEnabled() ? "enabled" : "disabled")
.arg(routeMode);
qDebug() << QString("App split tunneling is %1, route mode is %2")
.arg(m_settings->isAppsSplitTunnelingEnabled() ? "enabled" : "disabled")
.arg(appsRouteMode);
bool VpnConnection::startNetworkCheckIfReady()
{
#ifdef AMNEZIA_DESKTOP
if (!m_vpnProtocol || m_dockerContainer == DockerContainer::Ipsec) {
return false;
}
const QString gateway = m_vpnProtocol->vpnGateway();
const QString localAddress = m_vpnProtocol->vpnLocalAddress();
if (gateway.isEmpty() || localAddress.isEmpty()) {
return false;
}
return IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
QRemoteObjectPendingReply<bool> reply = iface->startNetworkCheck(gateway, localAddress);
return reply.waitForFinished(1000) && reply.returnValue();
});
#else
return false;
#endif
}
#ifdef Q_OS_ANDROID
@@ -423,27 +537,6 @@ QString VpnConnection::bytesPerSecToText(quint64 bytes)
return QString("%1 %2").arg(QString::number(mbps, 'f', 2)).arg(tr("Mbps")); // Mbit/s
}
void VpnConnection::reconnectToVpn() {
if (m_vpnProtocol.isNull())
return;
if (m_connectionState != Vpn::ConnectionState::Connected) {
qWarning() << QString("Reconnect triggered on %1 during inappropriate state: %2; ignoring slot")
.arg(QMetaEnum::fromType<Vpn::ConnectionState>().valueToKey(m_connectionState));
return;
}
qDebug() << "Reconnect triggered. Reconnecting to the server";
setConnectionState(Vpn::ConnectionState::Reconnecting);
m_vpnProtocol->stop();
if (ErrorCode err = m_vpnProtocol->start(); err != ErrorCode::NoError) {
setConnectionState(Vpn::ConnectionState::Error);
emit vpnProtocolError(err);
}
}
void VpnConnection::disconnectFromVpn()
{
#if defined(Q_OS_IOS) || defined(MACOS_NE)
@@ -453,25 +546,40 @@ void VpnConnection::disconnectFromVpn()
#endif
if (m_vpnProtocol.isNull()) {
setConnectionState(Vpn::ConnectionState::Disconnected);
emit connectionStateChanged(Vpn::ConnectionState::Disconnected);
return;
}
setConnectionState(Vpn::ConnectionState::Disconnecting);
m_vpnProtocol->stop();
#ifdef AMNEZIA_DESKTOP
IpcClient::withInterface([](QSharedPointer<IpcInterfaceReplica> iface) {
QRemoteObjectPendingReply<bool> flushReply = iface->flushDns();
if (flushReply.waitForFinished(5000) && flushReply.returnValue())
qDebug() << "VpnConnection::disconnectFromVpn(): Successfully flushed DNS";
else
qWarning() << "VpnConnection::disconnectFromVpn(): Failed to flush DNS";
QRemoteObjectPendingReply<bool> clearSavedRoutesReply = iface->clearSavedRoutes();
if (clearSavedRoutesReply.waitForFinished(5000) && clearSavedRoutesReply.returnValue())
qDebug() << "VpnConnection::disconnectFromVpn(): Successfully cleared saved routes";
else
qWarning() << "VpnConnection::disconnectFromVpn(): Failed to clear saved routes";
});
#endif
#ifdef Q_OS_ANDROID
auto *const connection = new QMetaObject::Connection;
*connection = connect(AndroidController::instance(), &AndroidController::vpnStateChanged, this,
[this, connection](AndroidController::ConnectionState state) {
if (state == AndroidController::ConnectionState::DISCONNECTED) {
setConnectionState(Vpn::ConnectionState::Disconnected);
onConnectionStateChanged(Vpn::ConnectionState::Disconnected);
disconnect(*connection);
delete connection;
}
});
#endif
m_vpnProtocol->stop();
#endif
#if !defined(Q_OS_ANDROID) && !defined(AMNEZIA_DESKTOP)
m_vpnProtocol->deleteLater();
@@ -480,12 +588,27 @@ void VpnConnection::disconnectFromVpn()
m_vpnProtocol = nullptr;
}
void VpnConnection::setConnectionState(Vpn::ConnectionState state) {
onConnectionStateChanged(state);
if (state == Vpn::Disconnected && m_connectionState == Vpn::Reconnecting)
return;
m_connectionState = state;
emit connectionStateChanged(state);
Vpn::ConnectionState VpnConnection::connectionState()
{
if (!m_vpnProtocol)
return Vpn::ConnectionState::Disconnected;
return m_vpnProtocol->connectionState();
}
bool VpnConnection::isConnected() const
{
if (m_vpnProtocol.isNull()) {
return false;
}
return m_vpnProtocol->isConnected();
}
bool VpnConnection::isDisconnected() const
{
if (m_vpnProtocol.isNull()) {
return true;
}
return m_vpnProtocol->isDisconnected();
}

View File

@@ -34,6 +34,10 @@ public:
ErrorCode lastError() const;
bool isConnected() const;
bool isDisconnected() const;
Vpn::ConnectionState connectionState();
QSharedPointer<VpnProtocol> vpnProtocol() const;
const QString &remoteAddress() const;
@@ -44,10 +48,15 @@ public:
#endif
public slots:
void connectToVpn(int serverIndex, const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration);
void reconnectToVpn();
void disconnectFromVpn();
void connectToVpn(int serverIndex,
const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration);
void disconnectFromVpn();
void restartConnection();
void addRoutes(const QStringList &ips);
void deleteRoutes(const QStringList &ips);
void flushDns();
void onKillSwitchModeChanged(bool enabled);
void disconnectSlots();
@@ -62,10 +71,10 @@ protected slots:
void onBytesChanged(quint64 receivedBytes, quint64 sentBytes);
void onConnectionStateChanged(Vpn::ConnectionState state);
void setConnectionState(Vpn::ConnectionState state);
protected:
QSharedPointer<VpnProtocol> m_vpnProtocol;
QMetaObject::Connection m_connectionLoseHandle;
QMetaObject::Connection m_networkChangeHandle;
private:
std::shared_ptr<Settings> m_settings;
@@ -73,6 +82,14 @@ private:
QJsonObject m_routeMode;
QString m_remoteAddress;
ServerCredentials m_serverCredentials;
int m_serverIndex;
DockerContainer m_dockerContainer;
// Track VPN state before sleep for smart reconnection
bool m_wasConnectedBeforeSleep = false;
bool m_pendingNetworkCheck = false;
// Only for iOS for now, check counters
QTimer m_checkTimer;
@@ -83,12 +100,11 @@ private:
void createAndroidConnections();
#endif
Vpn::ConnectionState m_connectionState;
void createProtocolConnections();
void appendSplitTunnelingConfig();
void appendKillSwitchConfig();
bool startNetworkCheckIfReady();
};
#endif // VPNCONNECTION_H

View File

@@ -11,7 +11,6 @@
namespace amnezia {
enum PermittedProcess {
Invalid,
OpenVPN,
Wireguard,
Tun2Socks,
@@ -20,18 +19,16 @@ enum PermittedProcess {
inline QString permittedProcessPath(PermittedProcess pid)
{
switch (pid) {
case PermittedProcess::OpenVPN:
return Utils::openVpnExecPath();
case PermittedProcess::Wireguard:
return Utils::wireguardExecPath();
case PermittedProcess::CertUtil:
return Utils::certUtilPath();
case PermittedProcess::Tun2Socks:
return Utils::tun2socksPath();
default:
return "";
if (pid == PermittedProcess::OpenVPN) {
return Utils::openVpnExecPath();
} else if (pid == PermittedProcess::Wireguard) {
return Utils::wireguardExecPath();
} else if (pid == PermittedProcess::CertUtil) {
return Utils::certUtilPath();
} else if (pid == PermittedProcess::Tun2Socks) {
return Utils::tun2socksPath();
}
return "";
}
@@ -51,51 +48,6 @@ inline QString getIpcProcessUrl(int pid) {
#endif
}
inline QStringList sanitizeArguments(PermittedProcess proc, const QStringList &args) {
using Validator = std::function<bool(const QString&)>;
QMap<QString, Validator> namedArgs;
QList<Validator> positionalArgs;
switch (proc) {
case Tun2Socks:
namedArgs["-device"] = [](const QString& v) { return v.startsWith("tun://"); };
namedArgs["-proxy"] = [](const QString& v) { return v.startsWith("socks5://"); };
break;
default:
//FIXME
return args;
}
QStringList sanitized;
for (int i = 0, pos = 0; i < args.size(); i++) {
const auto& key = args[i];
if (const auto found = namedArgs.find(key); found != namedArgs.end()) {
const auto validator = found.value();
if (validator) {
if (i + 1 < args.size()) {
const auto& value = args[i+1];
if (validator(value)) {
sanitized << key << value;
i++;
}
}
} else {
sanitized << key;
}
} else if (pos < positionalArgs.size()) {
if (const auto validator = positionalArgs[pos]; validator && validator(key)) {
sanitized << key;
pos++;
}
}
}
return sanitized;
}
} // namespace amnezia

View File

@@ -38,13 +38,12 @@ class IpcInterface
SLOT( bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) );
SLOT( bool restoreResolvers() );
SLOT(bool xrayStart(const QString &config));
SLOT(bool xrayStop());
SLOT(void xrayStart(const QString &config));
SLOT(void xrayStop());
SLOT( bool startNetworkCheck(const QString& serverIpv4Gateway, const QString& deviceIpv4Address) );
SLOT( bool stopNetworkCheck() );
SIGNAL( connectionLose() );
SIGNAL( wakeup() );
SIGNAL( networkChanged() );
SIGNAL( networkChange() );
};

View File

@@ -4,8 +4,6 @@
class IpcProcessInterface
{
SLOT( start() );
SLOT( terminate() );
SLOT( kill() );
SLOT( close() );
SLOT( setArguments(const QStringList &arguments) );
@@ -19,11 +17,6 @@ class IpcProcessInterface
SLOT( QByteArray readAllStandardError() );
SLOT( QByteArray readAllStandardOutput() );
SLOT( bool waitForFinished() );
SLOT( bool waitForFinished(int msecs) );
SLOT( bool waitForStarted() );
SLOT( bool waitForStarted(int msecs) );
SIGNAL( errorOccurred(QProcess::ProcessError error) );
SIGNAL( finished(int exitCode, QProcess::ExitStatus exitStatus) );

View File

@@ -0,0 +1,11 @@
#include <QtCore>
#include <QString>
class IpcProcessTun2Socks
{
SLOT( start() );
SLOT( stop() );
SIGNAL( setConnectionState(int state) );
SIGNAL( stateChanged(QProcess::ProcessState newState) );
};

View File

@@ -304,7 +304,7 @@ bool IpcServer::refreshKillSwitch(bool enabled)
return KillSwitch::instance()->refresh(enabled);
}
bool IpcServer::xrayStart(const QString& cfg)
void IpcServer::xrayStart(const QString& cfg)
{
#ifdef MZ_DEBUG
qDebug() << "IpcServer::xrayStart";
@@ -313,7 +313,7 @@ bool IpcServer::xrayStart(const QString& cfg)
return Xray::getInstance().startXray(cfg);
}
bool IpcServer::xrayStop()
void IpcServer::xrayStop()
{
#ifdef MZ_DEBUG
qDebug() << "IpcServer::xrayStop";

View File

@@ -10,8 +10,10 @@
#include "ipc.h"
#include "ipcserverprocess.h"
#include "ipctun2socksprocess.h"
#include "rep_ipc_interface_source.h"
#include "rep_ipc_process_tun2socks_source.h"
class IpcServer : public IpcInterfaceSource
{
@@ -42,8 +44,8 @@ public:
virtual bool refreshKillSwitch( bool enabled ) override;
virtual bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) override;
virtual bool restoreResolvers() override;
virtual bool xrayStart(const QString& cfg) override;
virtual bool xrayStop() override;
virtual void xrayStart(const QString& cfg) override;
virtual void xrayStop() override;
virtual bool startNetworkCheck(const QString& serverIpv4Gateway, const QString& deviceIpv4Address) override;
virtual bool stopNetworkCheck() override;
@@ -54,10 +56,12 @@ private:
ProcessDescriptor (QObject *parent = nullptr) {
serverNode = QSharedPointer<QRemoteObjectHost>(new QRemoteObjectHost(parent));
ipcProcess = QSharedPointer<IpcServerProcess>(new IpcServerProcess(parent));
tun2socksProcess = QSharedPointer<IpcProcessTun2Socks>(new IpcProcessTun2Socks(parent));
localServer = QSharedPointer<QLocalServer>(new QLocalServer(parent));
}
QSharedPointer<IpcServerProcess> ipcProcess;
QSharedPointer<IpcProcessTun2Socks> tun2socksProcess;
QSharedPointer<QRemoteObjectHost> serverNode;
QSharedPointer<QLocalServer> localServer;
};

View File

@@ -40,14 +40,6 @@ void IpcServerProcess::start()
m_process->waitForStarted();
}
void IpcServerProcess::terminate() {
m_process->terminate();
}
void IpcServerProcess::kill() {
m_process->kill();
}
void IpcServerProcess::close()
{
m_process->close();
@@ -55,7 +47,7 @@ void IpcServerProcess::close()
void IpcServerProcess::setArguments(const QStringList &arguments)
{
m_process->setArguments(amnezia::sanitizeArguments(m_program, arguments));
m_process->setArguments(arguments);
}
void IpcServerProcess::setInputChannelMode(QProcess::InputChannelMode mode)
@@ -77,9 +69,7 @@ void IpcServerProcess::setProcessChannelMode(QProcess::ProcessChannelMode mode)
void IpcServerProcess::setProgram(int programId)
{
m_program = static_cast<amnezia::PermittedProcess>(programId);
m_process->setProgram(amnezia::permittedProcessPath(m_program));
m_process->setArguments({});
m_process->setProgram(amnezia::permittedProcessPath(static_cast<amnezia::PermittedProcess>(programId)));
}
void IpcServerProcess::setWorkingDirectory(const QString &dir)
@@ -102,20 +92,4 @@ QByteArray IpcServerProcess::readAllStandardOutput()
return m_process->readAllStandardOutput();
}
bool IpcServerProcess::waitForStarted() {
return m_process->waitForStarted();
}
bool IpcServerProcess::waitForStarted(int msecs) {
return m_process->waitForStarted(msecs);
}
bool IpcServerProcess::waitForFinished() {
return m_process->waitForFinished();
}
bool IpcServerProcess::waitForFinished(int msecs) {
return m_process->waitForFinished(msecs);
}
#endif

View File

@@ -1,7 +1,6 @@
#ifndef IPCSERVERPROCESS_H
#define IPCSERVERPROCESS_H
#include "ipc.h"
#include <QObject>
#ifndef Q_OS_IOS
@@ -15,8 +14,6 @@ public:
virtual ~IpcServerProcess();
void start() override;
void terminate() override;
void kill() override;
void close() override;
void setArguments(const QStringList &arguments) override;
@@ -30,15 +27,9 @@ public:
QByteArray readAllStandardError() override;
QByteArray readAllStandardOutput() override;
bool waitForStarted() override;
bool waitForStarted(int msecs) override;
bool waitForFinished() override;
bool waitForFinished(int msecs) override;
signals:
private:
amnezia::PermittedProcess m_program = amnezia::PermittedProcess::Invalid;
QSharedPointer<QProcess> m_process;
};

View File

@@ -0,0 +1,75 @@
#include "ipctun2socksprocess.h"
#include "ipc.h"
#include <QProcess>
#include <QString>
#include "../protocols/protocols_defs.h"
#ifndef Q_OS_IOS
IpcProcessTun2Socks::IpcProcessTun2Socks(QObject *parent) :
IpcProcessTun2SocksSource(parent),
m_t2sProcess(QSharedPointer<QProcess>(new QProcess()))
{
qDebug() << "IpcProcessTun2Socks::IpcProcessTun2Socks()";
}
IpcProcessTun2Socks::~IpcProcessTun2Socks()
{
qDebug() << "IpcProcessTun2Socks::~IpcProcessTun2Socks()";
}
void IpcProcessTun2Socks::start()
{
connect(m_t2sProcess.data(), &QProcess::stateChanged, this, &IpcProcessTun2Socks::stateChanged);
qDebug() << "IpcProcessTun2Socks::start()";
m_t2sProcess->setProgram(amnezia::permittedProcessPath(static_cast<amnezia::PermittedProcess>(amnezia::PermittedProcess::Tun2Socks)));
QString XrayConStr = "socks5://127.0.0.1:10808";
#ifdef Q_OS_WIN
QStringList arguments({"-device", "tun://tun2?guid={081A8A84-8D12-4DF5-B8C4-396D5B0053E4}", "-proxy", XrayConStr });
#endif
#ifdef Q_OS_LINUX
QStringList arguments({"-device", "tun://tun2", "-proxy", XrayConStr});
#endif
#ifdef Q_OS_MAC
QStringList arguments({"-device", "utun22", "-proxy", XrayConStr});
#endif
m_t2sProcess->setArguments(arguments);
if (Utils::processIsRunning(Utils::executable("tun2socks", false))) {
qDebug().noquote() << "kill previos tun2socks";
Utils::killProcessByName(Utils::executable("tun2socks", false));
}
connect(m_t2sProcess.data(), &QProcess::readyReadStandardOutput, this, [this]() {
QString line = m_t2sProcess.data()->readAllStandardOutput();
if (line.contains("[STACK] tun://") && line.contains("<-> socks5://127.0.0.1")) {
emit setConnectionState(Vpn::ConnectionState::Connected);
}
});
connect(m_t2sProcess.data(), QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) {
qDebug().noquote() << "tun2socks finished, exitCode, exiStatus" << exitCode << exitStatus;
emit setConnectionState(Vpn::ConnectionState::Disconnected);
if ((exitStatus != QProcess::NormalExit) || (exitCode != 0)) {
emit setConnectionState(Vpn::ConnectionState::Error);
}
});
m_t2sProcess->start();
m_t2sProcess->waitForStarted();
}
void IpcProcessTun2Socks::stop()
{
qDebug() << "IpcProcessTun2Socks::stop()";
m_t2sProcess->disconnect();
m_t2sProcess->kill();
m_t2sProcess->waitForFinished(3000);
}
#endif

52
ipc/ipctun2socksprocess.h Normal file
View File

@@ -0,0 +1,52 @@
#ifndef IPCTUN2SOCKSPROCESS_H
#define IPCTUN2SOCKSPROCESS_H
#include <QObject>
#ifndef Q_OS_IOS
#include "rep_ipc_process_tun2socks_source.h"
namespace Vpn
{
Q_NAMESPACE
enum ConnectionState {
Unknown,
Disconnected,
Preparing,
Connecting,
Connected,
Disconnecting,
Reconnecting,
Error
};
Q_ENUM_NS(ConnectionState)
}
class IpcProcessTun2Socks : public IpcProcessTun2SocksSource
{
Q_OBJECT
public:
explicit IpcProcessTun2Socks(QObject *parent = nullptr);
virtual ~IpcProcessTun2Socks();
void start() override;
void stop() override;
signals:
private:
QSharedPointer<QProcess> m_t2sProcess;
};
#else
class IpcProcessTun2Socks : public QObject
{
Q_OBJECT
public:
explicit IpcProcessTun2Socks(QObject *parent = nullptr);
};
#endif
#endif // IPCTUN2SOCKSPROCESS_H

View File

@@ -6,6 +6,13 @@ project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION})
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(LINUX)
set(CMAKE_BUILD_RPATH "\$ORIGIN/../lib")
set(CMAKE_INSTALL_RPATH "\$ORIGIN/../lib")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
endif()
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
add_subdirectory(server)
endif()

View File

@@ -75,6 +75,7 @@ set(HEADERS
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipc.h
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.h
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserverprocess.h
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipctun2socksprocess.h
${CMAKE_CURRENT_LIST_DIR}/localserver.h
${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.h
${CMAKE_CURRENT_LIST_DIR}/router.h
@@ -96,6 +97,7 @@ set(SOURCES
${CMAKE_CURRENT_LIST_DIR}/../../client/core/networkUtilities.cpp
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.cpp
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserverprocess.cpp
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipctun2socksprocess.cpp
${CMAKE_CURRENT_LIST_DIR}/localserver.cpp
${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.cpp
${CMAKE_CURRENT_LIST_DIR}/main.cpp
@@ -351,6 +353,16 @@ include_directories(
add_executable(${PROJECT} ${SOURCES} ${HEADERS} ${RESOURCES})
if(LINUX)
target_link_options(${PROJECT} PRIVATE "LINKER:--disable-new-dtags")
set_target_properties(${PROJECT} PROPERTIES
BUILD_RPATH "\$ORIGIN/../lib"
INSTALL_RPATH "\$ORIGIN/../lib"
INSTALL_RPATH_USE_LINK_PATH FALSE
)
set_property(TARGET ${PROJECT} PROPERTY BUILD_WITH_INSTALL_RPATH TRUE)
endif()
target_link_libraries(${PROJECT} PRIVATE Qt6::Core Qt6::Widgets Qt6::Network Qt6::RemoteObjects Qt6::Core5Compat Qt6::DBus Qt6::Concurrent ${LIBS})
target_compile_definitions(${PROJECT} PRIVATE "MZ_$<UPPER_CASE:${MZ_PLATFORM_NAME}>")
@@ -387,6 +399,7 @@ endif()
qt_add_repc_sources(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipc_interface.rep)
qt_add_repc_sources(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipc_process_interface.rep)
qt_add_repc_sources(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipc_process_tun2socks.rep)
# copy deploy artifacts required to run the application to the debug build folder
if(WIN32)

View File

@@ -33,10 +33,18 @@ KillSwitch* KillSwitch::instance()
bool KillSwitch::init()
{
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
#ifdef Q_OS_LINUX
if (!LinuxFirewall::isInstalled()) {
LinuxFirewall::install();
}
m_appSettigns = QSharedPointer<SecureQSettings>(new SecureQSettings(ORGANIZATION_NAME, APPLICATION_NAME, nullptr));
#endif
#ifdef Q_OS_MACOS
if (!MacOSFirewall::isInstalled()) {
MacOSFirewall::install();
}
m_appSettigns = QSharedPointer<SecureQSettings>(new SecureQSettings(ORGANIZATION_NAME, APPLICATION_NAME, nullptr));
#endif
if (isStrictKillSwitchEnabled()) {
return disableAllTraffic();
}
@@ -71,6 +79,7 @@ bool KillSwitch::isStrictKillSwitchEnabled()
+ "\\" + QString(APPLICATION_NAME), QSettings::NativeFormat);
return RegHLM.value("strictKillSwitchEnabled", false).toBool();
#endif
m_appSettigns->sync();
return m_appSettigns->value("Conf/strictKillSwitchEnabled", false).toBool();
}

View File

@@ -40,6 +40,7 @@ LocalServer::LocalServer(QObject *parent) : QObject(parent),
if (!m_isRemotingEnabled) {
m_isRemotingEnabled = true;
m_serverNode.enableRemoting(&m_ipcServer);
m_serverNode.enableRemoting(&m_tun2socks);
}
});
@@ -50,8 +51,8 @@ LocalServer::LocalServer(QObject *parent) : QObject(parent),
}
m_networkWatcher.initialize();
connect(&m_networkWatcher, &NetworkWatcher::networkChanged, &m_ipcServer, &IpcServer::networkChanged);
connect(&m_networkWatcher, &NetworkWatcher::wakeup, &m_ipcServer, &IpcServer::wakeup);
connect(&m_networkWatcher, &NetworkWatcher::sleepMode, &m_ipcServer, &IpcServer::networkChange);
connect(&m_networkWatcher, &NetworkWatcher::networkChange, &m_ipcServer, &IpcServer::networkChange);
KillSwitch::instance()->init();
#ifdef Q_OS_LINUX

View File

@@ -38,6 +38,7 @@ public:
~LocalServer();
QSharedPointer<QLocalServer> m_server;
IpcServer m_ipcServer;
IpcProcessTun2Socks m_tun2socks;
QRemoteObjectHost m_serverNode;
bool m_isRemotingEnabled = false;

View File

@@ -318,40 +318,6 @@ bool RouterWin::createTun(const QString &dev, const QString &subnet)
return false;
}
HANDLE hEvent = CreateEvent(nullptr, true, false, nullptr);
if (!hEvent) {
qCritical() << "Failed to allocate event object";
return false;
}
auto _guardEvent = qScopeGuard([hEvent](){ CloseHandle(hEvent); });
struct {
HANDLE hEvent;
NET_LUID luid;
const QString &subnet;
bool found;
} ctx = { .hEvent = hEvent, .luid = luid, .subnet = subnet, .found = false };
auto cb = [](void *priv, MIB_UNICASTIPADDRESS_ROW *row, MIB_NOTIFICATION_TYPE NotificationType) {
auto* c = reinterpret_cast<decltype(ctx)*>(priv);
if (row != nullptr && row->InterfaceLuid.Value == c->luid.Value && row->Address.si_family == AF_INET) {
char ip[INET_ADDRSTRLEN];
inet_ntop(row->Address.Ipv4.sin_family, &row->Address.Ipv4.sin_addr, ip, INET_ADDRSTRLEN);
if (c->subnet == ip) {
c->found = true;
SetEvent(c->hEvent);
}
}
};
HANDLE hNotif;
res = NotifyUnicastIpAddressChange(AF_INET, cb, &ctx, false, &hNotif);
if (res != NO_ERROR) {
qCritical() << "Failed to subscribe to interface change";
return false;
}
auto _guardNotif = qScopeGuard([hNotif](){ CancelMibChangeNotify2(hNotif); });
MIB_UNICASTIPADDRESS_ROW row;
InitializeUnicastIpAddressEntry(&row);
@@ -371,13 +337,7 @@ bool RouterWin::createTun(const QString &dev, const QString &subnet)
return false;
}
res = WaitForSingleObject(hEvent, 10000);
if (res == WAIT_TIMEOUT) {
qCritical() << "Timeout of waiting for IP assignment for " << dev << " device";
return false;
}
return ctx.found;
return true;
}
void RouterWin::suspendWcmSvc(bool suspend)

View File

@@ -27,7 +27,7 @@
#include <sys/socket.h>
#endif
bool Xray::startXray(const QString &cfg)
void Xray::startXray(const QString &cfg)
{
qDebug() << "Xray::startXray()";
@@ -40,38 +40,34 @@ bool Xray::startXray(const QString &cfg)
if (auto err = amnezia_xray_setsockcallback(ctxSockCallback, this); err != nullptr) {
qDebug() << "[xray] sockopt failed: " << err;
amnezia_xray_free(err);
return false;
free(err);
return;
}
amnezia_xray_setloghandler(ctxLogHandler, this);
QByteArray bytes = cfg.toUtf8();
if (auto err = amnezia_xray_configure(bytes.data()); err != nullptr) {
qDebug() << "[xray] configuration failed: " << err;
amnezia_xray_free(err);
return false;
free(err);
return;
}
amnezia_xray_setloghandler(ctxLogHandler, this);
if (auto err = amnezia_xray_start(); err != nullptr) {
qDebug() << "[xray] failed to start: " << err;
amnezia_xray_free(err);
return false;
free(err);
return;
}
return true;
}
bool Xray::stopXray()
void Xray::stopXray()
{
qDebug() << "Xray::stopXray()";
if (auto err = amnezia_xray_stop(); err != nullptr) {
qDebug() << "[xray] failed to stop: " << err;
amnezia_xray_free(err);
return false;
free(err);
return;
}
return true;
}
void Xray::logHandler(char* str)

View File

@@ -12,8 +12,8 @@ public:
return instance;
}
bool startXray(const QString& cfg);
bool stopXray();
void startXray(const QString& cfg);
void stopXray();
private:
static void ctxSockCallback(uintptr_t fd, void* ctx) {