feat: decouple routing/killswitch from protocol handling

This commit is contained in:
cd-amn
2026-04-29 11:38:48 +04:00
parent 7c8a3a50a1
commit 5af1edbc54
9 changed files with 315 additions and 254 deletions

View File

@@ -64,6 +64,7 @@ set(HEADERS ${HEADERS}
${CLIENT_ROOT_DIR}/core/utils/utilities.h
${CLIENT_ROOT_DIR}/core/utils/managementServer.h
${CLIENT_ROOT_DIR}/core/utils/constants.h
${CLIENT_ROOT_DIR}/core/vpnTrafficGuard.h
)
# Mozilla headres
@@ -141,6 +142,7 @@ set(SOURCES ${SOURCES}
${CLIENT_ROOT_DIR}/core/utils/osSignalHandler.cpp
${CLIENT_ROOT_DIR}/core/utils/utilities.cpp
${CLIENT_ROOT_DIR}/core/utils/managementServer.cpp
${CLIENT_ROOT_DIR}/core/vpnTrafficGuard.cpp
)
# Mozilla sources

View File

@@ -56,15 +56,6 @@ void OpenVpnProtocol::stop()
m_managementServer.stop();
}
#if defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
IpcClient::withInterface([](QSharedPointer<IpcInterfaceReplica> iface) {
QRemoteObjectPendingReply<bool> reply = iface->disableKillSwitch();
if (!reply.waitForFinished(1000) && !reply.returnValue()) {
qWarning() << "OpenVpnProtocol::stop(): Failed to disable killswitch";
}
});
#endif
setConnectionState(Vpn::ConnectionState::Disconnected);
}
@@ -180,20 +171,6 @@ ErrorCode OpenVpnProtocol::start()
return lastError();
}
#ifdef AMNEZIA_DESKTOP
const ErrorCode res = IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
QString ip = NetworkUtilities::getIPAddress(m_configData.value(amnezia::configKey::hostName).toString());
QRemoteObjectPendingReply<bool> reply = iface->addKillSwitchAllowedRange(QStringList(ip));
if (!reply.waitForFinished(1000) || !reply.returnValue()) {
return ErrorCode::AmneziaServiceConnectionFailed;
}
return ErrorCode::NoError;
});
if (res != ErrorCode::NoError) {
return res;
}
#endif
// Detect default gateway
#ifdef Q_OS_MAC
QProcess p;
@@ -348,38 +325,9 @@ void OpenVpnProtocol::updateVpnGateway(const QString &line)
m_vpnGateway = l.split(" ").at(2);
#ifdef Q_OS_WIN
QThread::msleep(300);
IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
QList<QNetworkInterface> netInterfaces = QNetworkInterface::allInterfaces();
for (int i = 0; i < netInterfaces.size(); i++) {
for (int j=0; j < netInterfaces.at(i).addressEntries().size(); j++)
{
// killSwitch toggle
if (m_vpnLocalAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) {
if (QVariant(m_configData.value(configKey::killSwitchOption).toString()).toBool()) {
iface->enableKillSwitch(m_configData, netInterfaces.at(i).index());
}
m_configData.insert("vpnAdapterIndex", netInterfaces.at(i).index());
m_configData.insert("vpnGateway", m_vpnGateway);
m_configData.insert("vpnServer",
NetworkUtilities::getIPAddress(m_configData.value(amnezia::configKey::hostName).toString()));
iface->enablePeerTraffic(m_configData);
}
}
}
});
#endif
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
// killSwitch toggle
if (QVariant(m_configData.value(configKey::killSwitchOption).toString()).toBool()) {
m_configData.insert("vpnServer",
NetworkUtilities::getIPAddress(m_configData.value(amnezia::configKey::hostName).toString()));
IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
QRemoteObjectPendingReply<bool> reply = iface->enableKillSwitch(m_configData, 0);
if (!reply.waitForFinished(1000) || !reply.returnValue()) {
qWarning() << "OpenVpnProtocol::updateVpnGateway(): Failed to enable killswitch";
}
});
}
#if defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
emit tunnelAddressesUpdated(m_vpnGateway, m_vpnLocalAddress);
#endif
qDebug() << QString("Set vpn local address %1, gw %2").arg(m_vpnLocalAddress).arg(vpnGateway());
}

View File

@@ -33,8 +33,10 @@ WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject *
if ((!m_vpnGateway.isEmpty() && m_vpnGateway != previousGateway) ||
(!m_vpnLocalAddress.isEmpty() && m_vpnLocalAddress != previousLocal)) {
emit tunnelAddressesUpdated(m_vpnGateway, m_vpnLocalAddress);
}
if (m_connectionState == Vpn::ConnectionState::Connected) {
emit tunnelAddressesUpdated(m_vpnGateway, m_vpnLocalAddress);
}
}
});
connect(m_impl.get(), &ControllerImpl::disconnected, this,

View File

@@ -102,14 +102,6 @@ void XrayProtocol::stop()
qDebug() << "XrayProtocol::stop()";
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";
@@ -221,66 +213,7 @@ ErrorCode XrayProtocol::setupRouting()
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;
}
} 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
emit tunnelAddressesUpdated(m_vpnGateway, m_vpnLocalAddress);
return ErrorCode::NoError;
},
[]() { return ErrorCode::AmneziaServiceConnectionFailed; });

View File

@@ -0,0 +1,252 @@
#include "vpnTrafficGuard.h"
#include <QDebug>
#include <QHostInfo>
#include <QNetworkInterface>
#include <QPointer>
#include <QStringList>
#include <QTimer>
#include <QJsonObject>
#ifdef AMNEZIA_DESKTOP
#include "core/utils/ipcClient.h"
#endif
#include "core/utils/networkUtilities.h"
VpnTrafficGuard::VpnTrafficGuard(SecureAppSettingsRepository* appSettings, QObject *parent)
: QObject(parent), m_appSettingsRepository(appSettings)
{
}
VpnTrafficGuard::~VpnTrafficGuard()
{
}
void VpnTrafficGuard::setConfig(const QJsonObject &config)
{
m_config = config;
}
bool VpnTrafficGuard::allowEndpoint(const QString &remoteAddress)
{
#ifdef AMNEZIA_DESKTOP
if (remoteAddress.isEmpty()) {
return false;
}
return IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
QRemoteObjectPendingReply<bool> reply = iface->addKillSwitchAllowedRange(QStringList(remoteAddress));
return reply.waitForFinished(1000) && reply.returnValue();
});
#else
Q_UNUSED(remoteAddress)
return true;
#endif
}
void VpnTrafficGuard::setupRoutes(const QJsonObject &vpnConfiguration, const QSharedPointer<VpnProtocol> &protocol, const QString &remoteAddress)
{
#ifdef AMNEZIA_DESKTOP
if (!m_appSettingsRepository) {
qCritical() << "VpnTrafficGuard::setupRoutes: repositories not initialized";
return;
}
IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
iface->resetIpStack();
auto flushDns = iface->flushDns();
if (flushDns.waitForFinished() && flushDns.returnValue())
qDebug() << "VpnTrafficGuard::setupRoutes: Successfully flushed DNS";
else
qWarning() << "VpnTrafficGuard::setupRoutes: Failed to flush DNS";
const QString proto = vpnConfiguration.value(configKey::vpnProto).toString();
const bool isWgBased = (proto == ProtocolUtils::protoToString(Proto::Awg) ||
proto == ProtocolUtils::protoToString(Proto::WireGuard));
if (!isWgBased) {
QString dns1 = vpnConfiguration.value(configKey::dns1).toString();
QString dns2 = vpnConfiguration.value(configKey::dns2).toString();
#ifdef Q_OS_MACOS
if (!m_appSettingsRepository->isSitesSplitTunnelingEnabled() || m_appSettingsRepository->routeMode() != amnezia::RouteMode::VpnAllExceptSites) {
iface->routeAddList(protocol->vpnGateway(), QStringList() << dns1 << dns2);
}
#else
iface->routeAddList(protocol->vpnGateway(), QStringList() << dns1 << dns2);
#endif
// TODO: add error code handling for all routeAddList (or rework the code below)
if (!protocol) {
qWarning() << "VpnTrafficGuard::setupRoutes: protocol is null";
return;
}
iface->routeAddList(protocol->vpnGateway(), QStringList() << dns1 << dns2);
if (m_appSettingsRepository->isSitesSplitTunnelingEnabled()) {
iface->routeDeleteList(protocol->vpnGateway(), QStringList() << "0.0.0.0");
if (m_appSettingsRepository->routeMode() == amnezia::route_mode_ns::VpnOnlyForwardSites) {
QPointer<VpnProtocol> protocolPtr(protocol.data());
QTimer::singleShot(1000, protocol.data(),
[this, protocolPtr]() {
if (!protocolPtr) {
return;
}
addSplitTunnelRoutes(protocolPtr->vpnGateway(), m_appSettingsRepository->routeMode());
});
} else if (m_appSettingsRepository->routeMode() == amnezia::route_mode_ns::VpnAllExceptSites) {
iface->routeAddList(protocol->vpnGateway(), QStringList() << "0.0.0.0/1");
iface->routeAddList(protocol->vpnGateway(), QStringList() << "128.0.0.0/1");
iface->routeAddList(protocol->routeGateway(), QStringList() << remoteAddress);
#ifdef Q_OS_MACOS
iface->routeAddList(protocol->routeGateway(), QStringList() << dns1 << dns2);
#endif
addSplitTunnelRoutes(protocol->routeGateway(), m_appSettingsRepository->routeMode());
}
}
}
});
#endif
}
void VpnTrafficGuard::addSplitTunnelRoutes(const QString &gw, amnezia::RouteMode mode)
{
#ifdef AMNEZIA_DESKTOP
if (!m_appSettingsRepository) {
qCritical() << "VpnTrafficGuard::addSplitTunnelRoutes: repositories not initialized";
return;
}
QStringList ips;
QStringList sites;
const QVariantMap &m = m_appSettingsRepository->vpnSites(mode);
for (auto i = m.constBegin(); i != m.constEnd(); ++i) {
if (NetworkUtilities::checkIpSubnetFormat(i.key())) {
ips.append(i.key());
} else {
if (NetworkUtilities::checkIpSubnetFormat(i.value().toString())) {
ips.append(i.value().toString());
}
sites.append(i.key());
}
}
ips.removeDuplicates();
IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
iface->routeAddList(gw, ips);
});
// re-resolve domains
for (const QString &site : sites) {
const auto &cbResolv = [this, site, gw, mode, ips](const QHostInfo &hostInfo) {
for (const QHostAddress &addr : hostInfo.addresses()) {
if (addr.protocol() == QAbstractSocket::NetworkLayerProtocol::IPv4Protocol) {
const QString &ip = addr.toString();
if (!ips.contains(ip)) {
IpcClient::withInterface([gw, ip](QSharedPointer<IpcInterfaceReplica> iface) {
iface->routeAddList(gw, QStringList() << ip);
});
m_appSettingsRepository->addVpnSite(mode, site, ip);
}
IpcClient::withInterface([](QSharedPointer<IpcInterfaceReplica> iface) {
auto reply = iface->flushDns();
if (!reply.waitForFinished() || !reply.returnValue())
qWarning() << "VpnTrafficGuard::addSplitTunnelRoutes: Failed to flush DNS";
});
break;
}
}
};
QHostInfo::lookupHost(site, this, cbResolv);
}
#endif
}
void VpnTrafficGuard::applyFirewall(const QString &gateway, const QString &localAddress)
{
#ifdef AMNEZIA_DESKTOP
QJsonObject updatedConfig = m_config;
IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
#ifdef Q_OS_WIN
QList<QNetworkInterface> netInterfaces = QNetworkInterface::allInterfaces();
for (int i = 0; i < netInterfaces.size(); i++) {
for (int j=0; j < netInterfaces.at(i).addressEntries().size(); j++)
{
if (localAddress == netInterfaces.at(i).addressEntries().at(j).ip().toString()) {
updatedConfig.insert("vpnAdapterIndex", netInterfaces.at(i).index());
updatedConfig.insert("vpnGateway", gateway);
updatedConfig.insert("vpnServer", NetworkUtilities::getIPAddress(updatedConfig.value(config_key::hostName).toString()));
if (QVariant(updatedConfig.value(config_key::killSwitchOption).toString()).toBool()) {
iface->enableKillSwitch(updatedConfig, netInterfaces.at(i).index());
}
iface->enablePeerTraffic(updatedConfig);
}
}
}
#endif
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
if (QVariant(updatedConfig.value(configKey::killSwitchOption).toString()).toBool()) {
updatedConfig.insert("vpnServer",
NetworkUtilities::getIPAddress(updatedConfig.value(amnezia::configKey::hostName).toString()));
QRemoteObjectPendingReply<bool> reply = iface->enableKillSwitch(updatedConfig, 0);
//TODO: why it takes so long?
if (!reply.waitForFinished(5000) || !reply.returnValue()) {
qWarning() << "VpnTrafficGuard::applyFirewall: Failed to enable killswitch";
} else {
qDebug() << "VpnTrafficGuard::applyFirewall: Successfully enabled killswitch";
}
}
#endif
const QString proto = updatedConfig.value(configKey::vpnProto).toString();
const bool isXrayBased = (proto == ProtocolUtils::protoToString(Proto::Xray) ||
proto == ProtocolUtils::protoToString(Proto::SSXray));
if (isXrayBased) {
if (updatedConfig.value(configKey::splitTunnelType).toInt() == amnezia::route_mode_ns::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(gateway, subnets);
if (!routeAddList.waitForFinished() || routeAddList.returnValue() != subnets.count()) {
qCritical() << "Failed to set routes for TUN";
}
}
auto StopRoutingIpv6 = iface->StopRoutingIpv6();
if (!StopRoutingIpv6.waitForFinished() || !StopRoutingIpv6.returnValue()) {
qCritical() << "Failed to disable IPv6 routing";
} else {
m_ipv6RoutingStopped = true;
}
}
});
#endif
}
void VpnTrafficGuard::teardown()
{
#ifdef AMNEZIA_DESKTOP
IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
QRemoteObjectPendingReply<bool> reply = iface->disableKillSwitch();
//TODO: why it takes so long?
if (!reply.waitForFinished(5000) || !reply.returnValue()) {
qWarning() << "VpnTrafficGuard::teardown: Failed to disable killswitch";
} else {
qDebug() << "VpnTrafficGuard::teardown: Successfully disabled killswitch";
}
auto flushDns = iface->flushDns();
if (flushDns.waitForFinished() && flushDns.returnValue())
qDebug() << "VpnTrafficGuard::teardown: Successfully flushed DNS";
else
qWarning() << "VpnTrafficGuard::teardown: Failed to flush DNS";
auto clearSavedRoutes = iface->clearSavedRoutes();
if (clearSavedRoutes.waitForFinished() && clearSavedRoutes.returnValue())
qDebug() << "VpnTrafficGuard::teardown: Successfully cleared saved routes";
else
qWarning() << "VpnTrafficGuard::teardown: Failed to clear saved routes";
if (m_ipv6RoutingStopped) {
auto StartRoutingIpv6 = iface->StartRoutingIpv6();
if (!StartRoutingIpv6.waitForFinished() || !StartRoutingIpv6.returnValue()) {
qCritical() << "Failed to enable IPv6 routing";
} else {
m_ipv6RoutingStopped = false;
}
}
});
#endif
}

View File

@@ -0,0 +1,31 @@
#ifndef VPNTRAFFICGUARD_H
#define VPNTRAFFICGUARD_H
#include <qobject.h>
#include "core/repositories/secureAppSettingsRepository.h"
#include "protocols/vpnprotocol.h"
class VpnTrafficGuard : public QObject
{
Q_OBJECT
public:
explicit VpnTrafficGuard(SecureAppSettingsRepository* appSettings, QObject* parent = nullptr);
~VpnTrafficGuard() override;
void setConfig(const QJsonObject &config);
void setupRoutes(const QJsonObject &vpnConfiguration,
const QSharedPointer<VpnProtocol> &protocol,
const QString &remoteAddress);
void teardown();
bool allowEndpoint(const QString &remoteAddress);
void applyFirewall(const QString &vpnGateway, const QString &vpnLocalAddress);
private:
void addSplitTunnelRoutes(const QString &gateway, amnezia::RouteMode mode);
SecureAppSettingsRepository* m_appSettingsRepository;
QJsonObject m_config;
bool m_ipv6RoutingStopped = false;
};
#endif // VPNTRAFFICGUARD_H

View File

@@ -1,12 +1,12 @@
#include <QDebug>
#include <QTimer>
#include <QLocalSocket>
#include "amneziaApplication.h"
#include "core/utils/osSignalHandler.h"
#include "core/utils/migrations.h"
#include "version.h"
#include <QTimer>
#ifdef Q_OS_WIN
#include "Windows.h"

View File

@@ -30,12 +30,11 @@
#endif
#include "core/utils/networkUtilities.h"
#include "vpnConnection.h"
using namespace ProtocolUtils;
VpnConnection::VpnConnection(SecureServersRepository* serversRepository, SecureAppSettingsRepository* appSettingsRepository, QObject *parent)
: QObject(parent), m_serversRepository(serversRepository), m_appSettingsRepository(appSettingsRepository), m_checkTimer(this)
: QObject(parent), m_serversRepository(serversRepository), m_appSettingsRepository(appSettingsRepository), m_checkTimer(this), m_trafficGuard(new VpnTrafficGuard(appSettingsRepository, this))
{
#if defined(Q_OS_IOS) || defined(MACOS_NE)
m_checkTimer.setInterval(1000);
@@ -69,74 +68,17 @@ void VpnConnection::onKillSwitchModeChanged(bool enabled)
void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
{
#ifdef AMNEZIA_DESKTOP
if (!m_serversRepository || !m_appSettingsRepository) {
qCritical() << "VpnConnection::onConnectionStateChanged: repositories not initialized";
return;
switch (state) {
case Vpn::ConnectionState::Connected: {
m_trafficGuard->setupRoutes(m_vpnConfiguration, m_vpnProtocol, remoteAddress());
} break;
case Vpn::ConnectionState::Disconnected:
case Vpn::ConnectionState::Error: {
m_trafficGuard->teardown();
} break;
default:
break;
}
ServerConfig defaultServer = m_serversRepository->server(m_serversRepository->defaultServerIndex());
DockerContainer container = defaultServer.defaultContainer();
IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
switch (state) {
case Vpn::ConnectionState::Connected: {
iface->resetIpStack();
auto flushDns = iface->flushDns();
if (flushDns.waitForFinished() && flushDns.returnValue())
qDebug() << "VpnConnection::onConnectionStateChanged: Successfully flushed DNS";
else
qWarning() << "VpnConnection::onConnectionStateChanged: Failed to flush DNS";
if (!ContainerUtils::isAwgContainer(container) && container != DockerContainer::WireGuard) {
QString dns1 = m_vpnConfiguration.value(configKey::dns1).toString();
QString dns2 = m_vpnConfiguration.value(configKey::dns2).toString();
#ifdef Q_OS_MACOS
if (!m_appSettingsRepository->isSitesSplitTunnelingEnabled() || m_appSettingsRepository->routeMode() != amnezia::RouteMode::VpnAllExceptSites) {
iface->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << dns1 << dns2);
}
#else
iface->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << dns1 << dns2);
#endif
if (m_appSettingsRepository->isSitesSplitTunnelingEnabled()) {
iface->routeDeleteList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0");
RouteMode routeMode = m_appSettingsRepository->routeMode();
if (routeMode == amnezia::RouteMode::VpnOnlyForwardSites) {
QTimer::singleShot(1000, m_vpnProtocol.data(),
[this, routeMode]() { addSitesRoutes(m_vpnProtocol->vpnGateway(), routeMode); });
} else if (routeMode == amnezia::RouteMode::VpnAllExceptSites) {
iface->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0/1");
iface->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "128.0.0.0/1");
iface->routeAddList(m_vpnProtocol->routeGateway(), QStringList() << remoteAddress());
#ifdef Q_OS_MACOS
iface->routeAddList(m_vpnProtocol->routeGateway(), QStringList() << dns1 << dns2);
#endif
addSitesRoutes(m_vpnProtocol->routeGateway(), routeMode);
}
}
}
} break;
case Vpn::ConnectionState::Disconnected:
case Vpn::ConnectionState::Error: {
auto flushDns = iface->flushDns();
if (flushDns.waitForFinished() && flushDns.returnValue())
qDebug() << "VpnConnection::onConnectionStateChanged: Successfully flushed DNS";
else
qWarning() << "VpnConnection::onConnectionStateChanged: Failed to flush DNS";
auto clearSavedRoutes = iface->clearSavedRoutes();
if (clearSavedRoutes.waitForFinished() && clearSavedRoutes.returnValue())
qDebug() << "VpnConnection::onConnectionStateChanged: Successfully cleared saved routes";
else
qWarning() << "VpnConnection::onConnectionStateChanged: Failed to clear saved routes";
} break;
default:
break;
}
});
#endif
#if defined(Q_OS_IOS) || defined(MACOS_NE)
@@ -161,62 +103,6 @@ void VpnConnection::setRepositories(SecureServersRepository* serversRepository,
m_appSettingsRepository = appSettingsRepository;
}
void VpnConnection::addSitesRoutes(const QString &gw, amnezia::RouteMode mode)
{
#ifdef AMNEZIA_DESKTOP
if (!m_appSettingsRepository) {
qCritical() << "VpnConnection::addSitesRoutes: repositories not initialized";
return;
}
QStringList ips;
QStringList sites;
const QVariantMap &m = m_appSettingsRepository->vpnSites(mode);
for (auto i = m.constBegin(); i != m.constEnd(); ++i) {
if (NetworkUtilities::checkIpSubnetFormat(i.key())) {
ips.append(i.key());
} else {
if (NetworkUtilities::checkIpSubnetFormat(i.value().toString())) {
ips.append(i.value().toString());
}
sites.append(i.key());
}
}
ips.removeDuplicates();
IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
iface->routeAddList(gw, ips);
});
// re-resolve domains
for (const QString &site : sites) {
const auto &cbResolv = [this, site, gw, mode, ips](const QHostInfo &hostInfo) {
const QList<QHostAddress> &addresses = hostInfo.addresses();
QString ipv4Addr;
for (const QHostAddress &addr : hostInfo.addresses()) {
if (addr.protocol() == QAbstractSocket::NetworkLayerProtocol::IPv4Protocol) {
const QString &ip = addr.toString();
// qDebug() << "VpnConnection::addSitesRoutes updating site" << site << ip;
if (!ips.contains(ip)) {
IpcClient::withInterface([&gw, &ip](QSharedPointer<IpcInterfaceReplica> iface) {
iface->routeAddList(gw, QStringList() << ip);
});
m_appSettingsRepository->addVpnSite(mode, site, ip);
}
IpcClient::withInterface([](QSharedPointer<IpcInterfaceReplica> iface) {
auto reply = iface->flushDns();
if (reply.waitForFinished() || !reply.returnValue())
qWarning() << "VpnConnection::addSitesRoutes: Failed to flush DNS";
});
break;
}
}
};
QHostInfo::lookupHost(site, this, cbResolv);
}
#endif
}
QSharedPointer<VpnProtocol> VpnConnection::vpnProtocol() const
{
return m_vpnProtocol;
@@ -261,6 +147,13 @@ void VpnConnection::connectToVpn(int serverIndex, DockerContainer container, con
<< m_appSettingsRepository->routeMode();
m_remoteAddress = NetworkUtilities::getIPAddress(vpnConfiguration.value(configKey::hostName).toString());
#ifdef AMNEZIA_DESKTOP
if (!m_trafficGuard->allowEndpoint(m_remoteAddress)) {
setConnectionState(Vpn::ConnectionState::Error);
emit vpnProtocolError(ErrorCode::AmneziaServiceConnectionFailed);
return;
}
#endif
setConnectionState(Vpn::ConnectionState::Connecting);
m_vpnConfiguration = vpnConfiguration;
@@ -283,6 +176,7 @@ void VpnConnection::connectToVpn(int serverIndex, DockerContainer container, con
return;
}
m_vpnProtocol->prepare();
m_trafficGuard->setConfig(m_vpnConfiguration);
#elif defined Q_OS_ANDROID
androidVpnProtocol = createDefaultAndroidVpnProtocol();
createAndroidConnections();
@@ -308,6 +202,7 @@ void VpnConnection::createProtocolConnections()
connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
connect(m_vpnProtocol.data(), &VpnProtocol::connectionStateChanged, this, &VpnConnection::setConnectionState);
connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64)));
connect(m_vpnProtocol.data(), &VpnProtocol::tunnelAddressesUpdated, m_trafficGuard.data(), &VpnTrafficGuard::applyFirewall);
#ifdef AMNEZIA_DESKTOP
IpcClient::withInterface([this](QSharedPointer<IpcInterfaceReplica> rep) {

View File

@@ -15,9 +15,7 @@
#include "core/repositories/secureServersRepository.h"
#include "core/repositories/secureAppSettingsRepository.h"
#ifdef AMNEZIA_DESKTOP
#include "core/utils/ipcClient.h"
#endif
#include "core/vpnTrafficGuard.h"
#ifdef Q_OS_ANDROID
#include "core/protocols/androidVpnProtocol.h"
@@ -41,7 +39,6 @@ public:
QSharedPointer<VpnProtocol> vpnProtocol() const;
const QString &remoteAddress() const;
void addSitesRoutes(const QString &gw, amnezia::RouteMode mode);
#ifdef Q_OS_ANDROID
void restoreConnection();
@@ -75,6 +72,7 @@ protected:
private:
SecureServersRepository* m_serversRepository;
SecureAppSettingsRepository* m_appSettingsRepository;
QScopedPointer<VpnTrafficGuard> m_trafficGuard;
QJsonObject m_vpnConfiguration;
QJsonObject m_routeMode;