mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-24 02:00:24 +07:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 62fa095153 |
@@ -1,6 +1,9 @@
|
||||
#include "updateController.h"
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QFile>
|
||||
#include <QNetworkReply>
|
||||
#include <QProcess>
|
||||
#include <QVersionNumber>
|
||||
#include <QUrl>
|
||||
#include <QJsonDocument>
|
||||
@@ -29,6 +32,17 @@ namespace
|
||||
const QLatin1String kInstallerRemoteFileNamePattern("AmneziaVPN_%1_linux_x64.run");
|
||||
const QString kInstallerLocalPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/AmneziaVPN.run";
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_MACOS) && !defined(MACOS_NE)
|
||||
// Pinned signing identity of release installers. spctl prints the origin as
|
||||
// origin=Developer ID Installer: <Name> (<TEAMID>)
|
||||
// We require this exact substring so a different (even validly notarized)
|
||||
// package is rejected.
|
||||
// TODO(#1): replace with the real release identity before shipping, e.g.
|
||||
// "Developer ID Installer: Amnezia OU (XXXXXXXXXX)".
|
||||
// Until set, macOS update verification fails closed (updates are rejected).
|
||||
const QLatin1String kExpectedMacSigningOrigin("Developer ID Installer: SET_ME_BEFORE_RELEASE");
|
||||
#endif
|
||||
}
|
||||
|
||||
UpdateController::UpdateController(SecureAppSettingsRepository* appSettingsRepository, QObject *parent)
|
||||
@@ -121,6 +135,10 @@ void UpdateController::fetchGatewayUrl()
|
||||
}
|
||||
m_baseUrl = baseUrl;
|
||||
|
||||
// Expected installer SHA-256 for the requesting OS, delivered over the
|
||||
// encrypted gateway channel (used by Windows/Linux verification).
|
||||
m_expectedSha256 = gatewayData.value("sha256").toString().trimmed().toLower();
|
||||
|
||||
fetchVersionInfo();
|
||||
});
|
||||
});
|
||||
@@ -209,6 +227,53 @@ QString UpdateController::composeDownloadUrl() const
|
||||
#endif
|
||||
}
|
||||
|
||||
bool UpdateController::verifySha256(const QByteArray &data) const
|
||||
{
|
||||
if (m_expectedSha256.isEmpty()) {
|
||||
logger.error() << "No expected installer checksum provided by gateway; rejecting installer";
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString actual = QString::fromLatin1(QCryptographicHash::hash(data, QCryptographicHash::Sha256).toHex());
|
||||
if (actual.compare(m_expectedSha256, Qt::CaseInsensitive) != 0) {
|
||||
logger.error() << "Installer checksum mismatch. expected:" << m_expectedSha256 << "actual:" << actual;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(Q_OS_MACOS) && !defined(MACOS_NE)
|
||||
bool UpdateController::verifyMacInstallerSignature(const QString &installerPath) const
|
||||
{
|
||||
// Gatekeeper assessment for installer packages: the .pkg must be signed AND
|
||||
// notarized under our Apple Developer ID. spctl writes its verdict to stderr.
|
||||
QProcess spctl;
|
||||
spctl.start(QStringLiteral("/usr/sbin/spctl"),
|
||||
{ QStringLiteral("--assess"), QStringLiteral("--type"), QStringLiteral("install"),
|
||||
QStringLiteral("-vv"), installerPath });
|
||||
if (!spctl.waitForFinished(15000)) {
|
||||
logger.error() << "spctl assessment did not finish in time";
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString output = QString::fromUtf8(spctl.readAllStandardError()) + QString::fromUtf8(spctl.readAllStandardOutput());
|
||||
|
||||
if (spctl.exitStatus() != QProcess::NormalExit || spctl.exitCode() != 0) {
|
||||
logger.error() << "spctl rejected the installer:" << output.trimmed();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pin the signing identity so a different (even validly notarized) package is rejected.
|
||||
if (!output.contains(kExpectedMacSigningOrigin)) {
|
||||
logger.error() << "Installer signed by an unexpected identity:" << output.trimmed();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void UpdateController::runInstaller()
|
||||
{
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||
@@ -225,6 +290,8 @@ void UpdateController::runInstaller()
|
||||
|
||||
QObject::connect(reply, &QNetworkReply::finished, [this, reply]() {
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
const QByteArray data = reply->readAll();
|
||||
|
||||
QFile file(kInstallerLocalPath);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
logger.error() << "Failed to open installer file for writing:" << kInstallerLocalPath << "Error:" << file.errorString();
|
||||
@@ -232,7 +299,7 @@ void UpdateController::runInstaller()
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.write(reply->readAll()) == -1) {
|
||||
if (file.write(data) == -1) {
|
||||
logger.error() << "Failed to write installer data to file:" << kInstallerLocalPath << "Error:" << file.errorString();
|
||||
file.close();
|
||||
reply->deleteLater();
|
||||
@@ -241,6 +308,25 @@ void UpdateController::runInstaller()
|
||||
|
||||
file.close();
|
||||
|
||||
// Fail-closed: never launch an installer whose authenticity we could not verify.
|
||||
#if defined(Q_OS_MACOS) && !defined(MACOS_NE)
|
||||
if (!verifyMacInstallerSignature(kInstallerLocalPath)) {
|
||||
logger.error() << "Installer signature verification failed; refusing to launch";
|
||||
QFile::remove(kInstallerLocalPath);
|
||||
emit installerVerificationFailed(tr("Update verification failed: the installer signature is invalid. The update was not launched."));
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (!verifySha256(data)) {
|
||||
logger.error() << "Installer checksum verification failed; refusing to launch";
|
||||
QFile::remove(kInstallerLocalPath);
|
||||
emit installerVerificationFailed(tr("Update verification failed: the installer checksum does not match. The update was not launched."));
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_WINDOWS)
|
||||
runWindowsInstaller(kInstallerLocalPath);
|
||||
#elif defined(Q_OS_MACOS) && !defined(MACOS_NE)
|
||||
|
||||
@@ -23,6 +23,7 @@ public slots:
|
||||
|
||||
signals:
|
||||
void updateFound();
|
||||
void installerVerificationFailed(const QString &reason);
|
||||
|
||||
private:
|
||||
void finishUpdateCheck();
|
||||
@@ -43,11 +44,18 @@ private:
|
||||
QString m_version;
|
||||
QString m_releaseDate;
|
||||
QString m_downloadUrl;
|
||||
QString m_expectedSha256;
|
||||
bool m_updateCheckRunning = false;
|
||||
|
||||
// Verify the downloaded installer before launching it (fail-closed):
|
||||
// Windows/Linux compare a SHA-256 delivered over the encrypted gateway channel.
|
||||
bool verifySha256(const QByteArray &data) const;
|
||||
|
||||
#if defined(Q_OS_WINDOWS)
|
||||
int runWindowsInstaller(const QString &installerPath);
|
||||
#elif defined(Q_OS_MACOS)
|
||||
// macOS verifies the .pkg Gatekeeper assessment (notarized + pinned Developer ID).
|
||||
bool verifyMacInstallerSignature(const QString &installerPath) const;
|
||||
int runMacInstaller(const QString &installerPath);
|
||||
#elif defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
|
||||
int runLinuxInstaller(const QString &installerPath);
|
||||
|
||||
@@ -9,13 +9,10 @@
|
||||
#include "ipc.h"
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QTimer>
|
||||
#include <QJsonObject>
|
||||
#include <QNetworkInterface>
|
||||
#include <QNetworkProxy>
|
||||
#include <QTcpSocket>
|
||||
#include <QtCore/qlogging.h>
|
||||
#include <QtCore/qobjectdefs.h>
|
||||
#include <QtCore/qprocess.h>
|
||||
@@ -59,28 +56,6 @@ XrayProtocol::XrayProtocol(const QJsonObject &configuration, QObject *parent) :
|
||||
qWarning() << "Xray config string is not a valid JSON object";
|
||||
m_xrayConfig = {};
|
||||
}
|
||||
|
||||
m_serverPort = extractServerPort();
|
||||
}
|
||||
|
||||
int XrayProtocol::extractServerPort() const
|
||||
{
|
||||
const QJsonArray outbounds = m_xrayConfig.value(amnezia::protocols::xray::outbounds).toArray();
|
||||
if (outbounds.isEmpty())
|
||||
return 0;
|
||||
|
||||
const QJsonObject settings = outbounds.first().toObject().value(amnezia::protocols::xray::settings).toObject();
|
||||
|
||||
QJsonArray servers;
|
||||
if (settings.contains(amnezia::protocols::xray::vnext))
|
||||
servers = settings.value(amnezia::protocols::xray::vnext).toArray();
|
||||
else if (settings.contains(amnezia::protocols::xray::servers))
|
||||
servers = settings.value(amnezia::protocols::xray::servers).toArray();
|
||||
|
||||
if (servers.isEmpty())
|
||||
return 0;
|
||||
|
||||
return servers.first().toObject().value(amnezia::protocols::xray::port).toInt();
|
||||
}
|
||||
|
||||
XrayProtocol::~XrayProtocol()
|
||||
@@ -93,13 +68,6 @@ ErrorCode XrayProtocol::start()
|
||||
{
|
||||
qDebug() << "XrayProtocol::start()";
|
||||
|
||||
m_connectivityProbeStarted = false;
|
||||
|
||||
if (!probeServerReachable()) {
|
||||
qCritical() << "XrayProtocol: VPN server" << m_remoteAddress << "is unreachable";
|
||||
return ErrorCode::XrayServerUnreachable;
|
||||
}
|
||||
|
||||
// Inject SOCKS5 auth into the inbound before starting xray.
|
||||
// Re-uses existing credentials if the config already has them (e.g. imported config).
|
||||
amnezia::serialization::inbounds::InboundCredentials creds;
|
||||
@@ -136,50 +104,22 @@ ErrorCode XrayProtocol::start()
|
||||
qDebug() << "XrayProtocol: patched legacy inbound listen address to 127.0.0.1";
|
||||
}
|
||||
|
||||
startTimeoutTimer();
|
||||
|
||||
return IpcClient::withInterface(
|
||||
[&](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||
auto xrayStart = iface->xrayStart(xrayConfigStr);
|
||||
if (!xrayStart.waitForFinished() || !xrayStart.returnValue()) {
|
||||
qCritical() << "Failed to start xray";
|
||||
stopTimeoutTimer();
|
||||
return ErrorCode::XrayExecutableCrashed;
|
||||
}
|
||||
return startTun2Socks();
|
||||
},
|
||||
[this]() {
|
||||
stopTimeoutTimer();
|
||||
return ErrorCode::AmneziaServiceConnectionFailed;
|
||||
});
|
||||
[]() { return ErrorCode::AmneziaServiceConnectionFailed; });
|
||||
}
|
||||
|
||||
void XrayProtocol::stop()
|
||||
{
|
||||
qDebug() << "XrayProtocol::stop()";
|
||||
|
||||
stopTimeoutTimer();
|
||||
stopLivenessMonitor();
|
||||
|
||||
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();
|
||||
m_tun2socksProcess->waitForFinished(1000);
|
||||
}
|
||||
#else
|
||||
m_tun2socksProcess->kill();
|
||||
#endif
|
||||
|
||||
m_tun2socksProcess->close();
|
||||
m_tun2socksProcess.reset();
|
||||
}
|
||||
|
||||
IpcClient::withInterface([](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||
auto disableKillSwitch = iface->disableKillSwitch();
|
||||
if (!disableKillSwitch.waitForFinished() || !disableKillSwitch.returnValue())
|
||||
@@ -202,13 +142,31 @@ void XrayProtocol::stop()
|
||||
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_tunResourceBusy = false;
|
||||
|
||||
m_tun2socksProcess = IpcClient::CreatePrivilegedProcess();
|
||||
if (!m_tun2socksProcess->waitForSource()) {
|
||||
return ErrorCode::AmneziaServiceConnectionFailed;
|
||||
@@ -233,31 +191,15 @@ ErrorCode XrayProtocol::startTun2Socks()
|
||||
if (!line.contains("[TCP]") && !line.contains("[UDP]"))
|
||||
qDebug() << "[tun2socks]:" << line;
|
||||
|
||||
if (line.contains("resource busy"))
|
||||
m_tunResourceBusy = true;
|
||||
|
||||
if (line.contains("[STACK] tun://") && line.contains("<-> socks5://") && !m_connectivityProbeStarted) {
|
||||
m_connectivityProbeStarted = true;
|
||||
if (line.contains("[STACK] tun://") && line.contains("<-> socks5://")) {
|
||||
disconnect(m_tun2socksProcess.data(), &IpcProcessInterfaceReplica::readyReadStandardOutput, this, nullptr);
|
||||
disconnect(m_tun2socksProcess.data(), &IpcProcessInterfaceReplica::readyReadStandardError, this, nullptr);
|
||||
|
||||
runConnectivityProbe([this](bool ok) {
|
||||
if (!ok) {
|
||||
qCritical() << "Xray connectivity probe failed: no traffic flows through the tunnel";
|
||||
stop();
|
||||
setLastError(ErrorCode::XrayConnectivityCheckFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ErrorCode res = setupRouting(); res != ErrorCode::NoError) {
|
||||
stop();
|
||||
setLastError(res);
|
||||
} else {
|
||||
stopTimeoutTimer();
|
||||
setConnectionState(Vpn::ConnectionState::Connected);
|
||||
startLivenessMonitor();
|
||||
}
|
||||
});
|
||||
if (ErrorCode res = setupRouting(); res != ErrorCode::NoError) {
|
||||
stop();
|
||||
setLastError(res);
|
||||
} else {
|
||||
setConnectionState(Vpn::ConnectionState::Connected);
|
||||
}
|
||||
}
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
@@ -265,7 +207,15 @@ ErrorCode XrayProtocol::startTun2Socks()
|
||||
connect(
|
||||
m_tun2socksProcess.data(), &IpcProcessInterfaceReplica::finished, this,
|
||||
[this](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
const bool resourceBusy = m_tunResourceBusy;
|
||||
// Check stdout for "resource busy" — the TUN device was not yet released
|
||||
// by the previous tun2socks instance. Retry after a short delay.
|
||||
bool resourceBusy = false;
|
||||
if (m_tun2socksProcess) {
|
||||
auto readOut = m_tun2socksProcess->readAllStandardOutput();
|
||||
if (readOut.waitForFinished()) {
|
||||
resourceBusy = readOut.returnValue().contains("resource busy");
|
||||
}
|
||||
}
|
||||
|
||||
if (resourceBusy && m_tun2socksRetryCount < maxTun2SocksRetries) {
|
||||
m_tun2socksRetryCount++;
|
||||
@@ -381,98 +331,3 @@ ErrorCode XrayProtocol::setupRouting()
|
||||
},
|
||||
[]() { return ErrorCode::AmneziaServiceConnectionFailed; });
|
||||
}
|
||||
|
||||
bool XrayProtocol::probeServerReachable()
|
||||
{
|
||||
if (m_remoteAddress.isEmpty() || m_serverPort <= 0) {
|
||||
qWarning() << "XrayProtocol: skipping server reachability probe (address/port unknown)";
|
||||
return true;
|
||||
}
|
||||
|
||||
QTcpSocket sock;
|
||||
sock.connectToHost(m_remoteAddress, static_cast<quint16>(m_serverPort));
|
||||
const bool ok = sock.waitForConnected(m_serverProbeTimeoutMs);
|
||||
if (!ok) {
|
||||
qWarning() << "XrayProtocol: server" << m_remoteAddress << ":" << m_serverPort
|
||||
<< "unreachable:" << sock.errorString();
|
||||
}
|
||||
sock.abort();
|
||||
return ok;
|
||||
}
|
||||
|
||||
void XrayProtocol::runConnectivityProbe(std::function<void(bool)> onResult)
|
||||
{
|
||||
if (m_remoteAddress.isEmpty() || m_serverPort <= 0) {
|
||||
qWarning() << "XrayProtocol: connectivity probe skipped (server address/port unknown)";
|
||||
onResult(true);
|
||||
return;
|
||||
}
|
||||
|
||||
auto *sock = new QTcpSocket(this);
|
||||
|
||||
QNetworkProxy proxy(QNetworkProxy::Socks5Proxy, QStringLiteral("127.0.0.1"),
|
||||
static_cast<quint16>(m_socksPort), m_socksUser, m_socksPassword);
|
||||
proxy.setCapabilities(QNetworkProxy::TunnelingCapability | QNetworkProxy::HostNameLookupCapability);
|
||||
sock->setProxy(proxy);
|
||||
|
||||
auto *timeout = new QTimer(this);
|
||||
timeout->setSingleShot(true);
|
||||
|
||||
auto done = QSharedPointer<bool>::create(false);
|
||||
|
||||
auto finish = [=](bool ok) {
|
||||
if (*done)
|
||||
return;
|
||||
*done = true;
|
||||
timeout->stop();
|
||||
timeout->deleteLater();
|
||||
sock->abort();
|
||||
sock->deleteLater();
|
||||
onResult(ok);
|
||||
};
|
||||
|
||||
connect(sock, &QTcpSocket::connected, this, [=]() { finish(true); });
|
||||
connect(sock, &QAbstractSocket::errorOccurred, this, [=](QAbstractSocket::SocketError) { finish(false); });
|
||||
connect(timeout, &QTimer::timeout, this, [=]() { finish(false); });
|
||||
|
||||
timeout->start(m_connectivityProbeTimeoutMs);
|
||||
sock->connectToHost(m_remoteAddress, static_cast<quint16>(m_serverPort));
|
||||
}
|
||||
|
||||
void XrayProtocol::startLivenessMonitor()
|
||||
{
|
||||
if (!m_livenessTimer) {
|
||||
m_livenessTimer = new QTimer(this);
|
||||
connect(m_livenessTimer, &QTimer::timeout, this, [this]() {
|
||||
if (connectionState() != Vpn::ConnectionState::Connected)
|
||||
return;
|
||||
|
||||
runConnectivityProbe([this](bool ok) {
|
||||
if (connectionState() != Vpn::ConnectionState::Connected)
|
||||
return;
|
||||
|
||||
if (ok) {
|
||||
m_livenessFailures = 0;
|
||||
} else if (++m_livenessFailures >= m_maxLivenessFailures) {
|
||||
qCritical() << "XrayProtocol: liveness check failed" << m_livenessFailures
|
||||
<< "times in a row, the tunnel is dead";
|
||||
stop();
|
||||
setLastError(ErrorCode::XrayConnectionLost);
|
||||
} else {
|
||||
qWarning() << "XrayProtocol: liveness check failed (" << m_livenessFailures << "/"
|
||||
<< m_maxLivenessFailures << ")";
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
m_livenessFailures = 0;
|
||||
m_livenessTimer->start(m_livenessIntervalMs);
|
||||
}
|
||||
|
||||
void XrayProtocol::stopLivenessMonitor()
|
||||
{
|
||||
if (m_livenessTimer)
|
||||
m_livenessTimer->stop();
|
||||
m_livenessFailures = 0;
|
||||
}
|
||||
|
||||
@@ -6,16 +6,12 @@
|
||||
#include <QHostAddress>
|
||||
#include <QList>
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "core/utils/errorCodes.h"
|
||||
#include "core/utils/routeModes.h"
|
||||
#include "core/utils/commonStructs.h"
|
||||
#include "core/utils/ipcClient.h"
|
||||
#include "vpnProtocol.h"
|
||||
|
||||
class QTimer;
|
||||
|
||||
class XrayProtocol : public VpnProtocol
|
||||
{
|
||||
public:
|
||||
@@ -29,17 +25,10 @@ private:
|
||||
ErrorCode setupRouting();
|
||||
ErrorCode startTun2Socks();
|
||||
|
||||
bool probeServerReachable();
|
||||
void runConnectivityProbe(std::function<void(bool)> onResult);
|
||||
void startLivenessMonitor();
|
||||
void stopLivenessMonitor();
|
||||
int extractServerPort() const;
|
||||
|
||||
QJsonObject m_xrayConfig;
|
||||
amnezia::RouteMode m_routeMode;
|
||||
QList<QHostAddress> m_dnsServers;
|
||||
QString m_remoteAddress;
|
||||
int m_serverPort = 0;
|
||||
|
||||
QString m_socksUser;
|
||||
QString m_socksPassword;
|
||||
@@ -49,22 +38,6 @@ private:
|
||||
int m_tun2socksRetryCount = 0;
|
||||
static constexpr int maxTun2SocksRetries = 5;
|
||||
static constexpr int tun2socksRetryDelayMs = 400;
|
||||
|
||||
bool m_connectivityProbeStarted = false;
|
||||
bool m_tunResourceBusy = false;
|
||||
|
||||
QTimer *m_livenessTimer = nullptr;
|
||||
int m_livenessFailures = 0;
|
||||
|
||||
static constexpr int defaultServerProbeTimeoutMs = 5000;
|
||||
static constexpr int defaultConnectivityProbeTimeoutMs = 7000;
|
||||
static constexpr int defaultLivenessIntervalMs = 15000;
|
||||
static constexpr int defaultMaxLivenessFailures = 3;
|
||||
|
||||
int m_serverProbeTimeoutMs = defaultServerProbeTimeoutMs;
|
||||
int m_connectivityProbeTimeoutMs = defaultConnectivityProbeTimeoutMs;
|
||||
int m_livenessIntervalMs = defaultLivenessIntervalMs;
|
||||
int m_maxLivenessFailures = defaultMaxLivenessFailures;
|
||||
};
|
||||
|
||||
#endif // XRAYPROTOCOL_H
|
||||
|
||||
@@ -71,9 +71,6 @@ namespace amnezia
|
||||
OpenSslFailed = 800,
|
||||
XrayExecutableCrashed = 803,
|
||||
Tun2SockExecutableCrashed = 804,
|
||||
XrayServerUnreachable = 805,
|
||||
XrayConnectivityCheckFailed = 806,
|
||||
XrayConnectionLost = 807,
|
||||
|
||||
// import and install errors
|
||||
ImportInvalidConfigError = 900,
|
||||
|
||||
@@ -59,9 +59,6 @@ QString errorString(ErrorCode code) {
|
||||
case (ErrorCode::OpenVpnExecutableMissing): errorMessage = QObject::tr("OpenVPN executable missing"); break;
|
||||
case (ErrorCode::AmneziaServiceConnectionFailed): errorMessage = QObject::tr("Amnezia helper service error"); break;
|
||||
case (ErrorCode::OpenSslFailed): errorMessage = QObject::tr("OpenSSL failed"); break;
|
||||
case (ErrorCode::XrayServerUnreachable): errorMessage = QObject::tr("Can't connect: the VPN server is unreachable"); break;
|
||||
case (ErrorCode::XrayConnectivityCheckFailed): errorMessage = QObject::tr("Can't connect: no internet traffic flows through the tunnel"); break;
|
||||
case (ErrorCode::XrayConnectionLost): errorMessage = QObject::tr("Connection lost: traffic stopped flowing through the tunnel"); break;
|
||||
|
||||
// VPN errors
|
||||
case (ErrorCode::OpenVpnAdaptersInUseError): errorMessage = QObject::tr("Can't connect: another VPN connection is active"); break;
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <QFileInfo>
|
||||
#include <QLocalSocket>
|
||||
|
||||
#include "daemon.h"
|
||||
#include "daemonlocalserverconnection.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
@@ -59,11 +58,8 @@ bool DaemonLocalServer::initialize() {
|
||||
|
||||
DaemonLocalServerConnection* connection =
|
||||
new DaemonLocalServerConnection(&m_server, socket);
|
||||
connect(socket, &QLocalSocket::disconnected, connection, [connection]() {
|
||||
logger.debug() << "Client connection dropped, deactivating daemon";
|
||||
Daemon::instance()->deactivate(true);
|
||||
connection->deleteLater();
|
||||
});
|
||||
connect(socket, &QLocalSocket::disconnected, connection,
|
||||
&DaemonLocalServerConnection::deleteLater);
|
||||
});
|
||||
|
||||
return true;
|
||||
|
||||
@@ -5,6 +5,8 @@ UpdateUiController::UpdateUiController(UpdateController* updateController, QObje
|
||||
{
|
||||
if (m_updateController) {
|
||||
connect(m_updateController, &UpdateController::updateFound, this, &UpdateUiController::updateFound);
|
||||
connect(m_updateController, &UpdateController::installerVerificationFailed, this,
|
||||
&UpdateUiController::installerVerificationFailed);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ public slots:
|
||||
|
||||
signals:
|
||||
void updateFound();
|
||||
void installerVerificationFailed(const QString &message);
|
||||
|
||||
private:
|
||||
UpdateController* m_updateController;
|
||||
|
||||
@@ -454,4 +454,12 @@ Window {
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: UpdateController
|
||||
function onInstallerVerificationFailed(message) {
|
||||
PageController.showBusyIndicator(false)
|
||||
PageController.showNotificationMessage(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,42 +28,6 @@ IpcServer::IpcServer(QObject *parent) : IpcInterfaceSource(parent)
|
||||
connect(&m_pingHelper, &PingHelper::connectionLose, this, &IpcServer::connectionLose);
|
||||
}
|
||||
|
||||
IpcServer::~IpcServer()
|
||||
{
|
||||
}
|
||||
|
||||
void IpcServer::resetServiceState()
|
||||
{
|
||||
qDebug() << "IpcServer::resetServiceState — tearing down active VPN state";
|
||||
|
||||
Xray::getInstance().stopXray();
|
||||
|
||||
for (auto it = m_processes.cbegin(); it != m_processes.cend(); ++it) {
|
||||
const ProcessDescriptor &pd = it.value();
|
||||
if (!pd.ipcProcess)
|
||||
continue;
|
||||
pd.ipcProcess->terminate();
|
||||
if (!pd.ipcProcess->waitForFinished(1000)) {
|
||||
pd.ipcProcess->kill();
|
||||
pd.ipcProcess->waitForFinished(1000);
|
||||
}
|
||||
pd.ipcProcess->close();
|
||||
}
|
||||
m_processes.clear();
|
||||
|
||||
Utils::killProcessByName(Utils::tun2socksPath());
|
||||
Utils::killProcessByName(Utils::openVpnExecPath());
|
||||
|
||||
KillSwitch::instance()->disableKillSwitch();
|
||||
|
||||
Router::restoreResolvers();
|
||||
Router::clearSavedRoutes();
|
||||
Router::StartRoutingIpv6();
|
||||
Router::flushDns();
|
||||
|
||||
m_pingHelper.stop();
|
||||
}
|
||||
|
||||
int IpcServer::createPrivilegedProcess()
|
||||
{
|
||||
#ifdef MZ_DEBUG
|
||||
|
||||
@@ -17,10 +17,6 @@ class IpcServer : public IpcInterfaceSource
|
||||
{
|
||||
public:
|
||||
explicit IpcServer(QObject *parent = nullptr);
|
||||
virtual ~IpcServer();
|
||||
|
||||
void resetServiceState();
|
||||
|
||||
virtual int createPrivilegedProcess() override;
|
||||
|
||||
virtual int routeAddList(const QString &gw, const QStringList &ips) override;
|
||||
|
||||
@@ -35,20 +35,7 @@ LocalServer::LocalServer(QObject *parent) : QObject(parent),
|
||||
|
||||
QObject::connect(m_server.data(), &QLocalServer::newConnection, this, [this]() {
|
||||
qDebug() << "LocalServer new connection";
|
||||
|
||||
QLocalSocket *socket = m_server->nextPendingConnection();
|
||||
if (!socket)
|
||||
return;
|
||||
|
||||
m_activeClientSocket = socket;
|
||||
|
||||
QObject::connect(socket, &QLocalSocket::disconnected, this, [this, socket]() {
|
||||
qDebug() << "LocalServer: client disconnected";
|
||||
if (m_activeClientSocket == socket)
|
||||
m_ipcServer.resetServiceState();
|
||||
});
|
||||
|
||||
m_serverNode.addHostSideConnection(socket);
|
||||
m_serverNode.addHostSideConnection(m_server->nextPendingConnection());
|
||||
|
||||
if (!m_isRemotingEnabled) {
|
||||
m_isRemotingEnabled = true;
|
||||
|
||||
@@ -41,8 +41,6 @@ public:
|
||||
QRemoteObjectHost m_serverNode;
|
||||
bool m_isRemotingEnabled = false;
|
||||
|
||||
QPointer<QLocalSocket> m_activeClientSocket;
|
||||
|
||||
NetworkWatcher m_networkWatcher;
|
||||
#ifdef Q_OS_LINUX
|
||||
DaemonLocalServer server{qApp};
|
||||
|
||||
Reference in New Issue
Block a user