mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-17 16:26:08 +03:00
Compare commits
25 Commits
bugfix/lin
...
fix_backup
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88e3e2bcb4 | ||
|
|
ea648466de | ||
|
|
b782775016 | ||
|
|
89a7fe1081 | ||
|
|
e8bb096025 | ||
|
|
fd5c7c8322 | ||
|
|
e798d0f503 | ||
|
|
bbb0abb596 | ||
|
|
0925aec86a | ||
|
|
b084c4c284 | ||
|
|
87288ebccd | ||
|
|
fcd7eadf4c | ||
|
|
0373338fb7 | ||
|
|
42f070fe9d | ||
|
|
02be6dc5f9 | ||
|
|
bfcf7f0305 | ||
|
|
2bce595ade | ||
|
|
cd1e561fd4 | ||
|
|
9bd1e6a0f5 | ||
|
|
5058c9aa6f | ||
|
|
d78416835c | ||
|
|
40e6c6aae3 | ||
|
|
911a999c64 | ||
|
|
b4f4184aa6 | ||
|
|
5c6db4b7a4 |
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||
|
||||
set(PROJECT AmneziaVPN)
|
||||
set(AMNEZIAVPN_VERSION 4.8.13.0)
|
||||
set(AMNEZIAVPN_VERSION 4.8.14.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 2106)
|
||||
set(APP_ANDROID_VERSION_CODE 2110)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(MZ_PLATFORM_NAME "linux")
|
||||
@@ -61,6 +61,9 @@ 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")
|
||||
|
||||
Submodule client/3rd-prebuilt updated: 579673b2ed...568b8d720d
@@ -59,7 +59,6 @@ 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)
|
||||
|
||||
@@ -90,6 +90,10 @@ 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 val vpnServiceEventHandler: Handler by lazy(NONE) {
|
||||
object : Handler(Looper.getMainLooper()) {
|
||||
@@ -262,6 +266,10 @@ class AmneziaActivity : QtActivity() {
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
isActivityResumed = false
|
||||
hasWindowFocus = false
|
||||
// Cancel all pending operations when activity stops
|
||||
resumeHandler.removeCallbacksAndMessages(null)
|
||||
Log.d(TAG, "Stop Amnezia activity")
|
||||
doUnbindService()
|
||||
mainScope.launch {
|
||||
@@ -273,7 +281,13 @@ 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 {
|
||||
@@ -289,8 +303,41 @@ class AmneziaActivity : QtActivity() {
|
||||
KeyEvent.KEYCODE_BUTTON_X,
|
||||
KeyEvent.KEYCODE_BUTTON_Y,
|
||||
KeyEvent.KEYCODE_BUTTON_START,
|
||||
KeyEvent.KEYCODE_BUTTON_SELECT,
|
||||
KeyEvent.KEYCODE_BUTTON_SELECT -> {
|
||||
nativeGamepadKeyEvent(0, keyCode, true)
|
||||
nativeGamepadKeyEvent(0, keyCode, false)
|
||||
return true
|
||||
}
|
||||
KeyEvent.KEYCODE_DPAD_CENTER -> {
|
||||
if (isOnTv()) {
|
||||
val down = KeyEvent(
|
||||
event.downTime,
|
||||
event.eventTime,
|
||||
KeyEvent.ACTION_DOWN,
|
||||
KeyEvent.KEYCODE_ENTER,
|
||||
0,
|
||||
event.metaState,
|
||||
0,
|
||||
event.scanCode,
|
||||
event.flags,
|
||||
event.source
|
||||
)
|
||||
val up = KeyEvent(
|
||||
event.downTime,
|
||||
event.eventTime,
|
||||
KeyEvent.ACTION_UP,
|
||||
KeyEvent.KEYCODE_ENTER,
|
||||
0,
|
||||
event.metaState,
|
||||
0,
|
||||
event.scanCode,
|
||||
event.flags,
|
||||
event.source
|
||||
)
|
||||
super.dispatchKeyEvent(down)
|
||||
super.dispatchKeyEvent(up)
|
||||
return true
|
||||
}
|
||||
nativeGamepadKeyEvent(0, keyCode, true)
|
||||
nativeGamepadKeyEvent(0, keyCode, false)
|
||||
return true
|
||||
@@ -316,30 +363,42 @@ class AmneziaActivity : QtActivity() {
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
isActivityResumed = false
|
||||
// Cancel all pending operations when activity pauses
|
||||
resumeHandler.removeCallbacksAndMessages(null)
|
||||
Log.d(TAG, "Pause Amnezia activity")
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
/* if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
isActivityResumed = true
|
||||
Log.d(TAG, "Resume Amnezia activity")
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
window.decorView.apply {
|
||||
invalidate()
|
||||
|
||||
postDelayed({
|
||||
sendTouch(1f, 1f)
|
||||
resumeHandler.postDelayed({
|
||||
// Check if activity is still resumed and has focus before executing
|
||||
if (isActivityResumed && hasWindowFocus && !isFinishing && !isDestroyed) {
|
||||
sendTouch(1f, 1f)
|
||||
}
|
||||
}, 100)
|
||||
|
||||
postDelayed({
|
||||
sendTouch(2f, 2f)
|
||||
resumeHandler.postDelayed({
|
||||
if (isActivityResumed && hasWindowFocus && !isFinishing && !isDestroyed) {
|
||||
sendTouch(2f, 2f)
|
||||
}
|
||||
}, 200)
|
||||
|
||||
postDelayed({
|
||||
requestLayout()
|
||||
invalidate()
|
||||
resumeHandler.postDelayed({
|
||||
if (isActivityResumed && hasWindowFocus && !isFinishing && !isDestroyed) {
|
||||
requestLayout()
|
||||
invalidate()
|
||||
}
|
||||
}, 250)
|
||||
}
|
||||
} */
|
||||
Log.d(TAG, "Resume Amnezia activity")
|
||||
}
|
||||
}
|
||||
|
||||
private fun configureWindowForEdgeToEdge() {
|
||||
@@ -402,6 +461,10 @@ class AmneziaActivity : QtActivity() {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -181,7 +181,6 @@ 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
|
||||
@@ -194,7 +193,6 @@ 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
|
||||
|
||||
@@ -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_vpnConnection, m_sitesModel));
|
||||
m_sitesController.reset(new SitesController(m_settings, m_sitesModel));
|
||||
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
|
||||
|
||||
m_allowedDnsController.reset(new AllowedDnsController(m_settings, m_allowedDnsModel));
|
||||
|
||||
@@ -337,6 +337,9 @@ 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;
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ 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()
|
||||
@@ -33,68 +32,43 @@ QSharedPointer<IpcInterfaceReplica> IpcClient::Interface()
|
||||
return rep;
|
||||
}
|
||||
|
||||
QSharedPointer<IpcProcessTun2SocksReplica> IpcClient::InterfaceTun2Socks()
|
||||
QSharedPointer<IpcProcessInterfaceReplica> IpcClient::CreatePrivilegedProcess()
|
||||
{
|
||||
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(); });
|
||||
return withInterface([](QSharedPointer<IpcInterfaceReplica> &iface) -> QSharedPointer<IpcProcessInterfaceReplica> {
|
||||
auto createPrivilegedProcess = iface->createPrivilegedProcess();
|
||||
if (!createPrivilegedProcess.waitForFinished()) {
|
||||
qCritical() << "Failed to create privileged process";
|
||||
return nullptr;
|
||||
}
|
||||
});
|
||||
|
||||
pd->localSocket->connectToServer(amnezia::getIpcProcessUrl(pid));
|
||||
if (!pd->localSocket->waitForConnected()) {
|
||||
qCritical() << "IpcClient::createPrivilegedProcess: Failed to connect to process' socket";
|
||||
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> {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto processReplica = QSharedPointer<PrivilegedProcess>(pd->ipcProcess);
|
||||
return processReplica;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
#include <QObject>
|
||||
|
||||
#include "rep_ipc_interface_replica.h"
|
||||
#include "rep_ipc_process_tun2socks_replica.h"
|
||||
|
||||
#include "privileged_process.h"
|
||||
#include "rep_ipc_process_interface_replica.h"
|
||||
|
||||
class IpcClient : public QObject
|
||||
{
|
||||
@@ -18,8 +16,7 @@ public:
|
||||
static IpcClient& Instance();
|
||||
|
||||
static QSharedPointer<IpcInterfaceReplica> Interface();
|
||||
static QSharedPointer<IpcProcessTun2SocksReplica> InterfaceTun2Socks();
|
||||
static QSharedPointer<PrivilegedProcess> CreatePrivilegedProcess();
|
||||
static QSharedPointer<IpcProcessInterfaceReplica> CreatePrivilegedProcess();
|
||||
|
||||
template <typename Func>
|
||||
static auto withInterface(Func func)
|
||||
@@ -54,18 +51,6 @@ 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
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
#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();
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
#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
|
||||
|
||||
|
||||
@@ -72,9 +72,9 @@ void NetworkWatcher::initialize() {
|
||||
connect(m_impl, &NetworkWatcherImpl::unsecuredNetwork, this,
|
||||
&NetworkWatcher::unsecuredNetwork);
|
||||
connect(m_impl, &NetworkWatcherImpl::networkChanged, this,
|
||||
&NetworkWatcher::networkChange);
|
||||
connect(m_impl, &NetworkWatcherImpl::sleepMode, this,
|
||||
&NetworkWatcher::onSleepMode);
|
||||
&NetworkWatcher::networkChanged);
|
||||
connect(m_impl, &NetworkWatcherImpl::wakeup, this,
|
||||
&NetworkWatcher::wakeup);
|
||||
m_impl->initialize();
|
||||
|
||||
// Enable sleep/wake monitoring for VPN auto-reconnection
|
||||
@@ -97,12 +97,6 @@ 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)
|
||||
|
||||
@@ -29,13 +29,11 @@ public:
|
||||
// false to restore.
|
||||
void simulateDisconnection(bool simulatedDisconnection);
|
||||
|
||||
void onSleepMode();
|
||||
|
||||
QNetworkInformation::Reachability getReachability();
|
||||
|
||||
signals:
|
||||
void networkChange();
|
||||
void sleepMode();
|
||||
void networkChanged();
|
||||
void wakeup();
|
||||
|
||||
private:
|
||||
void settingsChanged();
|
||||
|
||||
@@ -41,7 +41,7 @@ signals:
|
||||
// TODO: Only windows-networkwatcher has this, the other plattforms should
|
||||
// too.
|
||||
void networkChanged(QString newBSSID);
|
||||
void sleepMode();
|
||||
void wakeup();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
@@ -41,8 +41,8 @@ void LinuxNetworkWatcher::initialize() {
|
||||
connect(m_worker, &LinuxNetworkWatcherWorker::unsecuredNetwork, this,
|
||||
&LinuxNetworkWatcher::unsecuredNetwork);
|
||||
|
||||
connect(m_worker, &LinuxNetworkWatcherWorker::sleepMode, this,
|
||||
&NetworkWatcherImpl::sleepMode);
|
||||
connect(m_worker, &LinuxNetworkWatcherWorker::wakeup, this,
|
||||
&NetworkWatcherImpl::wakeup);
|
||||
|
||||
// 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
|
||||
|
||||
@@ -200,7 +200,7 @@ void LinuxNetworkWatcherWorker::checkDevices() {
|
||||
void LinuxNetworkWatcherWorker::NMStateChanged(quint32 state)
|
||||
{
|
||||
if (state == NM_STATE_ASLEEP) {
|
||||
emit sleepMode();
|
||||
emit wakeup();
|
||||
}
|
||||
|
||||
logger.debug() << "NMStateChanged " << state;
|
||||
|
||||
@@ -23,7 +23,7 @@ class LinuxNetworkWatcherWorker final : public QObject {
|
||||
|
||||
signals:
|
||||
void unsecuredNetwork(const QString& networkName, const QString& networkId);
|
||||
void sleepMode();
|
||||
void wakeup();
|
||||
|
||||
public slots:
|
||||
void initialize();
|
||||
|
||||
@@ -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 sleepMode signal from dedicated CFRunLoop thread";
|
||||
logger.debug() << "System has powered on - emitting wakeup signal from dedicated CFRunLoop thread";
|
||||
if (listener->m_watcher) {
|
||||
// Use QMetaObject::invokeMethod for thread-safe signal emission
|
||||
QMetaObject::invokeMethod(listener->m_watcher, "sleepMode", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(listener->m_watcher, "wakeup", Qt::QueuedConnection);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -62,6 +62,9 @@ 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);
|
||||
|
||||
@@ -41,7 +41,7 @@ LRESULT WindowsNetworkWatcher::PowerWndProcCallback(HWND hwnd, UINT uMsg, WPARAM
|
||||
switch (uMsg) {
|
||||
case WM_POWERBROADCAST:
|
||||
if (wParam == PBT_APMRESUMESUSPEND) {
|
||||
emit obj->sleepMode();
|
||||
emit obj->wakeup();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -232,12 +232,6 @@ 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),
|
||||
@@ -246,13 +240,13 @@ ErrorCode OpenVpnProtocol::start()
|
||||
m_openVpnProcess->setArguments(arguments);
|
||||
|
||||
qDebug() << arguments.join(" ");
|
||||
connect(m_openVpnProcess.data(), &PrivilegedProcess::errorOccurred,
|
||||
connect(m_openVpnProcess.data(), &IpcProcessInterfaceReplica::errorOccurred,
|
||||
[&](QProcess::ProcessError error) { qDebug() << "PrivilegedProcess errorOccurred" << error; });
|
||||
|
||||
connect(m_openVpnProcess.data(), &PrivilegedProcess::stateChanged,
|
||||
connect(m_openVpnProcess.data(), &IpcProcessInterfaceReplica::stateChanged,
|
||||
[&](QProcess::ProcessState newState) { qDebug() << "PrivilegedProcess stateChanged" << newState; });
|
||||
|
||||
connect(m_openVpnProcess.data(), &PrivilegedProcess::finished, this,
|
||||
connect(m_openVpnProcess.data(), &IpcProcessInterfaceReplica::finished, this,
|
||||
[&]() { setConnectionState(Vpn::ConnectionState::Disconnected); });
|
||||
|
||||
m_openVpnProcess->start();
|
||||
|
||||
@@ -53,7 +53,7 @@ private:
|
||||
void updateRouteGateway(QString line);
|
||||
void updateVpnGateway(const QString &line);
|
||||
|
||||
QSharedPointer<PrivilegedProcess> m_openVpnProcess;
|
||||
QSharedPointer<IpcProcessInterfaceReplica> m_openVpnProcess;
|
||||
};
|
||||
|
||||
#endif // OPENVPNPROTOCOL_H
|
||||
|
||||
@@ -233,7 +233,7 @@ namespace amnezia
|
||||
constexpr char defaultResponsePacketMagicHeader[] = "3288052141";
|
||||
constexpr char defaultTransportPacketMagicHeader[] = "2528465083";
|
||||
constexpr char defaultUnderloadPacketMagicHeader[] = "1766607858";
|
||||
constexpr char defaultSpecialJunk1[] = "<b 0x084481800001000300000000077469636b65747306776964676574096b696e6f706f69736b0272750000010001c00c0005000100000039001806776964676574077469636b6574730679616e646578c025c0390005000100000039002b1765787465726e616c2d7469636b6574732d776964676574066166697368610679616e646578036e657400c05d000100010000001c000457fafe25>";
|
||||
constexpr char defaultSpecialJunk1[] = "<r 2><b 0x858000010001000000000669636c6f756403636f6d0000010001c00c000100010000105a00044d583737>";
|
||||
constexpr char defaultSpecialJunk2[] = "";
|
||||
constexpr char defaultSpecialJunk3[] = "";
|
||||
constexpr char defaultSpecialJunk4[] = "";
|
||||
|
||||
@@ -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) {
|
||||
emit connectionStateChanged(Vpn::ConnectionState::Connected);
|
||||
setConnectionState(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]() { emit connectionStateChanged(Vpn::ConnectionState::Disconnected); });
|
||||
[this]() { setConnectionState(Vpn::ConnectionState::Disconnected); });
|
||||
m_impl->initialize(nullptr, nullptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "xrayprotocol.h"
|
||||
|
||||
#include "core/ipcclient.h"
|
||||
#include "ipc.h"
|
||||
#include "utilities.h"
|
||||
#include "core/networkUtilities.h"
|
||||
|
||||
@@ -9,14 +10,37 @@
|
||||
#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_t2sProcess = IpcClient::InterfaceTun2Socks();
|
||||
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;
|
||||
}
|
||||
|
||||
XrayProtocol::~XrayProtocol()
|
||||
@@ -29,106 +53,16 @@ ErrorCode XrayProtocol::start()
|
||||
{
|
||||
qDebug() << "XrayProtocol::start()";
|
||||
|
||||
const ErrorCode err = IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||
iface->xrayStart(QJsonDocument(m_xrayConfig).toJson());
|
||||
return ErrorCode::NoError;
|
||||
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();
|
||||
}, [] () {
|
||||
return ErrorCode::AmneziaServiceConnectionFailed;
|
||||
});
|
||||
if (err != ErrorCode::NoError)
|
||||
return err;
|
||||
|
||||
setConnectionState(Vpn::ConnectionState::Connecting);
|
||||
return startTun2Sock();
|
||||
}
|
||||
|
||||
ErrorCode XrayProtocol::setupRouting() {
|
||||
return IpcClient::withInterface([this](QSharedPointer<IpcInterfaceReplica> iface) -> ErrorCode {
|
||||
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(1000) || !createTun.returnValue()) {
|
||||
qWarning() << "Failed to assign IP address for TUN";
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
|
||||
auto updateResolvers = iface->updateResolvers(tunName, dnsAddr);
|
||||
if (!updateResolvers.waitForFinished(1000) || !updateResolvers.returnValue()) {
|
||||
qWarning() << "Failed to set DNS resolvers for TUN";
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
#endif
|
||||
|
||||
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(1000) || routeAddList.returnValue() != subnets.count()) {
|
||||
qWarning() << "Failed to set routes for TUN";
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
}
|
||||
|
||||
auto StopRoutingIpv6 = iface->StopRoutingIpv6();
|
||||
if (!StopRoutingIpv6.waitForFinished(1000) || !StopRoutingIpv6.returnValue()) {
|
||||
qWarning() << "Failed to disable IPv6 routing";
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
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;
|
||||
},
|
||||
[] () {
|
||||
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()
|
||||
@@ -136,43 +70,177 @@ void XrayProtocol::stop()
|
||||
qDebug() << "XrayProtocol::stop()";
|
||||
|
||||
IpcClient::withInterface([](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
auto disableKillSwitch = iface->disableKillSwitch();
|
||||
if (!disableKillSwitch.waitForFinished() || !disableKillSwitch.returnValue())
|
||||
qWarning() << "Failed to disable killswitch";
|
||||
|
||||
auto StartRoutingIpv6 = iface->StartRoutingIpv6();
|
||||
if (!StartRoutingIpv6.waitForFinished(1000) || !StartRoutingIpv6.returnValue()) {
|
||||
qWarning() << "XrayProtocol::stop(): Failed to start routing ipv6";
|
||||
}
|
||||
if (!StartRoutingIpv6.waitForFinished() || !StartRoutingIpv6.returnValue())
|
||||
qWarning() << "Failed to start routing ipv6";
|
||||
|
||||
auto restoreResolvers = iface->restoreResolvers();
|
||||
if (!restoreResolvers.waitForFinished(1000) || !restoreResolvers.returnValue()) {
|
||||
qWarning() << "XrayProtocol::stop(): Failed to restore resolvers";
|
||||
}
|
||||
if (!restoreResolvers.waitForFinished() || !restoreResolvers.returnValue())
|
||||
qWarning() << "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();
|
||||
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_t2sProcess) {
|
||||
m_t2sProcess->stop();
|
||||
QThread::msleep(200);
|
||||
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);
|
||||
}
|
||||
|
||||
void XrayProtocol::readXrayConfiguration(const QJsonObject &configuration)
|
||||
ErrorCode XrayProtocol::startTun2Socks()
|
||||
{
|
||||
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_tun2socksProcess = IpcClient::CreatePrivilegedProcess();
|
||||
if (!m_tun2socksProcess->waitForSource()) {
|
||||
return ErrorCode::AmneziaServiceConnectionFailed;
|
||||
}
|
||||
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();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ErrorCode XrayProtocol::setupRouting() {
|
||||
return IpcClient::withInterface([this](QSharedPointer<IpcInterfaceReplica> iface) -> ErrorCode {
|
||||
#ifdef Q_OS_WIN
|
||||
const int inetAdapterIndex = NetworkUtilities::AdapterIndexTo(QHostAddress(m_remoteAddress));
|
||||
#endif
|
||||
auto createTun = iface->createTun(tunName, amnezia::protocols::xray::defaultLocalAddr);
|
||||
if (!createTun.waitForFinished() || !createTun.returnValue()) {
|
||||
qCritical() << "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";
|
||||
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";
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
}
|
||||
|
||||
auto StopRoutingIpv6 = iface->StopRoutingIpv6();
|
||||
if (!StopRoutingIpv6.waitForFinished() || !StopRoutingIpv6.returnValue()) {
|
||||
qCritical() << "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";
|
||||
#endif
|
||||
return ErrorCode::NoError;
|
||||
},
|
||||
[] () {
|
||||
return ErrorCode::AmneziaServiceConnectionFailed;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "core/ipcclient.h"
|
||||
#include "vpnprotocol.h"
|
||||
#include "settings.h"
|
||||
#include <QtCore/qsharedpointer.h>
|
||||
|
||||
class XrayProtocol : public VpnProtocol
|
||||
{
|
||||
@@ -18,16 +19,14 @@ public:
|
||||
|
||||
private:
|
||||
ErrorCode setupRouting();
|
||||
ErrorCode startTun2Sock();
|
||||
void readXrayConfiguration(const QJsonObject &configuration);
|
||||
|
||||
ErrorCode startTun2Socks();
|
||||
|
||||
QJsonObject m_xrayConfig;
|
||||
Settings::RouteMode m_routeMode;
|
||||
QString m_primaryDNS;
|
||||
QString m_secondaryDNS;
|
||||
#ifndef Q_OS_IOS
|
||||
QSharedPointer<IpcProcessTun2SocksReplica> m_t2sProcess;
|
||||
#endif
|
||||
QList<QHostAddress> m_dnsServers;
|
||||
QString m_remoteAddress;
|
||||
|
||||
QSharedPointer<IpcProcessInterfaceReplica> m_tun2socksProcess;
|
||||
};
|
||||
|
||||
#endif // XRAYPROTOCOL_H
|
||||
|
||||
@@ -35,13 +35,12 @@ 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(&mutex);
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
if (m_cache.contains(key)) {
|
||||
return m_cache.value(key);
|
||||
@@ -85,7 +84,7 @@ QVariant SecureQSettings::value(const QString &key, const QVariant &defaultValue
|
||||
|
||||
void SecureQSettings::setValue(const QString &key, const QVariant &value)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
if (encryptionRequired() && encryptedKeys.contains(key)) {
|
||||
if (!getEncKey().isEmpty() && !getEncIv().isEmpty()) {
|
||||
@@ -107,26 +106,20 @@ void SecureQSettings::setValue(const QString &key, const QVariant &value)
|
||||
}
|
||||
|
||||
m_cache.insert(key, value);
|
||||
sync();
|
||||
}
|
||||
|
||||
void SecureQSettings::remove(const QString &key)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
QMutexLocker locker(&m_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) {
|
||||
@@ -161,6 +154,8 @@ 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;
|
||||
@@ -173,10 +168,16 @@ 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;
|
||||
@@ -294,11 +295,3 @@ 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();
|
||||
}
|
||||
|
||||
@@ -16,14 +16,16 @@ public:
|
||||
explicit SecureQSettings(const QString &organization, const QString &application = QString(),
|
||||
QObject *parent = nullptr);
|
||||
|
||||
Q_INVOKABLE QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
|
||||
Q_INVOKABLE void setValue(const QString &key, const QVariant &value);
|
||||
QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
|
||||
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;
|
||||
|
||||
@@ -35,9 +37,6 @@ public:
|
||||
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;
|
||||
@@ -53,7 +52,7 @@ private:
|
||||
|
||||
const QByteArray magicString { "EncData" }; // Magic keyword used for mark encrypted QByteArray
|
||||
|
||||
mutable QMutex mutex;
|
||||
mutable QRecursiveMutex m_mutex;
|
||||
};
|
||||
|
||||
#endif // SECUREQSETTINGS_H
|
||||
|
||||
@@ -21,10 +21,10 @@ Settings::Settings(QObject *parent) : QObject(parent), m_settings(ORGANIZATION_N
|
||||
{
|
||||
// Import old settings
|
||||
if (serversCount() == 0) {
|
||||
QString user = value("Server/userName").toString();
|
||||
QString password = value("Server/password").toString();
|
||||
QString serverName = value("Server/serverName").toString();
|
||||
int port = value("Server/serverPort").toInt();
|
||||
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();
|
||||
|
||||
if (!user.isEmpty() && !password.isEmpty() && !serverName.isEmpty()) {
|
||||
QJsonObject server;
|
||||
@@ -222,7 +222,7 @@ QString Settings::nextAvailableServerName() const
|
||||
|
||||
void Settings::setSaveLogs(bool enabled)
|
||||
{
|
||||
setValue("Conf/saveLogs", enabled);
|
||||
m_settings.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 value("Conf/logEnableDate").toDateTime();
|
||||
return m_settings.value("Conf/logEnableDate").toDateTime();
|
||||
}
|
||||
|
||||
void Settings::setLogEnableDate(QDateTime date)
|
||||
{
|
||||
setValue("Conf/logEnableDate", date);
|
||||
m_settings.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>(value("Conf/routeMode", 0).toInt());
|
||||
return static_cast<RouteMode>(m_settings.value("Conf/routeMode", 0).toInt());
|
||||
}
|
||||
|
||||
bool Settings::isSitesSplitTunnelingEnabled() const
|
||||
{
|
||||
return value("Conf/sitesSplitTunnelingEnabled", false).toBool();
|
||||
return m_settings.value("Conf/sitesSplitTunnelingEnabled", false).toBool();
|
||||
}
|
||||
|
||||
void Settings::setSitesSplitTunnelingEnabled(bool enabled)
|
||||
{
|
||||
setValue("Conf/sitesSplitTunnelingEnabled", enabled);
|
||||
m_settings.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 value("Conf/primaryDns", cloudFlareNs1).toString();
|
||||
return m_settings.value("Conf/primaryDns", cloudFlareNs1).toString();
|
||||
}
|
||||
|
||||
QString Settings::secondaryDns() const
|
||||
{
|
||||
return value("Conf/secondaryDns", cloudFlareNs2).toString();
|
||||
return m_settings.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>(value("Conf/appsRouteMode", 0).toInt());
|
||||
return static_cast<AppsRouteMode>(m_settings.value("Conf/appsRouteMode", 0).toInt());
|
||||
}
|
||||
|
||||
void Settings::setAppsRouteMode(AppsRouteMode mode)
|
||||
{
|
||||
setValue("Conf/appsRouteMode", mode);
|
||||
m_settings.setValue("Conf/appsRouteMode", mode);
|
||||
}
|
||||
|
||||
QVector<InstalledAppInfo> Settings::getVpnApps(AppsRouteMode mode) const
|
||||
{
|
||||
QVector<InstalledAppInfo> apps;
|
||||
auto appsArray = value("Conf/" + appsRouteModeString(mode)).toJsonArray();
|
||||
auto appsArray = m_settings.value("Conf/" + appsRouteModeString(mode)).toJsonArray();
|
||||
for (const auto &app : appsArray) {
|
||||
InstalledAppInfo appInfo;
|
||||
appInfo.appName = app.toObject().value("appName").toString();
|
||||
@@ -419,43 +419,42 @@ void Settings::setVpnApps(AppsRouteMode mode, const QVector<InstalledAppInfo> &a
|
||||
appInfo.insert("appPath", app.appPath);
|
||||
appsArray.push_back(appInfo);
|
||||
}
|
||||
setValue("Conf/" + appsRouteModeString(mode), appsArray);
|
||||
m_settings.sync();
|
||||
m_settings.setValue("Conf/" + appsRouteModeString(mode), appsArray);
|
||||
}
|
||||
|
||||
bool Settings::isAppsSplitTunnelingEnabled() const
|
||||
{
|
||||
return value("Conf/appsSplitTunnelingEnabled", false).toBool();
|
||||
return m_settings.value("Conf/appsSplitTunnelingEnabled", false).toBool();
|
||||
}
|
||||
|
||||
void Settings::setAppsSplitTunnelingEnabled(bool enabled)
|
||||
{
|
||||
setValue("Conf/appsSplitTunnelingEnabled", enabled);
|
||||
m_settings.setValue("Conf/appsSplitTunnelingEnabled", enabled);
|
||||
}
|
||||
|
||||
bool Settings::isKillSwitchEnabled() const
|
||||
{
|
||||
return value("Conf/killSwitchEnabled", true).toBool();
|
||||
return m_settings.value("Conf/killSwitchEnabled", true).toBool();
|
||||
}
|
||||
|
||||
void Settings::setKillSwitchEnabled(bool enabled)
|
||||
{
|
||||
setValue("Conf/killSwitchEnabled", enabled);
|
||||
m_settings.setValue("Conf/killSwitchEnabled", enabled);
|
||||
}
|
||||
|
||||
bool Settings::isStrictKillSwitchEnabled() const
|
||||
{
|
||||
return value("Conf/strictKillSwitchEnabled", false).toBool();
|
||||
return m_settings.value("Conf/strictKillSwitchEnabled", false).toBool();
|
||||
}
|
||||
|
||||
void Settings::setStrictKillSwitchEnabled(bool enabled)
|
||||
{
|
||||
setValue("Conf/strictKillSwitchEnabled", enabled);
|
||||
m_settings.setValue("Conf/strictKillSwitchEnabled", enabled);
|
||||
}
|
||||
|
||||
QString Settings::getInstallationUuid(const bool needCreate)
|
||||
{
|
||||
auto uuid = value("Conf/installationUuid", "").toString();
|
||||
auto uuid = m_settings.value("Conf/installationUuid", "").toString();
|
||||
if (needCreate && uuid.isEmpty()) {
|
||||
uuid = QUuid::createUuid().toString();
|
||||
|
||||
@@ -476,7 +475,7 @@ QString Settings::getInstallationUuid(const bool needCreate)
|
||||
|
||||
void Settings::setInstallationUuid(const QString &uuid)
|
||||
{
|
||||
setValue("Conf/installationUuid", uuid);
|
||||
m_settings.setValue("Conf/installationUuid", uuid);
|
||||
}
|
||||
|
||||
ServerCredentials Settings::defaultServerCredentials() const
|
||||
@@ -497,28 +496,6 @@ 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;
|
||||
@@ -541,50 +518,50 @@ QString Settings::getGatewayEndpoint(bool isTestPurchase)
|
||||
|
||||
bool Settings::isDevGatewayEnv(bool isTestPurchase)
|
||||
{
|
||||
return isTestPurchase ? true : value("Conf/devGatewayEnv", false).toBool();
|
||||
return isTestPurchase ? true : m_settings.value("Conf/devGatewayEnv", false).toBool();
|
||||
}
|
||||
|
||||
void Settings::toggleDevGatewayEnv(bool enabled)
|
||||
{
|
||||
setValue("Conf/devGatewayEnv", enabled);
|
||||
m_settings.setValue("Conf/devGatewayEnv", enabled);
|
||||
}
|
||||
|
||||
bool Settings::isHomeAdLabelVisible()
|
||||
{
|
||||
return value("Conf/homeAdLabelVisible", true).toBool();
|
||||
return m_settings.value("Conf/homeAdLabelVisible", true).toBool();
|
||||
}
|
||||
|
||||
void Settings::disableHomeAdLabel()
|
||||
{
|
||||
setValue("Conf/homeAdLabelVisible", false);
|
||||
m_settings.setValue("Conf/homeAdLabelVisible", false);
|
||||
}
|
||||
|
||||
bool Settings::isPremV1MigrationReminderActive()
|
||||
{
|
||||
return value("Conf/premV1MigrationReminderActive", true).toBool();
|
||||
return m_settings.value("Conf/premV1MigrationReminderActive", true).toBool();
|
||||
}
|
||||
|
||||
void Settings::disablePremV1MigrationReminder()
|
||||
{
|
||||
setValue("Conf/premV1MigrationReminderActive", false);
|
||||
m_settings.setValue("Conf/premV1MigrationReminderActive", false);
|
||||
}
|
||||
|
||||
QStringList Settings::allowedDnsServers() const
|
||||
{
|
||||
return value("Conf/allowedDnsServers").toStringList();
|
||||
return m_settings.value("Conf/allowedDnsServers").toStringList();
|
||||
}
|
||||
|
||||
void Settings::setAllowedDnsServers(const QStringList &servers)
|
||||
{
|
||||
setValue("Conf/allowedDnsServers", servers);
|
||||
m_settings.setValue("Conf/allowedDnsServers", servers);
|
||||
}
|
||||
|
||||
QStringList Settings::readNewsIds() const
|
||||
{
|
||||
return value("News/readIds").toStringList();
|
||||
return m_settings.value("News/readIds").toStringList();
|
||||
}
|
||||
|
||||
void Settings::setReadNewsIds(const QStringList &ids)
|
||||
{
|
||||
setValue("News/readIds", ids);
|
||||
m_settings.setValue("News/readIds", ids);
|
||||
}
|
||||
|
||||
@@ -29,11 +29,11 @@ public:
|
||||
|
||||
QJsonArray serversArray() const
|
||||
{
|
||||
return QJsonDocument::fromJson(value("Servers/serversList").toByteArray()).array();
|
||||
return QJsonDocument::fromJson(m_settings.value("Servers/serversList").toByteArray()).array();
|
||||
}
|
||||
void setServersArray(const QJsonArray &servers)
|
||||
{
|
||||
setValue("Servers/serversList", QJsonDocument(servers).toJson());
|
||||
m_settings.setValue("Servers/serversList", QJsonDocument(servers).toJson());
|
||||
}
|
||||
|
||||
// Servers section
|
||||
@@ -45,11 +45,11 @@ public:
|
||||
|
||||
int defaultServerIndex() const
|
||||
{
|
||||
return value("Servers/defaultServerIndex", 0).toInt();
|
||||
return m_settings.value("Servers/defaultServerIndex", 0).toInt();
|
||||
}
|
||||
void setDefaultServer(int index)
|
||||
{
|
||||
setValue("Servers/defaultServerIndex", index);
|
||||
m_settings.setValue("Servers/defaultServerIndex", index);
|
||||
}
|
||||
QJsonObject defaultServer() const
|
||||
{
|
||||
@@ -78,34 +78,34 @@ public:
|
||||
// App settings section
|
||||
bool isAutoConnect() const
|
||||
{
|
||||
return value("Conf/autoConnect", false).toBool();
|
||||
return m_settings.value("Conf/autoConnect", false).toBool();
|
||||
}
|
||||
void setAutoConnect(bool enabled)
|
||||
{
|
||||
setValue("Conf/autoConnect", enabled);
|
||||
m_settings.setValue("Conf/autoConnect", enabled);
|
||||
}
|
||||
|
||||
bool isStartMinimized() const
|
||||
{
|
||||
return value("Conf/startMinimized", false).toBool();
|
||||
return m_settings.value("Conf/startMinimized", false).toBool();
|
||||
}
|
||||
void setStartMinimized(bool enabled)
|
||||
{
|
||||
setValue("Conf/startMinimized", enabled);
|
||||
m_settings.setValue("Conf/startMinimized", enabled);
|
||||
}
|
||||
|
||||
bool isNewsNotifications() const
|
||||
{
|
||||
return value("Conf/newsNotifications", true).toBool();
|
||||
return m_settings.value("Conf/newsNotifications", true).toBool();
|
||||
}
|
||||
void setNewsNotifications(bool enabled)
|
||||
{
|
||||
setValue("Conf/newsNotifications", enabled);
|
||||
m_settings.setValue("Conf/newsNotifications", enabled);
|
||||
}
|
||||
|
||||
bool isSaveLogs() const
|
||||
{
|
||||
return value("Conf/saveLogs", false).toBool();
|
||||
return m_settings.value("Conf/saveLogs", false).toBool();
|
||||
}
|
||||
void setSaveLogs(bool enabled);
|
||||
|
||||
@@ -122,19 +122,18 @@ public:
|
||||
QString routeModeString(RouteMode mode) const;
|
||||
|
||||
RouteMode routeMode() const;
|
||||
void setRouteMode(RouteMode mode) { setValue("Conf/routeMode", mode); }
|
||||
void setRouteMode(RouteMode mode) { m_settings.setValue("Conf/routeMode", mode); }
|
||||
|
||||
bool isSitesSplitTunnelingEnabled() const;
|
||||
void setSitesSplitTunnelingEnabled(bool enabled);
|
||||
|
||||
QVariantMap vpnSites(RouteMode mode) const
|
||||
{
|
||||
return value("Conf/" + routeModeString(mode)).toMap();
|
||||
return m_settings.value("Conf/" + routeModeString(mode)).toMap();
|
||||
}
|
||||
void setVpnSites(RouteMode mode, const QVariantMap &sites)
|
||||
{
|
||||
setValue("Conf/" + routeModeString(mode), sites);
|
||||
m_settings.sync();
|
||||
m_settings.setValue("Conf/" + routeModeString(mode), sites);
|
||||
}
|
||||
bool addVpnSite(RouteMode mode, const QString &site, const QString &ip = "");
|
||||
void addVpnSites(RouteMode mode, const QMap<QString, QString> &sites); // map <site, ip>
|
||||
@@ -147,11 +146,11 @@ public:
|
||||
|
||||
bool useAmneziaDns() const
|
||||
{
|
||||
return value("Conf/useAmneziaDns", true).toBool();
|
||||
return m_settings.value("Conf/useAmneziaDns", true).toBool();
|
||||
}
|
||||
void setUseAmneziaDns(bool enabled)
|
||||
{
|
||||
setValue("Conf/useAmneziaDns", enabled);
|
||||
m_settings.setValue("Conf/useAmneziaDns", enabled);
|
||||
}
|
||||
|
||||
QString primaryDns() const;
|
||||
@@ -160,13 +159,13 @@ public:
|
||||
// QString primaryDns() const { return m_primaryDns; }
|
||||
void setPrimaryDns(const QString &primaryDns)
|
||||
{
|
||||
setValue("Conf/primaryDns", primaryDns);
|
||||
m_settings.setValue("Conf/primaryDns", primaryDns);
|
||||
}
|
||||
|
||||
// QString secondaryDns() const { return m_secondaryDns; }
|
||||
void setSecondaryDns(const QString &secondaryDns)
|
||||
{
|
||||
setValue("Conf/secondaryDns", secondaryDns);
|
||||
m_settings.setValue("Conf/secondaryDns", secondaryDns);
|
||||
}
|
||||
|
||||
// static constexpr char openNicNs5[] = "94.103.153.176";
|
||||
@@ -188,16 +187,16 @@ public:
|
||||
};
|
||||
void setAppLanguage(QLocale locale)
|
||||
{
|
||||
setValue("Conf/appLanguage", locale.name());
|
||||
m_settings.setValue("Conf/appLanguage", locale.name());
|
||||
};
|
||||
|
||||
bool isScreenshotsEnabled() const
|
||||
{
|
||||
return value("Conf/screenshotsEnabled", true).toBool();
|
||||
return m_settings.value("Conf/screenshotsEnabled", true).toBool();
|
||||
}
|
||||
void setScreenshotsEnabled(bool enabled)
|
||||
{
|
||||
setValue("Conf/screenshotsEnabled", enabled);
|
||||
m_settings.setValue("Conf/screenshotsEnabled", enabled);
|
||||
emit screenshotsEnabledChanged(enabled);
|
||||
}
|
||||
|
||||
@@ -255,9 +254,6 @@ 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;
|
||||
|
||||
@@ -291,6 +291,8 @@ 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());
|
||||
|
||||
@@ -178,12 +178,11 @@ void SettingsController::backupAppConfig(const QString &fileName)
|
||||
|
||||
void SettingsController::restoreAppConfig(const QString &fileName)
|
||||
{
|
||||
QFile file(fileName);
|
||||
|
||||
file.open(QIODevice::ReadOnly);
|
||||
|
||||
QByteArray data = file.readAll();
|
||||
|
||||
QByteArray data;
|
||||
if (!SystemController::readFile(fileName, data)) {
|
||||
emit changeSettingsErrorOccurred(tr("Can't open file: %1").arg(fileName));
|
||||
return;
|
||||
}
|
||||
restoreAppConfigFromData(data);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,8 @@
|
||||
#include "systemController.h"
|
||||
#include "core/networkUtilities.h"
|
||||
|
||||
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)
|
||||
SitesController::SitesController(const std::shared_ptr<Settings> &settings, const QSharedPointer<SitesModel> &sitesModel, QObject *parent)
|
||||
: QObject(parent), m_settings(settings), m_sitesModel(sitesModel)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -34,32 +32,20 @@ void SitesController::addSite(QString hostname)
|
||||
hostname = hostname.split("/", Qt::SkipEmptyParts).first();
|
||||
}
|
||||
|
||||
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 auto &resolveCallback = [this](const QHostInfo &hostInfo) {
|
||||
const QList<QHostAddress> &addresses = hostInfo.addresses();
|
||||
for (const QHostAddress &addr : hostInfo.addresses()) {
|
||||
if (addr.protocol() == QAbstractSocket::NetworkLayerProtocol::IPv4Protocol) {
|
||||
processSite(hostInfo.hostName(), addr.toString());
|
||||
m_sitesModel->addSite(hostInfo.hostName(), addr.toString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
|
||||
processSite(hostname, "");
|
||||
m_sitesModel->addSite(hostname, "");
|
||||
} else {
|
||||
processSite(hostname, "");
|
||||
m_sitesModel->addSite(hostname, "");
|
||||
QHostInfo::lookupHost(hostname, this, resolveCallback);
|
||||
}
|
||||
|
||||
@@ -72,9 +58,6 @@ 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));
|
||||
}
|
||||
|
||||
@@ -128,8 +111,6 @@ 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"));
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,8 @@ class SitesController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SitesController(const std::shared_ptr<Settings> &settings,
|
||||
const QSharedPointer<VpnConnection> &vpnConnection,
|
||||
const QSharedPointer<SitesModel> &sitesModel, QObject *parent = nullptr);
|
||||
explicit SitesController(const std::shared_ptr<Settings> &settings, const QSharedPointer<SitesModel> &sitesModel,
|
||||
QObject *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void addSite(QString hostname);
|
||||
@@ -31,8 +30,6 @@ signals:
|
||||
|
||||
private:
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
|
||||
QSharedPointer<VpnConnection> m_vpnConnection;
|
||||
QSharedPointer<SitesModel> m_sitesModel;
|
||||
};
|
||||
|
||||
|
||||
@@ -396,9 +396,7 @@ PageType {
|
||||
PageController.showNotificationMessage(qsTr("Cannot remove server during active connection"))
|
||||
} else {
|
||||
PageController.showBusyIndicator(true)
|
||||
if (ApiConfigsController.deactivateDevice(true)) {
|
||||
InstallController.removeProcessedServer()
|
||||
}
|
||||
InstallController.removeProcessedServer()
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,6 +169,27 @@ PageType {
|
||||
var noButtonFunction = function() {
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
var showDialog = function() {
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
if (GC.isMobile() && Qt.platform.os === "android") {
|
||||
restoreBackupDelayTimer.dialogCallback = showDialog
|
||||
restoreBackupDelayTimer.restart()
|
||||
} else {
|
||||
showDialog()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: restoreBackupDelayTimer
|
||||
interval: 500
|
||||
repeat: false
|
||||
property var dialogCallback
|
||||
onTriggered: {
|
||||
if (dialogCallback && typeof dialogCallback === "function") {
|
||||
dialogCallback()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +278,6 @@ 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:
|
||||
@@ -304,7 +303,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
|
||||
|
||||
|
||||
@@ -39,9 +39,8 @@ 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::onConnectionStateChanged);
|
||||
connect(IosController::Instance(), &IosController::connectionStateChanged, this, &VpnConnection::setConnectionState);
|
||||
connect(IosController::Instance(), &IosController::bytesChanged, this, &VpnConnection::onBytesChanged);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -59,7 +58,7 @@ void VpnConnection::onKillSwitchModeChanged(bool enabled)
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
IpcClient::withInterface([enabled](QSharedPointer<IpcInterfaceReplica> iface){
|
||||
QRemoteObjectPendingReply<bool> reply = iface->refreshKillSwitch(enabled);
|
||||
if (reply.waitForFinished(1000) && reply.returnValue())
|
||||
if (reply.waitForFinished() && reply.returnValue())
|
||||
qDebug() << "VpnConnection::onKillSwitchModeChanged: Killswitch refreshed";
|
||||
else
|
||||
qWarning() << "VpnConnection::onKillSwitchModeChanged: Failed to execute remote refreshKillSwitch call";
|
||||
@@ -73,60 +72,57 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
|
||||
auto container = m_settings->defaultContainer(m_settings->defaultServerIndex());
|
||||
|
||||
IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||
if (state == Vpn::ConnectionState::Connected) {
|
||||
iface->resetIpStack();
|
||||
iface->flushDns();
|
||||
switch (state) {
|
||||
case Vpn::ConnectionState::Connected: {
|
||||
iface->resetIpStack();
|
||||
|
||||
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();
|
||||
auto flushDns = iface->flushDns();
|
||||
if (flushDns.waitForFinished() && flushDns.returnValue())
|
||||
qDebug() << "VpnConnection::onConnectionStateChanged: Successfully flushed DNS";
|
||||
else
|
||||
qWarning() << "VpnConnection::onConnectionStateChanged: Failed to clear saved routes";
|
||||
|
||||
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");
|
||||
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->routeGateway(), QStringList() << remoteAddress());
|
||||
addSitesRoutes(m_vpnProtocol->routeGateway(), m_settings->routeMode());
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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";
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
});
|
||||
#endif
|
||||
@@ -140,7 +136,6 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
|
||||
m_checkTimer.stop();
|
||||
}
|
||||
#endif
|
||||
emit connectionStateChanged(state);
|
||||
}
|
||||
|
||||
const QString &VpnConnection::remoteAddress() const
|
||||
@@ -185,7 +180,11 @@ void VpnConnection::addSitesRoutes(const QString &gw, Settings::RouteMode mode)
|
||||
});
|
||||
m_settings->addVpnSite(mode, site, ip);
|
||||
}
|
||||
flushDns();
|
||||
IpcClient::withInterface([](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||
auto reply = iface->flushDns();
|
||||
if (reply.waitForFinished() || !reply.returnValue())
|
||||
qWarning() << "VpnConnection::addSitesRoutes: Failed to flush DNS";
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -200,48 +199,6 @@ 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) {
|
||||
@@ -265,19 +222,15 @@ ErrorCode VpnConnection::lastError() const
|
||||
void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &vpnConfiguration)
|
||||
{
|
||||
qDebug() << QString("ConnectToVpn, Server index is %1, container is %2, route mode is")
|
||||
qDebug() << QString("Trying to connect to VPN, 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);
|
||||
emit connectionStateChanged(Vpn::ConnectionState::Connecting);
|
||||
setConnectionState(Vpn::ConnectionState::Connecting);
|
||||
|
||||
m_pendingNetworkCheck = false;
|
||||
m_vpnConfiguration = vpnConfiguration;
|
||||
m_serverIndex = serverIndex;
|
||||
m_serverCredentials = credentials;
|
||||
m_dockerContainer = container;
|
||||
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
if (m_vpnProtocol) {
|
||||
@@ -293,7 +246,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) {
|
||||
emit connectionStateChanged(Vpn::ConnectionState::Error);
|
||||
setConnectionState(Vpn::ConnectionState::Error);
|
||||
return;
|
||||
}
|
||||
m_vpnProtocol->prepare();
|
||||
@@ -311,75 +264,23 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede
|
||||
|
||||
createProtocolConnections();
|
||||
|
||||
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;
|
||||
if (ErrorCode err = m_vpnProtocol->start(); err != ErrorCode::NoError) {
|
||||
setConnectionState(Vpn::ConnectionState::Error);
|
||||
emit vpnProtocolError(err);
|
||||
}
|
||||
|
||||
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(), SIGNAL(connectionStateChanged(Vpn::ConnectionState)), this,
|
||||
SLOT(onConnectionStateChanged(Vpn::ConnectionState)));
|
||||
connect(m_vpnProtocol.data(), &VpnProtocol::connectionStateChanged, this, &VpnConnection::setConnectionState);
|
||||
connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64)));
|
||||
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
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;
|
||||
}
|
||||
});
|
||||
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);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -482,28 +383,13 @@ void VpnConnection::appendSplitTunnelingConfig()
|
||||
|
||||
m_vpnConfiguration.insert(config_key::appSplitTunnelType, appsRouteMode);
|
||||
m_vpnConfiguration.insert(config_key::splitTunnelApps, appsJsonArray);
|
||||
}
|
||||
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
@@ -537,6 +423,27 @@ 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)
|
||||
@@ -546,41 +453,26 @@ void VpnConnection::disconnectFromVpn()
|
||||
#endif
|
||||
|
||||
if (m_vpnProtocol.isNull()) {
|
||||
emit connectionStateChanged(Vpn::ConnectionState::Disconnected);
|
||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
setConnectionState(Vpn::ConnectionState::Disconnecting);
|
||||
|
||||
#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) {
|
||||
onConnectionStateChanged(Vpn::ConnectionState::Disconnected);
|
||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||
disconnect(*connection);
|
||||
delete connection;
|
||||
}
|
||||
});
|
||||
m_vpnProtocol->stop();
|
||||
#endif
|
||||
|
||||
m_vpnProtocol->stop();
|
||||
|
||||
#if !defined(Q_OS_ANDROID) && !defined(AMNEZIA_DESKTOP)
|
||||
m_vpnProtocol->deleteLater();
|
||||
#endif
|
||||
@@ -588,27 +480,12 @@ void VpnConnection::disconnectFromVpn()
|
||||
m_vpnProtocol = nullptr;
|
||||
}
|
||||
|
||||
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();
|
||||
void VpnConnection::setConnectionState(Vpn::ConnectionState state) {
|
||||
onConnectionStateChanged(state);
|
||||
|
||||
if (state == Vpn::Disconnected && m_connectionState == Vpn::Reconnecting)
|
||||
return;
|
||||
|
||||
m_connectionState = state;
|
||||
emit connectionStateChanged(state);
|
||||
}
|
||||
|
||||
@@ -34,10 +34,6 @@ public:
|
||||
|
||||
ErrorCode lastError() const;
|
||||
|
||||
bool isConnected() const;
|
||||
bool isDisconnected() const;
|
||||
|
||||
Vpn::ConnectionState connectionState();
|
||||
QSharedPointer<VpnProtocol> vpnProtocol() const;
|
||||
|
||||
const QString &remoteAddress() const;
|
||||
@@ -48,15 +44,10 @@ public:
|
||||
#endif
|
||||
|
||||
public slots:
|
||||
void connectToVpn(int serverIndex,
|
||||
const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration);
|
||||
|
||||
void connectToVpn(int serverIndex, const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration);
|
||||
void reconnectToVpn();
|
||||
void disconnectFromVpn();
|
||||
void restartConnection();
|
||||
|
||||
void addRoutes(const QStringList &ips);
|
||||
void deleteRoutes(const QStringList &ips);
|
||||
void flushDns();
|
||||
void onKillSwitchModeChanged(bool enabled);
|
||||
void disconnectSlots();
|
||||
|
||||
@@ -71,10 +62,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;
|
||||
@@ -82,14 +73,6 @@ 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;
|
||||
|
||||
@@ -100,11 +83,12 @@ private:
|
||||
void createAndroidConnections();
|
||||
#endif
|
||||
|
||||
Vpn::ConnectionState m_connectionState;
|
||||
|
||||
void createProtocolConnections();
|
||||
|
||||
void appendSplitTunnelingConfig();
|
||||
void appendKillSwitchConfig();
|
||||
bool startNetworkCheckIfReady();
|
||||
};
|
||||
|
||||
#endif // VPNCONNECTION_H
|
||||
|
||||
@@ -14,6 +14,8 @@ sc stop AmneziaVPN-service
|
||||
sc delete AmneziaVPN-service
|
||||
sc stop AmneziaWGTunnel$AmneziaVPN
|
||||
sc delete AmneziaWGTunnel$AmneziaVPN
|
||||
sc stop AmneziaVPNSplitTunnel
|
||||
sc delete AmneziaVPNSplitTunnel
|
||||
taskkill /IM "AmneziaVPN-service.exe" /F
|
||||
taskkill /IM "AmneziaVPN.exe" /F
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ sc stop AmneziaVPN-service
|
||||
sc delete AmneziaVPN-service
|
||||
sc stop AmneziaWGTunnel$AmneziaVPN
|
||||
sc delete AmneziaWGTunnel$AmneziaVPN
|
||||
sc stop AmneziaVPNSplitTunnel
|
||||
sc delete AmneziaVPNSplitTunnel
|
||||
taskkill /IM "AmneziaVPN-service.exe" /F
|
||||
taskkill /IM "AmneziaVPN.exe" /F
|
||||
|
||||
|
||||
66
ipc/ipc.h
66
ipc/ipc.h
@@ -11,6 +11,7 @@
|
||||
namespace amnezia {
|
||||
|
||||
enum PermittedProcess {
|
||||
Invalid,
|
||||
OpenVPN,
|
||||
Wireguard,
|
||||
Tun2Socks,
|
||||
@@ -19,16 +20,18 @@ enum PermittedProcess {
|
||||
|
||||
inline QString permittedProcessPath(PermittedProcess pid)
|
||||
{
|
||||
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();
|
||||
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 "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +51,51 @@ 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
|
||||
|
||||
|
||||
@@ -38,12 +38,13 @@ class IpcInterface
|
||||
SLOT( bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) );
|
||||
SLOT( bool restoreResolvers() );
|
||||
|
||||
SLOT(void xrayStart(const QString &config));
|
||||
SLOT(void xrayStop());
|
||||
SLOT(bool xrayStart(const QString &config));
|
||||
SLOT(bool xrayStop());
|
||||
|
||||
SLOT( bool startNetworkCheck(const QString& serverIpv4Gateway, const QString& deviceIpv4Address) );
|
||||
SLOT( bool stopNetworkCheck() );
|
||||
|
||||
SIGNAL( connectionLose() );
|
||||
SIGNAL( networkChange() );
|
||||
SIGNAL( wakeup() );
|
||||
SIGNAL( networkChanged() );
|
||||
};
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
class IpcProcessInterface
|
||||
{
|
||||
SLOT( start() );
|
||||
SLOT( terminate() );
|
||||
SLOT( kill() );
|
||||
SLOT( close() );
|
||||
|
||||
SLOT( setArguments(const QStringList &arguments) );
|
||||
@@ -17,6 +19,11 @@ 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) );
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
#include <QtCore>
|
||||
#include <QString>
|
||||
|
||||
class IpcProcessTun2Socks
|
||||
{
|
||||
SLOT( start() );
|
||||
SLOT( stop() );
|
||||
|
||||
SIGNAL( setConnectionState(int state) );
|
||||
SIGNAL( stateChanged(QProcess::ProcessState newState) );
|
||||
};
|
||||
@@ -304,7 +304,7 @@ bool IpcServer::refreshKillSwitch(bool enabled)
|
||||
return KillSwitch::instance()->refresh(enabled);
|
||||
}
|
||||
|
||||
void IpcServer::xrayStart(const QString& cfg)
|
||||
bool IpcServer::xrayStart(const QString& cfg)
|
||||
{
|
||||
#ifdef MZ_DEBUG
|
||||
qDebug() << "IpcServer::xrayStart";
|
||||
@@ -313,7 +313,7 @@ void IpcServer::xrayStart(const QString& cfg)
|
||||
return Xray::getInstance().startXray(cfg);
|
||||
}
|
||||
|
||||
void IpcServer::xrayStop()
|
||||
bool IpcServer::xrayStop()
|
||||
{
|
||||
#ifdef MZ_DEBUG
|
||||
qDebug() << "IpcServer::xrayStop";
|
||||
|
||||
@@ -10,10 +10,8 @@
|
||||
|
||||
#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
|
||||
{
|
||||
@@ -44,8 +42,8 @@ public:
|
||||
virtual bool refreshKillSwitch( bool enabled ) override;
|
||||
virtual bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) override;
|
||||
virtual bool restoreResolvers() override;
|
||||
virtual void xrayStart(const QString& cfg) override;
|
||||
virtual void xrayStop() override;
|
||||
virtual bool xrayStart(const QString& cfg) override;
|
||||
virtual bool xrayStop() override;
|
||||
virtual bool startNetworkCheck(const QString& serverIpv4Gateway, const QString& deviceIpv4Address) override;
|
||||
virtual bool stopNetworkCheck() override;
|
||||
|
||||
@@ -56,12 +54,10 @@ 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;
|
||||
};
|
||||
|
||||
@@ -40,6 +40,14 @@ void IpcServerProcess::start()
|
||||
m_process->waitForStarted();
|
||||
}
|
||||
|
||||
void IpcServerProcess::terminate() {
|
||||
m_process->terminate();
|
||||
}
|
||||
|
||||
void IpcServerProcess::kill() {
|
||||
m_process->kill();
|
||||
}
|
||||
|
||||
void IpcServerProcess::close()
|
||||
{
|
||||
m_process->close();
|
||||
@@ -47,7 +55,7 @@ void IpcServerProcess::close()
|
||||
|
||||
void IpcServerProcess::setArguments(const QStringList &arguments)
|
||||
{
|
||||
m_process->setArguments(arguments);
|
||||
m_process->setArguments(amnezia::sanitizeArguments(m_program, arguments));
|
||||
}
|
||||
|
||||
void IpcServerProcess::setInputChannelMode(QProcess::InputChannelMode mode)
|
||||
@@ -69,7 +77,9 @@ void IpcServerProcess::setProcessChannelMode(QProcess::ProcessChannelMode mode)
|
||||
|
||||
void IpcServerProcess::setProgram(int programId)
|
||||
{
|
||||
m_process->setProgram(amnezia::permittedProcessPath(static_cast<amnezia::PermittedProcess>(programId)));
|
||||
m_program = static_cast<amnezia::PermittedProcess>(programId);
|
||||
m_process->setProgram(amnezia::permittedProcessPath(m_program));
|
||||
m_process->setArguments({});
|
||||
}
|
||||
|
||||
void IpcServerProcess::setWorkingDirectory(const QString &dir)
|
||||
@@ -92,4 +102,20 @@ 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
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef IPCSERVERPROCESS_H
|
||||
#define IPCSERVERPROCESS_H
|
||||
|
||||
#include "ipc.h"
|
||||
#include <QObject>
|
||||
|
||||
#ifndef Q_OS_IOS
|
||||
@@ -14,6 +15,8 @@ public:
|
||||
virtual ~IpcServerProcess();
|
||||
|
||||
void start() override;
|
||||
void terminate() override;
|
||||
void kill() override;
|
||||
void close() override;
|
||||
|
||||
void setArguments(const QStringList &arguments) override;
|
||||
@@ -27,9 +30,15 @@ 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;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
#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
|
||||
@@ -1,52 +0,0 @@
|
||||
#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
|
||||
@@ -75,7 +75,6 @@ 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
|
||||
@@ -97,7 +96,6 @@ 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
|
||||
@@ -389,7 +387,6 @@ 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)
|
||||
|
||||
@@ -33,18 +33,10 @@ KillSwitch* KillSwitch::instance()
|
||||
|
||||
bool KillSwitch::init()
|
||||
{
|
||||
#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();
|
||||
}
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
|
||||
m_appSettigns = QSharedPointer<SecureQSettings>(new SecureQSettings(ORGANIZATION_NAME, APPLICATION_NAME, nullptr));
|
||||
#endif
|
||||
|
||||
if (isStrictKillSwitchEnabled()) {
|
||||
return disableAllTraffic();
|
||||
}
|
||||
@@ -79,7 +71,6 @@ 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@ LocalServer::LocalServer(QObject *parent) : QObject(parent),
|
||||
if (!m_isRemotingEnabled) {
|
||||
m_isRemotingEnabled = true;
|
||||
m_serverNode.enableRemoting(&m_ipcServer);
|
||||
m_serverNode.enableRemoting(&m_tun2socks);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -51,8 +50,8 @@ LocalServer::LocalServer(QObject *parent) : QObject(parent),
|
||||
}
|
||||
|
||||
m_networkWatcher.initialize();
|
||||
connect(&m_networkWatcher, &NetworkWatcher::sleepMode, &m_ipcServer, &IpcServer::networkChange);
|
||||
connect(&m_networkWatcher, &NetworkWatcher::networkChange, &m_ipcServer, &IpcServer::networkChange);
|
||||
connect(&m_networkWatcher, &NetworkWatcher::networkChanged, &m_ipcServer, &IpcServer::networkChanged);
|
||||
connect(&m_networkWatcher, &NetworkWatcher::wakeup, &m_ipcServer, &IpcServer::wakeup);
|
||||
KillSwitch::instance()->init();
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
|
||||
@@ -38,7 +38,6 @@ public:
|
||||
~LocalServer();
|
||||
QSharedPointer<QLocalServer> m_server;
|
||||
IpcServer m_ipcServer;
|
||||
IpcProcessTun2Socks m_tun2socks;
|
||||
QRemoteObjectHost m_serverNode;
|
||||
bool m_isRemotingEnabled = false;
|
||||
|
||||
|
||||
@@ -318,6 +318,40 @@ 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);
|
||||
|
||||
@@ -337,7 +371,13 @@ bool RouterWin::createTun(const QString &dev, const QString &subnet)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
res = WaitForSingleObject(hEvent, 10000);
|
||||
if (res == WAIT_TIMEOUT) {
|
||||
qCritical() << "Timeout of waiting for IP assignment for " << dev << " device";
|
||||
return false;
|
||||
}
|
||||
|
||||
return ctx.found;
|
||||
}
|
||||
|
||||
void RouterWin::suspendWcmSvc(bool suspend)
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
void Xray::startXray(const QString &cfg)
|
||||
bool Xray::startXray(const QString &cfg)
|
||||
{
|
||||
qDebug() << "Xray::startXray()";
|
||||
|
||||
@@ -40,34 +40,38 @@ void Xray::startXray(const QString &cfg)
|
||||
|
||||
if (auto err = amnezia_xray_setsockcallback(ctxSockCallback, this); err != nullptr) {
|
||||
qDebug() << "[xray] sockopt failed: " << err;
|
||||
free(err);
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray bytes = cfg.toUtf8();
|
||||
if (auto err = amnezia_xray_configure(bytes.data()); err != nullptr) {
|
||||
qDebug() << "[xray] configuration failed: " << err;
|
||||
free(err);
|
||||
return;
|
||||
amnezia_xray_free(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (auto err = amnezia_xray_start(); err != nullptr) {
|
||||
qDebug() << "[xray] failed to start: " << err;
|
||||
free(err);
|
||||
return;
|
||||
amnezia_xray_free(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Xray::stopXray()
|
||||
bool Xray::stopXray()
|
||||
{
|
||||
qDebug() << "Xray::stopXray()";
|
||||
if (auto err = amnezia_xray_stop(); err != nullptr) {
|
||||
qDebug() << "[xray] failed to stop: " << err;
|
||||
free(err);
|
||||
return;
|
||||
amnezia_xray_free(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Xray::logHandler(char* str)
|
||||
|
||||
@@ -12,8 +12,8 @@ public:
|
||||
return instance;
|
||||
}
|
||||
|
||||
void startXray(const QString& cfg);
|
||||
void stopXray();
|
||||
bool startXray(const QString& cfg);
|
||||
bool stopXray();
|
||||
|
||||
private:
|
||||
static void ctxSockCallback(uintptr_t fd, void* ctx) {
|
||||
|
||||
Reference in New Issue
Block a user