diff --git a/client/cmake/sources.cmake b/client/cmake/sources.cmake index 497757757..8e02b48d9 100644 --- a/client/cmake/sources.cmake +++ b/client/cmake/sources.cmake @@ -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 diff --git a/client/core/protocols/openVpnProtocol.cpp b/client/core/protocols/openVpnProtocol.cpp index 660846e51..a533268cf 100644 --- a/client/core/protocols/openVpnProtocol.cpp +++ b/client/core/protocols/openVpnProtocol.cpp @@ -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 iface) { - QRemoteObjectPendingReply 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 iface) { - QString ip = NetworkUtilities::getIPAddress(m_configData.value(amnezia::configKey::hostName).toString()); - QRemoteObjectPendingReply 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 iface) { - QList 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 iface) { - QRemoteObjectPendingReply 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()); } diff --git a/client/core/protocols/wireGuardProtocol.cpp b/client/core/protocols/wireGuardProtocol.cpp index c401147c9..631251619 100644 --- a/client/core/protocols/wireGuardProtocol.cpp +++ b/client/core/protocols/wireGuardProtocol.cpp @@ -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, diff --git a/client/core/protocols/xrayProtocol.cpp b/client/core/protocols/xrayProtocol.cpp index 893b8367e..113ac5281 100755 --- a/client/core/protocols/xrayProtocol.cpp +++ b/client/core/protocols/xrayProtocol.cpp @@ -102,14 +102,6 @@ void XrayProtocol::stop() qDebug() << "XrayProtocol::stop()"; IpcClient::withInterface([](QSharedPointer 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 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; }); diff --git a/client/core/vpnTrafficGuard.cpp b/client/core/vpnTrafficGuard.cpp new file mode 100644 index 000000000..f8681a778 --- /dev/null +++ b/client/core/vpnTrafficGuard.cpp @@ -0,0 +1,252 @@ +#include "vpnTrafficGuard.h" + +#include +#include +#include +#include +#include +#include +#include + +#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 iface) { + QRemoteObjectPendingReply 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 &protocol, const QString &remoteAddress) +{ +#ifdef AMNEZIA_DESKTOP + if (!m_appSettingsRepository) { + qCritical() << "VpnTrafficGuard::setupRoutes: repositories not initialized"; + return; + } + IpcClient::withInterface([&](QSharedPointer 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 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 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 iface) { + iface->routeAddList(gw, QStringList() << ip); + }); + m_appSettingsRepository->addVpnSite(mode, site, ip); + } + IpcClient::withInterface([](QSharedPointer 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 iface) { +#ifdef Q_OS_WIN + QList 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 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 iface) { + QRemoteObjectPendingReply 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 +} diff --git a/client/core/vpnTrafficGuard.h b/client/core/vpnTrafficGuard.h new file mode 100644 index 000000000..e8e5468c8 --- /dev/null +++ b/client/core/vpnTrafficGuard.h @@ -0,0 +1,31 @@ +#ifndef VPNTRAFFICGUARD_H +#define VPNTRAFFICGUARD_H + +#include +#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 &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 diff --git a/client/main.cpp b/client/main.cpp index 53a1fd574..edb3ec65a 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -1,12 +1,12 @@ #include #include +#include #include "amneziaApplication.h" #include "core/utils/osSignalHandler.h" #include "core/utils/migrations.h" #include "version.h" -#include #ifdef Q_OS_WIN #include "Windows.h" diff --git a/client/vpnConnection.cpp b/client/vpnConnection.cpp index ad8ce917d..448165c39 100644 --- a/client/vpnConnection.cpp +++ b/client/vpnConnection.cpp @@ -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 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 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 &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 iface) { - iface->routeAddList(gw, QStringList() << ip); - }); - m_appSettingsRepository->addVpnSite(mode, site, ip); - } - IpcClient::withInterface([](QSharedPointer 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 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 rep) { diff --git a/client/vpnConnection.h b/client/vpnConnection.h index 0a6857447..0d48b6a21 100644 --- a/client/vpnConnection.h +++ b/client/vpnConnection.h @@ -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() 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 m_trafficGuard; QJsonObject m_vpnConfiguration; QJsonObject m_routeMode;