mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-08 14:33:23 +00:00
fix: xray stability and split-tunneling (#2187)
* fix: xray heap corruption * fix: use proper configuration for split-tunneled apps * chore: enable killswitch * chore: xray windows split-tunneling cleanup * chore: proper xray killswitch log * feat: add wait for the tun device * chore: update amnezia_xray deps for macos * fix: add nullptr check for split-tunnel on win * fix: modernize vpnAdapter grabbing function * fix: remove network watcher due to its fragileness * chore: xrayprotocol cleanup * fix: correct wrong iface index on win * chore: move tun2socks implementation to the client from the service * chore: xrayprotocol cleanup * chore: more xrayprotocol cleanup * fix: consistent tun device with GUID specified * chore: tun2socks logs * chore: PrivilegedProcess cleanup * better error handling in establishment phase * terminate&kill ops for remote process * fix: straighforward killing the process on windows * fix: finally remove GUID setting from tun2socks due to instability * fix: add sanitizer to ipc process * chore: do not collect sensitive info from tun2socks
This commit is contained in:
Submodule client/3rd-prebuilt updated: 579673b2ed...b8c229288d
@@ -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))
|
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_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_interface.rep)
|
||||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_tun2socks.rep)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
qt6_add_resources(QRC ${QRC} ${CMAKE_CURRENT_LIST_DIR}/resources.qrc)
|
qt6_add_resources(QRC ${QRC} ${CMAKE_CURRENT_LIST_DIR}/resources.qrc)
|
||||||
|
|||||||
@@ -181,7 +181,6 @@ if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
|
|||||||
|
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CLIENT_ROOT_DIR}/core/ipcclient.h
|
${CLIENT_ROOT_DIR}/core/ipcclient.h
|
||||||
${CLIENT_ROOT_DIR}/core/privileged_process.h
|
|
||||||
${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.h
|
${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.h
|
||||||
${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.h
|
${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.h
|
||||||
${CLIENT_ROOT_DIR}/protocols/openvpnovercloakprotocol.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}
|
set(SOURCES ${SOURCES}
|
||||||
${CLIENT_ROOT_DIR}/core/ipcclient.cpp
|
${CLIENT_ROOT_DIR}/core/ipcclient.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/privileged_process.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
|
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
|
||||||
${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.cpp
|
${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.cpp
|
||||||
${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.cpp
|
${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.cpp
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ IpcClient::IpcClient(QObject *parent) : QObject(parent)
|
|||||||
{
|
{
|
||||||
m_node.connectToNode(QUrl("local:" + amnezia::getIpcServiceUrl()));
|
m_node.connectToNode(QUrl("local:" + amnezia::getIpcServiceUrl()));
|
||||||
m_interface.reset(m_node.acquire<IpcInterfaceReplica>());
|
m_interface.reset(m_node.acquire<IpcInterfaceReplica>());
|
||||||
m_tun2socks.reset(m_node.acquire<IpcProcessTun2SocksReplica>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IpcClient& IpcClient::Instance()
|
IpcClient& IpcClient::Instance()
|
||||||
@@ -33,68 +32,43 @@ QSharedPointer<IpcInterfaceReplica> IpcClient::Interface()
|
|||||||
return rep;
|
return rep;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<IpcProcessTun2SocksReplica> IpcClient::InterfaceTun2Socks()
|
QSharedPointer<IpcProcessInterfaceReplica> IpcClient::CreatePrivilegedProcess()
|
||||||
{
|
{
|
||||||
QSharedPointer<IpcProcessTun2SocksReplica> rep = Instance().m_tun2socks;
|
return withInterface([](QSharedPointer<IpcInterfaceReplica> &iface) -> QSharedPointer<IpcProcessInterfaceReplica> {
|
||||||
if (rep.isNull()) {
|
auto createPrivilegedProcess = iface->createPrivilegedProcess();
|
||||||
qCritical() << "IpcClient::InterfaceTun2Socks: Replica is undefined";
|
if (!createPrivilegedProcess.waitForFinished()) {
|
||||||
return nullptr;
|
qCritical() << "Failed to create privileged process";
|
||||||
}
|
return nullptr;
|
||||||
if (!rep->waitForSource(1000)) {
|
|
||||||
qCritical() << "IpcClient::InterfaceTun2Socks: Failed to initialize replica";
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (!rep->isReplicaValid()) {
|
|
||||||
qWarning() << "IpcClient::InterfaceTun2Socks(): Replica is invalid";
|
|
||||||
}
|
|
||||||
return rep;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSharedPointer<PrivilegedProcess> IpcClient::CreatePrivilegedProcess()
|
|
||||||
{
|
|
||||||
QSharedPointer<IpcInterfaceReplica> rep = Interface();
|
|
||||||
if (!rep) {
|
|
||||||
qCritical() << "IpcClient::createPrivilegedProcess: Replica is invalid";
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
QRemoteObjectPendingReply<int> pidReply = rep->createPrivilegedProcess();
|
|
||||||
if (!pidReply.waitForFinished(5000)){
|
|
||||||
qCritical() << "IpcClient::createPrivilegedProcess: Failed to execute RO createPrivilegedProcess call";
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int pid = pidReply.returnValue();
|
|
||||||
QSharedPointer<ProcessDescriptor> pd(new ProcessDescriptor());
|
|
||||||
|
|
||||||
pd->localSocket.reset(new QLocalSocket(pd->replicaNode.data()));
|
|
||||||
|
|
||||||
connect(pd->localSocket.data(), &QLocalSocket::connected, pd->replicaNode.data(), [pd]() {
|
|
||||||
pd->replicaNode->addClientSideConnection(pd->localSocket.data());
|
|
||||||
|
|
||||||
IpcProcessInterfaceReplica *repl = pd->replicaNode->acquire<IpcProcessInterfaceReplica>();
|
|
||||||
// TODO: rework the unsafe cast below
|
|
||||||
PrivilegedProcess *priv = static_cast<PrivilegedProcess *>(repl);
|
|
||||||
pd->ipcProcess.reset(priv);
|
|
||||||
if (!pd->ipcProcess) {
|
|
||||||
qWarning() << "Acquire PrivilegedProcess failed";
|
|
||||||
} else {
|
|
||||||
pd->ipcProcess->waitForSource(1000);
|
|
||||||
if (!pd->ipcProcess->isReplicaValid()) {
|
|
||||||
qWarning() << "PrivilegedProcess replica is not connected!";
|
|
||||||
}
|
|
||||||
|
|
||||||
QObject::connect(pd->ipcProcess.data(), &PrivilegedProcess::destroyed, pd->ipcProcess.data(),
|
|
||||||
[pd]() { pd->replicaNode->deleteLater(); });
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
pd->localSocket->connectToServer(amnezia::getIpcProcessUrl(pid));
|
const int pid = createPrivilegedProcess.returnValue();
|
||||||
if (!pd->localSocket->waitForConnected()) {
|
|
||||||
qCritical() << "IpcClient::createPrivilegedProcess: Failed to connect to process' socket";
|
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;
|
return nullptr;
|
||||||
}
|
});
|
||||||
|
|
||||||
auto processReplica = QSharedPointer<PrivilegedProcess>(pd->ipcProcess);
|
|
||||||
return processReplica;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,7 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include "rep_ipc_interface_replica.h"
|
#include "rep_ipc_interface_replica.h"
|
||||||
#include "rep_ipc_process_tun2socks_replica.h"
|
#include "rep_ipc_process_interface_replica.h"
|
||||||
|
|
||||||
#include "privileged_process.h"
|
|
||||||
|
|
||||||
class IpcClient : public QObject
|
class IpcClient : public QObject
|
||||||
{
|
{
|
||||||
@@ -18,8 +16,7 @@ public:
|
|||||||
static IpcClient& Instance();
|
static IpcClient& Instance();
|
||||||
|
|
||||||
static QSharedPointer<IpcInterfaceReplica> Interface();
|
static QSharedPointer<IpcInterfaceReplica> Interface();
|
||||||
static QSharedPointer<IpcProcessTun2SocksReplica> InterfaceTun2Socks();
|
static QSharedPointer<IpcProcessInterfaceReplica> CreatePrivilegedProcess();
|
||||||
static QSharedPointer<PrivilegedProcess> CreatePrivilegedProcess();
|
|
||||||
|
|
||||||
template <typename Func>
|
template <typename Func>
|
||||||
static auto withInterface(Func func)
|
static auto withInterface(Func func)
|
||||||
@@ -54,18 +51,6 @@ signals:
|
|||||||
private:
|
private:
|
||||||
QRemoteObjectNode m_node;
|
QRemoteObjectNode m_node;
|
||||||
QSharedPointer<IpcInterfaceReplica> m_interface;
|
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
|
#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
|
|
||||||
|
|
||||||
|
|
||||||
@@ -62,6 +62,9 @@ void WindowsDaemon::prepareActivation(const InterfaceConfig& config, int inetAda
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WindowsDaemon::activateSplitTunnel(const InterfaceConfig& config, int vpnAdapterIndex) {
|
void WindowsDaemon::activateSplitTunnel(const InterfaceConfig& config, int vpnAdapterIndex) {
|
||||||
|
if (m_splitTunnelManager == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
if (config.m_vpnDisabledApps.length() > 0) {
|
if (config.m_vpnDisabledApps.length() > 0) {
|
||||||
m_splitTunnelManager->start(m_inetAdapterIndex, vpnAdapterIndex);
|
m_splitTunnelManager->start(m_inetAdapterIndex, vpnAdapterIndex);
|
||||||
m_splitTunnelManager->excludeApps(config.m_vpnDisabledApps);
|
m_splitTunnelManager->excludeApps(config.m_vpnDisabledApps);
|
||||||
|
|||||||
@@ -232,12 +232,6 @@ ErrorCode OpenVpnProtocol::start()
|
|||||||
return ErrorCode::AmneziaServiceConnectionFailed;
|
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);
|
m_openVpnProcess->setProgram(PermittedProcess::OpenVPN);
|
||||||
QStringList arguments({
|
QStringList arguments({
|
||||||
"--config", configPath(), "--management", m_managementHost, QString::number(mgmtPort),
|
"--config", configPath(), "--management", m_managementHost, QString::number(mgmtPort),
|
||||||
@@ -246,13 +240,13 @@ ErrorCode OpenVpnProtocol::start()
|
|||||||
m_openVpnProcess->setArguments(arguments);
|
m_openVpnProcess->setArguments(arguments);
|
||||||
|
|
||||||
qDebug() << arguments.join(" ");
|
qDebug() << arguments.join(" ");
|
||||||
connect(m_openVpnProcess.data(), &PrivilegedProcess::errorOccurred,
|
connect(m_openVpnProcess.data(), &IpcProcessInterfaceReplica::errorOccurred,
|
||||||
[&](QProcess::ProcessError error) { qDebug() << "PrivilegedProcess errorOccurred" << error; });
|
[&](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; });
|
[&](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); });
|
[&]() { setConnectionState(Vpn::ConnectionState::Disconnected); });
|
||||||
|
|
||||||
m_openVpnProcess->start();
|
m_openVpnProcess->start();
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ private:
|
|||||||
void updateRouteGateway(QString line);
|
void updateRouteGateway(QString line);
|
||||||
void updateVpnGateway(const QString &line);
|
void updateVpnGateway(const QString &line);
|
||||||
|
|
||||||
QSharedPointer<PrivilegedProcess> m_openVpnProcess;
|
QSharedPointer<IpcProcessInterfaceReplica> m_openVpnProcess;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // OPENVPNPROTOCOL_H
|
#endif // OPENVPNPROTOCOL_H
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "xrayprotocol.h"
|
#include "xrayprotocol.h"
|
||||||
|
|
||||||
#include "core/ipcclient.h"
|
#include "core/ipcclient.h"
|
||||||
|
#include "ipc.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
#include "core/networkUtilities.h"
|
#include "core/networkUtilities.h"
|
||||||
|
|
||||||
@@ -9,14 +10,37 @@
|
|||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QNetworkInterface>
|
#include <QNetworkInterface>
|
||||||
#include <QJsonDocument>
|
#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)
|
XrayProtocol::XrayProtocol(const QJsonObject &configuration, QObject *parent) : VpnProtocol(configuration, parent)
|
||||||
{
|
{
|
||||||
readXrayConfiguration(configuration);
|
|
||||||
m_routeGateway = NetworkUtilities::getGatewayAndIface().first;
|
|
||||||
m_vpnGateway = amnezia::protocols::xray::defaultLocalAddr;
|
m_vpnGateway = amnezia::protocols::xray::defaultLocalAddr;
|
||||||
m_vpnLocalAddress = 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()
|
XrayProtocol::~XrayProtocol()
|
||||||
@@ -28,71 +52,193 @@ XrayProtocol::~XrayProtocol()
|
|||||||
ErrorCode XrayProtocol::start()
|
ErrorCode XrayProtocol::start()
|
||||||
{
|
{
|
||||||
qDebug() << "XrayProtocol::start()";
|
qDebug() << "XrayProtocol::start()";
|
||||||
|
setConnectionState(Vpn::ConnectionState::Connecting);
|
||||||
|
|
||||||
const ErrorCode err = IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
|
return IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||||
iface->xrayStart(QJsonDocument(m_xrayConfig).toJson());
|
auto xrayStart = iface->xrayStart(QJsonDocument(m_xrayConfig).toJson());
|
||||||
return ErrorCode::NoError;
|
if (!xrayStart.waitForFinished() || !xrayStart.returnValue()) {
|
||||||
|
qCritical() << "Failed to start xray";
|
||||||
|
return ErrorCode::XrayExecutableCrashed;
|
||||||
|
}
|
||||||
|
return startTun2Socks();
|
||||||
}, [] () {
|
}, [] () {
|
||||||
return ErrorCode::AmneziaServiceConnectionFailed;
|
return ErrorCode::AmneziaServiceConnectionFailed;
|
||||||
});
|
});
|
||||||
if (err != ErrorCode::NoError)
|
}
|
||||||
return err;
|
|
||||||
|
|
||||||
setConnectionState(Vpn::ConnectionState::Connecting);
|
void XrayProtocol::stop()
|
||||||
return startTun2Sock();
|
{
|
||||||
|
qDebug() << "XrayProtocol::stop()";
|
||||||
|
setConnectionState(Vpn::ConnectionState::Disconnecting);
|
||||||
|
|
||||||
|
IpcClient::withInterface([](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||||
|
auto disableKillSwitch = iface->disableKillSwitch();
|
||||||
|
if (!disableKillSwitch.waitForFinished() || !disableKillSwitch.returnValue())
|
||||||
|
qWarning() << "Failed to disable killswitch";
|
||||||
|
|
||||||
|
auto StartRoutingIpv6 = iface->StartRoutingIpv6();
|
||||||
|
if (!StartRoutingIpv6.waitForFinished() || !StartRoutingIpv6.returnValue())
|
||||||
|
qWarning() << "Failed to start routing ipv6";
|
||||||
|
|
||||||
|
auto restoreResolvers = iface->restoreResolvers();
|
||||||
|
if (!restoreResolvers.waitForFinished() || !restoreResolvers.returnValue())
|
||||||
|
qWarning() << "Failed to restore resolvers";
|
||||||
|
|
||||||
|
auto deleteTun = iface->deleteTun(tunName);
|
||||||
|
if (!deleteTun.waitForFinished() || !deleteTun.returnValue())
|
||||||
|
qWarning() << "Failed to delete tun";
|
||||||
|
|
||||||
|
auto xrayStop = iface->xrayStop();
|
||||||
|
if (!xrayStop.waitForFinished() || !xrayStop.returnValue())
|
||||||
|
qWarning() << "Failed to stop xray";
|
||||||
|
});
|
||||||
|
|
||||||
|
if (m_tun2socksProcess) {
|
||||||
|
m_tun2socksProcess->blockSignals(true);
|
||||||
|
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
m_tun2socksProcess->terminate();
|
||||||
|
auto waitForFinished = m_tun2socksProcess->waitForFinished(1000);
|
||||||
|
if (!waitForFinished.waitForFinished() || !waitForFinished.returnValue()) {
|
||||||
|
qWarning() << "Failed to terminate tun2socks. Killing the process...";
|
||||||
|
m_tun2socksProcess->kill();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// terminate does not do anything useful on Windows
|
||||||
|
// so just kill the process
|
||||||
|
m_tun2socksProcess->kill();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_tun2socksProcess->close();
|
||||||
|
m_tun2socksProcess.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode XrayProtocol::startTun2Socks()
|
||||||
|
{
|
||||||
|
m_tun2socksProcess = IpcClient::CreatePrivilegedProcess();
|
||||||
|
if (!m_tun2socksProcess->waitForSource()) {
|
||||||
|
return ErrorCode::AmneziaServiceConnectionFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_tun2socksProcess->setProgram(PermittedProcess::Tun2Socks);
|
||||||
|
m_tun2socksProcess->setArguments({"-device", QString("tun://%1").arg(tunName), "-proxy", "socks5://127.0.0.1:10808" });
|
||||||
|
|
||||||
|
connect(m_tun2socksProcess.data(), &IpcProcessInterfaceReplica::readyReadStandardOutput, this, [this]() {
|
||||||
|
auto readAllStandardOutput = m_tun2socksProcess->readAllStandardOutput();
|
||||||
|
if (!readAllStandardOutput.waitForFinished()) {
|
||||||
|
qWarning() << "Failed to read output from tun2socks";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString line = readAllStandardOutput.returnValue();
|
||||||
|
|
||||||
|
if (!line.contains("[TCP]") && !line.contains("[UDP]"))
|
||||||
|
qDebug() << "[tun2socks]:" << line;
|
||||||
|
|
||||||
|
if (line.contains("[STACK] tun://") && line.contains("<-> socks5://127.0.0.1")) {
|
||||||
|
disconnect(m_tun2socksProcess.data(), &IpcProcessInterfaceReplica::readyReadStandardOutput, this, nullptr);
|
||||||
|
|
||||||
|
if (ErrorCode res = setupRouting(); res != ErrorCode::NoError) {
|
||||||
|
stop();
|
||||||
|
setLastError(res);
|
||||||
|
} else {
|
||||||
|
setConnectionState(Vpn::ConnectionState::Connected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
connect(m_tun2socksProcess.data(), &IpcProcessInterfaceReplica::finished, this, [this](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||||
|
if (exitStatus == QProcess::ExitStatus::CrashExit) {
|
||||||
|
qCritical() << "Tun2socks process crashed!";
|
||||||
|
} else {
|
||||||
|
qCritical() << QString("Tun2socks process was closed with %1 exit code").arg(exitCode);
|
||||||
|
}
|
||||||
|
stop();
|
||||||
|
setLastError(ErrorCode::Tun2SockExecutableCrashed);
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
m_tun2socksProcess->start();
|
||||||
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode XrayProtocol::setupRouting() {
|
ErrorCode XrayProtocol::setupRouting() {
|
||||||
return IpcClient::withInterface([this](QSharedPointer<IpcInterfaceReplica> iface) -> ErrorCode {
|
return IpcClient::withInterface([this](QSharedPointer<IpcInterfaceReplica> iface) -> ErrorCode {
|
||||||
QList<QHostAddress> dnsAddr;
|
#ifdef Q_OS_WIN
|
||||||
|
const int inetAdapterIndex = NetworkUtilities::AdapterIndexTo(QHostAddress(m_remoteAddress));
|
||||||
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
|
#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) {
|
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" };
|
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);
|
auto routeAddList = iface->routeAddList(m_vpnGateway, subnets);
|
||||||
if (!routeAddList.waitForFinished(1000) || routeAddList.returnValue() != subnets.count()) {
|
if (!routeAddList.waitForFinished() || routeAddList.returnValue() != subnets.count()) {
|
||||||
qWarning() << "Failed to set routes for TUN";
|
qCritical() << "Failed to set routes for TUN";
|
||||||
return ErrorCode::InternalError;
|
return ErrorCode::InternalError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto StopRoutingIpv6 = iface->StopRoutingIpv6();
|
auto StopRoutingIpv6 = iface->StopRoutingIpv6();
|
||||||
if (!StopRoutingIpv6.waitForFinished(1000) || !StopRoutingIpv6.returnValue()) {
|
if (!StopRoutingIpv6.waitForFinished() || !StopRoutingIpv6.returnValue()) {
|
||||||
qWarning() << "Failed to disable IPv6 routing";
|
qCritical() << "Failed to disable IPv6 routing";
|
||||||
return ErrorCode::InternalError;
|
return ErrorCode::InternalError;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
auto enablePeerTraffic = iface->enablePeerTraffic(m_xrayConfig);
|
if (inetAdapterIndex != -1 && vpnAdapterIndex != -1) {
|
||||||
if (!enablePeerTraffic.waitForFinished(5000) || !enablePeerTraffic.returnValue()) {
|
QJsonObject config = m_rawConfig;
|
||||||
qWarning() << "Failed to enable peer traffic";
|
config.insert("inetAdapterIndex", inetAdapterIndex);
|
||||||
return ErrorCode::InternalError;
|
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
|
#endif
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
},
|
},
|
||||||
@@ -100,79 +246,3 @@ ErrorCode XrayProtocol::setupRouting() {
|
|||||||
return ErrorCode::AmneziaServiceConnectionFailed;
|
return ErrorCode::AmneziaServiceConnectionFailed;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode XrayProtocol::startTun2Sock()
|
|
||||||
{
|
|
||||||
m_t2sProcess->start();
|
|
||||||
|
|
||||||
connect(m_t2sProcess.data(), &IpcProcessTun2SocksReplica::stateChanged, this,
|
|
||||||
[&](QProcess::ProcessState newState) { qDebug() << "PrivilegedProcess stateChanged" << newState; });
|
|
||||||
|
|
||||||
connect(m_t2sProcess.data(), &IpcProcessTun2SocksReplica::setConnectionState, this, [&](int vpnState) {
|
|
||||||
QMetaObject::invokeMethod(this, [this, vpnState]() {
|
|
||||||
qDebug() << "PrivilegedProcess setConnectionState " << vpnState;
|
|
||||||
|
|
||||||
if (vpnState == Vpn::ConnectionState::Connected) {
|
|
||||||
setConnectionState(Vpn::ConnectionState::Connecting);
|
|
||||||
|
|
||||||
if (ErrorCode res = setupRouting(); res != ErrorCode::NoError) {
|
|
||||||
stop();
|
|
||||||
setLastError(res);
|
|
||||||
} else
|
|
||||||
setConnectionState(Vpn::ConnectionState::Connected);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vpnState == Vpn::ConnectionState::Disconnected)
|
|
||||||
stop();
|
|
||||||
|
|
||||||
}, Qt::QueuedConnection);
|
|
||||||
});
|
|
||||||
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
void XrayProtocol::stop()
|
|
||||||
{
|
|
||||||
qDebug() << "XrayProtocol::stop()";
|
|
||||||
|
|
||||||
IpcClient::withInterface([](QSharedPointer<IpcInterfaceReplica> iface) {
|
|
||||||
#ifdef AMNEZIA_DESKTOP
|
|
||||||
auto StartRoutingIpv6 = iface->StartRoutingIpv6();
|
|
||||||
if (!StartRoutingIpv6.waitForFinished(1000) || !StartRoutingIpv6.returnValue()) {
|
|
||||||
qWarning() << "XrayProtocol::stop(): Failed to start routing ipv6";
|
|
||||||
}
|
|
||||||
|
|
||||||
auto restoreResolvers = iface->restoreResolvers();
|
|
||||||
if (!restoreResolvers.waitForFinished(1000) || !restoreResolvers.returnValue()) {
|
|
||||||
qWarning() << "XrayProtocol::stop(): Failed to restore resolvers";
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(Q_OS_MACOS)
|
|
||||||
auto deleteTun = iface->deleteTun("tun2");
|
|
||||||
if (!deleteTun.waitForFinished(1000) || !deleteTun.returnValue()) {
|
|
||||||
qWarning() << "XrayProtocol::stop(): Failed to delete tun";
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
iface->xrayStop();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (m_t2sProcess) {
|
|
||||||
m_t2sProcess->stop();
|
|
||||||
QThread::msleep(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XrayProtocol::readXrayConfiguration(const QJsonObject &configuration)
|
|
||||||
{
|
|
||||||
QJsonObject xrayConfiguration = configuration.value(ProtocolProps::key_proto_config_data(Proto::Xray)).toObject();
|
|
||||||
if (xrayConfiguration.isEmpty()) {
|
|
||||||
xrayConfiguration = configuration.value(ProtocolProps::key_proto_config_data(Proto::SSXray)).toObject();
|
|
||||||
}
|
|
||||||
m_xrayConfig = xrayConfiguration;
|
|
||||||
m_routeMode = static_cast<Settings::RouteMode>(configuration.value(amnezia::config_key::splitTunnelType).toInt());
|
|
||||||
m_primaryDNS = configuration.value(amnezia::config_key::dns1).toString();
|
|
||||||
m_secondaryDNS = configuration.value(amnezia::config_key::dns2).toString();
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "core/ipcclient.h"
|
#include "core/ipcclient.h"
|
||||||
#include "vpnprotocol.h"
|
#include "vpnprotocol.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
#include <QtCore/qsharedpointer.h>
|
||||||
|
|
||||||
class XrayProtocol : public VpnProtocol
|
class XrayProtocol : public VpnProtocol
|
||||||
{
|
{
|
||||||
@@ -18,16 +19,14 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
ErrorCode setupRouting();
|
ErrorCode setupRouting();
|
||||||
ErrorCode startTun2Sock();
|
ErrorCode startTun2Socks();
|
||||||
void readXrayConfiguration(const QJsonObject &configuration);
|
|
||||||
|
|
||||||
QJsonObject m_xrayConfig;
|
QJsonObject m_xrayConfig;
|
||||||
Settings::RouteMode m_routeMode;
|
Settings::RouteMode m_routeMode;
|
||||||
QString m_primaryDNS;
|
QList<QHostAddress> m_dnsServers;
|
||||||
QString m_secondaryDNS;
|
QString m_remoteAddress;
|
||||||
#ifndef Q_OS_IOS
|
|
||||||
QSharedPointer<IpcProcessTun2SocksReplica> m_t2sProcess;
|
QSharedPointer<IpcProcessInterfaceReplica> m_tun2socksProcess;
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // XRAYPROTOCOL_H
|
#endif // XRAYPROTOCOL_H
|
||||||
|
|||||||
@@ -99,21 +99,7 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
} else if (state == Vpn::ConnectionState::Error) {
|
||||||
m_pendingNetworkCheck = false;
|
|
||||||
iface->flushDns();
|
iface->flushDns();
|
||||||
|
|
||||||
if (m_settings->isSitesSplitTunnelingEnabled()) {
|
if (m_settings->isSitesSplitTunnelingEnabled()) {
|
||||||
@@ -121,12 +107,6 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
|
|||||||
iface->clearSavedRoutes();
|
iface->clearSavedRoutes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (state == Vpn::ConnectionState::Connecting) {
|
|
||||||
|
|
||||||
} else if (state == Vpn::ConnectionState::Disconnected) {
|
|
||||||
m_pendingNetworkCheck = false;
|
|
||||||
auto result = iface->stopNetworkCheck();
|
|
||||||
result.waitForFinished(3000);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
@@ -273,11 +253,7 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede
|
|||||||
m_remoteAddress = NetworkUtilities::getIPAddress(credentials.hostName);
|
m_remoteAddress = NetworkUtilities::getIPAddress(credentials.hostName);
|
||||||
emit connectionStateChanged(Vpn::ConnectionState::Connecting);
|
emit connectionStateChanged(Vpn::ConnectionState::Connecting);
|
||||||
|
|
||||||
m_pendingNetworkCheck = false;
|
|
||||||
m_vpnConfiguration = vpnConfiguration;
|
m_vpnConfiguration = vpnConfiguration;
|
||||||
m_serverIndex = serverIndex;
|
|
||||||
m_serverCredentials = credentials;
|
|
||||||
m_dockerContainer = container;
|
|
||||||
|
|
||||||
#ifdef AMNEZIA_DESKTOP
|
#ifdef AMNEZIA_DESKTOP
|
||||||
if (m_vpnProtocol) {
|
if (m_vpnProtocol) {
|
||||||
@@ -316,71 +292,12 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede
|
|||||||
emit connectionStateChanged(Vpn::ConnectionState::Error);
|
emit connectionStateChanged(Vpn::ConnectionState::Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VpnConnection::restartConnection()
|
|
||||||
{
|
|
||||||
// Only reconnect if VPN was connected before sleep/network change
|
|
||||||
if (!m_wasConnectedBeforeSleep) {
|
|
||||||
qDebug() << "VPN was not connected before sleep/network change, skipping reconnection";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "VPN was connected before sleep/network change, attempting reconnection";
|
|
||||||
this->disconnectFromVpn();
|
|
||||||
#ifdef Q_OS_LINUX
|
|
||||||
QThread::msleep(5000);
|
|
||||||
#endif
|
|
||||||
this->connectToVpn(m_serverIndex, m_serverCredentials, m_dockerContainer, m_vpnConfiguration);
|
|
||||||
|
|
||||||
// Reset the flag after reconnection attempt
|
|
||||||
m_wasConnectedBeforeSleep = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VpnConnection::createProtocolConnections()
|
void VpnConnection::createProtocolConnections()
|
||||||
{
|
{
|
||||||
connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
|
connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
|
||||||
connect(m_vpnProtocol.data(), SIGNAL(connectionStateChanged(Vpn::ConnectionState)), this,
|
connect(m_vpnProtocol.data(), SIGNAL(connectionStateChanged(Vpn::ConnectionState)), this,
|
||||||
SLOT(onConnectionStateChanged(Vpn::ConnectionState)));
|
SLOT(onConnectionStateChanged(Vpn::ConnectionState)));
|
||||||
connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64)));
|
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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VpnConnection::appendKillSwitchConfig()
|
void VpnConnection::appendKillSwitchConfig()
|
||||||
@@ -491,28 +408,6 @@ void VpnConnection::appendSplitTunnelingConfig()
|
|||||||
.arg(appsRouteMode);
|
.arg(appsRouteMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VpnConnection::startNetworkCheckIfReady()
|
|
||||||
{
|
|
||||||
#ifdef AMNEZIA_DESKTOP
|
|
||||||
if (!m_vpnProtocol || m_dockerContainer == DockerContainer::Ipsec) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString gateway = m_vpnProtocol->vpnGateway();
|
|
||||||
const QString localAddress = m_vpnProtocol->vpnLocalAddress();
|
|
||||||
if (gateway.isEmpty() || localAddress.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
|
|
||||||
QRemoteObjectPendingReply<bool> reply = iface->startNetworkCheck(gateway, localAddress);
|
|
||||||
return reply.waitForFinished(1000) && reply.returnValue();
|
|
||||||
});
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
void VpnConnection::restoreConnection()
|
void VpnConnection::restoreConnection()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ public slots:
|
|||||||
const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration);
|
const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration);
|
||||||
|
|
||||||
void disconnectFromVpn();
|
void disconnectFromVpn();
|
||||||
void restartConnection();
|
|
||||||
|
|
||||||
void addRoutes(const QStringList &ips);
|
void addRoutes(const QStringList &ips);
|
||||||
void deleteRoutes(const QStringList &ips);
|
void deleteRoutes(const QStringList &ips);
|
||||||
@@ -73,8 +72,6 @@ protected slots:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
QSharedPointer<VpnProtocol> m_vpnProtocol;
|
QSharedPointer<VpnProtocol> m_vpnProtocol;
|
||||||
QMetaObject::Connection m_connectionLoseHandle;
|
|
||||||
QMetaObject::Connection m_networkChangeHandle;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Settings> m_settings;
|
std::shared_ptr<Settings> m_settings;
|
||||||
@@ -82,14 +79,6 @@ private:
|
|||||||
QJsonObject m_routeMode;
|
QJsonObject m_routeMode;
|
||||||
QString m_remoteAddress;
|
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
|
// Only for iOS for now, check counters
|
||||||
QTimer m_checkTimer;
|
QTimer m_checkTimer;
|
||||||
|
|
||||||
@@ -104,7 +93,6 @@ private:
|
|||||||
|
|
||||||
void appendSplitTunnelingConfig();
|
void appendSplitTunnelingConfig();
|
||||||
void appendKillSwitchConfig();
|
void appendKillSwitchConfig();
|
||||||
bool startNetworkCheckIfReady();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VPNCONNECTION_H
|
#endif // VPNCONNECTION_H
|
||||||
|
|||||||
66
ipc/ipc.h
66
ipc/ipc.h
@@ -11,6 +11,7 @@
|
|||||||
namespace amnezia {
|
namespace amnezia {
|
||||||
|
|
||||||
enum PermittedProcess {
|
enum PermittedProcess {
|
||||||
|
Invalid,
|
||||||
OpenVPN,
|
OpenVPN,
|
||||||
Wireguard,
|
Wireguard,
|
||||||
Tun2Socks,
|
Tun2Socks,
|
||||||
@@ -19,16 +20,18 @@ enum PermittedProcess {
|
|||||||
|
|
||||||
inline QString permittedProcessPath(PermittedProcess pid)
|
inline QString permittedProcessPath(PermittedProcess pid)
|
||||||
{
|
{
|
||||||
if (pid == PermittedProcess::OpenVPN) {
|
switch (pid) {
|
||||||
return Utils::openVpnExecPath();
|
case PermittedProcess::OpenVPN:
|
||||||
} else if (pid == PermittedProcess::Wireguard) {
|
return Utils::openVpnExecPath();
|
||||||
return Utils::wireguardExecPath();
|
case PermittedProcess::Wireguard:
|
||||||
} else if (pid == PermittedProcess::CertUtil) {
|
return Utils::wireguardExecPath();
|
||||||
return Utils::certUtilPath();
|
case PermittedProcess::CertUtil:
|
||||||
} else if (pid == PermittedProcess::Tun2Socks) {
|
return Utils::certUtilPath();
|
||||||
return Utils::tun2socksPath();
|
case PermittedProcess::Tun2Socks:
|
||||||
|
return Utils::tun2socksPath();
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -48,6 +51,51 @@ inline QString getIpcProcessUrl(int pid) {
|
|||||||
#endif
|
#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
|
} // namespace amnezia
|
||||||
|
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ class IpcInterface
|
|||||||
SLOT( bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) );
|
SLOT( bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) );
|
||||||
SLOT( bool restoreResolvers() );
|
SLOT( bool restoreResolvers() );
|
||||||
|
|
||||||
SLOT(void xrayStart(const QString &config));
|
SLOT(bool xrayStart(const QString &config));
|
||||||
SLOT(void xrayStop());
|
SLOT(bool xrayStop());
|
||||||
|
|
||||||
SLOT( bool startNetworkCheck(const QString& serverIpv4Gateway, const QString& deviceIpv4Address) );
|
SLOT( bool startNetworkCheck(const QString& serverIpv4Gateway, const QString& deviceIpv4Address) );
|
||||||
SLOT( bool stopNetworkCheck() );
|
SLOT( bool stopNetworkCheck() );
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
class IpcProcessInterface
|
class IpcProcessInterface
|
||||||
{
|
{
|
||||||
SLOT( start() );
|
SLOT( start() );
|
||||||
|
SLOT( terminate() );
|
||||||
|
SLOT( kill() );
|
||||||
SLOT( close() );
|
SLOT( close() );
|
||||||
|
|
||||||
SLOT( setArguments(const QStringList &arguments) );
|
SLOT( setArguments(const QStringList &arguments) );
|
||||||
@@ -17,6 +19,11 @@ class IpcProcessInterface
|
|||||||
SLOT( QByteArray readAllStandardError() );
|
SLOT( QByteArray readAllStandardError() );
|
||||||
SLOT( QByteArray readAllStandardOutput() );
|
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( errorOccurred(QProcess::ProcessError error) );
|
||||||
SIGNAL( finished(int exitCode, QProcess::ExitStatus exitStatus) );
|
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);
|
return KillSwitch::instance()->refresh(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IpcServer::xrayStart(const QString& cfg)
|
bool IpcServer::xrayStart(const QString& cfg)
|
||||||
{
|
{
|
||||||
#ifdef MZ_DEBUG
|
#ifdef MZ_DEBUG
|
||||||
qDebug() << "IpcServer::xrayStart";
|
qDebug() << "IpcServer::xrayStart";
|
||||||
@@ -313,7 +313,7 @@ void IpcServer::xrayStart(const QString& cfg)
|
|||||||
return Xray::getInstance().startXray(cfg);
|
return Xray::getInstance().startXray(cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IpcServer::xrayStop()
|
bool IpcServer::xrayStop()
|
||||||
{
|
{
|
||||||
#ifdef MZ_DEBUG
|
#ifdef MZ_DEBUG
|
||||||
qDebug() << "IpcServer::xrayStop";
|
qDebug() << "IpcServer::xrayStop";
|
||||||
|
|||||||
@@ -10,10 +10,8 @@
|
|||||||
|
|
||||||
#include "ipc.h"
|
#include "ipc.h"
|
||||||
#include "ipcserverprocess.h"
|
#include "ipcserverprocess.h"
|
||||||
#include "ipctun2socksprocess.h"
|
|
||||||
|
|
||||||
#include "rep_ipc_interface_source.h"
|
#include "rep_ipc_interface_source.h"
|
||||||
#include "rep_ipc_process_tun2socks_source.h"
|
|
||||||
|
|
||||||
class IpcServer : public IpcInterfaceSource
|
class IpcServer : public IpcInterfaceSource
|
||||||
{
|
{
|
||||||
@@ -44,8 +42,8 @@ public:
|
|||||||
virtual bool refreshKillSwitch( bool enabled ) override;
|
virtual bool refreshKillSwitch( bool enabled ) override;
|
||||||
virtual bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) override;
|
virtual bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) override;
|
||||||
virtual bool restoreResolvers() override;
|
virtual bool restoreResolvers() override;
|
||||||
virtual void xrayStart(const QString& cfg) override;
|
virtual bool xrayStart(const QString& cfg) override;
|
||||||
virtual void xrayStop() override;
|
virtual bool xrayStop() override;
|
||||||
virtual bool startNetworkCheck(const QString& serverIpv4Gateway, const QString& deviceIpv4Address) override;
|
virtual bool startNetworkCheck(const QString& serverIpv4Gateway, const QString& deviceIpv4Address) override;
|
||||||
virtual bool stopNetworkCheck() override;
|
virtual bool stopNetworkCheck() override;
|
||||||
|
|
||||||
@@ -56,12 +54,10 @@ private:
|
|||||||
ProcessDescriptor (QObject *parent = nullptr) {
|
ProcessDescriptor (QObject *parent = nullptr) {
|
||||||
serverNode = QSharedPointer<QRemoteObjectHost>(new QRemoteObjectHost(parent));
|
serverNode = QSharedPointer<QRemoteObjectHost>(new QRemoteObjectHost(parent));
|
||||||
ipcProcess = QSharedPointer<IpcServerProcess>(new IpcServerProcess(parent));
|
ipcProcess = QSharedPointer<IpcServerProcess>(new IpcServerProcess(parent));
|
||||||
tun2socksProcess = QSharedPointer<IpcProcessTun2Socks>(new IpcProcessTun2Socks(parent));
|
|
||||||
localServer = QSharedPointer<QLocalServer>(new QLocalServer(parent));
|
localServer = QSharedPointer<QLocalServer>(new QLocalServer(parent));
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<IpcServerProcess> ipcProcess;
|
QSharedPointer<IpcServerProcess> ipcProcess;
|
||||||
QSharedPointer<IpcProcessTun2Socks> tun2socksProcess;
|
|
||||||
QSharedPointer<QRemoteObjectHost> serverNode;
|
QSharedPointer<QRemoteObjectHost> serverNode;
|
||||||
QSharedPointer<QLocalServer> localServer;
|
QSharedPointer<QLocalServer> localServer;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -40,6 +40,14 @@ void IpcServerProcess::start()
|
|||||||
m_process->waitForStarted();
|
m_process->waitForStarted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IpcServerProcess::terminate() {
|
||||||
|
m_process->terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IpcServerProcess::kill() {
|
||||||
|
m_process->kill();
|
||||||
|
}
|
||||||
|
|
||||||
void IpcServerProcess::close()
|
void IpcServerProcess::close()
|
||||||
{
|
{
|
||||||
m_process->close();
|
m_process->close();
|
||||||
@@ -47,7 +55,7 @@ void IpcServerProcess::close()
|
|||||||
|
|
||||||
void IpcServerProcess::setArguments(const QStringList &arguments)
|
void IpcServerProcess::setArguments(const QStringList &arguments)
|
||||||
{
|
{
|
||||||
m_process->setArguments(arguments);
|
m_process->setArguments(amnezia::sanitizeArguments(m_program, arguments));
|
||||||
}
|
}
|
||||||
|
|
||||||
void IpcServerProcess::setInputChannelMode(QProcess::InputChannelMode mode)
|
void IpcServerProcess::setInputChannelMode(QProcess::InputChannelMode mode)
|
||||||
@@ -69,7 +77,9 @@ void IpcServerProcess::setProcessChannelMode(QProcess::ProcessChannelMode mode)
|
|||||||
|
|
||||||
void IpcServerProcess::setProgram(int programId)
|
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)
|
void IpcServerProcess::setWorkingDirectory(const QString &dir)
|
||||||
@@ -92,4 +102,20 @@ QByteArray IpcServerProcess::readAllStandardOutput()
|
|||||||
return m_process->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
|
#endif
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#ifndef IPCSERVERPROCESS_H
|
#ifndef IPCSERVERPROCESS_H
|
||||||
#define IPCSERVERPROCESS_H
|
#define IPCSERVERPROCESS_H
|
||||||
|
|
||||||
|
#include "ipc.h"
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#ifndef Q_OS_IOS
|
#ifndef Q_OS_IOS
|
||||||
@@ -14,6 +15,8 @@ public:
|
|||||||
virtual ~IpcServerProcess();
|
virtual ~IpcServerProcess();
|
||||||
|
|
||||||
void start() override;
|
void start() override;
|
||||||
|
void terminate() override;
|
||||||
|
void kill() override;
|
||||||
void close() override;
|
void close() override;
|
||||||
|
|
||||||
void setArguments(const QStringList &arguments) override;
|
void setArguments(const QStringList &arguments) override;
|
||||||
@@ -27,9 +30,15 @@ public:
|
|||||||
QByteArray readAllStandardError() override;
|
QByteArray readAllStandardError() override;
|
||||||
QByteArray readAllStandardOutput() override;
|
QByteArray readAllStandardOutput() override;
|
||||||
|
|
||||||
|
bool waitForStarted() override;
|
||||||
|
bool waitForStarted(int msecs) override;
|
||||||
|
bool waitForFinished() override;
|
||||||
|
bool waitForFinished(int msecs) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
amnezia::PermittedProcess m_program = amnezia::PermittedProcess::Invalid;
|
||||||
QSharedPointer<QProcess> m_process;
|
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/ipc.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.h
|
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserverprocess.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}/localserver.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.h
|
${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/router.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}/../../client/core/networkUtilities.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.cpp
|
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserverprocess.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}/localserver.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.cpp
|
${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/main.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_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_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
|
# copy deploy artifacts required to run the application to the debug build folder
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ LocalServer::LocalServer(QObject *parent) : QObject(parent),
|
|||||||
if (!m_isRemotingEnabled) {
|
if (!m_isRemotingEnabled) {
|
||||||
m_isRemotingEnabled = true;
|
m_isRemotingEnabled = true;
|
||||||
m_serverNode.enableRemoting(&m_ipcServer);
|
m_serverNode.enableRemoting(&m_ipcServer);
|
||||||
m_serverNode.enableRemoting(&m_tun2socks);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ public:
|
|||||||
~LocalServer();
|
~LocalServer();
|
||||||
QSharedPointer<QLocalServer> m_server;
|
QSharedPointer<QLocalServer> m_server;
|
||||||
IpcServer m_ipcServer;
|
IpcServer m_ipcServer;
|
||||||
IpcProcessTun2Socks m_tun2socks;
|
|
||||||
QRemoteObjectHost m_serverNode;
|
QRemoteObjectHost m_serverNode;
|
||||||
bool m_isRemotingEnabled = false;
|
bool m_isRemotingEnabled = false;
|
||||||
|
|
||||||
|
|||||||
@@ -318,6 +318,40 @@ bool RouterWin::createTun(const QString &dev, const QString &subnet)
|
|||||||
return false;
|
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;
|
MIB_UNICASTIPADDRESS_ROW row;
|
||||||
InitializeUnicastIpAddressEntry(&row);
|
InitializeUnicastIpAddressEntry(&row);
|
||||||
|
|
||||||
@@ -337,7 +371,13 @@ bool RouterWin::createTun(const QString &dev, const QString &subnet)
|
|||||||
return false;
|
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)
|
void RouterWin::suspendWcmSvc(bool suspend)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void Xray::startXray(const QString &cfg)
|
bool Xray::startXray(const QString &cfg)
|
||||||
{
|
{
|
||||||
qDebug() << "Xray::startXray()";
|
qDebug() << "Xray::startXray()";
|
||||||
|
|
||||||
@@ -40,34 +40,38 @@ void Xray::startXray(const QString &cfg)
|
|||||||
|
|
||||||
if (auto err = amnezia_xray_setsockcallback(ctxSockCallback, this); err != nullptr) {
|
if (auto err = amnezia_xray_setsockcallback(ctxSockCallback, this); err != nullptr) {
|
||||||
qDebug() << "[xray] sockopt failed: " << err;
|
qDebug() << "[xray] sockopt failed: " << err;
|
||||||
free(err);
|
amnezia_xray_free(err);
|
||||||
return;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray bytes = cfg.toUtf8();
|
|
||||||
if (auto err = amnezia_xray_configure(bytes.data()); err != nullptr) {
|
|
||||||
qDebug() << "[xray] configuration failed: " << err;
|
|
||||||
free(err);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
amnezia_xray_setloghandler(ctxLogHandler, this);
|
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) {
|
if (auto err = amnezia_xray_start(); err != nullptr) {
|
||||||
qDebug() << "[xray] failed to start: " << err;
|
qDebug() << "[xray] failed to start: " << err;
|
||||||
free(err);
|
amnezia_xray_free(err);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Xray::stopXray()
|
bool Xray::stopXray()
|
||||||
{
|
{
|
||||||
qDebug() << "Xray::stopXray()";
|
qDebug() << "Xray::stopXray()";
|
||||||
if (auto err = amnezia_xray_stop(); err != nullptr) {
|
if (auto err = amnezia_xray_stop(); err != nullptr) {
|
||||||
qDebug() << "[xray] failed to stop: " << err;
|
qDebug() << "[xray] failed to stop: " << err;
|
||||||
free(err);
|
amnezia_xray_free(err);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Xray::logHandler(char* str)
|
void Xray::logHandler(char* str)
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ public:
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void startXray(const QString& cfg);
|
bool startXray(const QString& cfg);
|
||||||
void stopXray();
|
bool stopXray();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void ctxSockCallback(uintptr_t fd, void* ctx) {
|
static void ctxSockCallback(uintptr_t fd, void* ctx) {
|
||||||
|
|||||||
Reference in New Issue
Block a user