fix: fixed xray config parsing in xrayprotocol (#2557)

This commit is contained in:
vkamn
2026-05-02 12:25:07 +08:00
committed by GitHub
parent b05ee0a654
commit 396ce23228

View File

@@ -1,16 +1,16 @@
#include "xrayProtocol.h" #include "xrayProtocol.h"
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h" #include "core/utils/constants/configKeys.h"
#include "core/utils/ipcClient.h" #include "core/utils/ipcClient.h"
#include "core/utils/networkUtilities.h" #include "core/utils/networkUtilities.h"
#include "core/protocols/protocolUtils.h"
#include "core/utils/serialization/serialization.h" #include "core/utils/serialization/serialization.h"
#include "ipc.h" #include "ipc.h"
#include <QCryptographicHash> #include <QCryptographicHash>
#include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QNetworkInterface> #include <QNetworkInterface>
#include <QJsonDocument>
#include <QtCore/qlogging.h> #include <QtCore/qlogging.h>
#include <QtCore/qobjectdefs.h> #include <QtCore/qobjectdefs.h>
#include <QtCore/qprocess.h> #include <QtCore/qprocess.h>
@@ -43,7 +43,17 @@ XrayProtocol::XrayProtocol(const QJsonObject &configuration, QObject *parent) :
if (xrayConfiguration.isEmpty()) { if (xrayConfiguration.isEmpty()) {
xrayConfiguration = configuration.value(ProtocolUtils::key_proto_config_data(Proto::SSXray)).toObject(); xrayConfiguration = configuration.value(ProtocolUtils::key_proto_config_data(Proto::SSXray)).toObject();
} }
m_xrayConfig = xrayConfiguration;
if (xrayConfiguration.isEmpty()) {
qWarning() << "Xray config wrapper is empty";
m_xrayConfig = {};
}
m_xrayConfig = QJsonDocument::fromJson(xrayConfiguration.value(amnezia::configKey::config).toString().toUtf8()).object();
if (m_xrayConfig.isEmpty()) {
qWarning() << "Xray config string is not a valid JSON object";
m_xrayConfig = {};
}
} }
XrayProtocol::~XrayProtocol() XrayProtocol::~XrayProtocol()
@@ -65,9 +75,9 @@ ErrorCode XrayProtocol::start()
qCritical() << "EnsureInboundAuth failed:" << e.what(); qCritical() << "EnsureInboundAuth failed:" << e.what();
return ErrorCode::InternalError; return ErrorCode::InternalError;
} }
m_socksUser = creds.username; m_socksUser = creds.username;
m_socksPassword = creds.password; m_socksPassword = creds.password;
m_socksPort = creds.port; m_socksPort = creds.port;
const QString xrayConfigStr = QJsonDocument(m_xrayConfig).toJson(QJsonDocument::Compact); const QString xrayConfigStr = QJsonDocument(m_xrayConfig).toJson(QJsonDocument::Compact);
if (xrayConfigStr.isEmpty()) { if (xrayConfigStr.isEmpty()) {
@@ -75,16 +85,16 @@ ErrorCode XrayProtocol::start()
return ErrorCode::XrayExecutableCrashed; return ErrorCode::XrayExecutableCrashed;
} }
return IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) { return IpcClient::withInterface(
auto xrayStart = iface->xrayStart(xrayConfigStr); [&](QSharedPointer<IpcInterfaceReplica> iface) {
if (!xrayStart.waitForFinished() || !xrayStart.returnValue()) { auto xrayStart = iface->xrayStart(xrayConfigStr);
qCritical() << "Failed to start xray"; if (!xrayStart.waitForFinished() || !xrayStart.returnValue()) {
return ErrorCode::XrayExecutableCrashed; qCritical() << "Failed to start xray";
} return ErrorCode::XrayExecutableCrashed;
return startTun2Socks(); }
}, [] () { return startTun2Socks();
return ErrorCode::AmneziaServiceConnectionFailed; },
}); []() { return ErrorCode::AmneziaServiceConnectionFailed; });
} }
void XrayProtocol::stop() void XrayProtocol::stop()
@@ -143,129 +153,135 @@ ErrorCode XrayProtocol::startTun2Socks()
return ErrorCode::AmneziaServiceConnectionFailed; return ErrorCode::AmneziaServiceConnectionFailed;
} }
const QString proxyUrl = QString("socks5://%1:%2@127.0.0.1:%3") const QString proxyUrl = QString("socks5://%1:%2@127.0.0.1:%3").arg(m_socksUser, m_socksPassword, QString::number(m_socksPort));
.arg(m_socksUser, m_socksPassword, QString::number(m_socksPort));
m_tun2socksProcess->setProgram(PermittedProcess::Tun2Socks); m_tun2socksProcess->setProgram(PermittedProcess::Tun2Socks);
m_tun2socksProcess->setArguments({"-device", QString("tun://%1").arg(tunName), "-proxy", proxyUrl}); m_tun2socksProcess->setArguments({ "-device", QString("tun://%1").arg(tunName), "-proxy", proxyUrl });
connect(m_tun2socksProcess.data(), &IpcProcessInterfaceReplica::readyReadStandardOutput, this, [this]() { connect(
auto readAllStandardOutput = m_tun2socksProcess->readAllStandardOutput(); m_tun2socksProcess.data(), &IpcProcessInterfaceReplica::readyReadStandardOutput, this,
if (!readAllStandardOutput.waitForFinished()) { [this]() {
qWarning() << "Failed to read output from tun2socks"; auto readAllStandardOutput = m_tun2socksProcess->readAllStandardOutput();
return; if (!readAllStandardOutput.waitForFinished()) {
} qWarning() << "Failed to read output from tun2socks";
return;
}
const QString line = readAllStandardOutput.returnValue(); const QString line = readAllStandardOutput.returnValue();
if (!line.contains("[TCP]") && !line.contains("[UDP]")) if (!line.contains("[TCP]") && !line.contains("[UDP]"))
qDebug() << "[tun2socks]:" << line; qDebug() << "[tun2socks]:" << line;
if (line.contains("[STACK] tun://") && line.contains("<-> socks5://")) {
disconnect(m_tun2socksProcess.data(), &IpcProcessInterfaceReplica::readyReadStandardOutput, this, nullptr);
if (ErrorCode res = setupRouting(); res != ErrorCode::NoError) { if (line.contains("[STACK] tun://") && line.contains("<-> socks5://")) {
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(); stop();
setLastError(res); setLastError(ErrorCode::Tun2SockExecutableCrashed);
} else { },
setConnectionState(Vpn::ConnectionState::Connected); Qt::QueuedConnection);
}
}
}, 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(); m_tun2socksProcess->start();
return ErrorCode::NoError; 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 {
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
const int inetAdapterIndex = NetworkUtilities::AdapterIndexTo(QHostAddress(m_remoteAddress)); const int inetAdapterIndex = NetworkUtilities::AdapterIndexTo(QHostAddress(m_remoteAddress));
#endif #endif
auto createTun = iface->createTun(tunName, amnezia::protocols::xray::defaultLocalAddr); auto createTun = iface->createTun(tunName, amnezia::protocols::xray::defaultLocalAddr);
if (!createTun.waitForFinished() || !createTun.returnValue()) { if (!createTun.waitForFinished() || !createTun.returnValue()) {
qCritical() << "Failed to assign IP address for TUN"; 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(configKey::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; return ErrorCode::InternalError;
} }
} else
qWarning() << "Failed to get vpnAdapterIndex. Killswitch disabled";
}
if (m_routeMode == amnezia::RouteMode::VpnAllSites) { auto updateResolvers = iface->updateResolvers(tunName, m_dnsServers);
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" }; if (!updateResolvers.waitForFinished() || !updateResolvers.returnValue()) {
qCritical() << "Failed to set DNS resolvers for TUN";
auto routeAddList = iface->routeAddList(m_vpnGateway, subnets); return ErrorCode::InternalError;
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 #ifdef Q_OS_WIN
if (inetAdapterIndex != -1 && vpnAdapterIndex != -1) { int vpnAdapterIndex = -1;
QJsonObject config = m_rawConfig; QList<QNetworkInterface> netInterfaces = QNetworkInterface::allInterfaces();
config.insert("inetAdapterIndex", inetAdapterIndex); for (auto &netInterface : netInterfaces) {
config.insert("vpnAdapterIndex", vpnAdapterIndex); for (auto &address : netInterface.addressEntries()) {
config.insert("vpnGateway", m_vpnGateway); if (m_vpnLocalAddress == address.ip().toString())
config.insert("vpnServer", m_remoteAddress); vpnAdapterIndex = netInterface.index();
}
auto enablePeerTraffic = iface->enablePeerTraffic(config); }
if (!enablePeerTraffic.waitForFinished() || !enablePeerTraffic.returnValue()) { #else
qCritical() << "Failed to enable peer traffic"; static const int vpnAdapterIndex = 0;
return ErrorCode::InternalError;
}
} else
qWarning() << "Failed to get adapter indexes. Split-tunneling disabled";
#endif #endif
return ErrorCode::NoError; const bool killSwitchEnabled = QVariant(m_rawConfig.value(configKey::killSwitchOption).toString()).toBool();
}, if (killSwitchEnabled) {
[] () { if (vpnAdapterIndex != -1) {
return ErrorCode::AmneziaServiceConnectionFailed; 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 == amnezia::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; });
} }