mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-19 02:00:45 +07:00
fix: close traffic leak during seamless tunnel switch
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#include "vpnTrafficGuard.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QEventLoop>
|
||||
#include <QHostInfo>
|
||||
#include <QNetworkInterface>
|
||||
#include <QPointer>
|
||||
@@ -484,7 +485,37 @@ void VpnTrafficGuard::swap(Tunnel* from, Tunnel* to)
|
||||
if (from) {
|
||||
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);
|
||||
|
||||
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) {
|
||||
m_allowedEndpoints.removeAll(from->remoteAddress());
|
||||
#ifndef Q_OS_WIN
|
||||
|
||||
@@ -577,9 +577,14 @@ void VpnConnection::onTunnelPrepared()
|
||||
m_remoteAddress = m_active->remoteAddress();
|
||||
m_trafficGuard->setConfig(m_vpnConfiguration);
|
||||
|
||||
m_trafficGuard->swap(oldTunnel, m_active);
|
||||
delete oldTunnel;
|
||||
releaseIfname(oldIfname);
|
||||
// Run the swap from a clean event-loop tick so the nested QEventLoop inside
|
||||
// VpnTrafficGuard::swap does not deadlock the LSC.readData stack frame that
|
||||
// delivered Tunnel::prepared.
|
||||
QMetaObject::invokeMethod(this, [this, oldTunnel, oldIfname]() {
|
||||
m_trafficGuard->swap(oldTunnel, m_active);
|
||||
delete oldTunnel;
|
||||
releaseIfname(oldIfname);
|
||||
}, Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user