mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-11-01 09:38:29 +09:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ce0317840 | ||
|
|
65c7327a67 | ||
|
|
4b57f16788 | ||
|
|
9bfc90b69e | ||
|
|
91fd02b6f6 | ||
|
|
482f6d8828 | ||
|
|
f5b391adf0 | ||
|
|
835ff3639e | ||
|
|
dc4df33465 | ||
|
|
a966db46be | ||
|
|
0f2244e92d |
No files matched your search
@@ -148,10 +148,6 @@ ErrorCode SettingsController::restoreAppConfigFromData(const QByteArray &data)
|
||||
newConfigData.value("Conf/appsSplitTunnelingEnabled").toVariant().toString().toLower() == "true";
|
||||
emit appSplitTunnelingRouteModeChanged(static_cast<AppsRouteMode>(appSplitTunnelingRouteMode));
|
||||
|
||||
#if defined(Q_OS_WINDOWS)
|
||||
emit appSplitTunnelingRouteModeChanged(AppsRouteMode::VpnAllExceptApps);
|
||||
#endif
|
||||
|
||||
if (newConfigData.contains("AppPlatform")) {
|
||||
if (newConfigData.value("AppPlatform").toString() != getPlatform()) {
|
||||
emit appSplitTunnelingClearAppsList();
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "ipc.h"
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QTimer>
|
||||
#include <QJsonObject>
|
||||
@@ -121,6 +122,12 @@ void XrayProtocol::stop()
|
||||
qDebug() << "XrayProtocol::stop()";
|
||||
|
||||
IpcClient::withInterface([](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||
#ifdef Q_OS_WIN
|
||||
auto routeDeleteDefault = iface->routeDeleteDefault(tunName);
|
||||
if (!routeDeleteDefault.waitForFinished() || !routeDeleteDefault.returnValue())
|
||||
qWarning() << "Failed to delete the default route for TUN";
|
||||
#endif
|
||||
|
||||
auto disableKillSwitch = iface->disableKillSwitch();
|
||||
if (!disableKillSwitch.waitForFinished() || !disableKillSwitch.returnValue())
|
||||
qWarning() << "Failed to disable killswitch";
|
||||
@@ -254,6 +261,12 @@ ErrorCode XrayProtocol::setupRouting()
|
||||
[this](QSharedPointer<IpcInterfaceReplica> iface) -> ErrorCode {
|
||||
#ifdef Q_OS_WIN
|
||||
const int inetAdapterIndex = NetworkUtilities::AdapterIndexTo(QHostAddress(m_remoteAddress));
|
||||
const bool hasSplitTunnelApps =
|
||||
!m_rawConfig.value(amnezia::configKey::splitTunnelApps).toArray().isEmpty();
|
||||
const bool includeOnlyApps =
|
||||
hasSplitTunnelApps &&
|
||||
m_rawConfig.value(amnezia::configKey::appSplitTunnelType).toInt() ==
|
||||
static_cast<int>(amnezia::AppsRouteMode::VpnOnlyForwardApps);
|
||||
#endif
|
||||
auto createTun = iface->createTun(tunName, amnezia::protocols::xray::defaultLocalAddr);
|
||||
if (!createTun.waitForFinished() || !createTun.returnValue()) {
|
||||
@@ -294,23 +307,6 @@ ErrorCode XrayProtocol::setupRouting()
|
||||
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;
|
||||
@@ -324,9 +320,38 @@ ErrorCode XrayProtocol::setupRouting()
|
||||
qCritical() << "Failed to enable peer traffic";
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
qWarning() << "Failed to get adapter indexes. Split-tunneling disabled";
|
||||
if (includeOnlyApps) {
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_routeMode == amnezia::RouteMode::VpnAllSites) {
|
||||
#ifdef Q_OS_WIN
|
||||
auto routeAddDefault = iface->routeAddDefault(tunName);
|
||||
if (!routeAddDefault.waitForFinished() || !routeAddDefault.returnValue()) {
|
||||
qCritical() << "Failed to set the default route for TUN";
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
#else
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
auto StopRoutingIpv6 = iface->StopRoutingIpv6();
|
||||
if (!StopRoutingIpv6.waitForFinished() || !StopRoutingIpv6.returnValue()) {
|
||||
qCritical() << "Failed to disable IPv6 routing";
|
||||
return ErrorCode::InternalError;
|
||||
}
|
||||
return ErrorCode::NoError;
|
||||
},
|
||||
[]() { return ErrorCode::AmneziaServiceConnectionFailed; });
|
||||
|
||||
@@ -386,6 +386,7 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
||||
if (!parseStringList(obj, "vpnDisabledApps", config.m_vpnDisabledApps)) {
|
||||
return false;
|
||||
}
|
||||
config.m_appSplitTunnelType = obj.value("appSplitTunnelType").toInt();
|
||||
if (!parseStringList(obj, "allowedDnsServers", config.m_allowedDnsServers)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ class Daemon : public QObject {
|
||||
// Callback before any Activating measure is done
|
||||
virtual void prepareActivation(const InterfaceConfig& config, int inetAdapterIndex = 0) {
|
||||
Q_UNUSED(config) };
|
||||
virtual void activateSplitTunnel(const InterfaceConfig& config, int vpnAdapterIndex = 0) {
|
||||
Q_UNUSED(config) };
|
||||
virtual bool activateSplitTunnel(const InterfaceConfig& config, int vpnAdapterIndex = 0) {
|
||||
Q_UNUSED(config) Q_UNUSED(vpnAdapterIndex) return true; };
|
||||
|
||||
QString logs();
|
||||
void cleanLogs();
|
||||
|
||||
@@ -64,6 +64,7 @@ QJsonObject InterfaceConfig::toJson() const {
|
||||
disabledApps.append(QJsonValue(i));
|
||||
}
|
||||
json.insert("vpnDisabledApps", disabledApps);
|
||||
json.insert("appSplitTunnelType", m_appSplitTunnelType);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ class InterfaceConfig {
|
||||
QList<IPAddress> m_allowedIPAddressRanges;
|
||||
QStringList m_excludedAddresses;
|
||||
QStringList m_vpnDisabledApps;
|
||||
int m_appSplitTunnelType = 0;
|
||||
QStringList m_allowedDnsServers;
|
||||
bool m_killSwitchEnabled;
|
||||
#if defined(MZ_ANDROID) || defined(MZ_IOS)
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "core/protocols/protocolUtils.h"
|
||||
#include "core/utils/constants/configKeys.h"
|
||||
#include "core/utils/constants/protocolConstants.h"
|
||||
#include "core/utils/routeModes.h"
|
||||
|
||||
// How many times do we try to reconnect.
|
||||
constexpr int MAX_CONNECTION_RETRY = 10;
|
||||
@@ -199,6 +200,9 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
} else {
|
||||
|
||||
// Use APP split tunnel
|
||||
const bool includeOnlyApps =
|
||||
appSplitTunnelType == static_cast<int>(amnezia::AppsRouteMode::VpnOnlyForwardApps)
|
||||
&& !splitTunnelApps.isEmpty();
|
||||
if (splitTunnelType == 0 || splitTunnelType == 2) {
|
||||
QJsonObject range_ipv4;
|
||||
range_ipv4.insert("address", "0.0.0.0");
|
||||
@@ -206,11 +210,13 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
range_ipv4.insert("isIpv6", false);
|
||||
jsAllowedIPAddesses.append(range_ipv4);
|
||||
|
||||
QJsonObject range_ipv6;
|
||||
range_ipv6.insert("address", "::");
|
||||
range_ipv6.insert("range", 0);
|
||||
range_ipv6.insert("isIpv6", true);
|
||||
jsAllowedIPAddesses.append(range_ipv6);
|
||||
if (!includeOnlyApps) {
|
||||
QJsonObject range_ipv6;
|
||||
range_ipv6.insert("address", "::");
|
||||
range_ipv6.insert("range", 0);
|
||||
range_ipv6.insert("isIpv6", true);
|
||||
jsAllowedIPAddesses.append(range_ipv6);
|
||||
}
|
||||
}
|
||||
|
||||
if (splitTunnelType == 1) {
|
||||
@@ -247,6 +253,7 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
json.insert("excludedAddresses", jsExcludedAddresses);
|
||||
|
||||
json.insert("vpnDisabledApps", splitTunnelApps);
|
||||
json.insert(amnezia::configKey::appSplitTunnelType, appSplitTunnelType);
|
||||
|
||||
json.insert("allowedDnsServers", allowedDns);
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#include "windowsfirewall.h"
|
||||
|
||||
#include "core/utils/networkUtilities.h"
|
||||
#include "core/utils/routeModes.h"
|
||||
#include "../windowscommons.h"
|
||||
|
||||
namespace {
|
||||
Logger logger("WindowsDaemon");
|
||||
@@ -61,33 +63,41 @@ void WindowsDaemon::prepareActivation(const InterfaceConfig& config, int inetAda
|
||||
}
|
||||
}
|
||||
|
||||
void WindowsDaemon::activateSplitTunnel(const InterfaceConfig& config, int vpnAdapterIndex) {
|
||||
bool WindowsDaemon::activateSplitTunnel(const InterfaceConfig& config, int vpnAdapterIndex) {
|
||||
const bool includeOnly =
|
||||
config.m_appSplitTunnelType ==
|
||||
static_cast<int>(amnezia::AppsRouteMode::VpnOnlyForwardApps);
|
||||
if (m_splitTunnelManager == nullptr) {
|
||||
if (config.m_vpnDisabledApps.length() > 0) {
|
||||
logger.error() << "Split tunnel manager is not initialized";
|
||||
emit backendFailure(DaemonError::ERROR_SPLIT_TUNNEL_INIT_FAILURE);
|
||||
return false;
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (config.m_vpnDisabledApps.length() > 0) {
|
||||
if (!m_splitTunnelManager->start(m_inetAdapterIndex, vpnAdapterIndex)) {
|
||||
if (!m_splitTunnelManager->start(m_inetAdapterIndex, vpnAdapterIndex,
|
||||
includeOnly)) {
|
||||
logger.error() << "Failed to start split tunnel";
|
||||
emit backendFailure(DaemonError::ERROR_SPLIT_TUNNEL_START_FAILURE);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (!m_splitTunnelManager->excludeApps(config.m_vpnDisabledApps)) {
|
||||
logger.error() << "Failed to apply split tunnel app exclusions";
|
||||
emit backendFailure(DaemonError::ERROR_SPLIT_TUNNEL_EXCLUDE_FAILURE);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (!m_splitTunnelManager->isRunning()) {
|
||||
logger.error() << "Split tunnel did not reach running state";
|
||||
emit backendFailure(DaemonError::ERROR_SPLIT_TUNNEL_START_FAILURE);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
m_splitTunnelManager->stop();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowsDaemon::run(Op op, const InterfaceConfig& config) {
|
||||
@@ -108,7 +118,14 @@ bool WindowsDaemon::run(Op op, const InterfaceConfig& config) {
|
||||
return true;
|
||||
}
|
||||
if (config.m_vpnDisabledApps.length() > 0) {
|
||||
if (!m_splitTunnelManager->start(m_inetAdapterIndex)) {
|
||||
const bool includeOnly =
|
||||
config.m_appSplitTunnelType ==
|
||||
static_cast<int>(amnezia::AppsRouteMode::VpnOnlyForwardApps);
|
||||
int vpnAdapterIndex = WindowsCommons::VPNAdapterIndex();
|
||||
if (vpnAdapterIndex < 0) {
|
||||
vpnAdapterIndex = 0;
|
||||
}
|
||||
if (!m_splitTunnelManager->start(m_inetAdapterIndex, vpnAdapterIndex, includeOnly)) {
|
||||
logger.error() << "Split tunnel start failed";
|
||||
emit backendFailure(DaemonError::ERROR_SPLIT_TUNNEL_START_FAILURE);
|
||||
return false;
|
||||
|
||||
@@ -24,7 +24,7 @@ class WindowsDaemon final : public Daemon {
|
||||
~WindowsDaemon();
|
||||
|
||||
void prepareActivation(const InterfaceConfig& config, int inetAdapterIndex = 0) override;
|
||||
void activateSplitTunnel(const InterfaceConfig& config, int vpnAdapterIndex = 0) override;
|
||||
bool activateSplitTunnel(const InterfaceConfig& config, int vpnAdapterIndex = 0) override;
|
||||
|
||||
protected:
|
||||
bool run(Op op, const InterfaceConfig& config) override;
|
||||
|
||||
@@ -173,7 +173,7 @@ bool WindowsFirewall::initSublayer() {
|
||||
L"DNS filters for split tunneling");
|
||||
}
|
||||
|
||||
bool WindowsFirewall::enableInterface(int vpnAdapterIndex) {
|
||||
bool WindowsFirewall::enableInterface(int vpnAdapterIndex, bool blockDns) {
|
||||
// Checks if the FW_Rule was enabled succesfully,
|
||||
// disables the whole killswitch and returns false if not.
|
||||
#define FW_OK(rule) \
|
||||
@@ -216,7 +216,9 @@ bool WindowsFirewall::enableInterface(int vpnAdapterIndex) {
|
||||
FW_OK(allowHyperVTraffic(MAX_WEIGHT, "Allow Hyper-V Traffic"));
|
||||
FW_OK(allowTrafficForAppOnAll(getCurrentPath(), MAX_WEIGHT,
|
||||
"Allow all for AmneziaVPN.exe"));
|
||||
FW_OK(blockTrafficOnPort(53, MED_WEIGHT, "Block all DNS"));
|
||||
if (blockDns) {
|
||||
FW_OK(blockTrafficOnPort(53, MED_WEIGHT, "Block all DNS"));
|
||||
}
|
||||
FW_OK(allowLoopbackTraffic(MED_WEIGHT,
|
||||
"Allow Loopback traffic on device %1"));
|
||||
|
||||
@@ -287,6 +289,8 @@ bool WindowsFirewall::allowTrafficRange(const QStringList& ranges) {
|
||||
|
||||
|
||||
bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
||||
const bool includeOnly =
|
||||
config.m_appSplitTunnelType == 1 && !config.m_vpnDisabledApps.isEmpty();
|
||||
// Start the firewall transaction
|
||||
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
@@ -301,9 +305,11 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
||||
// Build the firewall rules for this peer.
|
||||
logger.info() << "Enabling traffic for peer"
|
||||
<< config.m_serverPublicKey;
|
||||
if (!blockTrafficTo(config.m_allowedIPAddressRanges, LOW_WEIGHT,
|
||||
"Block Internet", config.m_serverPublicKey)) {
|
||||
return false;
|
||||
if (!includeOnly) {
|
||||
if (!blockTrafficTo(config.m_allowedIPAddressRanges, LOW_WEIGHT,
|
||||
"Block Internet", config.m_serverPublicKey)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!config.m_primaryDnsServer.isEmpty()) {
|
||||
if (!allowTrafficTo(QHostAddress(config.m_primaryDnsServer), 53, HIGH_WEIGHT,
|
||||
|
||||
@@ -38,7 +38,7 @@ class WindowsFirewall final : public QObject {
|
||||
static WindowsFirewall* create(QObject* parent);
|
||||
~WindowsFirewall() override;
|
||||
|
||||
bool enableInterface(int vpnAdapterIndex);
|
||||
bool enableInterface(int vpnAdapterIndex, bool blockDns = true);
|
||||
bool enableLanBypass(const QList<IPAddress>& ranges);
|
||||
bool enablePeerTraffic(const InterfaceConfig& config);
|
||||
bool disablePeerTraffic(const QString& pubkey);
|
||||
|
||||
@@ -319,7 +319,8 @@ bool WindowsSplitTunnel::excludeApps(const QStringList& appPaths) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowsSplitTunnel::start(int inetAdapterIndex, int vpnAdapterIndex) {
|
||||
bool WindowsSplitTunnel::start(int inetAdapterIndex, int vpnAdapterIndex,
|
||||
bool invertTunnelInternet) {
|
||||
// To Start we need to send 2 things:
|
||||
// Network info (what is vpn what is network)
|
||||
logger.debug() << "Starting SplitTunnel";
|
||||
@@ -361,7 +362,8 @@ bool WindowsSplitTunnel::start(int inetAdapterIndex, int vpnAdapterIndex) {
|
||||
}
|
||||
logger.debug() << "Driver is ready || new State:" << stateString();
|
||||
|
||||
auto config = generateIPConfiguration(inetAdapterIndex, vpnAdapterIndex);
|
||||
auto config = generateIPConfiguration(inetAdapterIndex, vpnAdapterIndex,
|
||||
invertTunnelInternet);
|
||||
if (config.empty()) {
|
||||
logger.error() << "Network configuration blob is empty. Internet adapter:"
|
||||
<< inetAdapterIndex << "VPN adapter:" << vpnAdapterIndex;
|
||||
@@ -480,7 +482,7 @@ std::vector<uint8_t> WindowsSplitTunnel::generateAppConfiguration(
|
||||
}
|
||||
|
||||
std::vector<std::byte> WindowsSplitTunnel::generateIPConfiguration(
|
||||
int inetAdapterIndex, int vpnAdapterIndex) {
|
||||
int inetAdapterIndex, int vpnAdapterIndex, bool invertTunnelInternet) {
|
||||
std::vector<std::byte> out(sizeof(IP_ADDRESSES_CONFIG));
|
||||
|
||||
auto config = reinterpret_cast<IP_ADDRESSES_CONFIG*>(&out[0]);
|
||||
@@ -488,46 +490,68 @@ std::vector<std::byte> WindowsSplitTunnel::generateIPConfiguration(
|
||||
if (vpnAdapterIndex == 0) {
|
||||
vpnAdapterIndex = WindowsCommons::VPNAdapterIndex();
|
||||
}
|
||||
// Always the VPN
|
||||
if (!getAddress(vpnAdapterIndex, &config->TunnelIpv4,
|
||||
&config->TunnelIpv6)) {
|
||||
|
||||
IN_ADDR vpnIpv4{};
|
||||
IN6_ADDR vpnIpv6{};
|
||||
if (!getAddress(vpnAdapterIndex, &vpnIpv4, &vpnIpv6)) {
|
||||
return {};
|
||||
}
|
||||
// 2nd best route is usually the internet adapter
|
||||
if (!getAddress(inetAdapterIndex, &config->InternetIpv4,
|
||||
&config->InternetIpv6)) {
|
||||
IN_ADDR internetIpv4{};
|
||||
IN6_ADDR internetIpv6{};
|
||||
if (!getAddress(inetAdapterIndex, &internetIpv4, &internetIpv6)) {
|
||||
return {};
|
||||
};
|
||||
}
|
||||
|
||||
if (invertTunnelInternet) {
|
||||
config->TunnelIpv4 = internetIpv4;
|
||||
config->InternetIpv4 = vpnIpv4;
|
||||
if (!IN6_IS_ADDR_UNSPECIFIED(&vpnIpv6)) {
|
||||
config->TunnelIpv6 = internetIpv6;
|
||||
config->InternetIpv6 = vpnIpv6;
|
||||
}
|
||||
} else {
|
||||
config->TunnelIpv4 = vpnIpv4;
|
||||
config->TunnelIpv6 = vpnIpv6;
|
||||
config->InternetIpv4 = internetIpv4;
|
||||
config->InternetIpv6 = internetIpv6;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
bool WindowsSplitTunnel::getAddress(int adapterIndex, IN_ADDR* out_ipv4,
|
||||
IN6_ADDR* out_ipv6) {
|
||||
QNetworkInterface target =
|
||||
QNetworkInterface::interfaceFromIndex(adapterIndex);
|
||||
logger.debug() << "Getting adapter info for:" << target.humanReadableName();
|
||||
|
||||
auto get = [&target](QAbstractSocket::NetworkLayerProtocol protocol) {
|
||||
for (auto address : target.addressEntries()) {
|
||||
if (address.ip().protocol() != protocol) {
|
||||
for (const QNetworkAddressEntry& entry : target.addressEntries()) {
|
||||
const QHostAddress address = entry.ip();
|
||||
if (address.protocol() != protocol) {
|
||||
continue;
|
||||
}
|
||||
return address.ip().toString().toStdWString();
|
||||
if (address.isNull() || address.isLoopback() || address.isLinkLocal() ||
|
||||
address.isBroadcast() || address.isMulticast()) {
|
||||
continue;
|
||||
}
|
||||
return address.toString().split('%').first().toStdWString();
|
||||
}
|
||||
return std::wstring{};
|
||||
};
|
||||
auto ipv4 = get(QAbstractSocket::IPv4Protocol);
|
||||
auto ipv6 = get(QAbstractSocket::IPv6Protocol);
|
||||
|
||||
if (InetPtonW(AF_INET, ipv4.c_str(), out_ipv4) != 1) {
|
||||
logger.debug() << "Ipv4 Conversation error" << WSAGetLastError();
|
||||
if (ipv4.empty()) {
|
||||
logger.error() << "No routable IPv4 address on adapter" << adapterIndex;
|
||||
return false;
|
||||
}
|
||||
if (ipv6.empty()) {
|
||||
std::memset(out_ipv6, 0x00, sizeof(IN6_ADDR));
|
||||
return true;
|
||||
if (InetPtonW(AF_INET, ipv4.c_str(), out_ipv4) != 1) {
|
||||
logger.error() << "Ipv4 conversion error" << WSAGetLastError();
|
||||
return false;
|
||||
}
|
||||
if (InetPtonW(AF_INET6, ipv6.c_str(), out_ipv6) != 1) {
|
||||
logger.debug() << "Ipv6 Conversation error" << WSAGetLastError();
|
||||
|
||||
std::memset(out_ipv6, 0x00, sizeof(IN6_ADDR));
|
||||
if (!ipv6.empty() && InetPtonW(AF_INET6, ipv6.c_str(), out_ipv6) != 1) {
|
||||
logger.error() << "Ipv6 conversion error" << WSAGetLastError();
|
||||
std::memset(out_ipv6, 0x00, sizeof(IN6_ADDR));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,8 @@ class WindowsSplitTunnel final {
|
||||
bool excludeApps(const QStringList& appPaths);
|
||||
|
||||
// Fetches and Pushed needed info to move to engaged mode
|
||||
bool start(int inetAdapterIndex, int vpnAdapterIndex = 0);
|
||||
bool start(int inetAdapterIndex, int vpnAdapterIndex = 0,
|
||||
bool invertTunnelInternet = false);
|
||||
// Deletes Rules and puts the driver into passive mode
|
||||
void stop();
|
||||
|
||||
@@ -85,7 +86,9 @@ class WindowsSplitTunnel final {
|
||||
// Generates a Configuration for Each APP
|
||||
std::vector<uint8_t> generateAppConfiguration(const QStringList& appPaths);
|
||||
// Generates a Configuration which IP's are VPN and which network
|
||||
std::vector<std::byte> generateIPConfiguration(int inetAdapterIndex, int vpnAdapterIndex = 0);
|
||||
std::vector<std::byte> generateIPConfiguration(
|
||||
int inetAdapterIndex, int vpnAdapterIndex = 0,
|
||||
bool invertTunnelInternet = false);
|
||||
std::vector<uint8_t> generateProcessBlob();
|
||||
|
||||
[[nodiscard]] bool getAddress(int adapterIndex, IN_ADDR* out_ipv4,
|
||||
|
||||
@@ -15,11 +15,43 @@
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "windowsfirewall.h"
|
||||
#include "core/utils/routeModes.h"
|
||||
|
||||
#pragma comment(lib, "iphlpapi.lib")
|
||||
|
||||
namespace {
|
||||
Logger logger("WireguardUtilsWindows");
|
||||
|
||||
static constexpr ULONG kIncludeAppsInterfaceMetric = 5000;
|
||||
|
||||
static bool setInterfaceMetric(quint64 luidValue, ULONG metric)
|
||||
{
|
||||
NET_LUID luid;
|
||||
luid.Value = luidValue;
|
||||
|
||||
MIB_IPINTERFACE_ROW iface;
|
||||
InitializeIpInterfaceEntry(&iface);
|
||||
iface.Family = AF_INET;
|
||||
iface.InterfaceLuid = luid;
|
||||
|
||||
DWORD res = GetIpInterfaceEntry(&iface);
|
||||
if (res != NO_ERROR) {
|
||||
logger.error() << "GetIpInterfaceEntry failed, error:" << res;
|
||||
return false;
|
||||
}
|
||||
|
||||
iface.UseAutomaticMetric = FALSE;
|
||||
iface.Metric = metric;
|
||||
iface.SitePrefixLength = 0;
|
||||
|
||||
res = SetIpInterfaceEntry(&iface);
|
||||
if (res != NO_ERROR) {
|
||||
logger.error() << "SetIpInterfaceEntry failed, error:" << res;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}; // namespace
|
||||
|
||||
std::unique_ptr<WireguardUtilsWindows> WireguardUtilsWindows::create(
|
||||
@@ -125,13 +157,17 @@ bool WireguardUtilsWindows::addInterface(const InterfaceConfig& config) {
|
||||
}
|
||||
m_luid = luid.Value;
|
||||
m_routeMonitor = new WindowsRouteMonitor(luid.Value, this);
|
||||
m_includeOnlyApps =
|
||||
config.m_appSplitTunnelType ==
|
||||
static_cast<int>(amnezia::AppsRouteMode::VpnOnlyForwardApps) &&
|
||||
!config.m_vpnDisabledApps.isEmpty();
|
||||
|
||||
if (config.m_killSwitchEnabled) {
|
||||
// Enable the windows firewall
|
||||
NET_IFINDEX ifindex;
|
||||
ConvertInterfaceLuidToIndex(&luid, &ifindex);
|
||||
m_firewall->allowAllTraffic();
|
||||
m_firewall->enableInterface(ifindex);
|
||||
m_firewall->enableInterface(ifindex, !m_includeOnlyApps);
|
||||
}
|
||||
|
||||
logger.debug() << "Registration completed";
|
||||
@@ -143,6 +179,7 @@ bool WireguardUtilsWindows::deleteInterface() {
|
||||
m_routeMonitor->deleteLater();
|
||||
}
|
||||
|
||||
m_includeOnlyApps = false;
|
||||
m_firewall->disableKillSwitch();
|
||||
m_tunnel.stop();
|
||||
return true;
|
||||
@@ -255,7 +292,15 @@ void WireguardUtilsWindows::buildMibForwardRow(const IPAddress& prefix,
|
||||
}
|
||||
|
||||
bool WireguardUtilsWindows::updateRoutePrefix(const IPAddress& prefix) {
|
||||
if (m_routeMonitor && (prefix.prefixLength() == 0)) {
|
||||
if (m_includeOnlyApps && prefix.prefixLength() == 0) {
|
||||
if (prefix.type() == QAbstractSocket::IPv6Protocol) {
|
||||
return true;
|
||||
}
|
||||
if (!setInterfaceMetric(m_luid, kIncludeAppsInterfaceMetric)) {
|
||||
logger.error() << "Refusing to install a default route without include metric";
|
||||
return false;
|
||||
}
|
||||
} else if (m_routeMonitor && (prefix.prefixLength() == 0)) {
|
||||
// If we are setting up a default route, instruct the route monitor to
|
||||
// capture traffic to all non-excluded destinations
|
||||
m_routeMonitor->setDetaultRouteCapture(true);
|
||||
@@ -286,7 +331,11 @@ bool WireguardUtilsWindows::updateRoutePrefix(const IPAddress& prefix) {
|
||||
}
|
||||
|
||||
bool WireguardUtilsWindows::deleteRoutePrefix(const IPAddress& prefix) {
|
||||
if (m_routeMonitor && (prefix.prefixLength() == 0)) {
|
||||
if (m_includeOnlyApps && prefix.prefixLength() == 0 &&
|
||||
prefix.type() == QAbstractSocket::IPv6Protocol) {
|
||||
return true;
|
||||
}
|
||||
if (m_routeMonitor && (prefix.prefixLength() == 0) && !m_includeOnlyApps) {
|
||||
// Deactivate the route capture feature.
|
||||
m_routeMonitor->setDetaultRouteCapture(false);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ class WireguardUtilsWindows final : public WireguardUtils {
|
||||
void buildMibForwardRow(const IPAddress& prefix, void* row);
|
||||
|
||||
quint64 m_luid = 0;
|
||||
bool m_includeOnlyApps = false;
|
||||
WindowsTunnelService m_tunnel;
|
||||
QPointer<WindowsRouteMonitor> m_routeMonitor;
|
||||
QPointer<WindowsFirewall> m_firewall;
|
||||
|
||||
@@ -111,7 +111,7 @@ PageType {
|
||||
|
||||
headerText: qsTr("Mode")
|
||||
|
||||
enabled: (Qt.platform.os === "android") && root.pageEnabled
|
||||
enabled: root.pageEnabled
|
||||
|
||||
listView: ListViewWithRadioButtonType {
|
||||
rootWidth: root.width
|
||||
@@ -151,10 +151,12 @@ PageType {
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
textString: qsTr("Only \"Apps from the list should not have access via VPN\" mode is available on Windows")
|
||||
textString: qsTr("Site-based split tunneling is ignored when only listed apps use the VPN")
|
||||
iconPath: "qrc:/images/controls/alert-circle.svg"
|
||||
|
||||
visible: (Qt.platform.os === "windows") && root.pageEnabled
|
||||
visible: (Qt.platform.os === "windows")
|
||||
&& root.pageEnabled
|
||||
&& AppSplitTunnelingController.routeMode === routeMode.onlyForwardApps
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -136,7 +136,20 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
|
||||
QString dns1 = m_vpnConfiguration.value(configKey::dns1).toString();
|
||||
QString dns2 = m_vpnConfiguration.value(configKey::dns2).toString();
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
#ifdef Q_OS_WIN
|
||||
const RouteMode effectiveRouteMode =
|
||||
static_cast<RouteMode>(m_vpnConfiguration.value(configKey::splitTunnelType).toInt());
|
||||
const bool isXray = container == DockerContainer::Xray
|
||||
|| container == DockerContainer::SSXray;
|
||||
const bool includeOnlyApps =
|
||||
m_vpnConfiguration.value(configKey::appSplitTunnelType).toInt() ==
|
||||
static_cast<int>(amnezia::AppsRouteMode::VpnOnlyForwardApps);
|
||||
|
||||
if (!isXray || effectiveRouteMode != amnezia::RouteMode::VpnAllSites
|
||||
|| includeOnlyApps) {
|
||||
iface->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << dns1 << dns2);
|
||||
}
|
||||
#elif defined(Q_OS_MACOS)
|
||||
if (!m_appSettingsRepository->isSitesSplitTunnelingEnabled() || m_appSettingsRepository->routeMode() != amnezia::RouteMode::VpnAllExceptSites) {
|
||||
iface->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << dns1 << dns2);
|
||||
}
|
||||
@@ -494,9 +507,6 @@ void VpnConnection::appendSplitTunnelingConfig()
|
||||
}
|
||||
}
|
||||
|
||||
m_vpnConfiguration.insert(configKey::splitTunnelType, routeMode);
|
||||
m_vpnConfiguration.insert(configKey::splitTunnelSites, sitesJsonArray);
|
||||
|
||||
amnezia::AppsRouteMode appsRouteMode = amnezia::AppsRouteMode::VpnAllApps;
|
||||
QJsonArray appsJsonArray;
|
||||
if (m_appSettingsRepository->isAppsSplitTunnelingEnabled()) {
|
||||
@@ -512,6 +522,20 @@ void VpnConnection::appendSplitTunnelingConfig()
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
if (appsRouteMode == amnezia::AppsRouteMode::VpnOnlyForwardApps &&
|
||||
m_appSettingsRepository->isStrictKillSwitchEnabled()) {
|
||||
appsRouteMode = amnezia::AppsRouteMode::VpnAllApps;
|
||||
appsJsonArray = QJsonArray();
|
||||
}
|
||||
if (appsRouteMode == amnezia::AppsRouteMode::VpnOnlyForwardApps) {
|
||||
routeMode = amnezia::RouteMode::VpnAllSites;
|
||||
sitesJsonArray = QJsonArray();
|
||||
}
|
||||
#endif
|
||||
|
||||
m_vpnConfiguration.insert(configKey::splitTunnelType, routeMode);
|
||||
m_vpnConfiguration.insert(configKey::splitTunnelSites, sitesJsonArray);
|
||||
m_vpnConfiguration.insert(configKey::appSplitTunnelType, appsRouteMode);
|
||||
m_vpnConfiguration.insert(configKey::splitTunnelApps, appsJsonArray);
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ class IpcInterface
|
||||
SLOT( int routeAddList(const QString &gw, const QStringList &ips) );
|
||||
SLOT( bool clearSavedRoutes() );
|
||||
SLOT( bool routeDeleteList(const QString &gw, const QStringList &ip) );
|
||||
SLOT( bool routeAddDefault(const QString &dev) );
|
||||
SLOT( bool routeDeleteDefault(const QString &dev) );
|
||||
SLOT( bool flushDns() );
|
||||
SLOT( void resetIpStack() );
|
||||
|
||||
|
||||
@@ -91,6 +91,24 @@ bool IpcServer::routeDeleteList(const QString &gw, const QStringList &ips)
|
||||
return Router::routeDeleteList(gw, ips);
|
||||
}
|
||||
|
||||
bool IpcServer::routeAddDefault(const QString &dev)
|
||||
{
|
||||
#ifdef MZ_DEBUG
|
||||
qDebug() << "IpcServer::routeAddDefault";
|
||||
#endif
|
||||
|
||||
return Router::routeAddDefault(dev);
|
||||
}
|
||||
|
||||
bool IpcServer::routeDeleteDefault(const QString &dev)
|
||||
{
|
||||
#ifdef MZ_DEBUG
|
||||
qDebug() << "IpcServer::routeDeleteDefault";
|
||||
#endif
|
||||
|
||||
return Router::routeDeleteDefault(dev);
|
||||
}
|
||||
|
||||
bool IpcServer::flushDns()
|
||||
{
|
||||
#ifdef MZ_DEBUG
|
||||
|
||||
@@ -22,6 +22,8 @@ public:
|
||||
virtual int routeAddList(const QString &gw, const QStringList &ips) override;
|
||||
virtual bool clearSavedRoutes() override;
|
||||
virtual bool routeDeleteList(const QString &gw, const QStringList &ips) override;
|
||||
virtual bool routeAddDefault(const QString &dev) override;
|
||||
virtual bool routeDeleteDefault(const QString &dev) override;
|
||||
virtual bool flushDns() override;
|
||||
virtual void resetIpStack() override;
|
||||
virtual bool checkAndInstallDriver() override;
|
||||
|
||||
@@ -34,6 +34,7 @@ static bool isValidIpOrCidr(const QString &value) {
|
||||
#ifdef Q_OS_WIN
|
||||
#include "../client/platforms/windows/daemon/windowsfirewall.h"
|
||||
#include "../client/platforms/windows/daemon/windowsdaemon.h"
|
||||
#include "router_win.h"
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
@@ -97,7 +98,30 @@ bool KillSwitch::isStrictKillSwitchEnabled()
|
||||
return m_appSettigns->value("Conf/strictKillSwitchEnabled", false).toBool();
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
static bool isIncludeOnlyApps(const QJsonObject &configStr)
|
||||
{
|
||||
return configStr.value(amnezia::configKey::appSplitTunnelType).toInt() == 1
|
||||
&& !configStr.value(amnezia::configKey::splitTunnelApps).toArray().isEmpty();
|
||||
}
|
||||
|
||||
static void applyDefaultRouteInterfaceMetric(const QJsonObject &configStr)
|
||||
{
|
||||
static constexpr ULONG kExcludeAppsMetric = 1;
|
||||
static constexpr ULONG kIncludeAppsMetric = 5000;
|
||||
RouterWin::Instance().setDefaultRouteInterfaceMetric(
|
||||
isIncludeOnlyApps(configStr) ? kIncludeAppsMetric : kExcludeAppsMetric);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool KillSwitch::disableKillSwitch() {
|
||||
#ifdef Q_OS_WIN
|
||||
RouterWin::Instance().setDefaultRouteInterfaceMetric(1);
|
||||
if (Daemon *daemon = WindowsDaemon::instance()) {
|
||||
daemon->activateSplitTunnel(InterfaceConfig());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
if (isStrictKillSwitchEnabled()) {
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
|
||||
@@ -272,6 +296,8 @@ bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) {
|
||||
}
|
||||
}
|
||||
|
||||
config.m_appSplitTunnelType =
|
||||
configStr.value(amnezia::configKey::appSplitTunnelType).toInt();
|
||||
for (const QJsonValue &i : configStr.value(amnezia::configKey::splitTunnelApps).toArray()) {
|
||||
if (!i.isString()) {
|
||||
break;
|
||||
@@ -286,23 +312,25 @@ bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) {
|
||||
config.m_allowedDnsServers.append(dns.toString());
|
||||
}
|
||||
|
||||
applyDefaultRouteInterfaceMetric(configStr);
|
||||
|
||||
// killSwitch toggle
|
||||
if (QVariant(configStr.value(amnezia::configKey::killSwitchOption).toString()).toBool()) {
|
||||
WindowsFirewall::create(this)->enablePeerTraffic(config);
|
||||
}
|
||||
|
||||
WindowsDaemon::instance()->prepareActivation(config, inetAdapterIndex);
|
||||
WindowsDaemon::instance()->activateSplitTunnel(config, vpnAdapterIndex);
|
||||
return WindowsDaemon::instance()->activateSplitTunnel(config, vpnAdapterIndex);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex) {
|
||||
#ifdef Q_OS_WIN
|
||||
if (configStr.value("splitTunnelType").toInt() != 0) {
|
||||
if (configStr.value("splitTunnelType").toInt() != 0 || isIncludeOnlyApps(configStr)) {
|
||||
WindowsFirewall::create(this)->allowAllTraffic();
|
||||
}
|
||||
return WindowsFirewall::create(this)->enableInterface(vpnAdapterIndex);
|
||||
return WindowsFirewall::create(this)->enableInterface(vpnAdapterIndex, !isIncludeOnlyApps(configStr));
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
|
||||
|
||||
@@ -42,6 +42,26 @@ int Router::routeDeleteList(const QString &gw, const QStringList &ips)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Router::routeAddDefault(const QString &dev)
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
return RouterWin::Instance().routeAddDefault(dev);
|
||||
#else
|
||||
Q_UNUSED(dev)
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Router::routeDeleteDefault(const QString &dev)
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
return RouterWin::Instance().routeDeleteDefault(dev);
|
||||
#else
|
||||
Q_UNUSED(dev)
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Router::flushDns()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
@@ -19,6 +19,8 @@ public:
|
||||
static int routeAddList(const QString &gw, const QStringList &ips);
|
||||
static bool clearSavedRoutes();
|
||||
static int routeDeleteList(const QString &gw, const QStringList &ips);
|
||||
static bool routeAddDefault(const QString &dev);
|
||||
static bool routeDeleteDefault(const QString &dev);
|
||||
static bool flushDns();
|
||||
static void resetIpStack();
|
||||
static bool createTun(const QString &dev, const QString &subnet);
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
LONG (NTAPI * NtSuspendProcess)(HANDLE ProcessHandle) = NULL;
|
||||
LONG (NTAPI * NtResumeProcess)(HANDLE ProcessHandle) = NULL;
|
||||
|
||||
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
|
||||
#define STATUS_SUCCESS ((LONG)0x00000000L)
|
||||
|
||||
QList<QString> RouterWin::kIpv6Subnets = { "fc00::/7", "2000::/4", "3000::/4" };
|
||||
|
||||
@@ -149,8 +149,122 @@ int RouterWin::routeAddList(const QString &gw, const QStringList &ips)
|
||||
return success_count;
|
||||
}
|
||||
|
||||
bool RouterWin::buildDefaultRow(const QString &dev, MIB_IPFORWARD_ROW2 *entry)
|
||||
{
|
||||
NET_LUID luid;
|
||||
DWORD res = ConvertInterfaceAliasToLuid(reinterpret_cast<const wchar_t *>(dev.utf16()), &luid);
|
||||
if (res != NO_ERROR) {
|
||||
qWarning() << "RouterWin: failed to resolve luid for" << dev << ", error:" << res;
|
||||
return false;
|
||||
}
|
||||
|
||||
InitializeIpForwardEntry(entry);
|
||||
|
||||
entry->InterfaceLuid = luid;
|
||||
entry->DestinationPrefix.Prefix.si_family = AF_INET;
|
||||
entry->DestinationPrefix.Prefix.Ipv4.sin_family = AF_INET;
|
||||
entry->DestinationPrefix.Prefix.Ipv4.sin_addr.s_addr = INADDR_ANY;
|
||||
entry->DestinationPrefix.PrefixLength = 0;
|
||||
|
||||
entry->NextHop.si_family = AF_INET;
|
||||
entry->NextHop.Ipv4.sin_family = AF_INET;
|
||||
entry->NextHop.Ipv4.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
entry->ValidLifetime = 0xffffffff;
|
||||
entry->PreferredLifetime = 0xffffffff;
|
||||
entry->Metric = 0;
|
||||
entry->Protocol = MIB_IPPROTO_NETMGMT;
|
||||
entry->Loopback = false;
|
||||
entry->AutoconfigureAddress = false;
|
||||
entry->Publish = false;
|
||||
entry->Immortal = false;
|
||||
entry->Age = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static constexpr ULONG kTunnelInterfaceMetric = 1;
|
||||
|
||||
void RouterWin::setDefaultRouteInterfaceMetric(ULONG metric)
|
||||
{
|
||||
m_defaultRouteInterfaceMetric = metric == 0 ? kTunnelInterfaceMetric : metric;
|
||||
}
|
||||
|
||||
static bool setInterfaceMetric(const NET_LUID &luid, ULONG metric)
|
||||
{
|
||||
MIB_IPINTERFACE_ROW iface;
|
||||
InitializeIpInterfaceEntry(&iface);
|
||||
iface.Family = AF_INET;
|
||||
iface.InterfaceLuid = luid;
|
||||
|
||||
DWORD res = GetIpInterfaceEntry(&iface);
|
||||
if (res != NO_ERROR) {
|
||||
qWarning() << "RouterWin: GetIpInterfaceEntry failed, error:" << res;
|
||||
return false;
|
||||
}
|
||||
|
||||
iface.UseAutomaticMetric = FALSE;
|
||||
iface.Metric = metric;
|
||||
iface.SitePrefixLength = 0;
|
||||
|
||||
res = SetIpInterfaceEntry(&iface);
|
||||
if (res != NO_ERROR) {
|
||||
qWarning() << "RouterWin: SetIpInterfaceEntry failed, error:" << res;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RouterWin::routeAddDefault(const QString &dev)
|
||||
{
|
||||
MIB_IPFORWARD_ROW2 entry;
|
||||
if (!buildDefaultRow(dev, &entry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!setInterfaceMetric(entry.InterfaceLuid, m_defaultRouteInterfaceMetric)) {
|
||||
qCritical() << "RouterWin::routeAddDefault: refusing to install a default route on" << dev
|
||||
<< "without a deterministic interface metric";
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD res = CreateIpForwardEntry2(&entry);
|
||||
if (res != NO_ERROR && res != ERROR_OBJECT_ALREADY_EXISTS) {
|
||||
qCritical() << "RouterWin::routeAddDefault: failed for" << dev << ", error:" << res;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_defaultRoutes.insert(dev, entry);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RouterWin::routeDeleteDefault(const QString &dev)
|
||||
{
|
||||
MIB_IPFORWARD_ROW2 entry;
|
||||
if (m_defaultRoutes.contains(dev)) {
|
||||
entry = m_defaultRoutes.take(dev);
|
||||
} else if (!buildDefaultRow(dev, &entry)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
DWORD res = DeleteIpForwardEntry2(&entry);
|
||||
if (res != NO_ERROR && res != ERROR_NOT_FOUND) {
|
||||
qCritical() << "RouterWin::routeDeleteDefault: failed for" << dev << ", error:" << res;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RouterWin::clearSavedRoutes()
|
||||
{
|
||||
const QStringList defaultRouteDevices = m_defaultRoutes.keys();
|
||||
for (const QString &dev : defaultRouteDevices) {
|
||||
routeDeleteDefault(dev);
|
||||
}
|
||||
|
||||
if (m_ipForwardRows.isEmpty()) return true;
|
||||
|
||||
qDebug() << "RouterWin::clearSavedRoutes forward rows size:" << m_ipForwardRows.size();
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <WS2tcpip.h>
|
||||
|
||||
#include <iphlpapi.h>
|
||||
#include <IcmpAPI.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -39,6 +38,9 @@ public:
|
||||
int routeAddList(const QString &gw, const QStringList &ips);
|
||||
bool clearSavedRoutes();
|
||||
int routeDeleteList(const QString &gw, const QStringList &ips);
|
||||
bool routeAddDefault(const QString &dev);
|
||||
bool routeDeleteDefault(const QString &dev);
|
||||
void setDefaultRouteInterfaceMetric(ULONG metric);
|
||||
bool flushDns();
|
||||
void resetIpStack();
|
||||
|
||||
@@ -63,10 +65,13 @@ private:
|
||||
BOOL SuspendProcess(BOOL fSuspend, DWORD dwProcessId);
|
||||
|
||||
QNetworkInterface findLoopbackIface();
|
||||
static bool buildDefaultRow(const QString &dev, MIB_IPFORWARD_ROW2 *entry);
|
||||
|
||||
private:
|
||||
RouterWin() {m_dnsUtil = new DnsUtilsWindows(this);}
|
||||
QMultiMap<QString, MIB_IPFORWARDROW> m_ipForwardRows;
|
||||
QMap<QString, MIB_IPFORWARD_ROW2> m_defaultRoutes;
|
||||
ULONG m_defaultRouteInterfaceMetric = 1;
|
||||
bool m_suspended = false;
|
||||
DnsUtilsWindows *m_dnsUtil;
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user