Compare commits
4 commits
dev
...
feat/locat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5134d479f7 | ||
|
|
ce754d45e9 | ||
|
|
5af1edbc54 | ||
|
|
7c8a3a50a1 |
22 changed files with 482 additions and 354 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -421,7 +421,23 @@ ErrorCode SubscriptionController::updateServiceFromGateway(int serverIndex, cons
|
|||
}
|
||||
const bool isTestPurchase = apiV2->apiConfig.isTestPurchase;
|
||||
QString serviceProtocol = apiV2->serviceProtocol();
|
||||
ProtocolData protocolData = generateProtocolData(serviceProtocol);
|
||||
ProtocolData protocolData;
|
||||
if (serviceProtocol == configKey::awg && !isConnectEvent) {
|
||||
DockerContainer container = apiV2->defaultContainer;
|
||||
ContainerConfig containerConfig = apiV2->containerConfig(container);
|
||||
AwgProtocolConfig* awgConfig = containerConfig.protocolConfig.as<AwgProtocolConfig>();
|
||||
if (awgConfig && awgConfig->hasClientConfig()) {
|
||||
AwgClientConfig clientConfig = awgConfig->clientConfig.value();
|
||||
QString clientPrivKey = clientConfig.clientPrivateKey;
|
||||
QString clientPubKey = clientConfig.clientPublicKey;
|
||||
protocolData.wireGuardClientPubKey = clientPubKey;
|
||||
protocolData.wireGuardClientPrivKey = clientPrivKey;
|
||||
} else {
|
||||
protocolData = generateProtocolData(serviceProtocol);
|
||||
}
|
||||
} else {
|
||||
protocolData = generateProtocolData(serviceProtocol);
|
||||
}
|
||||
|
||||
QJsonObject authDataJson = apiV2->authData.toJson();
|
||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ ConnectionController::ConnectionController(SecureServersRepository* serversRepos
|
|||
{
|
||||
connect(m_vpnConnection, &VpnConnection::connectionStateChanged, this, &ConnectionController::connectionStateChanged);
|
||||
connect(this, &ConnectionController::openConnectionRequested, m_vpnConnection, &VpnConnection::connectToVpn, Qt::QueuedConnection);
|
||||
connect(this, &ConnectionController::switchConnectionRequested, m_vpnConnection, &VpnConnection::switchToVpn, Qt::QueuedConnection);
|
||||
connect(this, &ConnectionController::closeConnectionRequested, m_vpnConnection, &VpnConnection::disconnectFromVpn, Qt::QueuedConnection);
|
||||
connect(this, &ConnectionController::setConnectionStateRequested, m_vpnConnection, &VpnConnection::setConnectionState, Qt::QueuedConnection);
|
||||
connect(this, &ConnectionController::killSwitchModeChangedRequested, m_vpnConnection, &VpnConnection::onKillSwitchModeChanged, Qt::QueuedConnection);
|
||||
|
|
@ -86,8 +87,11 @@ ErrorCode ConnectionController::openConnection(int serverIndex)
|
|||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
emit openConnectionRequested(serverIndex, container, vpnConfiguration);
|
||||
if (isConnected()) {
|
||||
emit switchConnectionRequested(serverIndex, container, vpnConfiguration);
|
||||
} else {
|
||||
emit openConnectionRequested(serverIndex, container, vpnConfiguration);
|
||||
}
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ public:
|
|||
signals:
|
||||
void connectionStateChanged(Vpn::ConnectionState state);
|
||||
void openConnectionRequested(int serverIndex, DockerContainer container, const QJsonObject &vpnConfiguration);
|
||||
void switchConnectionRequested(int serverIndex, DockerContainer container, const QJsonObject &vpnConfiguration);
|
||||
void closeConnectionRequested();
|
||||
void setConnectionStateRequested(Vpn::ConnectionState state);
|
||||
void killSwitchModeChangedRequested(bool enabled);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
#include <QJsonObject>
|
||||
#include <QSysInfo>
|
||||
#include <QTimer>
|
||||
#include <QStandardPaths>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
#include "amneziaApplication.h"
|
||||
#include "logger.h"
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -160,3 +160,10 @@ bool VpnProtocol::isDisconnected() const
|
|||
{
|
||||
return m_connectionState == Vpn::ConnectionState::Disconnected;
|
||||
}
|
||||
|
||||
ErrorCode VpnProtocol::switchServer(const QJsonObject &newConfig)
|
||||
{
|
||||
stop();
|
||||
m_rawConfig = newConfig;
|
||||
return start();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ public:
|
|||
virtual bool isDisconnected() const;
|
||||
virtual ErrorCode start() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual ErrorCode switchServer(const QJsonObject &newConfig);
|
||||
|
||||
Vpn::ConnectionState connectionState() const;
|
||||
ErrorCode lastError() const;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -77,3 +79,9 @@ ErrorCode WireguardProtocol::start()
|
|||
{
|
||||
return startMzImpl();
|
||||
}
|
||||
|
||||
ErrorCode WireguardProtocol::switchServer(const QJsonObject &newConfig)
|
||||
{
|
||||
m_rawConfig = newConfig;
|
||||
return startMzImpl();
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ public:
|
|||
|
||||
ErrorCode startMzImpl();
|
||||
ErrorCode stopMzImpl();
|
||||
ErrorCode switchServer(const QJsonObject &newConfig);
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
|||
|
|
@ -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; });
|
||||
|
|
|
|||
|
|
@ -286,7 +286,7 @@ QPair<QString, QNetworkInterface> NetworkUtilities::getGatewayAndIface()
|
|||
return { resGateway, QNetworkInterface::interfaceFromIndex(resIndex) };
|
||||
#endif
|
||||
#ifdef Q_OS_LINUX
|
||||
constexpr int BUFFER_SIZE = 100;
|
||||
constexpr int BUFFER_SIZE = 8192;
|
||||
int received_bytes = 0, msg_len = 0, route_attribute_len = 0;
|
||||
int sock = -1, msgseq = 0;
|
||||
struct nlmsghdr *nlh, *nlmsg;
|
||||
|
|
@ -294,7 +294,7 @@ QPair<QString, QNetworkInterface> NetworkUtilities::getGatewayAndIface()
|
|||
// This struct contain route attributes (route type)
|
||||
struct rtattr *route_attribute;
|
||||
char gateway_address[INET_ADDRSTRLEN], interface[IF_NAMESIZE];
|
||||
char msgbuf[BUFFER_SIZE], buffer[BUFFER_SIZE];
|
||||
char msgbuf[100], buffer[BUFFER_SIZE];
|
||||
char *ptr = buffer;
|
||||
struct timeval tv;
|
||||
|
||||
|
|
@ -339,8 +339,8 @@ QPair<QString, QNetworkInterface> NetworkUtilities::getGatewayAndIface()
|
|||
nlh = (struct nlmsghdr *) ptr;
|
||||
|
||||
/* Check if the header is valid */
|
||||
if((NLMSG_OK(nlmsg, received_bytes) == 0) ||
|
||||
(nlmsg->nlmsg_type == NLMSG_ERROR))
|
||||
if((NLMSG_OK(nlh, received_bytes) == 0) ||
|
||||
(nlh->nlmsg_type == NLMSG_ERROR))
|
||||
{
|
||||
perror("Error in received packet");
|
||||
return {};
|
||||
|
|
@ -355,13 +355,15 @@ QPair<QString, QNetworkInterface> NetworkUtilities::getGatewayAndIface()
|
|||
}
|
||||
|
||||
/* Break if its not a multi part message */
|
||||
if ((nlmsg->nlmsg_flags & NLM_F_MULTI) == 0)
|
||||
if ((nlh->nlmsg_flags & NLM_F_MULTI) == 0)
|
||||
break;
|
||||
}
|
||||
while ((nlmsg->nlmsg_seq != msgseq) || (nlmsg->nlmsg_pid != getpid()));
|
||||
while ((nlh->nlmsg_seq != msgseq) || (nlh->nlmsg_pid != getpid()));
|
||||
|
||||
/* parse response */
|
||||
for ( ; NLMSG_OK(nlh, received_bytes); nlh = NLMSG_NEXT(nlh, received_bytes))
|
||||
int remaining = msg_len + received_bytes;
|
||||
nlh = (struct nlmsghdr *) buffer;
|
||||
for ( ; NLMSG_OK(nlh, remaining); nlh = NLMSG_NEXT(nlh, remaining))
|
||||
{
|
||||
/* Get the route data */
|
||||
route_entry = (struct rtmsg *) NLMSG_DATA(nlh);
|
||||
|
|
@ -370,6 +372,10 @@ QPair<QString, QNetworkInterface> NetworkUtilities::getGatewayAndIface()
|
|||
if (route_entry->rtm_table != RT_TABLE_MAIN)
|
||||
continue;
|
||||
|
||||
/* Reset per-route to avoid cross-route state pollution */
|
||||
memset(gateway_address, 0, sizeof(gateway_address));
|
||||
memset(interface, 0, sizeof(interface));
|
||||
|
||||
route_attribute = (struct rtattr *) RTM_RTA(route_entry);
|
||||
route_attribute_len = RTM_PAYLOAD(nlh);
|
||||
|
||||
|
|
@ -395,6 +401,8 @@ QPair<QString, QNetworkInterface> NetworkUtilities::getGatewayAndIface()
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (!(*gateway_address) || !(*interface))
|
||||
qDebug() << "getGatewayAndIface: no gateway found";
|
||||
close(sock);
|
||||
return { gateway_address, QNetworkInterface::interfaceFromName(interface) };
|
||||
#endif
|
||||
|
|
|
|||
252
client/core/vpnTrafficGuard.cpp
Normal file
252
client/core/vpnTrafficGuard.cpp
Normal 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
|
||||
}
|
||||
31
client/core/vpnTrafficGuard.h
Normal file
31
client/core/vpnTrafficGuard.h
Normal 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
|
||||
|
|
@ -501,9 +501,7 @@ bool Daemon::supportServerSwitching(const InterfaceConfig& config) const {
|
|||
|
||||
return current.m_privateKey == config.m_privateKey &&
|
||||
current.m_deviceIpv4Address == config.m_deviceIpv4Address &&
|
||||
current.m_deviceIpv6Address == config.m_deviceIpv6Address &&
|
||||
current.m_serverIpv4Gateway == config.m_serverIpv4Gateway &&
|
||||
current.m_serverIpv6Gateway == config.m_serverIpv6Gateway;
|
||||
current.m_deviceIpv6Address == config.m_deviceIpv6Address;
|
||||
}
|
||||
|
||||
bool Daemon::switchServer(const InterfaceConfig& config) {
|
||||
|
|
@ -613,7 +611,7 @@ void Daemon::checkHandshake() {
|
|||
pendingHandshakes++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Check again if there were connections that haven't completed a handshake.
|
||||
if (pendingHandshakes > 0) {
|
||||
m_handshakeTimer.start(HANDSHAKE_POLL_MSEC);
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -25,6 +25,34 @@ constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg";
|
|||
namespace {
|
||||
Logger logger("WireguardUtilsLinux");
|
||||
Logger logwireguard("WireguardGo");
|
||||
|
||||
bool appendAwgInterfaceParams(const InterfaceConfig& config, QTextStream& out) {
|
||||
bool hasParams = false;
|
||||
const auto appendField = [&](const char* key, const QString& value) {
|
||||
if (!value.isEmpty()) {
|
||||
out << key << "=" << value << "\n";
|
||||
hasParams = true;
|
||||
}
|
||||
};
|
||||
|
||||
appendField("jc", config.m_junkPacketCount);
|
||||
appendField("jmin", config.m_junkPacketMinSize);
|
||||
appendField("jmax", config.m_junkPacketMaxSize);
|
||||
appendField("s1", config.m_initPacketJunkSize);
|
||||
appendField("s2", config.m_responsePacketJunkSize);
|
||||
appendField("s3", config.m_cookieReplyPacketJunkSize);
|
||||
appendField("s4", config.m_transportPacketJunkSize);
|
||||
appendField("h1", config.m_initPacketMagicHeader);
|
||||
appendField("h2", config.m_responsePacketMagicHeader);
|
||||
appendField("h3", config.m_underloadPacketMagicHeader);
|
||||
appendField("h4", config.m_transportPacketMagicHeader);
|
||||
|
||||
for (const QString& key : config.m_specialJunk.keys()) {
|
||||
out << key.toLower() << "=" << config.m_specialJunk.value(key) << "\n";
|
||||
hasParams = true;
|
||||
}
|
||||
return hasParams;
|
||||
}
|
||||
}; // namespace
|
||||
|
||||
WireguardUtilsLinux::WireguardUtilsLinux(QObject* parent)
|
||||
|
|
@ -59,7 +87,6 @@ void WireguardUtilsLinux::tunnelErrorOccurred(QProcess::ProcessError error) {
|
|||
}
|
||||
|
||||
bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) {
|
||||
Q_UNUSED(config);
|
||||
if (m_tunnel.state() != QProcess::NotRunning) {
|
||||
logger.warning() << "Unable to start: tunnel process already running";
|
||||
return false;
|
||||
|
|
@ -104,45 +131,7 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) {
|
|||
QTextStream out(&message);
|
||||
out << "private_key=" << QString(privateKey.toHex()) << "\n";
|
||||
out << "replace_peers=true\n";
|
||||
|
||||
|
||||
if (!config.m_junkPacketCount.isEmpty()) {
|
||||
out << "jc=" << config.m_junkPacketCount << "\n";
|
||||
}
|
||||
if (!config.m_junkPacketMinSize.isEmpty()) {
|
||||
out << "jmin=" << config.m_junkPacketMinSize << "\n";
|
||||
}
|
||||
if (!config.m_junkPacketMaxSize.isEmpty()) {
|
||||
out << "jmax=" << config.m_junkPacketMaxSize << "\n";
|
||||
}
|
||||
if (!config.m_initPacketJunkSize.isEmpty()) {
|
||||
out << "s1=" << config.m_initPacketJunkSize << "\n";
|
||||
}
|
||||
if (!config.m_responsePacketJunkSize.isEmpty()) {
|
||||
out << "s2=" << config.m_responsePacketJunkSize << "\n";
|
||||
}
|
||||
if (!config.m_cookieReplyPacketJunkSize.isEmpty()) {
|
||||
out << "s3=" << config.m_cookieReplyPacketJunkSize << "\n";
|
||||
}
|
||||
if (!config.m_transportPacketJunkSize.isEmpty()) {
|
||||
out << "s4=" << config.m_transportPacketJunkSize << "\n";
|
||||
}
|
||||
if (!config.m_initPacketMagicHeader.isEmpty()) {
|
||||
out << "h1=" << config.m_initPacketMagicHeader << "\n";
|
||||
}
|
||||
if (!config.m_responsePacketMagicHeader.isEmpty()) {
|
||||
out << "h2=" << config.m_responsePacketMagicHeader << "\n";
|
||||
}
|
||||
if (!config.m_underloadPacketMagicHeader.isEmpty()) {
|
||||
out << "h3=" << config.m_underloadPacketMagicHeader << "\n";
|
||||
}
|
||||
if (!config.m_transportPacketMagicHeader.isEmpty()) {
|
||||
out << "h4=" << config.m_transportPacketMagicHeader << "\n";
|
||||
}
|
||||
|
||||
for (const QString& key : config.m_specialJunk.keys()) {
|
||||
out << key.toLower() << "=" << config.m_specialJunk.value(key) << "\n";
|
||||
}
|
||||
appendAwgInterfaceParams(config, out);
|
||||
|
||||
int err = uapiErrno(uapiCommand(message));
|
||||
if (err != 0) {
|
||||
|
|
@ -201,7 +190,6 @@ bool WireguardUtilsLinux::deleteInterface() {
|
|||
return true;
|
||||
}
|
||||
|
||||
// dummy implementations for now
|
||||
bool WireguardUtilsLinux::updatePeer(const InterfaceConfig& config) {
|
||||
QByteArray publicKey =
|
||||
QByteArray::fromBase64(qPrintable(config.m_serverPublicKey));
|
||||
|
|
@ -214,6 +202,7 @@ bool WireguardUtilsLinux::updatePeer(const InterfaceConfig& config) {
|
|||
QString message;
|
||||
QTextStream out(&message);
|
||||
out << "set=1\n";
|
||||
appendAwgInterfaceParams(config, out);
|
||||
out << "public_key=" << QString(publicKey.toHex()) << "\n";
|
||||
if (!config.m_serverPskKey.isNull()) {
|
||||
out << "preshared_key=" << QString(pskKey.toHex()) << "\n";
|
||||
|
|
|
|||
|
|
@ -24,6 +24,34 @@ constexpr const char* WG_RUNTIME_DIR = "/var/run/amneziawg";
|
|||
namespace {
|
||||
Logger logger("WireguardUtilsMacos");
|
||||
Logger logwireguard("WireguardGo");
|
||||
|
||||
bool appendAwgInterfaceParams(const InterfaceConfig& config, QTextStream& out) {
|
||||
bool hasParams = false;
|
||||
const auto appendField = [&](const char* key, const QString& value) {
|
||||
if (!value.isEmpty()) {
|
||||
out << key << "=" << value << "\n";
|
||||
hasParams = true;
|
||||
}
|
||||
};
|
||||
|
||||
appendField("jc", config.m_junkPacketCount);
|
||||
appendField("jmin", config.m_junkPacketMinSize);
|
||||
appendField("jmax", config.m_junkPacketMaxSize);
|
||||
appendField("s1", config.m_initPacketJunkSize);
|
||||
appendField("s2", config.m_responsePacketJunkSize);
|
||||
appendField("s3", config.m_cookieReplyPacketJunkSize);
|
||||
appendField("s4", config.m_transportPacketJunkSize);
|
||||
appendField("h1", config.m_initPacketMagicHeader);
|
||||
appendField("h2", config.m_responsePacketMagicHeader);
|
||||
appendField("h3", config.m_underloadPacketMagicHeader);
|
||||
appendField("h4", config.m_transportPacketMagicHeader);
|
||||
|
||||
for (const QString& key : config.m_specialJunk.keys()) {
|
||||
out << key.toLower() << "=" << config.m_specialJunk.value(key) << "\n";
|
||||
hasParams = true;
|
||||
}
|
||||
return hasParams;
|
||||
}
|
||||
}; // namespace
|
||||
|
||||
WireguardUtilsMacos::WireguardUtilsMacos(QObject* parent)
|
||||
|
|
@ -58,7 +86,6 @@ void WireguardUtilsMacos::tunnelErrorOccurred(QProcess::ProcessError error) {
|
|||
}
|
||||
|
||||
bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) {
|
||||
Q_UNUSED(config);
|
||||
if (m_tunnel.state() != QProcess::NotRunning) {
|
||||
logger.warning() << "Unable to start: tunnel process already running";
|
||||
return false;
|
||||
|
|
@ -103,44 +130,7 @@ bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) {
|
|||
QTextStream out(&message);
|
||||
out << "private_key=" << QString(privateKey.toHex()) << "\n";
|
||||
out << "replace_peers=true\n";
|
||||
|
||||
if (!config.m_junkPacketCount.isEmpty()) {
|
||||
out << "jc=" << config.m_junkPacketCount << "\n";
|
||||
}
|
||||
if (!config.m_junkPacketMinSize.isEmpty()) {
|
||||
out << "jmin=" << config.m_junkPacketMinSize << "\n";
|
||||
}
|
||||
if (!config.m_junkPacketMaxSize.isEmpty()) {
|
||||
out << "jmax=" << config.m_junkPacketMaxSize << "\n";
|
||||
}
|
||||
if (!config.m_initPacketJunkSize.isEmpty()) {
|
||||
out << "s1=" << config.m_initPacketJunkSize << "\n";
|
||||
}
|
||||
if (!config.m_responsePacketJunkSize.isEmpty()) {
|
||||
out << "s2=" << config.m_responsePacketJunkSize << "\n";
|
||||
}
|
||||
if (!config.m_cookieReplyPacketJunkSize.isEmpty()) {
|
||||
out << "s3=" << config.m_cookieReplyPacketJunkSize << "\n";
|
||||
}
|
||||
if (!config.m_transportPacketJunkSize.isEmpty()) {
|
||||
out << "s4=" << config.m_transportPacketJunkSize << "\n";
|
||||
}
|
||||
if (!config.m_initPacketMagicHeader.isEmpty()) {
|
||||
out << "h1=" << config.m_initPacketMagicHeader << "\n";
|
||||
}
|
||||
if (!config.m_responsePacketMagicHeader.isEmpty()) {
|
||||
out << "h2=" << config.m_responsePacketMagicHeader << "\n";
|
||||
}
|
||||
if (!config.m_underloadPacketMagicHeader.isEmpty()) {
|
||||
out << "h3=" << config.m_underloadPacketMagicHeader << "\n";
|
||||
}
|
||||
if (!config.m_transportPacketMagicHeader.isEmpty()) {
|
||||
out << "h4=" << config.m_transportPacketMagicHeader << "\n";
|
||||
}
|
||||
|
||||
for (const QString& key : config.m_specialJunk.keys()) {
|
||||
out << key.toLower() << "=" << config.m_specialJunk.value(key) << "\n";
|
||||
}
|
||||
appendAwgInterfaceParams(config, out);
|
||||
|
||||
int err = uapiErrno(uapiCommand(message));
|
||||
if (err != 0) {
|
||||
|
|
@ -200,7 +190,6 @@ bool WireguardUtilsMacos::deleteInterface() {
|
|||
return true;
|
||||
}
|
||||
|
||||
// dummy implementations for now
|
||||
bool WireguardUtilsMacos::updatePeer(const InterfaceConfig& config) {
|
||||
QByteArray publicKey =
|
||||
QByteArray::fromBase64(qPrintable(config.m_serverPublicKey));
|
||||
|
|
@ -214,6 +203,7 @@ bool WireguardUtilsMacos::updatePeer(const InterfaceConfig& config) {
|
|||
QString message;
|
||||
QTextStream out(&message);
|
||||
out << "set=1\n";
|
||||
appendAwgInterfaceParams(config, out);
|
||||
out << "public_key=" << QString(publicKey.toHex()) << "\n";
|
||||
if (!config.m_serverPskKey.isNull()) {
|
||||
out << "preshared_key=" << QString(pskKey.toHex()) << "\n";
|
||||
|
|
|
|||
|
|
@ -20,6 +20,34 @@
|
|||
|
||||
namespace {
|
||||
Logger logger("WireguardUtilsWindows");
|
||||
|
||||
bool appendAwgInterfaceParams(const InterfaceConfig& config, QTextStream& out) {
|
||||
bool hasParams = false;
|
||||
const auto appendField = [&](const char* key, const QString& value) {
|
||||
if (!value.isEmpty()) {
|
||||
out << key << "=" << value << "\n";
|
||||
hasParams = true;
|
||||
}
|
||||
};
|
||||
|
||||
appendField("jc", config.m_junkPacketCount);
|
||||
appendField("jmin", config.m_junkPacketMinSize);
|
||||
appendField("jmax", config.m_junkPacketMaxSize);
|
||||
appendField("s1", config.m_initPacketJunkSize);
|
||||
appendField("s2", config.m_responsePacketJunkSize);
|
||||
appendField("s3", config.m_cookieReplyPacketJunkSize);
|
||||
appendField("s4", config.m_transportPacketJunkSize);
|
||||
appendField("h1", config.m_initPacketMagicHeader);
|
||||
appendField("h2", config.m_responsePacketMagicHeader);
|
||||
appendField("h3", config.m_underloadPacketMagicHeader);
|
||||
appendField("h4", config.m_transportPacketMagicHeader);
|
||||
|
||||
for (const QString& key : config.m_specialJunk.keys()) {
|
||||
out << key.toLower() << "=" << config.m_specialJunk.value(key) << "\n";
|
||||
hasParams = true;
|
||||
}
|
||||
return hasParams;
|
||||
}
|
||||
}; // namespace
|
||||
|
||||
std::unique_ptr<WireguardUtilsWindows> WireguardUtilsWindows::create(
|
||||
|
|
@ -165,6 +193,7 @@ bool WireguardUtilsWindows::updatePeer(const InterfaceConfig& config) {
|
|||
QString message;
|
||||
QTextStream out(&message);
|
||||
out << "set=1\n";
|
||||
appendAwgInterfaceParams(config, out);
|
||||
out << "public_key=" << QString(publicKey.toHex()) << "\n";
|
||||
if (!config.m_serverPskKey.isNull()) {
|
||||
out << "preshared_key=" << QString(pskKey.toHex()) << "\n";
|
||||
|
|
|
|||
|
|
@ -184,24 +184,23 @@ PageType {
|
|||
imageSource: "qrc:/images/controls/download.svg"
|
||||
|
||||
checked: index === ApiCountryModel.currentIndex
|
||||
checkable: !ConnectionController.isConnected
|
||||
checkable: !ConnectionController.isConnectionInProgress
|
||||
|
||||
onClicked: {
|
||||
if (ConnectionController.isConnectionInProgress) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change server location while trying to make an active connection"))
|
||||
return
|
||||
}
|
||||
if (ConnectionController.isConnected) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change server location while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
if (index !== ApiCountryModel.currentIndex) {
|
||||
PageController.showBusyIndicator(true)
|
||||
var prevIndex = ApiCountryModel.currentIndex
|
||||
var wasConnected = ConnectionController.isConnected
|
||||
ApiCountryModel.currentIndex = index
|
||||
if (!SubscriptionUiController.updateServiceFromGateway(ServersUiController.getProcessedServerIndex(), countryCode, countryName)) {
|
||||
ApiCountryModel.currentIndex = prevIndex
|
||||
} else if (wasConnected) {
|
||||
ConnectionController.openConnection()
|
||||
}
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
@ -303,11 +197,27 @@ void VpnConnection::connectToVpn(int serverIndex, DockerContainer container, con
|
|||
}
|
||||
}
|
||||
|
||||
void VpnConnection::switchToVpn(int serverIndex, DockerContainer container, const QJsonObject &vpnConfiguration)
|
||||
{
|
||||
if (!m_vpnProtocol.isNull() && ContainerUtils::isAwgContainer(container) && m_connectionState == Vpn::ConnectionState::Connected) {
|
||||
m_remoteAddress = NetworkUtilities::getIPAddress(vpnConfiguration.value(configKey::hostName).toString());
|
||||
m_trafficGuard->allowEndpoint(m_remoteAddress);
|
||||
m_vpnConfiguration = vpnConfiguration;
|
||||
appendKillSwitchConfig();
|
||||
appendSplitTunnelingConfig();
|
||||
m_trafficGuard->setConfig(m_vpnConfiguration);
|
||||
m_vpnProtocol->switchServer(m_vpnConfiguration);
|
||||
} else {
|
||||
connectToVpn(serverIndex, container, vpnConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
@ -50,6 +47,7 @@ public:
|
|||
public slots:
|
||||
void setRepositories(SecureServersRepository* serversRepository, SecureAppSettingsRepository* appSettingsRepository);
|
||||
void connectToVpn(int serverIndex, DockerContainer container, const QJsonObject &vpnConfiguration);
|
||||
void switchToVpn(int serverIndex, DockerContainer container, const QJsonObject &vpnConfiguration);
|
||||
void reconnectToVpn();
|
||||
void disconnectFromVpn();
|
||||
|
||||
|
|
@ -75,6 +73,7 @@ protected:
|
|||
private:
|
||||
SecureServersRepository* m_serversRepository;
|
||||
SecureAppSettingsRepository* m_appSettingsRepository;
|
||||
QScopedPointer<VpnTrafficGuard> m_trafficGuard;
|
||||
|
||||
QJsonObject m_vpnConfiguration;
|
||||
QJsonObject m_routeMode;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue