mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-20 02:00:55 +07:00
fix: close traffic leak during seamless tunnel switch
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
#include "vpnTrafficGuard.h"
|
#include "vpnTrafficGuard.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QEventLoop>
|
||||||
#include <QHostInfo>
|
#include <QHostInfo>
|
||||||
#include <QNetworkInterface>
|
#include <QNetworkInterface>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
@@ -484,7 +485,37 @@ void VpnTrafficGuard::swap(Tunnel* from, Tunnel* to)
|
|||||||
if (from) {
|
if (from) {
|
||||||
to->setHandoverIfname(from->ifname());
|
to->setHandoverIfname(from->ifname());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QEventLoop loop;
|
||||||
|
QTimer guard;
|
||||||
|
guard.setSingleShot(true);
|
||||||
|
const auto activatedConn = connect(to, &Tunnel::activated, &loop, &QEventLoop::quit);
|
||||||
|
const auto failedConn = connect(to, &Tunnel::failed, &loop, [&loop](amnezia::ErrorCode) { loop.quit(); });
|
||||||
|
const auto timeoutConn = connect(&guard, &QTimer::timeout, &loop, [&loop]() {
|
||||||
|
qWarning() << "VpnTrafficGuard::swap: timed out waiting for new tunnel activation";
|
||||||
|
loop.quit();
|
||||||
|
});
|
||||||
|
|
||||||
commit(to);
|
commit(to);
|
||||||
|
|
||||||
|
guard.start(5000);
|
||||||
|
loop.exec();
|
||||||
|
guard.stop();
|
||||||
|
|
||||||
|
disconnect(activatedConn);
|
||||||
|
disconnect(failedConn);
|
||||||
|
disconnect(timeoutConn);
|
||||||
|
|
||||||
|
// Service IPCs are processed in order on a single connection. Wait on a trailing
|
||||||
|
// sync request so the killswitch enable from the activation handlers is fully
|
||||||
|
// applied before we deactivate the previous tunnel.
|
||||||
|
IpcClient::withInterface([](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||||
|
auto reply = iface->flushDns();
|
||||||
|
if (!reply.waitForFinished(5000) || !reply.returnValue()) {
|
||||||
|
qWarning() << "VpnTrafficGuard::swap: trailing sync IPC timed out or failed";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (from) {
|
if (from) {
|
||||||
m_allowedEndpoints.removeAll(from->remoteAddress());
|
m_allowedEndpoints.removeAll(from->remoteAddress());
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN
|
||||||
|
|||||||
@@ -577,9 +577,14 @@ void VpnConnection::onTunnelPrepared()
|
|||||||
m_remoteAddress = m_active->remoteAddress();
|
m_remoteAddress = m_active->remoteAddress();
|
||||||
m_trafficGuard->setConfig(m_vpnConfiguration);
|
m_trafficGuard->setConfig(m_vpnConfiguration);
|
||||||
|
|
||||||
m_trafficGuard->swap(oldTunnel, m_active);
|
// Run the swap from a clean event-loop tick so the nested QEventLoop inside
|
||||||
delete oldTunnel;
|
// VpnTrafficGuard::swap does not deadlock the LSC.readData stack frame that
|
||||||
releaseIfname(oldIfname);
|
// delivered Tunnel::prepared.
|
||||||
|
QMetaObject::invokeMethod(this, [this, oldTunnel, oldIfname]() {
|
||||||
|
m_trafficGuard->swap(oldTunnel, m_active);
|
||||||
|
delete oldTunnel;
|
||||||
|
releaseIfname(oldIfname);
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user