feature/mozilla upstream (#1237)
* cherry-pick 4dfcad96506fb5b88c5bb27342b6d9413fc361c9 from mozilla upstream * cherry-pick a95fa8c088b9edaff2de18751336942c2d145a9a from mozilla * cherry-pick commit 4fc1ebbad86a9abcafdc761725a7afd811c8d2d3 from mozilla * cherry-pick 4dfcad96506fb5b88c5bb27342b6d9413fc361c9 from mozilla upstream * cherry-pick 22de4fcbd454c64ff496c3380eeaeeb6afff4d64 from mozilla upstream * cherry-pick 649673be561b66c96367adf379da1545f8838763 from mozilla upstream * cherry-pick 41bdad34517d0ddaef32139482e5505d92e4b533 from mozilla upstream * cherry-pick f6e49a85538eaa230d3a8634fa7600966132ccab from mozilla upstream * cherry-pick 86c585387efa0a09c7937dfe799a90a666404fcd from mozilla upstream * cherry-pick a18c1fac740469ca3566751b74a16227518630c4 from mozilla upstream * fixed missing ; * added excludeLocalNetworks() for linux * build fixes on windows after cherry-picks * Add rules for excluded sites splittunell mode * Fix app splittunell when ipv6 is not setup * Fix Linux build --------- Co-authored-by: Mykola Baibuz <mykola.baibuz@gmail.com>
This commit is contained in:
parent
f1c6067485
commit
8ca31e0c90
27 changed files with 1119 additions and 607 deletions
|
|
@ -114,12 +114,23 @@ bool Daemon::activate(const InterfaceConfig& config) {
|
||||||
|
|
||||||
// Bring up the wireguard interface if not already done.
|
// Bring up the wireguard interface if not already done.
|
||||||
if (!wgutils()->interfaceExists()) {
|
if (!wgutils()->interfaceExists()) {
|
||||||
|
// Create the interface.
|
||||||
if (!wgutils()->addInterface(config)) {
|
if (!wgutils()->addInterface(config)) {
|
||||||
logger.error() << "Interface creation failed.";
|
logger.error() << "Interface creation failed.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bring the interface up.
|
||||||
|
if (supportIPUtils()) {
|
||||||
|
if (!iputils()->addInterfaceIPs(config)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!iputils()->setMTUAndUp(config)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Configure routing for excluded addresses.
|
// Configure routing for excluded addresses.
|
||||||
for (const QString& i : config.m_excludedAddresses) {
|
for (const QString& i : config.m_excludedAddresses) {
|
||||||
addExclusionRoute(IPAddress(i));
|
addExclusionRoute(IPAddress(i));
|
||||||
|
|
@ -135,15 +146,6 @@ bool Daemon::activate(const InterfaceConfig& config) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (supportIPUtils()) {
|
|
||||||
if (!iputils()->addInterfaceIPs(config)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!iputils()->setMTUAndUp(config)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set routing
|
// set routing
|
||||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||||
if (!wgutils()->updateRoutePrefix(ip)) {
|
if (!wgutils()->updateRoutePrefix(ip)) {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "daemon/daemonerrors.h"
|
||||||
|
#include "daemonerrors.h"
|
||||||
#include "dnsutils.h"
|
#include "dnsutils.h"
|
||||||
#include "interfaceconfig.h"
|
#include "interfaceconfig.h"
|
||||||
#include "iputils.h"
|
#include "iputils.h"
|
||||||
|
|
@ -51,7 +53,7 @@ class Daemon : public QObject {
|
||||||
*/
|
*/
|
||||||
void activationFailure();
|
void activationFailure();
|
||||||
void disconnected();
|
void disconnected();
|
||||||
void backendFailure();
|
void backendFailure(DaemonError reason = DaemonError::ERROR_FATAL);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool maybeUpdateResolvers(const InterfaceConfig& config);
|
bool maybeUpdateResolvers(const InterfaceConfig& config);
|
||||||
|
|
|
||||||
17
client/daemon/daemonerrors.h
Normal file
17
client/daemon/daemonerrors.h
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
enum class DaemonError : uint8_t {
|
||||||
|
ERROR_NONE = 0u,
|
||||||
|
ERROR_FATAL = 1u,
|
||||||
|
ERROR_SPLIT_TUNNEL_INIT_FAILURE = 2u,
|
||||||
|
ERROR_SPLIT_TUNNEL_START_FAILURE = 3u,
|
||||||
|
ERROR_SPLIT_TUNNEL_EXCLUDE_FAILURE = 4u,
|
||||||
|
|
||||||
|
DAEMON_ERROR_MAX = 5u,
|
||||||
|
};
|
||||||
|
|
@ -159,9 +159,10 @@ void DaemonLocalServerConnection::disconnected() {
|
||||||
write(obj);
|
write(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DaemonLocalServerConnection::backendFailure() {
|
void DaemonLocalServerConnection::backendFailure(DaemonError err) {
|
||||||
QJsonObject obj;
|
QJsonObject obj;
|
||||||
obj.insert("type", "backendFailure");
|
obj.insert("type", "backendFailure");
|
||||||
|
obj.insert("errorCode", static_cast<int>(err));
|
||||||
write(obj);
|
write(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "daemonerrors.h"
|
||||||
|
|
||||||
class QLocalSocket;
|
class QLocalSocket;
|
||||||
|
|
||||||
class DaemonLocalServerConnection final : public QObject {
|
class DaemonLocalServerConnection final : public QObject {
|
||||||
|
|
@ -23,7 +25,7 @@ class DaemonLocalServerConnection final : public QObject {
|
||||||
|
|
||||||
void connected(const QString& pubkey);
|
void connected(const QString& pubkey);
|
||||||
void disconnected();
|
void disconnected();
|
||||||
void backendFailure();
|
void backendFailure(DaemonError err);
|
||||||
|
|
||||||
void write(const QJsonObject& obj);
|
void write(const QJsonObject& obj);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,8 @@ class WireguardUtils : public QObject {
|
||||||
|
|
||||||
virtual bool addExclusionRoute(const IPAddress& prefix) = 0;
|
virtual bool addExclusionRoute(const IPAddress& prefix) = 0;
|
||||||
virtual bool deleteExclusionRoute(const IPAddress& prefix) = 0;
|
virtual bool deleteExclusionRoute(const IPAddress& prefix) = 0;
|
||||||
|
|
||||||
|
virtual bool excludeLocalNetworks(const QList<IPAddress>& addresses) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // WIREGUARDUTILS_H
|
#endif // WIREGUARDUTILS_H
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include "protocols/protocols_defs.h"
|
|
||||||
#include "localsocketcontroller.h"
|
#include "localsocketcontroller.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QHostAddress>
|
#include <QHostAddress>
|
||||||
|
|
@ -17,6 +18,9 @@
|
||||||
#include "leakdetector.h"
|
#include "leakdetector.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "models/server.h"
|
#include "models/server.h"
|
||||||
|
#include "daemon/daemonerrors.h"
|
||||||
|
|
||||||
|
#include "protocols/protocols_defs.h"
|
||||||
|
|
||||||
// How many times do we try to reconnect.
|
// How many times do we try to reconnect.
|
||||||
constexpr int MAX_CONNECTION_RETRY = 10;
|
constexpr int MAX_CONNECTION_RETRY = 10;
|
||||||
|
|
@ -451,9 +455,40 @@ void LocalSocketController::parseCommand(const QByteArray& command) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == "backendFailure") {
|
if (type == "backendFailure") {
|
||||||
qCritical() << "backendFailure";
|
if (!obj.contains("errorCode")) {
|
||||||
|
// report a generic error if we dont know what it is.
|
||||||
|
logger.error() << "generic backend failure error";
|
||||||
|
// REPORTERROR(ErrorHandler::ControllerError, "controller");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
auto errorCode = static_cast<uint8_t>(obj["errorCode"].toInt());
|
||||||
|
if (errorCode >= (uint8_t)DaemonError::DAEMON_ERROR_MAX) {
|
||||||
|
// Also report a generic error if the code is invalid.
|
||||||
|
logger.error() << "invalid backend failure error code";
|
||||||
|
// REPORTERROR(ErrorHandler::ControllerError, "controller");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (static_cast<DaemonError>(errorCode)) {
|
||||||
|
case DaemonError::ERROR_NONE:
|
||||||
|
[[fallthrough]];
|
||||||
|
case DaemonError::ERROR_FATAL:
|
||||||
|
logger.error() << "generic backend failure error (fatal or error none)";
|
||||||
|
// REPORTERROR(ErrorHandler::ControllerError, "controller");
|
||||||
|
break;
|
||||||
|
case DaemonError::ERROR_SPLIT_TUNNEL_INIT_FAILURE:
|
||||||
|
[[fallthrough]];
|
||||||
|
case DaemonError::ERROR_SPLIT_TUNNEL_START_FAILURE:
|
||||||
|
[[fallthrough]];
|
||||||
|
case DaemonError::ERROR_SPLIT_TUNNEL_EXCLUDE_FAILURE:
|
||||||
|
logger.error() << "split tunnel backend failure error";
|
||||||
|
//REPORTERROR(ErrorHandler::SplitTunnelError, "controller");
|
||||||
|
break;
|
||||||
|
case DaemonError::DAEMON_ERROR_MAX:
|
||||||
|
// We should not get here.
|
||||||
|
Q_ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (type == "logs") {
|
if (type == "logs") {
|
||||||
// We don't care if we are not waiting for logs.
|
// We don't care if we are not waiting for logs.
|
||||||
|
|
|
||||||
|
|
@ -297,31 +297,6 @@ QList<WireguardUtils::PeerStatus> WireguardUtilsLinux::getPeerStatus() {
|
||||||
return peerList;
|
return peerList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WireguardUtilsLinux::applyFirewallRules(FirewallParams& params)
|
|
||||||
{
|
|
||||||
// double-check + ensure our firewall is installed and enabled
|
|
||||||
if (!LinuxFirewall::isInstalled()) LinuxFirewall::install();
|
|
||||||
|
|
||||||
// Note: rule precedence is handled inside IpTablesFirewall
|
|
||||||
LinuxFirewall::ensureRootAnchorPriority();
|
|
||||||
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), params.blockAll);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), params.allowNets);
|
|
||||||
LinuxFirewall::updateAllowNets(params.allowAddrs);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), params.blockNets);
|
|
||||||
LinuxFirewall::updateBlockNets(params.blockAddrs);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), true);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), true);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), true);
|
|
||||||
LinuxFirewall::updateDNSServers(params.dnsServers);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true);
|
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WireguardUtilsLinux::updateRoutePrefix(const IPAddress& prefix) {
|
bool WireguardUtilsLinux::updateRoutePrefix(const IPAddress& prefix) {
|
||||||
if (!m_rtmonitor) {
|
if (!m_rtmonitor) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -377,6 +352,26 @@ bool WireguardUtilsLinux::deleteExclusionRoute(const IPAddress& prefix) {
|
||||||
return m_rtmonitor->deleteExclusionRoute(prefix);
|
return m_rtmonitor->deleteExclusionRoute(prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WireguardUtilsLinux::excludeLocalNetworks(const QList<IPAddress>& routes) {
|
||||||
|
if (!m_rtmonitor) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explicitly discard LAN traffic that makes its way into the tunnel. This
|
||||||
|
// doesn't really exclude the LAN traffic, we just don't take any action to
|
||||||
|
// overrule the routes of other interfaces.
|
||||||
|
bool result = true;
|
||||||
|
for (const auto& prefix : routes) {
|
||||||
|
logger.error() << "Attempting to exclude:" << prefix.toString();
|
||||||
|
if (!m_rtmonitor->insertRoute(prefix)) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: A kill switch would be nice though :)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
QString WireguardUtilsLinux::uapiCommand(const QString& command) {
|
QString WireguardUtilsLinux::uapiCommand(const QString& command) {
|
||||||
QLocalSocket socket;
|
QLocalSocket socket;
|
||||||
QTimer uapiTimeout;
|
QTimer uapiTimeout;
|
||||||
|
|
@ -450,3 +445,27 @@ QString WireguardUtilsLinux::waitForTunnelName(const QString& filename) {
|
||||||
|
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WireguardUtilsLinux::applyFirewallRules(FirewallParams& params)
|
||||||
|
{
|
||||||
|
// double-check + ensure our firewall is installed and enabled
|
||||||
|
if (!LinuxFirewall::isInstalled()) LinuxFirewall::install();
|
||||||
|
|
||||||
|
// Note: rule precedence is handled inside IpTablesFirewall
|
||||||
|
LinuxFirewall::ensureRootAnchorPriority();
|
||||||
|
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), params.blockAll);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), params.allowNets);
|
||||||
|
LinuxFirewall::updateAllowNets(params.allowAddrs);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), params.blockNets);
|
||||||
|
LinuxFirewall::updateBlockNets(params.blockAddrs);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), true);
|
||||||
|
LinuxFirewall::updateDNSServers(params.dnsServers);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,9 @@ public:
|
||||||
|
|
||||||
bool addExclusionRoute(const IPAddress& prefix) override;
|
bool addExclusionRoute(const IPAddress& prefix) override;
|
||||||
bool deleteExclusionRoute(const IPAddress& prefix) override;
|
bool deleteExclusionRoute(const IPAddress& prefix) override;
|
||||||
|
|
||||||
|
bool excludeLocalNetworks(const QList<IPAddress>& lanAddressRanges) override;
|
||||||
|
|
||||||
void applyFirewallRules(FirewallParams& params);
|
void applyFirewallRules(FirewallParams& params);
|
||||||
signals:
|
signals:
|
||||||
void backendFailure();
|
void backendFailure();
|
||||||
|
|
|
||||||
|
|
@ -358,8 +358,8 @@ void MacosRouteMonitor::rtmAppendAddr(struct rt_msghdr* rtm, size_t maxlen,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacosRouteMonitor::rtmSendRoute(int action, const IPAddress& prefix,
|
bool MacosRouteMonitor::rtmSendRoute(int action, const IPAddress& prefix,
|
||||||
unsigned int ifindex,
|
unsigned int ifindex, const void* gateway,
|
||||||
const void* gateway) {
|
int flags) {
|
||||||
constexpr size_t rtm_max_size = sizeof(struct rt_msghdr) +
|
constexpr size_t rtm_max_size = sizeof(struct rt_msghdr) +
|
||||||
sizeof(struct sockaddr_in6) * 2 +
|
sizeof(struct sockaddr_in6) * 2 +
|
||||||
sizeof(struct sockaddr_storage);
|
sizeof(struct sockaddr_storage);
|
||||||
|
|
@ -370,7 +370,7 @@ bool MacosRouteMonitor::rtmSendRoute(int action, const IPAddress& prefix,
|
||||||
rtm->rtm_version = RTM_VERSION;
|
rtm->rtm_version = RTM_VERSION;
|
||||||
rtm->rtm_type = action;
|
rtm->rtm_type = action;
|
||||||
rtm->rtm_index = ifindex;
|
rtm->rtm_index = ifindex;
|
||||||
rtm->rtm_flags = RTF_STATIC | RTF_UP;
|
rtm->rtm_flags = flags | RTF_STATIC | RTF_UP;
|
||||||
rtm->rtm_addrs = 0;
|
rtm->rtm_addrs = 0;
|
||||||
rtm->rtm_pid = 0;
|
rtm->rtm_pid = 0;
|
||||||
rtm->rtm_seq = m_rtseq++;
|
rtm->rtm_seq = m_rtseq++;
|
||||||
|
|
@ -490,7 +490,7 @@ bool MacosRouteMonitor::rtmFetchRoutes(int family) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacosRouteMonitor::insertRoute(const IPAddress& prefix) {
|
bool MacosRouteMonitor::insertRoute(const IPAddress& prefix, int flags) {
|
||||||
struct sockaddr_dl datalink;
|
struct sockaddr_dl datalink;
|
||||||
memset(&datalink, 0, sizeof(datalink));
|
memset(&datalink, 0, sizeof(datalink));
|
||||||
datalink.sdl_family = AF_LINK;
|
datalink.sdl_family = AF_LINK;
|
||||||
|
|
@ -502,11 +502,11 @@ bool MacosRouteMonitor::insertRoute(const IPAddress& prefix) {
|
||||||
datalink.sdl_slen = 0;
|
datalink.sdl_slen = 0;
|
||||||
memcpy(&datalink.sdl_data, qPrintable(m_ifname), datalink.sdl_nlen);
|
memcpy(&datalink.sdl_data, qPrintable(m_ifname), datalink.sdl_nlen);
|
||||||
|
|
||||||
return rtmSendRoute(RTM_ADD, prefix, m_ifindex, &datalink);
|
return rtmSendRoute(RTM_ADD, prefix, m_ifindex, &datalink, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacosRouteMonitor::deleteRoute(const IPAddress& prefix) {
|
bool MacosRouteMonitor::deleteRoute(const IPAddress& prefix, int flags) {
|
||||||
return rtmSendRoute(RTM_DELETE, prefix, m_ifindex, nullptr);
|
return rtmSendRoute(RTM_DELETE, prefix, m_ifindex, nullptr, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacosRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
|
bool MacosRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,8 @@ class MacosRouteMonitor final : public QObject {
|
||||||
MacosRouteMonitor(const QString& ifname, QObject* parent = nullptr);
|
MacosRouteMonitor(const QString& ifname, QObject* parent = nullptr);
|
||||||
~MacosRouteMonitor();
|
~MacosRouteMonitor();
|
||||||
|
|
||||||
bool insertRoute(const IPAddress& prefix);
|
bool insertRoute(const IPAddress& prefix, int flags = 0);
|
||||||
bool deleteRoute(const IPAddress& prefix);
|
bool deleteRoute(const IPAddress& prefix, int flags = 0);
|
||||||
int interfaceFlags() { return m_ifflags; }
|
int interfaceFlags() { return m_ifflags; }
|
||||||
|
|
||||||
bool addExclusionRoute(const IPAddress& prefix);
|
bool addExclusionRoute(const IPAddress& prefix);
|
||||||
|
|
@ -37,7 +37,7 @@ class MacosRouteMonitor final : public QObject {
|
||||||
void handleRtmUpdate(const struct rt_msghdr* msg, const QByteArray& payload);
|
void handleRtmUpdate(const struct rt_msghdr* msg, const QByteArray& payload);
|
||||||
void handleIfaceInfo(const struct if_msghdr* msg, const QByteArray& payload);
|
void handleIfaceInfo(const struct if_msghdr* msg, const QByteArray& payload);
|
||||||
bool rtmSendRoute(int action, const IPAddress& prefix, unsigned int ifindex,
|
bool rtmSendRoute(int action, const IPAddress& prefix, unsigned int ifindex,
|
||||||
const void* gateway);
|
const void* gateway, int flags = 0);
|
||||||
bool rtmFetchRoutes(int family);
|
bool rtmFetchRoutes(int family);
|
||||||
static void rtmAppendAddr(struct rt_msghdr* rtm, size_t maxlen, int rtaddr,
|
static void rtmAppendAddr(struct rt_msghdr* rtm, size_t maxlen, int rtaddr,
|
||||||
const void* sa);
|
const void* sa);
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#include "wireguardutilsmacos.h"
|
#include "wireguardutilsmacos.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <net/route.h>
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
|
@ -130,7 +131,6 @@ bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int err = uapiErrno(uapiCommand(message));
|
int err = uapiErrno(uapiCommand(message));
|
||||||
|
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
logger.error() << "Interface configuration failed:" << strerror(err);
|
logger.error() << "Interface configuration failed:" << strerror(err);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -211,7 +211,6 @@ bool WireguardUtilsMacos::updatePeer(const InterfaceConfig& config) {
|
||||||
logger.warning() << "Failed to create peer with no endpoints";
|
logger.warning() << "Failed to create peer with no endpoints";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
out << config.m_serverPort << "\n";
|
out << config.m_serverPort << "\n";
|
||||||
|
|
||||||
out << "replace_allowed_ips=true\n";
|
out << "replace_allowed_ips=true\n";
|
||||||
|
|
@ -323,10 +322,10 @@ bool WireguardUtilsMacos::deleteRoutePrefix(const IPAddress& prefix) {
|
||||||
if (!m_rtmonitor) {
|
if (!m_rtmonitor) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (prefix.prefixLength() > 0) {
|
|
||||||
return m_rtmonitor->insertRoute(prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (prefix.prefixLength() > 0) {
|
||||||
|
return m_rtmonitor->deleteRoute(prefix);
|
||||||
|
}
|
||||||
// Ensure that we do not replace the default route.
|
// Ensure that we do not replace the default route.
|
||||||
if (prefix.type() == QAbstractSocket::IPv4Protocol) {
|
if (prefix.type() == QAbstractSocket::IPv4Protocol) {
|
||||||
return m_rtmonitor->deleteRoute(IPAddress("0.0.0.0/1")) &&
|
return m_rtmonitor->deleteRoute(IPAddress("0.0.0.0/1")) &&
|
||||||
|
|
@ -346,31 +345,6 @@ bool WireguardUtilsMacos::addExclusionRoute(const IPAddress& prefix) {
|
||||||
return m_rtmonitor->addExclusionRoute(prefix);
|
return m_rtmonitor->addExclusionRoute(prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WireguardUtilsMacos::applyFirewallRules(FirewallParams& params)
|
|
||||||
{
|
|
||||||
// double-check + ensure our firewall is installed and enabled. This is necessary as
|
|
||||||
// other software may disable pfctl before re-enabling with their own rules (e.g other VPNs)
|
|
||||||
if (!MacOSFirewall::isInstalled()) MacOSFirewall::install();
|
|
||||||
|
|
||||||
MacOSFirewall::ensureRootAnchorPriority();
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true);
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), params.blockAll);
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), params.allowNets);
|
|
||||||
MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), params.allowNets,
|
|
||||||
QStringLiteral("allownets"), params.allowAddrs);
|
|
||||||
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("120.blockNets"), params.blockNets);
|
|
||||||
MacOSFirewall::setAnchorTable(QStringLiteral("120.blockNets"), params.blockNets,
|
|
||||||
QStringLiteral("blocknets"), params.blockAddrs);
|
|
||||||
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("200.allowVPN"), true);
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true);
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), true);
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), true);
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true);
|
|
||||||
MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), params.dnsServers);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WireguardUtilsMacos::deleteExclusionRoute(const IPAddress& prefix) {
|
bool WireguardUtilsMacos::deleteExclusionRoute(const IPAddress& prefix) {
|
||||||
if (!m_rtmonitor) {
|
if (!m_rtmonitor) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -378,6 +352,26 @@ bool WireguardUtilsMacos::deleteExclusionRoute(const IPAddress& prefix) {
|
||||||
return m_rtmonitor->deleteExclusionRoute(prefix);
|
return m_rtmonitor->deleteExclusionRoute(prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WireguardUtilsMacos::excludeLocalNetworks(const QList<IPAddress>& routes) {
|
||||||
|
if (!m_rtmonitor) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explicitly discard LAN traffic that makes its way into the tunnel. This
|
||||||
|
// doesn't really exclude the LAN traffic, we just don't take any action to
|
||||||
|
// overrule the routes of other interfaces.
|
||||||
|
bool result = true;
|
||||||
|
for (const auto& prefix : routes) {
|
||||||
|
logger.error() << "Attempting to exclude:" << prefix.toString();
|
||||||
|
if (!m_rtmonitor->insertRoute(prefix, RTF_IFSCOPE | RTF_REJECT)) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: A kill switch would be nice though :)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
QString WireguardUtilsMacos::uapiCommand(const QString& command) {
|
QString WireguardUtilsMacos::uapiCommand(const QString& command) {
|
||||||
QLocalSocket socket;
|
QLocalSocket socket;
|
||||||
QTimer uapiTimeout;
|
QTimer uapiTimeout;
|
||||||
|
|
@ -454,3 +448,28 @@ QString WireguardUtilsMacos::waitForTunnelName(const QString& filename) {
|
||||||
|
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WireguardUtilsMacos::applyFirewallRules(FirewallParams& params)
|
||||||
|
{
|
||||||
|
// double-check + ensure our firewall is installed and enabled. This is necessary as
|
||||||
|
// other software may disable pfctl before re-enabling with their own rules (e.g other VPNs)
|
||||||
|
if (!MacOSFirewall::isInstalled()) MacOSFirewall::install();
|
||||||
|
|
||||||
|
MacOSFirewall::ensureRootAnchorPriority();
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), params.blockAll);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), params.allowNets);
|
||||||
|
MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), params.allowNets,
|
||||||
|
QStringLiteral("allownets"), params.allowAddrs);
|
||||||
|
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("120.blockNets"), params.blockNets);
|
||||||
|
MacOSFirewall::setAnchorTable(QStringLiteral("120.blockNets"), params.blockNets,
|
||||||
|
QStringLiteral("blocknets"), params.blockAddrs);
|
||||||
|
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("200.allowVPN"), true);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), true);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), true);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true);
|
||||||
|
MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), params.dnsServers);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,9 @@ class WireguardUtilsMacos final : public WireguardUtils {
|
||||||
|
|
||||||
bool addExclusionRoute(const IPAddress& prefix) override;
|
bool addExclusionRoute(const IPAddress& prefix) override;
|
||||||
bool deleteExclusionRoute(const IPAddress& prefix) override;
|
bool deleteExclusionRoute(const IPAddress& prefix) override;
|
||||||
|
|
||||||
|
bool excludeLocalNetworks(const QList<IPAddress>& lanAddressRanges) override;
|
||||||
|
|
||||||
void applyFirewallRules(FirewallParams& params);
|
void applyFirewallRules(FirewallParams& params);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#include "windowsdaemon.h"
|
#include "windowsdaemon.h"
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
#include <qassert.h>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
|
@ -15,28 +16,34 @@
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
#include "daemon/daemonerrors.h"
|
||||||
#include "dnsutilswindows.h"
|
#include "dnsutilswindows.h"
|
||||||
#include "leakdetector.h"
|
#include "leakdetector.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "core/networkUtilities.h"
|
#include "platforms/windows/daemon/windowsfirewall.h"
|
||||||
|
#include "platforms/windows/daemon/windowssplittunnel.h"
|
||||||
#include "platforms/windows/windowscommons.h"
|
#include "platforms/windows/windowscommons.h"
|
||||||
#include "platforms/windows/windowsservicemanager.h"
|
|
||||||
#include "windowsfirewall.h"
|
#include "windowsfirewall.h"
|
||||||
|
|
||||||
|
#include "core/networkUtilities.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
Logger logger("WindowsDaemon");
|
Logger logger("WindowsDaemon");
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowsDaemon::WindowsDaemon() : Daemon(nullptr), m_splitTunnelManager(this) {
|
WindowsDaemon::WindowsDaemon() : Daemon(nullptr) {
|
||||||
MZ_COUNT_CTOR(WindowsDaemon);
|
MZ_COUNT_CTOR(WindowsDaemon);
|
||||||
|
m_firewallManager = WindowsFirewall::create(this);
|
||||||
|
Q_ASSERT(m_firewallManager != nullptr);
|
||||||
|
|
||||||
m_wgutils = new WireguardUtilsWindows(this);
|
m_wgutils = WireguardUtilsWindows::create(m_firewallManager, this);
|
||||||
m_dnsutils = new DnsUtilsWindows(this);
|
m_dnsutils = new DnsUtilsWindows(this);
|
||||||
|
m_splitTunnelManager = WindowsSplitTunnel::create(m_firewallManager);
|
||||||
|
|
||||||
connect(m_wgutils, &WireguardUtilsWindows::backendFailure, this,
|
connect(m_wgutils.get(), &WireguardUtilsWindows::backendFailure, this,
|
||||||
&WindowsDaemon::monitorBackendFailure);
|
&WindowsDaemon::monitorBackendFailure);
|
||||||
connect(this, &WindowsDaemon::activationFailure,
|
connect(this, &WindowsDaemon::activationFailure,
|
||||||
[]() { WindowsFirewall::instance()->disableKillSwitch(); });
|
[this]() { m_firewallManager->disableKillSwitch(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowsDaemon::~WindowsDaemon() {
|
WindowsDaemon::~WindowsDaemon() {
|
||||||
|
|
@ -57,28 +64,42 @@ void WindowsDaemon::prepareActivation(const InterfaceConfig& config, int inetAda
|
||||||
|
|
||||||
void WindowsDaemon::activateSplitTunnel(const InterfaceConfig& config, int vpnAdapterIndex) {
|
void WindowsDaemon::activateSplitTunnel(const InterfaceConfig& config, int vpnAdapterIndex) {
|
||||||
if (config.m_vpnDisabledApps.length() > 0) {
|
if (config.m_vpnDisabledApps.length() > 0) {
|
||||||
m_splitTunnelManager.start(m_inetAdapterIndex, vpnAdapterIndex);
|
m_splitTunnelManager->start(m_inetAdapterIndex, vpnAdapterIndex);
|
||||||
m_splitTunnelManager.setRules(config.m_vpnDisabledApps);
|
m_splitTunnelManager->excludeApps(config.m_vpnDisabledApps);
|
||||||
} else {
|
} else {
|
||||||
m_splitTunnelManager.stop();
|
m_splitTunnelManager->stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowsDaemon::run(Op op, const InterfaceConfig& config) {
|
bool WindowsDaemon::run(Op op, const InterfaceConfig& config) {
|
||||||
if (op == Down) {
|
if (!m_splitTunnelManager) {
|
||||||
m_splitTunnelManager.stop();
|
if (config.m_vpnDisabledApps.length() > 0) {
|
||||||
|
// The Client has sent us a list of disabled apps, but we failed
|
||||||
|
// to init the the split tunnel driver.
|
||||||
|
// So let the client know this was not possible
|
||||||
|
emit backendFailure(DaemonError::ERROR_SPLIT_TUNNEL_INIT_FAILURE);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (op == Up) {
|
if (op == Down) {
|
||||||
logger.debug() << "Tunnel UP, Starting SplitTunneling";
|
m_splitTunnelManager->stop();
|
||||||
if (!WindowsSplitTunnel::isInstalled()) {
|
return true;
|
||||||
logger.warning() << "Split Tunnel Driver not Installed yet, fixing this.";
|
|
||||||
WindowsSplitTunnel::installDriver();
|
|
||||||
}
|
}
|
||||||
|
if (config.m_vpnDisabledApps.length() > 0) {
|
||||||
|
if (!m_splitTunnelManager->start(m_inetAdapterIndex)) {
|
||||||
|
emit backendFailure(DaemonError::ERROR_SPLIT_TUNNEL_START_FAILURE);
|
||||||
|
};
|
||||||
|
if (!m_splitTunnelManager->excludeApps(config.m_vpnDisabledApps)) {
|
||||||
|
emit backendFailure(DaemonError::ERROR_SPLIT_TUNNEL_EXCLUDE_FAILURE);
|
||||||
|
};
|
||||||
|
// Now the driver should be running (State == 4)
|
||||||
|
if (!m_splitTunnelManager->isRunning()) {
|
||||||
|
emit backendFailure(DaemonError::ERROR_SPLIT_TUNNEL_START_FAILURE);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
activateSplitTunnel(config);
|
}
|
||||||
|
m_splitTunnelManager->stop();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,11 @@
|
||||||
#ifndef WINDOWSDAEMON_H
|
#ifndef WINDOWSDAEMON_H
|
||||||
#define WINDOWSDAEMON_H
|
#define WINDOWSDAEMON_H
|
||||||
|
|
||||||
|
#include <qpointer.h>
|
||||||
|
|
||||||
#include "daemon/daemon.h"
|
#include "daemon/daemon.h"
|
||||||
#include "dnsutilswindows.h"
|
#include "dnsutilswindows.h"
|
||||||
|
#include "windowsfirewall.h"
|
||||||
#include "windowssplittunnel.h"
|
#include "windowssplittunnel.h"
|
||||||
#include "windowstunnelservice.h"
|
#include "windowstunnelservice.h"
|
||||||
#include "wireguardutilswindows.h"
|
#include "wireguardutilswindows.h"
|
||||||
|
|
@ -25,7 +28,7 @@ class WindowsDaemon final : public Daemon {
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool run(Op op, const InterfaceConfig& config) override;
|
bool run(Op op, const InterfaceConfig& config) override;
|
||||||
WireguardUtils* wgutils() const override { return m_wgutils; }
|
WireguardUtils* wgutils() const override { return m_wgutils.get(); }
|
||||||
DnsUtils* dnsutils() override { return m_dnsutils; }
|
DnsUtils* dnsutils() override { return m_dnsutils; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -39,9 +42,10 @@ class WindowsDaemon final : public Daemon {
|
||||||
|
|
||||||
int m_inetAdapterIndex = -1;
|
int m_inetAdapterIndex = -1;
|
||||||
|
|
||||||
WireguardUtilsWindows* m_wgutils = nullptr;
|
std::unique_ptr<WireguardUtilsWindows> m_wgutils;
|
||||||
DnsUtilsWindows* m_dnsutils = nullptr;
|
DnsUtilsWindows* m_dnsutils = nullptr;
|
||||||
WindowsSplitTunnel m_splitTunnelManager;
|
std::unique_ptr<WindowsSplitTunnel> m_splitTunnelManager;
|
||||||
|
QPointer<WindowsFirewall> m_firewallManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // WINDOWSDAEMON_H
|
#endif // WINDOWSDAEMON_H
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,12 @@
|
||||||
#include <guiddef.h>
|
#include <guiddef.h>
|
||||||
#include <initguid.h>
|
#include <initguid.h>
|
||||||
#include <netfw.h>
|
#include <netfw.h>
|
||||||
//#include <qaccessible.h>
|
#include <qaccessible.h>
|
||||||
#include <Ws2tcpip.h>
|
#include <qassert.h>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include <Ws2tcpip.h>
|
||||||
|
#include "winsock.h"
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
|
@ -27,7 +28,6 @@
|
||||||
#include "leakdetector.h"
|
#include "leakdetector.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "platforms/windows/windowsutils.h"
|
#include "platforms/windows/windowsutils.h"
|
||||||
#include "winsock.h"
|
|
||||||
|
|
||||||
#define IPV6_ADDRESS_SIZE 16
|
#define IPV6_ADDRESS_SIZE 16
|
||||||
|
|
||||||
|
|
@ -49,18 +49,13 @@ constexpr uint8_t HIGH_WEIGHT = 13;
|
||||||
constexpr uint8_t MAX_WEIGHT = 15;
|
constexpr uint8_t MAX_WEIGHT = 15;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
WindowsFirewall* WindowsFirewall::instance() {
|
WindowsFirewall* WindowsFirewall::create(QObject* parent) {
|
||||||
if (s_instance == nullptr) {
|
if (s_instance != nullptr) {
|
||||||
s_instance = new WindowsFirewall(qApp);
|
// Only one instance of the firewall is allowed
|
||||||
}
|
// Q_ASSERT(false);
|
||||||
return s_instance;
|
return s_instance;
|
||||||
}
|
}
|
||||||
|
HANDLE engineHandle = nullptr;
|
||||||
WindowsFirewall::WindowsFirewall(QObject* parent) : QObject(parent) {
|
|
||||||
MZ_COUNT_CTOR(WindowsFirewall);
|
|
||||||
Q_ASSERT(s_instance == nullptr);
|
|
||||||
|
|
||||||
HANDLE engineHandle = NULL;
|
|
||||||
DWORD result = ERROR_SUCCESS;
|
DWORD result = ERROR_SUCCESS;
|
||||||
// Use dynamic sessions for efficiency and safety:
|
// Use dynamic sessions for efficiency and safety:
|
||||||
// -> Filtering policy objects are deleted even when the application crashes/
|
// -> Filtering policy objects are deleted even when the application crashes/
|
||||||
|
|
@ -71,15 +66,24 @@ WindowsFirewall::WindowsFirewall(QObject* parent) : QObject(parent) {
|
||||||
|
|
||||||
logger.debug() << "Opening the filter engine.";
|
logger.debug() << "Opening the filter engine.";
|
||||||
|
|
||||||
result =
|
result = FwpmEngineOpen0(nullptr, RPC_C_AUTHN_WINNT, nullptr, &session,
|
||||||
FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &engineHandle);
|
&engineHandle);
|
||||||
|
|
||||||
if (result != ERROR_SUCCESS) {
|
if (result != ERROR_SUCCESS) {
|
||||||
WindowsUtils::windowsLog("FwpmEngineOpen0 failed");
|
WindowsUtils::windowsLog("FwpmEngineOpen0 failed");
|
||||||
return;
|
return nullptr;
|
||||||
}
|
}
|
||||||
logger.debug() << "Filter engine opened successfully.";
|
logger.debug() << "Filter engine opened successfully.";
|
||||||
m_sessionHandle = engineHandle;
|
if (!initSublayer()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
s_instance = new WindowsFirewall(engineHandle, parent);
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowsFirewall::WindowsFirewall(HANDLE session, QObject* parent)
|
||||||
|
: QObject(parent), m_sessionHandle(session) {
|
||||||
|
MZ_COUNT_CTOR(WindowsFirewall);
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowsFirewall::~WindowsFirewall() {
|
WindowsFirewall::~WindowsFirewall() {
|
||||||
|
|
@ -89,15 +93,8 @@ WindowsFirewall::~WindowsFirewall() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowsFirewall::init() {
|
// static
|
||||||
if (m_init) {
|
bool WindowsFirewall::initSublayer() {
|
||||||
logger.warning() << "Alread initialised FW_WFP layer";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (m_sessionHandle == INVALID_HANDLE_VALUE) {
|
|
||||||
logger.error() << "Cant Init Sublayer with invalid wfp handle";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// If we were not able to aquire a handle, this will fail anyway.
|
// If we were not able to aquire a handle, this will fail anyway.
|
||||||
// We need to open up another handle because of wfp rules:
|
// We need to open up another handle because of wfp rules:
|
||||||
// If a wfp resource was created with SESSION_DYNAMIC,
|
// If a wfp resource was created with SESSION_DYNAMIC,
|
||||||
|
|
@ -157,11 +154,10 @@ bool WindowsFirewall::init() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
logger.debug() << "Initialised Sublayer";
|
logger.debug() << "Initialised Sublayer";
|
||||||
m_init = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowsFirewall::enableKillSwitch(int vpnAdapterIndex) {
|
bool WindowsFirewall::enableInterface(int vpnAdapterIndex) {
|
||||||
// Checks if the FW_Rule was enabled succesfully,
|
// Checks if the FW_Rule was enabled succesfully,
|
||||||
// disables the whole killswitch and returns false if not.
|
// disables the whole killswitch and returns false if not.
|
||||||
#define FW_OK(rule) \
|
#define FW_OK(rule) \
|
||||||
|
|
@ -184,7 +180,7 @@ bool WindowsFirewall::enableKillSwitch(int vpnAdapterIndex) {
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info() << "Enabling Killswitch Using Adapter:" << vpnAdapterIndex;
|
logger.info() << "Enabling firewall Using Adapter:" << vpnAdapterIndex;
|
||||||
FW_OK(allowTrafficOfAdapter(vpnAdapterIndex, MED_WEIGHT,
|
FW_OK(allowTrafficOfAdapter(vpnAdapterIndex, MED_WEIGHT,
|
||||||
"Allow usage of VPN Adapter"));
|
"Allow usage of VPN Adapter"));
|
||||||
FW_OK(allowDHCPTraffic(MED_WEIGHT, "Allow DHCP Traffic"));
|
FW_OK(allowDHCPTraffic(MED_WEIGHT, "Allow DHCP Traffic"));
|
||||||
|
|
@ -200,6 +196,36 @@ bool WindowsFirewall::enableKillSwitch(int vpnAdapterIndex) {
|
||||||
#undef FW_OK
|
#undef FW_OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allow unprotected traffic sent to the following local address ranges.
|
||||||
|
bool WindowsFirewall::enableLanBypass(const QList<IPAddress>& ranges) {
|
||||||
|
// Start the firewall transaction
|
||||||
|
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
disableKillSwitch();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto cleanup = qScopeGuard([&] {
|
||||||
|
FwpmTransactionAbort0(m_sessionHandle);
|
||||||
|
disableKillSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Blocking unprotected traffic
|
||||||
|
for (const IPAddress& prefix : ranges) {
|
||||||
|
if (!allowTrafficTo(prefix, LOW_WEIGHT + 1, "Allow LAN bypass traffic")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = FwpmTransactionCommit0(m_sessionHandle);
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
logger.error() << "FwpmTransactionCommit0 failed with error:" << result;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup.dismiss();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
||||||
// Start the firewall transaction
|
// Start the firewall transaction
|
||||||
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
||||||
|
|
@ -238,9 +264,9 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
||||||
|
|
||||||
if (!config.m_excludedAddresses.empty()) {
|
if (!config.m_excludedAddresses.empty()) {
|
||||||
for (const QString& i : config.m_excludedAddresses) {
|
for (const QString& i : config.m_excludedAddresses) {
|
||||||
logger.debug() << "range: " << i;
|
logger.debug() << "excludedAddresses range: " << i;
|
||||||
|
|
||||||
if (!allowTrafficToRange(i, HIGH_WEIGHT,
|
if (!allowTrafficTo(i, HIGH_WEIGHT,
|
||||||
"Allow Ecxlude route", config.m_serverPublicKey)) {
|
"Allow Ecxlude route", config.m_serverPublicKey)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -421,6 +447,56 @@ bool WindowsFirewall::allowTrafficOfAdapter(int networkAdapter, uint8_t weight,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WindowsFirewall::allowTrafficTo(const IPAddress& addr, int weight,
|
||||||
|
const QString& title,
|
||||||
|
const QString& peer) {
|
||||||
|
GUID layerKeyOut;
|
||||||
|
GUID layerKeyIn;
|
||||||
|
if (addr.type() == QAbstractSocket::IPv4Protocol) {
|
||||||
|
layerKeyOut = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
|
||||||
|
layerKeyIn = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4;
|
||||||
|
} else {
|
||||||
|
layerKeyOut = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
|
||||||
|
layerKeyIn = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match the IP address range.
|
||||||
|
FWPM_FILTER_CONDITION0 cond[1] = {};
|
||||||
|
FWP_RANGE0 ipRange;
|
||||||
|
QByteArray lowIpV6Buffer;
|
||||||
|
QByteArray highIpV6Buffer;
|
||||||
|
|
||||||
|
importAddress(addr.address(), ipRange.valueLow, &lowIpV6Buffer);
|
||||||
|
importAddress(addr.broadcastAddress(), ipRange.valueHigh, &highIpV6Buffer);
|
||||||
|
|
||||||
|
cond[0].fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS;
|
||||||
|
cond[0].matchType = FWP_MATCH_RANGE;
|
||||||
|
cond[0].conditionValue.type = FWP_RANGE_TYPE;
|
||||||
|
cond[0].conditionValue.rangeValue = &ipRange;
|
||||||
|
|
||||||
|
// Assemble the Filter base
|
||||||
|
FWPM_FILTER0 filter;
|
||||||
|
memset(&filter, 0, sizeof(filter));
|
||||||
|
filter.action.type = FWP_ACTION_PERMIT;
|
||||||
|
filter.weight.type = FWP_UINT8;
|
||||||
|
filter.weight.uint8 = weight;
|
||||||
|
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
|
||||||
|
filter.numFilterConditions = 1;
|
||||||
|
filter.filterCondition = cond;
|
||||||
|
|
||||||
|
// Send the filters down to the firewall.
|
||||||
|
QString description = "Permit traffic %1 " + addr.toString();
|
||||||
|
filter.layerKey = layerKeyOut;
|
||||||
|
if (!enableFilter(&filter, title, description.arg("to"), peer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
filter.layerKey = layerKeyIn;
|
||||||
|
if (!enableFilter(&filter, title, description.arg("from"), peer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool WindowsFirewall::allowTrafficTo(const QHostAddress& targetIP, uint port,
|
bool WindowsFirewall::allowTrafficTo(const QHostAddress& targetIP, uint port,
|
||||||
int weight, const QString& title,
|
int weight, const QString& title,
|
||||||
const QString& peer) {
|
const QString& peer) {
|
||||||
|
|
@ -484,57 +560,6 @@ bool WindowsFirewall::allowTrafficTo(const QHostAddress& targetIP, uint port,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowsFirewall::allowTrafficToRange(const IPAddress& addr, uint8_t weight,
|
|
||||||
const QString& title,
|
|
||||||
const QString& peer) {
|
|
||||||
QString description("Allow traffic %1 %2 ");
|
|
||||||
|
|
||||||
auto lower = addr.address();
|
|
||||||
auto upper = addr.broadcastAddress();
|
|
||||||
|
|
||||||
const bool isV4 = addr.type() == QAbstractSocket::IPv4Protocol;
|
|
||||||
const GUID layerKeyOut =
|
|
||||||
isV4 ? FWPM_LAYER_ALE_AUTH_CONNECT_V4 : FWPM_LAYER_ALE_AUTH_CONNECT_V6;
|
|
||||||
const GUID layerKeyIn = isV4 ? FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4
|
|
||||||
: FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6;
|
|
||||||
|
|
||||||
// Assemble the Filter base
|
|
||||||
FWPM_FILTER0 filter;
|
|
||||||
memset(&filter, 0, sizeof(filter));
|
|
||||||
filter.action.type = FWP_ACTION_PERMIT;
|
|
||||||
filter.weight.type = FWP_UINT8;
|
|
||||||
filter.weight.uint8 = weight;
|
|
||||||
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
|
|
||||||
|
|
||||||
FWPM_FILTER_CONDITION0 cond[1] = {0};
|
|
||||||
FWP_RANGE0 ipRange;
|
|
||||||
QByteArray lowIpV6Buffer;
|
|
||||||
QByteArray highIpV6Buffer;
|
|
||||||
|
|
||||||
importAddress(lower, ipRange.valueLow, &lowIpV6Buffer);
|
|
||||||
importAddress(upper, ipRange.valueHigh, &highIpV6Buffer);
|
|
||||||
|
|
||||||
cond[0].fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS;
|
|
||||||
cond[0].matchType = FWP_MATCH_RANGE;
|
|
||||||
cond[0].conditionValue.type = FWP_RANGE_TYPE;
|
|
||||||
cond[0].conditionValue.rangeValue = &ipRange;
|
|
||||||
|
|
||||||
filter.numFilterConditions = 1;
|
|
||||||
filter.filterCondition = cond;
|
|
||||||
|
|
||||||
filter.layerKey = layerKeyOut;
|
|
||||||
if (!enableFilter(&filter, title, description.arg("to").arg(addr.toString()),
|
|
||||||
peer)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
filter.layerKey = layerKeyIn;
|
|
||||||
if (!enableFilter(&filter, title,
|
|
||||||
description.arg("from").arg(addr.toString()), peer)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title) {
|
bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title) {
|
||||||
// Allow outbound DHCPv4
|
// Allow outbound DHCPv4
|
||||||
{
|
{
|
||||||
|
|
@ -734,7 +759,7 @@ bool WindowsFirewall::blockTrafficTo(const IPAddress& addr, uint8_t weight,
|
||||||
filter.weight.uint8 = weight;
|
filter.weight.uint8 = weight;
|
||||||
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
|
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
|
||||||
|
|
||||||
FWPM_FILTER_CONDITION0 cond[1] = {0};
|
FWPM_FILTER_CONDITION0 cond[1] = {};
|
||||||
FWP_RANGE0 ipRange;
|
FWP_RANGE0 ipRange;
|
||||||
QByteArray lowIpV6Buffer;
|
QByteArray lowIpV6Buffer;
|
||||||
QByteArray highIpV6Buffer;
|
QByteArray highIpV6Buffer;
|
||||||
|
|
|
||||||
|
|
@ -26,18 +26,27 @@ struct FWP_CONDITION_VALUE0_;
|
||||||
|
|
||||||
class WindowsFirewall final : public QObject {
|
class WindowsFirewall final : public QObject {
|
||||||
public:
|
public:
|
||||||
~WindowsFirewall();
|
/**
|
||||||
|
* @brief Opens the Windows Filtering Platform, initializes the session,
|
||||||
|
* sublayer. Returns a WindowsFirewall object if successful, otherwise
|
||||||
|
* nullptr. If there is already a WindowsFirewall object, it will be returned.
|
||||||
|
*
|
||||||
|
* @param parent - parent QObject
|
||||||
|
* @return WindowsFirewall* - nullptr if failed to open the Windows Filtering
|
||||||
|
* Platform.
|
||||||
|
*/
|
||||||
|
static WindowsFirewall* create(QObject* parent);
|
||||||
|
~WindowsFirewall() override;
|
||||||
|
|
||||||
static WindowsFirewall* instance();
|
bool enableInterface(int vpnAdapterIndex);
|
||||||
bool init();
|
bool enableLanBypass(const QList<IPAddress>& ranges);
|
||||||
|
|
||||||
bool enableKillSwitch(int vpnAdapterIndex);
|
|
||||||
bool enablePeerTraffic(const InterfaceConfig& config);
|
bool enablePeerTraffic(const InterfaceConfig& config);
|
||||||
bool disablePeerTraffic(const QString& pubkey);
|
bool disablePeerTraffic(const QString& pubkey);
|
||||||
bool disableKillSwitch();
|
bool disableKillSwitch();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WindowsFirewall(QObject* parent);
|
static bool initSublayer();
|
||||||
|
WindowsFirewall(HANDLE session, QObject* parent);
|
||||||
HANDLE m_sessionHandle;
|
HANDLE m_sessionHandle;
|
||||||
bool m_init = false;
|
bool m_init = false;
|
||||||
QList<uint64_t> m_activeRules;
|
QList<uint64_t> m_activeRules;
|
||||||
|
|
@ -50,11 +59,10 @@ class WindowsFirewall final : public QObject {
|
||||||
bool blockTrafficTo(const IPAddress& addr, uint8_t weight,
|
bool blockTrafficTo(const IPAddress& addr, uint8_t weight,
|
||||||
const QString& title, const QString& peer = QString());
|
const QString& title, const QString& peer = QString());
|
||||||
bool blockTrafficOnPort(uint port, uint8_t weight, const QString& title);
|
bool blockTrafficOnPort(uint port, uint8_t weight, const QString& title);
|
||||||
|
bool allowTrafficTo(const IPAddress& addr, int weight, const QString& title,
|
||||||
|
const QString& peer = QString());
|
||||||
bool allowTrafficTo(const QHostAddress& targetIP, uint port, int weight,
|
bool allowTrafficTo(const QHostAddress& targetIP, uint port, int weight,
|
||||||
const QString& title, const QString& peer = QString());
|
const QString& title, const QString& peer = QString());
|
||||||
bool allowTrafficToRange(const IPAddress& addr, uint8_t weight,
|
|
||||||
const QString& title,
|
|
||||||
const QString& peer);
|
|
||||||
bool allowTrafficOfAdapter(int networkAdapter, uint8_t weight,
|
bool allowTrafficOfAdapter(int networkAdapter, uint8_t weight,
|
||||||
const QString& title);
|
const QString& title);
|
||||||
bool allowDHCPTraffic(uint8_t weight, const QString& title);
|
bool allowDHCPTraffic(uint8_t weight, const QString& title);
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,12 @@ namespace {
|
||||||
Logger logger("WindowsRouteMonitor");
|
Logger logger("WindowsRouteMonitor");
|
||||||
}; // namespace
|
}; // namespace
|
||||||
|
|
||||||
|
// Attempt to mark routing entries that we create with a relatively
|
||||||
|
// high metric. This ensures that we can skip over routes of our own
|
||||||
|
// creation when processing route changes, and ensures that we give
|
||||||
|
// way to other routing entries.
|
||||||
|
constexpr const ULONG EXCLUSION_ROUTE_METRIC = 0x5e72;
|
||||||
|
|
||||||
// Called by the kernel on route changes - perform some basic filtering and
|
// Called by the kernel on route changes - perform some basic filtering and
|
||||||
// invoke the routeChanged slot to do the real work.
|
// invoke the routeChanged slot to do the real work.
|
||||||
static void routeChangeCallback(PVOID context, PMIB_IPFORWARD_ROW2 row,
|
static void routeChangeCallback(PVOID context, PMIB_IPFORWARD_ROW2 row,
|
||||||
|
|
@ -20,23 +26,18 @@ static void routeChangeCallback(PVOID context, PMIB_IPFORWARD_ROW2 row,
|
||||||
WindowsRouteMonitor* monitor = (WindowsRouteMonitor*)context;
|
WindowsRouteMonitor* monitor = (WindowsRouteMonitor*)context;
|
||||||
Q_UNUSED(type);
|
Q_UNUSED(type);
|
||||||
|
|
||||||
// Ignore host route changes, and unsupported protocols.
|
// Ignore route changes that we created.
|
||||||
if (row->DestinationPrefix.Prefix.si_family == AF_INET6) {
|
if ((row->Protocol == MIB_IPPROTO_NETMGMT) &&
|
||||||
if (row->DestinationPrefix.PrefixLength >= 128) {
|
(row->Metric == EXCLUSION_ROUTE_METRIC)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (row->DestinationPrefix.Prefix.si_family == AF_INET) {
|
if (monitor->getLuid() == row->InterfaceLuid.Value) {
|
||||||
if (row->DestinationPrefix.PrefixLength >= 32) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (monitor->getLuid() != row->InterfaceLuid.Value) {
|
// Invoke the route changed signal to do the real work in Qt.
|
||||||
QMetaObject::invokeMethod(monitor, "routeChanged", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(monitor, "routeChanged", Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Perform prefix matching comparison on IP addresses in host order.
|
// Perform prefix matching comparison on IP addresses in host order.
|
||||||
static int prefixcmp(const void* a, const void* b, size_t bits) {
|
static int prefixcmp(const void* a, const void* b, size_t bits) {
|
||||||
|
|
@ -57,7 +58,8 @@ static int prefixcmp(const void* a, const void* b, size_t bits) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowsRouteMonitor::WindowsRouteMonitor(QObject* parent) : QObject(parent) {
|
WindowsRouteMonitor::WindowsRouteMonitor(quint64 luid, QObject* parent)
|
||||||
|
: QObject(parent), m_luid(luid) {
|
||||||
MZ_COUNT_CTOR(WindowsRouteMonitor);
|
MZ_COUNT_CTOR(WindowsRouteMonitor);
|
||||||
logger.debug() << "WindowsRouteMonitor created.";
|
logger.debug() << "WindowsRouteMonitor created.";
|
||||||
|
|
||||||
|
|
@ -67,11 +69,13 @@ WindowsRouteMonitor::WindowsRouteMonitor(QObject* parent) : QObject(parent) {
|
||||||
WindowsRouteMonitor::~WindowsRouteMonitor() {
|
WindowsRouteMonitor::~WindowsRouteMonitor() {
|
||||||
MZ_COUNT_DTOR(WindowsRouteMonitor);
|
MZ_COUNT_DTOR(WindowsRouteMonitor);
|
||||||
CancelMibChangeNotify2(m_routeHandle);
|
CancelMibChangeNotify2(m_routeHandle);
|
||||||
flushExclusionRoutes();
|
|
||||||
|
flushRouteTable(m_exclusionRoutes);
|
||||||
|
flushRouteTable(m_clonedRoutes);
|
||||||
logger.debug() << "WindowsRouteMonitor destroyed.";
|
logger.debug() << "WindowsRouteMonitor destroyed.";
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowsRouteMonitor::updateValidInterfaces(int family) {
|
void WindowsRouteMonitor::updateInterfaceMetrics(int family) {
|
||||||
PMIB_IPINTERFACE_TABLE table;
|
PMIB_IPINTERFACE_TABLE table;
|
||||||
DWORD result = GetIpInterfaceTable(family, &table);
|
DWORD result = GetIpInterfaceTable(family, &table);
|
||||||
if (result != NO_ERROR) {
|
if (result != NO_ERROR) {
|
||||||
|
|
@ -82,10 +86,10 @@ void WindowsRouteMonitor::updateValidInterfaces(int family) {
|
||||||
|
|
||||||
// Flush the list of interfaces that are valid for routing.
|
// Flush the list of interfaces that are valid for routing.
|
||||||
if ((family == AF_INET) || (family == AF_UNSPEC)) {
|
if ((family == AF_INET) || (family == AF_UNSPEC)) {
|
||||||
m_validInterfacesIpv4.clear();
|
m_interfaceMetricsIpv4.clear();
|
||||||
}
|
}
|
||||||
if ((family == AF_INET6) || (family == AF_UNSPEC)) {
|
if ((family == AF_INET6) || (family == AF_UNSPEC)) {
|
||||||
m_validInterfacesIpv6.clear();
|
m_interfaceMetricsIpv6.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rebuild the list of interfaces that are valid for routing.
|
// Rebuild the list of interfaces that are valid for routing.
|
||||||
|
|
@ -101,12 +105,12 @@ void WindowsRouteMonitor::updateValidInterfaces(int family) {
|
||||||
if (row->Family == AF_INET) {
|
if (row->Family == AF_INET) {
|
||||||
logger.debug() << "Interface" << row->InterfaceIndex
|
logger.debug() << "Interface" << row->InterfaceIndex
|
||||||
<< "is valid for IPv4 routing";
|
<< "is valid for IPv4 routing";
|
||||||
m_validInterfacesIpv4.append(row->InterfaceLuid.Value);
|
m_interfaceMetricsIpv4[row->InterfaceLuid.Value] = row->Metric;
|
||||||
}
|
}
|
||||||
if (row->Family == AF_INET6) {
|
if (row->Family == AF_INET6) {
|
||||||
logger.debug() << "Interface" << row->InterfaceIndex
|
logger.debug() << "Interface" << row->InterfaceIndex
|
||||||
<< "is valid for IPv6 routing";
|
<< "is valid for IPv6 routing";
|
||||||
m_validInterfacesIpv6.append(row->InterfaceLuid.Value);
|
m_interfaceMetricsIpv6[row->InterfaceLuid.Value] = row->Metric;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -126,72 +130,72 @@ void WindowsRouteMonitor::updateExclusionRoute(MIB_IPFORWARD_ROW2* data,
|
||||||
if (row->InterfaceLuid.Value == m_luid) {
|
if (row->InterfaceLuid.Value == m_luid) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Ignore host routes, and shorter potential matches.
|
if (row->DestinationPrefix.PrefixLength < bestMatch) {
|
||||||
if (row->DestinationPrefix.PrefixLength >=
|
|
||||||
data->DestinationPrefix.PrefixLength) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (row->DestinationPrefix.PrefixLength < bestMatch) {
|
// Ignore routes of our own creation.
|
||||||
|
if ((row->Protocol == data->Protocol) && (row->Metric == data->Metric)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the routing table entry matches the destination.
|
// Check if the routing table entry matches the destination.
|
||||||
|
if (!routeContainsDest(&row->DestinationPrefix, &data->DestinationPrefix)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the combined interface and routing metric.
|
||||||
|
ULONG routeMetric = row->Metric;
|
||||||
if (data->DestinationPrefix.Prefix.si_family == AF_INET6) {
|
if (data->DestinationPrefix.Prefix.si_family == AF_INET6) {
|
||||||
if (row->DestinationPrefix.Prefix.Ipv6.sin6_family != AF_INET6) {
|
if (!m_interfaceMetricsIpv6.contains(row->InterfaceLuid.Value)) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!m_validInterfacesIpv6.contains(row->InterfaceLuid.Value)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (prefixcmp(&data->DestinationPrefix.Prefix.Ipv6.sin6_addr,
|
|
||||||
&row->DestinationPrefix.Prefix.Ipv6.sin6_addr,
|
|
||||||
row->DestinationPrefix.PrefixLength) != 0) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
routeMetric += m_interfaceMetricsIpv6[row->InterfaceLuid.Value];
|
||||||
} else if (data->DestinationPrefix.Prefix.si_family == AF_INET) {
|
} else if (data->DestinationPrefix.Prefix.si_family == AF_INET) {
|
||||||
if (row->DestinationPrefix.Prefix.Ipv4.sin_family != AF_INET) {
|
if (!m_interfaceMetricsIpv4.contains(row->InterfaceLuid.Value)) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!m_validInterfacesIpv4.contains(row->InterfaceLuid.Value)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (prefixcmp(&data->DestinationPrefix.Prefix.Ipv4.sin_addr,
|
|
||||||
&row->DestinationPrefix.Prefix.Ipv4.sin_addr,
|
|
||||||
row->DestinationPrefix.PrefixLength) != 0) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
routeMetric += m_interfaceMetricsIpv4[row->InterfaceLuid.Value];
|
||||||
} else {
|
} else {
|
||||||
// Unsupported destination address family.
|
// Unsupported destination address family.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (routeMetric < row->Metric) {
|
||||||
|
routeMetric = ULONG_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
// Prefer routes with lower metric if we find multiple matches
|
// Prefer routes with lower metric if we find multiple matches
|
||||||
// with the same prefix length.
|
// with the same prefix length.
|
||||||
if ((row->DestinationPrefix.PrefixLength == bestMatch) &&
|
if ((row->DestinationPrefix.PrefixLength == bestMatch) &&
|
||||||
(row->Metric >= bestMetric)) {
|
(routeMetric >= bestMetric)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we got here, then this is the longest prefix match so far.
|
// If we got here, then this is the longest prefix match so far.
|
||||||
memcpy(&nexthop, &row->NextHop, sizeof(SOCKADDR_INET));
|
memcpy(&nexthop, &row->NextHop, sizeof(SOCKADDR_INET));
|
||||||
bestLuid = row->InterfaceLuid.Value;
|
|
||||||
bestMatch = row->DestinationPrefix.PrefixLength;
|
bestMatch = row->DestinationPrefix.PrefixLength;
|
||||||
bestMetric = row->Metric;
|
bestMetric = routeMetric;
|
||||||
|
if (bestMatch == data->DestinationPrefix.PrefixLength) {
|
||||||
|
bestLuid = 0; // Don't write to the table if we find an exact match.
|
||||||
|
} else {
|
||||||
|
bestLuid = row->InterfaceLuid.Value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If neither the interface nor next-hop have changed, then do nothing.
|
// If neither the interface nor next-hop have changed, then do nothing.
|
||||||
if ((data->InterfaceLuid.Value) == bestLuid &&
|
if (data->InterfaceLuid.Value == bestLuid &&
|
||||||
memcmp(&nexthop, &data->NextHop, sizeof(SOCKADDR_INET)) == 0) {
|
memcmp(&nexthop, &data->NextHop, sizeof(SOCKADDR_INET)) == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the routing table entry.
|
// Delete the previous routing table entry, if any.
|
||||||
if (data->InterfaceLuid.Value != 0) {
|
if (data->InterfaceLuid.Value != 0) {
|
||||||
DWORD result = DeleteIpForwardEntry2(data);
|
DWORD result = DeleteIpForwardEntry2(data);
|
||||||
if ((result != NO_ERROR) && (result != ERROR_NOT_FOUND)) {
|
if ((result != NO_ERROR) && (result != ERROR_NOT_FOUND)) {
|
||||||
logger.error() << "Failed to delete route:" << result;
|
logger.error() << "Failed to delete route:" << result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the routing table entry.
|
||||||
data->InterfaceLuid.Value = bestLuid;
|
data->InterfaceLuid.Value = bestLuid;
|
||||||
memcpy(&data->NextHop, &nexthop, sizeof(SOCKADDR_INET));
|
memcpy(&data->NextHop, &nexthop, sizeof(SOCKADDR_INET));
|
||||||
if (data->InterfaceLuid.Value != 0) {
|
if (data->InterfaceLuid.Value != 0) {
|
||||||
|
|
@ -202,10 +206,178 @@ void WindowsRouteMonitor::updateExclusionRoute(MIB_IPFORWARD_ROW2* data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
bool WindowsRouteMonitor::routeContainsDest(const IP_ADDRESS_PREFIX* route,
|
||||||
|
const IP_ADDRESS_PREFIX* dest) {
|
||||||
|
if (route->Prefix.si_family != dest->Prefix.si_family) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (route->PrefixLength > dest->PrefixLength) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (route->Prefix.si_family == AF_INET) {
|
||||||
|
return prefixcmp(&route->Prefix.Ipv4.sin_addr, &dest->Prefix.Ipv4.sin_addr,
|
||||||
|
route->PrefixLength) == 0;
|
||||||
|
} else if (route->Prefix.si_family == AF_INET6) {
|
||||||
|
return prefixcmp(&route->Prefix.Ipv6.sin6_addr,
|
||||||
|
&dest->Prefix.Ipv6.sin6_addr, route->PrefixLength) == 0;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
QHostAddress WindowsRouteMonitor::prefixToAddress(
|
||||||
|
const IP_ADDRESS_PREFIX* dest) {
|
||||||
|
if (dest->Prefix.si_family == AF_INET6) {
|
||||||
|
return QHostAddress(dest->Prefix.Ipv6.sin6_addr.s6_addr);
|
||||||
|
} else if (dest->Prefix.si_family == AF_INET) {
|
||||||
|
quint32 addr = htonl(dest->Prefix.Ipv4.sin_addr.s_addr);
|
||||||
|
return QHostAddress(addr);
|
||||||
|
} else {
|
||||||
|
return QHostAddress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowsRouteMonitor::isRouteExcluded(const IP_ADDRESS_PREFIX* dest) const {
|
||||||
|
auto i = m_exclusionRoutes.constBegin();
|
||||||
|
while (i != m_exclusionRoutes.constEnd()) {
|
||||||
|
const MIB_IPFORWARD_ROW2* row = i.value();
|
||||||
|
if (routeContainsDest(&row->DestinationPrefix, dest)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowsRouteMonitor::updateCapturedRoutes(int family) {
|
||||||
|
if (!m_defaultRouteCapture) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PMIB_IPFORWARD_TABLE2 table;
|
||||||
|
DWORD error = GetIpForwardTable2(family, &table);
|
||||||
|
if (error != NO_ERROR) {
|
||||||
|
updateCapturedRoutes(family, table);
|
||||||
|
FreeMibTable(table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) {
|
||||||
|
PMIB_IPFORWARD_TABLE2 table = reinterpret_cast<PMIB_IPFORWARD_TABLE2>(ptable);
|
||||||
|
if (!m_defaultRouteCapture) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ULONG i = 0; i < table->NumEntries; i++) {
|
||||||
|
MIB_IPFORWARD_ROW2* row = &table->Table[i];
|
||||||
|
// Ignore routes into the VPN interface.
|
||||||
|
if (row->InterfaceLuid.Value == m_luid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Ignore the default route
|
||||||
|
if (row->DestinationPrefix.PrefixLength == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Ignore routes of our own creation.
|
||||||
|
if ((row->Protocol == MIB_IPPROTO_NETMGMT) &&
|
||||||
|
(row->Metric == EXCLUSION_ROUTE_METRIC)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Ignore routes which should be excluded.
|
||||||
|
if (isRouteExcluded(&row->DestinationPrefix)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QHostAddress destination = prefixToAddress(&row->DestinationPrefix);
|
||||||
|
if (destination.isLoopback() || destination.isBroadcast() ||
|
||||||
|
destination.isLinkLocal() || destination.isMulticast()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get here, this route should be cloned.
|
||||||
|
IPAddress prefix(destination, row->DestinationPrefix.PrefixLength);
|
||||||
|
MIB_IPFORWARD_ROW2* data = m_clonedRoutes.value(prefix, nullptr);
|
||||||
|
if (data != nullptr) {
|
||||||
|
// Count the number of matching entries in the main table.
|
||||||
|
data->Age++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
logger.debug() << "Capturing route to"
|
||||||
|
<< logger.sensitive(prefix.toString());
|
||||||
|
|
||||||
|
// Clone the route and direct it into the VPN tunnel.
|
||||||
|
data = new MIB_IPFORWARD_ROW2;
|
||||||
|
InitializeIpForwardEntry(data);
|
||||||
|
data->InterfaceLuid.Value = m_luid;
|
||||||
|
data->DestinationPrefix = row->DestinationPrefix;
|
||||||
|
data->NextHop.si_family = data->DestinationPrefix.Prefix.si_family;
|
||||||
|
|
||||||
|
// Set the rest of the flags for a static route.
|
||||||
|
data->ValidLifetime = 0xffffffff;
|
||||||
|
data->PreferredLifetime = 0xffffffff;
|
||||||
|
data->Metric = 0;
|
||||||
|
data->Protocol = MIB_IPPROTO_NETMGMT;
|
||||||
|
data->Loopback = false;
|
||||||
|
data->AutoconfigureAddress = false;
|
||||||
|
data->Publish = false;
|
||||||
|
data->Immortal = false;
|
||||||
|
data->Age = 0;
|
||||||
|
|
||||||
|
// Route this traffic into the VPN tunnel.
|
||||||
|
DWORD result = CreateIpForwardEntry2(data);
|
||||||
|
if (result != NO_ERROR) {
|
||||||
|
logger.error() << "Failed to update route:" << result;
|
||||||
|
delete data;
|
||||||
|
} else {
|
||||||
|
m_clonedRoutes.insert(prefix, data);
|
||||||
|
data->Age++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally scan for any routes which were removed from the table. We do this
|
||||||
|
// by reusing the age field to count the number of matching entries in the
|
||||||
|
// main table.
|
||||||
|
auto i = m_clonedRoutes.begin();
|
||||||
|
while (i != m_clonedRoutes.end()) {
|
||||||
|
MIB_IPFORWARD_ROW2* data = i.value();
|
||||||
|
if (data->Age > 0) {
|
||||||
|
// Entry is in use, don't delete it.
|
||||||
|
data->Age = 0;
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((family != AF_UNSPEC) &&
|
||||||
|
(data->DestinationPrefix.Prefix.si_family != family)) {
|
||||||
|
// We are not processing updates to this address family.
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug() << "Removing route capture for"
|
||||||
|
<< logger.sensitive(i.key().toString());
|
||||||
|
|
||||||
|
// Otherwise, this route is no longer in use.
|
||||||
|
DWORD result = DeleteIpForwardEntry2(data);
|
||||||
|
if ((result != NO_ERROR) && (result != ERROR_NOT_FOUND)) {
|
||||||
|
logger.error() << "Failed to delete route:" << result;
|
||||||
|
}
|
||||||
|
delete data;
|
||||||
|
i = m_clonedRoutes.erase(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
|
bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
|
||||||
logger.debug() << "Adding exclusion route for"
|
logger.debug() << "Adding exclusion route for"
|
||||||
<< logger.sensitive(prefix.toString());
|
<< logger.sensitive(prefix.toString());
|
||||||
|
|
||||||
|
// Silently ignore non-routeable addresses.
|
||||||
|
QHostAddress addr = prefix.address();
|
||||||
|
if (addr.isLoopback() || addr.isBroadcast() || addr.isLinkLocal() ||
|
||||||
|
addr.isMulticast()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_exclusionRoutes.contains(prefix)) {
|
if (m_exclusionRoutes.contains(prefix)) {
|
||||||
logger.warning() << "Exclusion route already exists";
|
logger.warning() << "Exclusion route already exists";
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -232,7 +404,7 @@ bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
|
||||||
// Set the rest of the flags for a static route.
|
// Set the rest of the flags for a static route.
|
||||||
data->ValidLifetime = 0xffffffff;
|
data->ValidLifetime = 0xffffffff;
|
||||||
data->PreferredLifetime = 0xffffffff;
|
data->PreferredLifetime = 0xffffffff;
|
||||||
data->Metric = 0;
|
data->Metric = EXCLUSION_ROUTE_METRIC;
|
||||||
data->Protocol = MIB_IPPROTO_NETMGMT;
|
data->Protocol = MIB_IPPROTO_NETMGMT;
|
||||||
data->Loopback = false;
|
data->Loopback = false;
|
||||||
data->AutoconfigureAddress = false;
|
data->AutoconfigureAddress = false;
|
||||||
|
|
@ -254,7 +426,8 @@ bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
|
||||||
delete data;
|
delete data;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
updateValidInterfaces(family);
|
updateInterfaceMetrics(family);
|
||||||
|
updateCapturedRoutes(family, table);
|
||||||
updateExclusionRoute(data, table);
|
updateExclusionRoute(data, table);
|
||||||
FreeMibTable(table);
|
FreeMibTable(table);
|
||||||
|
|
||||||
|
|
@ -266,10 +439,9 @@ bool WindowsRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) {
|
||||||
logger.debug() << "Deleting exclusion route for"
|
logger.debug() << "Deleting exclusion route for"
|
||||||
<< logger.sensitive(prefix.address().toString());
|
<< logger.sensitive(prefix.address().toString());
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
MIB_IPFORWARD_ROW2* data = m_exclusionRoutes.take(prefix);
|
MIB_IPFORWARD_ROW2* data = m_exclusionRoutes.take(prefix);
|
||||||
if (data == nullptr) {
|
if (data == nullptr) {
|
||||||
break;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD result = DeleteIpForwardEntry2(data);
|
DWORD result = DeleteIpForwardEntry2(data);
|
||||||
|
|
@ -278,14 +450,17 @@ bool WindowsRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) {
|
||||||
<< logger.sensitive(prefix.toString())
|
<< logger.sensitive(prefix.toString())
|
||||||
<< "result:" << result;
|
<< "result:" << result;
|
||||||
}
|
}
|
||||||
delete data;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Captured routes might have changed.
|
||||||
|
updateCapturedRoutes(data->DestinationPrefix.Prefix.si_family);
|
||||||
|
|
||||||
|
delete data;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowsRouteMonitor::flushExclusionRoutes() {
|
void WindowsRouteMonitor::flushRouteTable(
|
||||||
for (auto i = m_exclusionRoutes.begin(); i != m_exclusionRoutes.end(); i++) {
|
QHash<IPAddress, MIB_IPFORWARD_ROW2*>& table) {
|
||||||
|
for (auto i = table.begin(); i != table.end(); i++) {
|
||||||
MIB_IPFORWARD_ROW2* data = i.value();
|
MIB_IPFORWARD_ROW2* data = i.value();
|
||||||
DWORD result = DeleteIpForwardEntry2(data);
|
DWORD result = DeleteIpForwardEntry2(data);
|
||||||
if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) {
|
if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) {
|
||||||
|
|
@ -295,7 +470,17 @@ void WindowsRouteMonitor::flushExclusionRoutes() {
|
||||||
}
|
}
|
||||||
delete data;
|
delete data;
|
||||||
}
|
}
|
||||||
m_exclusionRoutes.clear();
|
table.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowsRouteMonitor::setDetaultRouteCapture(bool enable) {
|
||||||
|
m_defaultRouteCapture = enable;
|
||||||
|
|
||||||
|
// Flush any captured routes when disabling the feature.
|
||||||
|
if (!m_defaultRouteCapture) {
|
||||||
|
flushRouteTable(m_clonedRoutes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowsRouteMonitor::routeChanged() {
|
void WindowsRouteMonitor::routeChanged() {
|
||||||
|
|
@ -308,7 +493,8 @@ void WindowsRouteMonitor::routeChanged() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateValidInterfaces(AF_UNSPEC);
|
updateInterfaceMetrics(AF_UNSPEC);
|
||||||
|
updateCapturedRoutes(AF_UNSPEC, table);
|
||||||
for (MIB_IPFORWARD_ROW2* data : m_exclusionRoutes) {
|
for (MIB_IPFORWARD_ROW2* data : m_exclusionRoutes) {
|
||||||
updateExclusionRoute(data, table);
|
updateExclusionRoute(data, table);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <ws2ipdef.h>
|
#include <ws2ipdef.h>
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
#include <QMap>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include "ipaddress.h"
|
#include "ipaddress.h"
|
||||||
|
|
@ -19,28 +21,41 @@ class WindowsRouteMonitor final : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WindowsRouteMonitor(QObject* parent);
|
WindowsRouteMonitor(quint64 luid, QObject* parent);
|
||||||
~WindowsRouteMonitor();
|
~WindowsRouteMonitor();
|
||||||
|
|
||||||
|
void setDetaultRouteCapture(bool enable);
|
||||||
|
|
||||||
bool addExclusionRoute(const IPAddress& prefix);
|
bool addExclusionRoute(const IPAddress& prefix);
|
||||||
bool deleteExclusionRoute(const IPAddress& prefix);
|
bool deleteExclusionRoute(const IPAddress& prefix);
|
||||||
void flushExclusionRoutes();
|
void flushExclusionRoutes() { return flushRouteTable(m_exclusionRoutes); };
|
||||||
|
|
||||||
void setLuid(quint64 luid) { m_luid = luid; }
|
quint64 getLuid() const { return m_luid; }
|
||||||
quint64 getLuid() { return m_luid; }
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void routeChanged();
|
void routeChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool isRouteExcluded(const IP_ADDRESS_PREFIX* dest) const;
|
||||||
|
static bool routeContainsDest(const IP_ADDRESS_PREFIX* route,
|
||||||
|
const IP_ADDRESS_PREFIX* dest);
|
||||||
|
static QHostAddress prefixToAddress(const IP_ADDRESS_PREFIX* dest);
|
||||||
|
|
||||||
|
void flushRouteTable(QHash<IPAddress, MIB_IPFORWARD_ROW2*>& table);
|
||||||
void updateExclusionRoute(MIB_IPFORWARD_ROW2* data, void* table);
|
void updateExclusionRoute(MIB_IPFORWARD_ROW2* data, void* table);
|
||||||
void updateValidInterfaces(int family);
|
void updateInterfaceMetrics(int family);
|
||||||
|
void updateCapturedRoutes(int family);
|
||||||
|
void updateCapturedRoutes(int family, void* table);
|
||||||
|
|
||||||
QHash<IPAddress, MIB_IPFORWARD_ROW2*> m_exclusionRoutes;
|
QHash<IPAddress, MIB_IPFORWARD_ROW2*> m_exclusionRoutes;
|
||||||
QList<quint64> m_validInterfacesIpv4;
|
QMap<quint64, ULONG> m_interfaceMetricsIpv4;
|
||||||
QList<quint64> m_validInterfacesIpv6;
|
QMap<quint64, ULONG> m_interfaceMetricsIpv6;
|
||||||
|
|
||||||
quint64 m_luid = 0;
|
// Default route cloning
|
||||||
|
bool m_defaultRouteCapture = false;
|
||||||
|
QHash<IPAddress, MIB_IPFORWARD_ROW2*> m_clonedRoutes;
|
||||||
|
|
||||||
|
const quint64 m_luid = 0;
|
||||||
HANDLE m_routeHandle = INVALID_HANDLE_VALUE;
|
HANDLE m_routeHandle = INVALID_HANDLE_VALUE;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,15 @@
|
||||||
|
|
||||||
#include "windowssplittunnel.h"
|
#include "windowssplittunnel.h"
|
||||||
|
|
||||||
|
#include <qassert.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "../windowscommons.h"
|
#include "../windowscommons.h"
|
||||||
#include "../windowsservicemanager.h"
|
#include "../windowsservicemanager.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
#include "platforms/windows/daemon/windowsfirewall.h"
|
||||||
|
#include "platforms/windows/daemon/windowssplittunnel.h"
|
||||||
#include "platforms/windows/windowsutils.h"
|
#include "platforms/windows/windowsutils.h"
|
||||||
#include "windowsfirewall.h"
|
#include "windowsfirewall.h"
|
||||||
|
|
||||||
|
|
@ -18,34 +24,252 @@
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QNetworkInterface>
|
#include <QNetworkInterface>
|
||||||
#include <QScopeGuard>
|
#include <QScopeGuard>
|
||||||
#include <QThread>
|
|
||||||
|
#pragma region
|
||||||
|
|
||||||
|
// Driver Configuration structures
|
||||||
|
using CONFIGURATION_ENTRY = struct {
|
||||||
|
// Offset into buffer region that follows all entries.
|
||||||
|
// The image name uses the device path.
|
||||||
|
SIZE_T ImageNameOffset;
|
||||||
|
// Length of the String
|
||||||
|
USHORT ImageNameLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
using CONFIGURATION_HEADER = struct {
|
||||||
|
// Number of entries immediately following the header.
|
||||||
|
SIZE_T NumEntries;
|
||||||
|
|
||||||
|
// Total byte length: header + entries + string buffer.
|
||||||
|
SIZE_T TotalLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Used to Configure Which IP is network/vpn
|
||||||
|
using IP_ADDRESSES_CONFIG = struct {
|
||||||
|
IN_ADDR TunnelIpv4;
|
||||||
|
IN_ADDR InternetIpv4;
|
||||||
|
|
||||||
|
IN6_ADDR TunnelIpv6;
|
||||||
|
IN6_ADDR InternetIpv6;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Used to Define Which Processes are alive on activation
|
||||||
|
using PROCESS_DISCOVERY_HEADER = struct {
|
||||||
|
SIZE_T NumEntries;
|
||||||
|
SIZE_T TotalLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
using PROCESS_DISCOVERY_ENTRY = struct {
|
||||||
|
HANDLE ProcessId;
|
||||||
|
HANDLE ParentProcessId;
|
||||||
|
|
||||||
|
SIZE_T ImageNameOffset;
|
||||||
|
USHORT ImageNameLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ProcessInfo = struct {
|
||||||
|
DWORD ProcessId;
|
||||||
|
DWORD ParentProcessId;
|
||||||
|
FILETIME CreationTime;
|
||||||
|
std::wstring DevicePath;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef CTL_CODE
|
||||||
|
|
||||||
|
# define FILE_ANY_ACCESS 0x0000
|
||||||
|
|
||||||
|
# define METHOD_BUFFERED 0
|
||||||
|
# define METHOD_IN_DIRECT 1
|
||||||
|
# define METHOD_NEITHER 3
|
||||||
|
|
||||||
|
# define CTL_CODE(DeviceType, Function, Method, Access) \
|
||||||
|
(((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Known ControlCodes
|
||||||
|
#define IOCTL_INITIALIZE CTL_CODE(0x8000, 1, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
#define IOCTL_DEQUEUE_EVENT \
|
||||||
|
CTL_CODE(0x8000, 2, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
#define IOCTL_REGISTER_PROCESSES \
|
||||||
|
CTL_CODE(0x8000, 3, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
#define IOCTL_REGISTER_IP_ADDRESSES \
|
||||||
|
CTL_CODE(0x8000, 4, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
#define IOCTL_GET_IP_ADDRESSES \
|
||||||
|
CTL_CODE(0x8000, 5, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
#define IOCTL_SET_CONFIGURATION \
|
||||||
|
CTL_CODE(0x8000, 6, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
#define IOCTL_GET_CONFIGURATION \
|
||||||
|
CTL_CODE(0x8000, 7, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
#define IOCTL_CLEAR_CONFIGURATION \
|
||||||
|
CTL_CODE(0x8000, 8, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
#define IOCTL_GET_STATE CTL_CODE(0x8000, 9, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
#define IOCTL_QUERY_PROCESS \
|
||||||
|
CTL_CODE(0x8000, 10, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
#define IOCTL_ST_RESET CTL_CODE(0x8000, 11, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
|
constexpr static const auto DRIVER_SYMLINK = L"\\\\.\\MULLVADSPLITTUNNEL";
|
||||||
|
constexpr static const auto DRIVER_FILENAME = "mullvad-split-tunnel.sys";
|
||||||
|
constexpr static const auto DRIVER_SERVICE_NAME = L"AmneziaVPNSplitTunnel";
|
||||||
|
constexpr static const auto MV_SERVICE_NAME = L"MullvadVPN";
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
Logger logger("WindowsSplitTunnel");
|
Logger logger("WindowsSplitTunnel");
|
||||||
|
|
||||||
|
ProcessInfo getProcessInfo(HANDLE process, const PROCESSENTRY32W& processMeta) {
|
||||||
|
ProcessInfo pi;
|
||||||
|
pi.ParentProcessId = processMeta.th32ParentProcessID;
|
||||||
|
pi.ProcessId = processMeta.th32ProcessID;
|
||||||
|
pi.CreationTime = {0, 0};
|
||||||
|
pi.DevicePath = L"";
|
||||||
|
|
||||||
|
FILETIME creationTime, null_time;
|
||||||
|
auto ok = GetProcessTimes(process, &creationTime, &null_time, &null_time,
|
||||||
|
&null_time);
|
||||||
|
if (ok) {
|
||||||
|
pi.CreationTime = creationTime;
|
||||||
|
}
|
||||||
|
wchar_t imagepath[MAX_PATH + 1];
|
||||||
|
if (K32GetProcessImageFileNameW(
|
||||||
|
process, imagepath, sizeof(imagepath) / sizeof(*imagepath)) != 0) {
|
||||||
|
pi.DevicePath = imagepath;
|
||||||
|
}
|
||||||
|
return pi;
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowsSplitTunnel::WindowsSplitTunnel(QObject* parent) : QObject(parent) {
|
} // namespace
|
||||||
|
|
||||||
|
std::unique_ptr<WindowsSplitTunnel> WindowsSplitTunnel::create(
|
||||||
|
WindowsFirewall* fw) {
|
||||||
|
if (fw == nullptr) {
|
||||||
|
// Pre-Condition:
|
||||||
|
// Make sure the Windows Firewall has created the sublayer
|
||||||
|
// otherwise the driver will fail to initialize
|
||||||
|
logger.error() << "Failed to did not pass a WindowsFirewall obj"
|
||||||
|
<< "The Driver cannot work with the sublayer not created";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
// 00: Check if we conflict with mullvad, if so.
|
||||||
if (detectConflict()) {
|
if (detectConflict()) {
|
||||||
logger.error() << "Conflict detected, abort Split-Tunnel init.";
|
logger.error() << "Conflict detected, abort Split-Tunnel init.";
|
||||||
uninstallDriver();
|
return nullptr;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
// 01: Check if the driver is installed, if not do so.
|
||||||
m_tries = 0;
|
|
||||||
|
|
||||||
if (!isInstalled()) {
|
if (!isInstalled()) {
|
||||||
logger.debug() << "Driver is not Installed, doing so";
|
logger.debug() << "Driver is not Installed, doing so";
|
||||||
auto handle = installDriver();
|
auto handle = installDriver();
|
||||||
if (handle == INVALID_HANDLE_VALUE) {
|
if (handle == INVALID_HANDLE_VALUE) {
|
||||||
WindowsUtils::windowsLog("Failed to install Driver");
|
WindowsUtils::windowsLog("Failed to install Driver");
|
||||||
return;
|
return nullptr;
|
||||||
}
|
}
|
||||||
logger.debug() << "Driver installed";
|
logger.debug() << "Driver installed";
|
||||||
CloseServiceHandle(handle);
|
CloseServiceHandle(handle);
|
||||||
} else {
|
} else {
|
||||||
logger.debug() << "Driver is installed";
|
logger.debug() << "Driver was installed";
|
||||||
}
|
}
|
||||||
initDriver();
|
// 02: Now check if the service is running
|
||||||
|
auto driver_manager =
|
||||||
|
WindowsServiceManager::open(QString::fromWCharArray(DRIVER_SERVICE_NAME));
|
||||||
|
if (Q_UNLIKELY(driver_manager == nullptr)) {
|
||||||
|
// Let's be fair if we end up here,
|
||||||
|
// after checking it exists and installing it,
|
||||||
|
// this is super unlikeley
|
||||||
|
Q_ASSERT(false);
|
||||||
|
logger.error()
|
||||||
|
<< "WindowsServiceManager was unable fo find Split Tunnel service?";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (!driver_manager->isRunning()) {
|
||||||
|
logger.debug() << "Driver is not running, starting it";
|
||||||
|
// Start the service
|
||||||
|
if (!driver_manager->startService()) {
|
||||||
|
logger.error() << "Failed to start Split Tunnel Service";
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// 03: Open the Driver Symlink
|
||||||
|
auto driverFile = CreateFileW(DRIVER_SYMLINK, GENERIC_READ | GENERIC_WRITE, 0,
|
||||||
|
nullptr, OPEN_EXISTING, 0, nullptr);
|
||||||
|
;
|
||||||
|
if (driverFile == INVALID_HANDLE_VALUE) {
|
||||||
|
WindowsUtils::windowsLog("Failed to open Driver: ");
|
||||||
|
// Only once, if the opening did not work. Try to reboot it. #
|
||||||
|
logger.info()
|
||||||
|
<< "Failed to open driver, attempting only once to reboot driver";
|
||||||
|
if (!driver_manager->stopService()) {
|
||||||
|
logger.error() << "Unable stop driver";
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
logger.info() << "Stopped driver, starting it again.";
|
||||||
|
if (!driver_manager->startService()) {
|
||||||
|
logger.error() << "Unable start driver";
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
logger.info() << "Opening again.";
|
||||||
|
driverFile = CreateFileW(DRIVER_SYMLINK, GENERIC_READ | GENERIC_WRITE, 0,
|
||||||
|
nullptr, OPEN_EXISTING, 0, nullptr);
|
||||||
|
if (driverFile == INVALID_HANDLE_VALUE) {
|
||||||
|
logger.error() << "Opening Failed again, sorry!";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!initDriver(driverFile)) {
|
||||||
|
logger.error() << "Failed to init driver";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
// We're ready to talk to the driver, it's alive and setup.
|
||||||
|
return std::make_unique<WindowsSplitTunnel>(driverFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowsSplitTunnel::initDriver(HANDLE driverIO) {
|
||||||
|
// We need to now check the state and init it, if required
|
||||||
|
auto state = getState(driverIO);
|
||||||
|
if (state == STATE_UNKNOWN) {
|
||||||
|
logger.debug() << "Cannot check if driver is initialized";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (state >= STATE_INITIALIZED) {
|
||||||
|
logger.debug() << "Driver already initialized: " << state;
|
||||||
|
// Reset Driver as it has wfp handles probably >:(
|
||||||
|
resetDriver(driverIO);
|
||||||
|
|
||||||
|
auto newState = getState(driverIO);
|
||||||
|
logger.debug() << "New state after reset:" << newState;
|
||||||
|
if (newState >= STATE_INITIALIZED) {
|
||||||
|
logger.debug() << "Reset unsuccesfull";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD bytesReturned;
|
||||||
|
auto ok = DeviceIoControl(driverIO, IOCTL_INITIALIZE, nullptr, 0, nullptr, 0,
|
||||||
|
&bytesReturned, nullptr);
|
||||||
|
if (!ok) {
|
||||||
|
auto err = GetLastError();
|
||||||
|
logger.error() << "Driver init failed err -" << err;
|
||||||
|
logger.error() << "State:" << getState(driverIO);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
logger.debug() << "Driver initialized" << getState(driverIO);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowsSplitTunnel::WindowsSplitTunnel(HANDLE driverIO) : m_driver(driverIO) {
|
||||||
|
logger.debug() << "Connected to the Driver";
|
||||||
|
|
||||||
|
Q_ASSERT(getState() == STATE_INITIALIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowsSplitTunnel::~WindowsSplitTunnel() {
|
WindowsSplitTunnel::~WindowsSplitTunnel() {
|
||||||
|
|
@ -53,73 +277,12 @@ WindowsSplitTunnel::~WindowsSplitTunnel() {
|
||||||
uninstallDriver();
|
uninstallDriver();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowsSplitTunnel::initDriver() {
|
bool WindowsSplitTunnel::excludeApps(const QStringList& appPaths) {
|
||||||
if (detectConflict()) {
|
|
||||||
logger.error() << "Conflict detected, abort Split-Tunnel init.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
logger.debug() << "Try to open Split Tunnel Driver";
|
|
||||||
// Open the Driver Symlink
|
|
||||||
m_driver = CreateFileW(DRIVER_SYMLINK, GENERIC_READ | GENERIC_WRITE, 0,
|
|
||||||
nullptr, OPEN_EXISTING, 0, nullptr);
|
|
||||||
;
|
|
||||||
if (m_driver == INVALID_HANDLE_VALUE && m_tries < 500) {
|
|
||||||
WindowsUtils::windowsLog("Failed to open Driver: ");
|
|
||||||
m_tries++;
|
|
||||||
Sleep(100);
|
|
||||||
// If the handle is not present, try again after the serivce has started;
|
|
||||||
auto driver_manager = WindowsServiceManager(DRIVER_SERVICE_NAME);
|
|
||||||
QObject::connect(&driver_manager, &WindowsServiceManager::serviceStarted,
|
|
||||||
this, &WindowsSplitTunnel::initDriver);
|
|
||||||
driver_manager.startService();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug() << "Connected to the Driver";
|
|
||||||
// Reset Driver as it has wfp handles probably >:(
|
|
||||||
|
|
||||||
if (!WindowsFirewall::instance()->init()) {
|
|
||||||
logger.error() << "Init WFP-Sublayer failed, driver won't be functional";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to now check the state and init it, if required
|
|
||||||
|
|
||||||
auto state = getState();
|
|
||||||
if (state == STATE_UNKNOWN) {
|
|
||||||
logger.debug() << "Cannot check if driver is initialized";
|
|
||||||
}
|
|
||||||
if (state >= STATE_INITIALIZED) {
|
|
||||||
logger.debug() << "Driver already initialized: " << state;
|
|
||||||
reset();
|
|
||||||
|
|
||||||
auto newState = getState();
|
|
||||||
logger.debug() << "New state after reset:" << newState;
|
|
||||||
if (newState >= STATE_INITIALIZED) {
|
|
||||||
logger.debug() << "Reset unsuccesfull";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD bytesReturned;
|
|
||||||
auto ok = DeviceIoControl(m_driver, IOCTL_INITIALIZE, nullptr, 0, nullptr, 0,
|
|
||||||
&bytesReturned, nullptr);
|
|
||||||
if (!ok) {
|
|
||||||
auto err = GetLastError();
|
|
||||||
logger.error() << "Driver init failed err -" << err;
|
|
||||||
logger.error() << "State:" << getState();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
logger.debug() << "Driver initialized" << getState();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WindowsSplitTunnel::setRules(const QStringList& appPaths) {
|
|
||||||
auto state = getState();
|
auto state = getState();
|
||||||
if (state != STATE_READY && state != STATE_RUNNING) {
|
if (state != STATE_READY && state != STATE_RUNNING) {
|
||||||
logger.warning() << "Driver is not in the right State to set Rules"
|
logger.warning() << "Driver is not in the right State to set Rules"
|
||||||
<< state;
|
<< state;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug() << "Pushing new Ruleset for Split-Tunnel " << state;
|
logger.debug() << "Pushing new Ruleset for Split-Tunnel " << state;
|
||||||
|
|
@ -133,12 +296,13 @@ void WindowsSplitTunnel::setRules(const QStringList& appPaths) {
|
||||||
auto err = GetLastError();
|
auto err = GetLastError();
|
||||||
WindowsUtils::windowsLog("Set Config Failed:");
|
WindowsUtils::windowsLog("Set Config Failed:");
|
||||||
logger.error() << "Failed to set Config err code " << err;
|
logger.error() << "Failed to set Config err code " << err;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
logger.debug() << "New Configuration applied: " << getState();
|
logger.debug() << "New Configuration applied: " << stateString();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowsSplitTunnel::start(int inetAdapterIndex, int vpnAdapterIndex) {
|
bool WindowsSplitTunnel::start(int inetAdapterIndex, int vpnAdapterIndex) {
|
||||||
// To Start we need to send 2 things:
|
// To Start we need to send 2 things:
|
||||||
// Network info (what is vpn what is network)
|
// Network info (what is vpn what is network)
|
||||||
logger.debug() << "Starting SplitTunnel";
|
logger.debug() << "Starting SplitTunnel";
|
||||||
|
|
@ -151,7 +315,7 @@ void WindowsSplitTunnel::start(int inetAdapterIndex, int vpnAdapterIndex) {
|
||||||
0, &bytesReturned, nullptr);
|
0, &bytesReturned, nullptr);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
logger.error() << "Driver init failed";
|
logger.error() << "Driver init failed";
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -164,16 +328,16 @@ void WindowsSplitTunnel::start(int inetAdapterIndex, int vpnAdapterIndex) {
|
||||||
nullptr);
|
nullptr);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
logger.error() << "Failed to set Process Config";
|
logger.error() << "Failed to set Process Config";
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
logger.debug() << "Set Process Config ok || new State:" << getState();
|
logger.debug() << "Set Process Config ok || new State:" << stateString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getState() == STATE_INITIALIZED) {
|
if (getState() == STATE_INITIALIZED) {
|
||||||
logger.warning() << "Driver is still not ready after process list send";
|
logger.warning() << "Driver is still not ready after process list send";
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
logger.debug() << "Driver is ready || new State:" << getState();
|
logger.debug() << "Driver is ready || new State:" << stateString();
|
||||||
|
|
||||||
auto config = generateIPConfiguration(inetAdapterIndex, vpnAdapterIndex);
|
auto config = generateIPConfiguration(inetAdapterIndex, vpnAdapterIndex);
|
||||||
auto ok = DeviceIoControl(m_driver, IOCTL_REGISTER_IP_ADDRESSES, &config[0],
|
auto ok = DeviceIoControl(m_driver, IOCTL_REGISTER_IP_ADDRESSES, &config[0],
|
||||||
|
|
@ -181,9 +345,10 @@ void WindowsSplitTunnel::start(int inetAdapterIndex, int vpnAdapterIndex) {
|
||||||
nullptr);
|
nullptr);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
logger.error() << "Failed to set Network Config";
|
logger.error() << "Failed to set Network Config";
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
logger.debug() << "New Network Config Applied || new State:" << getState();
|
logger.debug() << "New Network Config Applied || new State:" << stateString();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowsSplitTunnel::stop() {
|
void WindowsSplitTunnel::stop() {
|
||||||
|
|
@ -197,25 +362,27 @@ void WindowsSplitTunnel::stop() {
|
||||||
logger.debug() << "Stopping Split tunnel successfull";
|
logger.debug() << "Stopping Split tunnel successfull";
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowsSplitTunnel::reset() {
|
bool WindowsSplitTunnel::resetDriver(HANDLE driverIO) {
|
||||||
DWORD bytesReturned;
|
DWORD bytesReturned;
|
||||||
auto ok = DeviceIoControl(m_driver, IOCTL_ST_RESET, nullptr, 0, nullptr, 0,
|
auto ok = DeviceIoControl(driverIO, IOCTL_ST_RESET, nullptr, 0, nullptr, 0,
|
||||||
&bytesReturned, nullptr);
|
&bytesReturned, nullptr);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
logger.error() << "Reset Split tunnel not successfull";
|
logger.error() << "Reset Split tunnel not successfull";
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
logger.debug() << "Reset Split tunnel successfull";
|
logger.debug() << "Reset Split tunnel successfull";
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
DRIVER_STATE WindowsSplitTunnel::getState() {
|
// static
|
||||||
if (m_driver == INVALID_HANDLE_VALUE) {
|
WindowsSplitTunnel::DRIVER_STATE WindowsSplitTunnel::getState(HANDLE driverIO) {
|
||||||
|
if (driverIO == INVALID_HANDLE_VALUE) {
|
||||||
logger.debug() << "Can't query State from non Opened Driver";
|
logger.debug() << "Can't query State from non Opened Driver";
|
||||||
return STATE_UNKNOWN;
|
return STATE_UNKNOWN;
|
||||||
}
|
}
|
||||||
DWORD bytesReturned;
|
DWORD bytesReturned;
|
||||||
SIZE_T outBuffer;
|
SIZE_T outBuffer;
|
||||||
bool ok = DeviceIoControl(m_driver, IOCTL_GET_STATE, nullptr, 0, &outBuffer,
|
bool ok = DeviceIoControl(driverIO, IOCTL_GET_STATE, nullptr, 0, &outBuffer,
|
||||||
sizeof(outBuffer), &bytesReturned, nullptr);
|
sizeof(outBuffer), &bytesReturned, nullptr);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
WindowsUtils::windowsLog("getState response failure");
|
WindowsUtils::windowsLog("getState response failure");
|
||||||
|
|
@ -225,7 +392,10 @@ DRIVER_STATE WindowsSplitTunnel::getState() {
|
||||||
WindowsUtils::windowsLog("getState response is empty");
|
WindowsUtils::windowsLog("getState response is empty");
|
||||||
return STATE_UNKNOWN;
|
return STATE_UNKNOWN;
|
||||||
}
|
}
|
||||||
return static_cast<DRIVER_STATE>(outBuffer);
|
return static_cast<WindowsSplitTunnel::DRIVER_STATE>(outBuffer);
|
||||||
|
}
|
||||||
|
WindowsSplitTunnel::DRIVER_STATE WindowsSplitTunnel::getState() {
|
||||||
|
return getState(m_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> WindowsSplitTunnel::generateAppConfiguration(
|
std::vector<uint8_t> WindowsSplitTunnel::generateAppConfiguration(
|
||||||
|
|
@ -273,9 +443,9 @@ std::vector<uint8_t> WindowsSplitTunnel::generateAppConfiguration(
|
||||||
return outBuffer;
|
return outBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> WindowsSplitTunnel::generateIPConfiguration(
|
std::vector<std::byte> WindowsSplitTunnel::generateIPConfiguration(
|
||||||
int inetAdapterIndex, int vpnAdapterIndex) {
|
int inetAdapterIndex, int vpnAdapterIndex) {
|
||||||
std::vector<uint8_t> out(sizeof(IP_ADDRESSES_CONFIG));
|
std::vector<std::byte> out(sizeof(IP_ADDRESSES_CONFIG));
|
||||||
|
|
||||||
auto config = reinterpret_cast<IP_ADDRESSES_CONFIG*>(&out[0]);
|
auto config = reinterpret_cast<IP_ADDRESSES_CONFIG*>(&out[0]);
|
||||||
|
|
||||||
|
|
@ -284,47 +454,48 @@ std::vector<uint8_t> WindowsSplitTunnel::generateIPConfiguration(
|
||||||
if (vpnAdapterIndex == 0) {
|
if (vpnAdapterIndex == 0) {
|
||||||
vpnAdapterIndex = WindowsCommons::VPNAdapterIndex();
|
vpnAdapterIndex = WindowsCommons::VPNAdapterIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always the VPN
|
// Always the VPN
|
||||||
getAddress(vpnAdapterIndex, &config->TunnelIpv4,
|
if (!getAddress(vpnAdapterIndex, &config->TunnelIpv4,
|
||||||
&config->TunnelIpv6);
|
&config->TunnelIpv6)) {
|
||||||
// 2nd best route
|
return {};
|
||||||
getAddress(inetAdapterIndex, &config->InternetIpv4, &config->InternetIpv6);
|
}
|
||||||
|
// 2nd best route is usually the internet adapter
|
||||||
|
if (!getAddress(inetAdapterIndex, &config->InternetIpv4,
|
||||||
|
&config->InternetIpv6)) {
|
||||||
|
return {};
|
||||||
|
};
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
void WindowsSplitTunnel::getAddress(int adapterIndex, IN_ADDR* out_ipv4,
|
bool WindowsSplitTunnel::getAddress(int adapterIndex, IN_ADDR* out_ipv4,
|
||||||
IN6_ADDR* out_ipv6) {
|
IN6_ADDR* out_ipv6) {
|
||||||
QNetworkInterface target =
|
QNetworkInterface target =
|
||||||
QNetworkInterface::interfaceFromIndex(adapterIndex);
|
QNetworkInterface::interfaceFromIndex(adapterIndex);
|
||||||
logger.debug() << "Getting adapter info for:" << target.humanReadableName();
|
logger.debug() << "Getting adapter info for:" << target.humanReadableName();
|
||||||
|
|
||||||
// take the first v4/v6 Adress and convert to in_addr
|
auto get = [&target](QAbstractSocket::NetworkLayerProtocol protocol) {
|
||||||
for (auto address : target.addressEntries()) {
|
for (auto address : target.addressEntries()) {
|
||||||
if (address.ip().protocol() == QAbstractSocket::IPv4Protocol) {
|
if (address.ip().protocol() != protocol) {
|
||||||
auto adrr = address.ip().toString();
|
continue;
|
||||||
std::wstring wstr = adrr.toStdWString();
|
}
|
||||||
logger.debug() << "IpV4" << logger.sensitive(adrr);
|
return address.ip().toString().toStdWString();
|
||||||
PCWSTR w_str_ip = wstr.c_str();
|
}
|
||||||
auto ok = InetPtonW(AF_INET, w_str_ip, out_ipv4);
|
return std::wstring{};
|
||||||
if (ok != 1) {
|
};
|
||||||
|
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();
|
logger.debug() << "Ipv4 Conversation error" << WSAGetLastError();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
if (ipv6.empty()) {
|
||||||
}
|
std::memset(out_ipv6, 0x00, sizeof(IN6_ADDR));
|
||||||
}
|
return true;
|
||||||
for (auto address : target.addressEntries()) {
|
|
||||||
if (address.ip().protocol() == QAbstractSocket::IPv6Protocol) {
|
|
||||||
auto adrr = address.ip().toString();
|
|
||||||
std::wstring wstr = adrr.toStdWString();
|
|
||||||
logger.debug() << "IpV6" << logger.sensitive(adrr);
|
|
||||||
PCWSTR w_str_ip = wstr.c_str();
|
|
||||||
auto ok = InetPtonW(AF_INET6, w_str_ip, out_ipv6);
|
|
||||||
if (ok != 1) {
|
|
||||||
logger.error() << "Ipv6 Conversation error" << WSAGetLastError();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
if (InetPtonW(AF_INET6, ipv6.c_str(), out_ipv6) != 1) {
|
||||||
|
logger.debug() << "Ipv6 Conversation error" << WSAGetLastError();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> WindowsSplitTunnel::generateProcessBlob() {
|
std::vector<uint8_t> WindowsSplitTunnel::generateProcessBlob() {
|
||||||
|
|
@ -411,33 +582,6 @@ std::vector<uint8_t> WindowsSplitTunnel::generateProcessBlob() {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowsSplitTunnel::close() {
|
|
||||||
CloseHandle(m_driver);
|
|
||||||
m_driver = INVALID_HANDLE_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
ProcessInfo WindowsSplitTunnel::getProcessInfo(
|
|
||||||
HANDLE process, const PROCESSENTRY32W& processMeta) {
|
|
||||||
ProcessInfo pi;
|
|
||||||
pi.ParentProcessId = processMeta.th32ParentProcessID;
|
|
||||||
pi.ProcessId = processMeta.th32ProcessID;
|
|
||||||
pi.CreationTime = {0, 0};
|
|
||||||
pi.DevicePath = L"";
|
|
||||||
|
|
||||||
FILETIME creationTime, null_time;
|
|
||||||
auto ok = GetProcessTimes(process, &creationTime, &null_time, &null_time,
|
|
||||||
&null_time);
|
|
||||||
if (ok) {
|
|
||||||
pi.CreationTime = creationTime;
|
|
||||||
}
|
|
||||||
wchar_t imagepath[MAX_PATH + 1];
|
|
||||||
if (K32GetProcessImageFileNameW(
|
|
||||||
process, imagepath, sizeof(imagepath) / sizeof(*imagepath)) != 0) {
|
|
||||||
pi.DevicePath = imagepath;
|
|
||||||
}
|
|
||||||
return pi;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
SC_HANDLE WindowsSplitTunnel::installDriver() {
|
SC_HANDLE WindowsSplitTunnel::installDriver() {
|
||||||
LPCWSTR displayName = L"Amnezia Split Tunnel Service";
|
LPCWSTR displayName = L"Amnezia Split Tunnel Service";
|
||||||
|
|
@ -448,15 +592,15 @@ SC_HANDLE WindowsSplitTunnel::installDriver() {
|
||||||
return (SC_HANDLE)INVALID_HANDLE_VALUE;
|
return (SC_HANDLE)INVALID_HANDLE_VALUE;
|
||||||
}
|
}
|
||||||
auto path = driver.absolutePath() + "/" + DRIVER_FILENAME;
|
auto path = driver.absolutePath() + "/" + DRIVER_FILENAME;
|
||||||
LPCWSTR binPath = (const wchar_t*)path.utf16();
|
auto binPath = (const wchar_t*)path.utf16();
|
||||||
auto scm_rights = SC_MANAGER_ALL_ACCESS;
|
auto scm_rights = SC_MANAGER_ALL_ACCESS;
|
||||||
auto serviceManager = OpenSCManager(NULL, // local computer
|
auto serviceManager = OpenSCManager(nullptr, // local computer
|
||||||
NULL, // servicesActive database
|
nullptr, // servicesActive database
|
||||||
scm_rights);
|
scm_rights);
|
||||||
auto service = CreateService(serviceManager, DRIVER_SERVICE_NAME, displayName,
|
auto service = CreateService(
|
||||||
SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER,
|
serviceManager, DRIVER_SERVICE_NAME, displayName, SERVICE_ALL_ACCESS,
|
||||||
SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
|
SERVICE_KERNEL_DRIVER, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, binPath,
|
||||||
binPath, nullptr, 0, nullptr, nullptr, nullptr);
|
nullptr, nullptr, nullptr, nullptr, nullptr);
|
||||||
CloseServiceHandle(serviceManager);
|
CloseServiceHandle(serviceManager);
|
||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
|
|
@ -554,3 +698,25 @@ bool WindowsSplitTunnel::detectConflict() {
|
||||||
CloseServiceHandle(servicehandle);
|
CloseServiceHandle(servicehandle);
|
||||||
return err == ERROR_SERVICE_DOES_NOT_EXIST;
|
return err == ERROR_SERVICE_DOES_NOT_EXIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WindowsSplitTunnel::isRunning() { return getState() == STATE_RUNNING; }
|
||||||
|
QString WindowsSplitTunnel::stateString() {
|
||||||
|
switch (getState()) {
|
||||||
|
case STATE_UNKNOWN:
|
||||||
|
return "STATE_UNKNOWN";
|
||||||
|
case STATE_NONE:
|
||||||
|
return "STATE_NONE";
|
||||||
|
case STATE_STARTED:
|
||||||
|
return "STATE_STARTED";
|
||||||
|
case STATE_INITIALIZED:
|
||||||
|
return "STATE_INITIALIZED";
|
||||||
|
case STATE_READY:
|
||||||
|
return "STATE_READY";
|
||||||
|
case STATE_RUNNING:
|
||||||
|
return "STATE_RUNNING";
|
||||||
|
case STATE_ZOMBIE:
|
||||||
|
return "STATE_ZOMBIE";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
// Note: the ws2tcpip.h import must come before the others.
|
// Note: the ws2tcpip.h import must come before the others.
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
|
@ -18,6 +19,45 @@
|
||||||
#include <tlhelp32.h>
|
#include <tlhelp32.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
|
class WindowsFirewall;
|
||||||
|
|
||||||
|
class WindowsSplitTunnel final {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Installs and Initializes the Split Tunnel Driver.
|
||||||
|
*
|
||||||
|
* @param fw -
|
||||||
|
* @return std::unique_ptr<WindowsSplitTunnel> - Is null on failure.
|
||||||
|
*/
|
||||||
|
static std::unique_ptr<WindowsSplitTunnel> create(WindowsFirewall* fw);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construct a new Windows Split Tunnel object
|
||||||
|
*
|
||||||
|
* @param driverIO - The Handle to the Driver's IO file, it assumes the driver
|
||||||
|
* is in STATE_INITIALIZED and the Firewall has been setup.
|
||||||
|
* Prefer using create() to get to this state.
|
||||||
|
*/
|
||||||
|
WindowsSplitTunnel(HANDLE driverIO);
|
||||||
|
/**
|
||||||
|
* @brief Destroy the Windows Split Tunnel object and uninstalls the Driver.
|
||||||
|
*/
|
||||||
|
~WindowsSplitTunnel();
|
||||||
|
|
||||||
|
// void excludeApps(const QStringList& paths);
|
||||||
|
// Excludes an Application from the VPN
|
||||||
|
bool excludeApps(const QStringList& appPaths);
|
||||||
|
|
||||||
|
// Fetches and Pushed needed info to move to engaged mode
|
||||||
|
bool start(int inetAdapterIndex, int vpnAdapterIndex = 0);
|
||||||
|
// Deletes Rules and puts the driver into passive mode
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
// Returns true if the split-tunnel driver is now up and running.
|
||||||
|
bool isRunning();
|
||||||
|
|
||||||
|
static bool detectConflict();
|
||||||
|
|
||||||
// States for GetState
|
// States for GetState
|
||||||
enum DRIVER_STATE {
|
enum DRIVER_STATE {
|
||||||
STATE_UNKNOWN = -1,
|
STATE_UNKNOWN = -1,
|
||||||
|
|
@ -29,149 +69,28 @@ enum DRIVER_STATE {
|
||||||
STATE_ZOMBIE = 5,
|
STATE_ZOMBIE = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef CTL_CODE
|
private:
|
||||||
|
|
||||||
# define FILE_ANY_ACCESS 0x0000
|
|
||||||
|
|
||||||
# define METHOD_BUFFERED 0
|
|
||||||
# define METHOD_IN_DIRECT 1
|
|
||||||
# define METHOD_NEITHER 3
|
|
||||||
|
|
||||||
# define CTL_CODE(DeviceType, Function, Method, Access) \
|
|
||||||
(((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Known ControlCodes
|
|
||||||
#define IOCTL_INITIALIZE CTL_CODE(0x8000, 1, METHOD_NEITHER, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
#define IOCTL_DEQUEUE_EVENT \
|
|
||||||
CTL_CODE(0x8000, 2, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
#define IOCTL_REGISTER_PROCESSES \
|
|
||||||
CTL_CODE(0x8000, 3, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
#define IOCTL_REGISTER_IP_ADDRESSES \
|
|
||||||
CTL_CODE(0x8000, 4, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
#define IOCTL_GET_IP_ADDRESSES \
|
|
||||||
CTL_CODE(0x8000, 5, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
#define IOCTL_SET_CONFIGURATION \
|
|
||||||
CTL_CODE(0x8000, 6, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
#define IOCTL_GET_CONFIGURATION \
|
|
||||||
CTL_CODE(0x8000, 7, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
#define IOCTL_CLEAR_CONFIGURATION \
|
|
||||||
CTL_CODE(0x8000, 8, METHOD_NEITHER, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
#define IOCTL_GET_STATE CTL_CODE(0x8000, 9, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
#define IOCTL_QUERY_PROCESS \
|
|
||||||
CTL_CODE(0x8000, 10, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
#define IOCTL_ST_RESET CTL_CODE(0x8000, 11, METHOD_NEITHER, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
// Driver Configuration structures
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
// Offset into buffer region that follows all entries.
|
|
||||||
// The image name uses the device path.
|
|
||||||
SIZE_T ImageNameOffset;
|
|
||||||
// Length of the String
|
|
||||||
USHORT ImageNameLength;
|
|
||||||
} CONFIGURATION_ENTRY;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
// Number of entries immediately following the header.
|
|
||||||
SIZE_T NumEntries;
|
|
||||||
|
|
||||||
// Total byte length: header + entries + string buffer.
|
|
||||||
SIZE_T TotalLength;
|
|
||||||
} CONFIGURATION_HEADER;
|
|
||||||
|
|
||||||
// Used to Configure Which IP is network/vpn
|
|
||||||
typedef struct {
|
|
||||||
IN_ADDR TunnelIpv4;
|
|
||||||
IN_ADDR InternetIpv4;
|
|
||||||
|
|
||||||
IN6_ADDR TunnelIpv6;
|
|
||||||
IN6_ADDR InternetIpv6;
|
|
||||||
} IP_ADDRESSES_CONFIG;
|
|
||||||
|
|
||||||
// Used to Define Which Processes are alive on activation
|
|
||||||
typedef struct {
|
|
||||||
SIZE_T NumEntries;
|
|
||||||
SIZE_T TotalLength;
|
|
||||||
} PROCESS_DISCOVERY_HEADER;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
HANDLE ProcessId;
|
|
||||||
HANDLE ParentProcessId;
|
|
||||||
|
|
||||||
SIZE_T ImageNameOffset;
|
|
||||||
USHORT ImageNameLength;
|
|
||||||
} PROCESS_DISCOVERY_ENTRY;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
DWORD ProcessId;
|
|
||||||
DWORD ParentProcessId;
|
|
||||||
FILETIME CreationTime;
|
|
||||||
std::wstring DevicePath;
|
|
||||||
} ProcessInfo;
|
|
||||||
|
|
||||||
class WindowsSplitTunnel final : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
Q_DISABLE_COPY_MOVE(WindowsSplitTunnel)
|
|
||||||
public:
|
|
||||||
explicit WindowsSplitTunnel(QObject* parent);
|
|
||||||
~WindowsSplitTunnel();
|
|
||||||
|
|
||||||
// void excludeApps(const QStringList& paths);
|
|
||||||
// Excludes an Application from the VPN
|
|
||||||
void setRules(const QStringList& appPaths);
|
|
||||||
|
|
||||||
// Fetches and Pushed needed info to move to engaged mode
|
|
||||||
void start(int inetAdapterIndex, int vpnAdapterIndex = 0);
|
|
||||||
// Deletes Rules and puts the driver into passive mode
|
|
||||||
void stop();
|
|
||||||
// Resets the Whole Driver
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
// Just close connection, leave state as is
|
|
||||||
void close();
|
|
||||||
|
|
||||||
// Installes the Kernel Driver as Driver Service
|
// Installes the Kernel Driver as Driver Service
|
||||||
static SC_HANDLE installDriver();
|
static SC_HANDLE installDriver();
|
||||||
static bool uninstallDriver();
|
static bool uninstallDriver();
|
||||||
static bool isInstalled();
|
static bool isInstalled();
|
||||||
static bool detectConflict();
|
static bool initDriver(HANDLE driverIO);
|
||||||
|
static DRIVER_STATE getState(HANDLE driverIO);
|
||||||
|
static bool resetDriver(HANDLE driverIO);
|
||||||
|
|
||||||
private slots:
|
|
||||||
void initDriver();
|
|
||||||
|
|
||||||
private:
|
|
||||||
HANDLE m_driver = INVALID_HANDLE_VALUE;
|
HANDLE m_driver = INVALID_HANDLE_VALUE;
|
||||||
constexpr static const auto DRIVER_SYMLINK = L"\\\\.\\MULLVADSPLITTUNNEL";
|
|
||||||
constexpr static const auto DRIVER_FILENAME = "mullvad-split-tunnel.sys";
|
|
||||||
constexpr static const auto DRIVER_SERVICE_NAME = L"AmneziaVPNSplitTunnel";
|
|
||||||
constexpr static const auto MV_SERVICE_NAME = L"MullvadVPN";
|
|
||||||
DRIVER_STATE getState();
|
DRIVER_STATE getState();
|
||||||
|
QString stateString();
|
||||||
int m_tries;
|
|
||||||
// Initializes the WFP Sublayer
|
|
||||||
bool initSublayer();
|
|
||||||
|
|
||||||
// Generates a Configuration for Each APP
|
// Generates a Configuration for Each APP
|
||||||
std::vector<uint8_t> generateAppConfiguration(const QStringList& appPaths);
|
std::vector<uint8_t> generateAppConfiguration(const QStringList& appPaths);
|
||||||
// Generates a Configuration which IP's are VPN and which network
|
// Generates a Configuration which IP's are VPN and which network
|
||||||
std::vector<uint8_t> generateIPConfiguration(int inetAdapterIndex, int vpnAdapterIndex = 0);
|
std::vector<std::byte> generateIPConfiguration(int inetAdapterIndex, int vpnAdapterIndex = 0);
|
||||||
std::vector<uint8_t> generateProcessBlob();
|
std::vector<uint8_t> generateProcessBlob();
|
||||||
|
|
||||||
void getAddress(int adapterIndex, IN_ADDR* out_ipv4, IN6_ADDR* out_ipv6);
|
[[nodiscard]] bool getAddress(int adapterIndex, IN_ADDR* out_ipv4,
|
||||||
|
IN6_ADDR* out_ipv6);
|
||||||
// Collects info about an Opened Process
|
// Collects info about an Opened Process
|
||||||
ProcessInfo getProcessInfo(HANDLE process,
|
|
||||||
const PROCESSENTRY32W& processMeta);
|
|
||||||
|
|
||||||
// Converts a path to a Dos Path:
|
// Converts a path to a Dos Path:
|
||||||
// e.g C:/a.exe -> /harddisk0/a.exe
|
// e.g C:/a.exe -> /harddisk0/a.exe
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,20 @@ namespace {
|
||||||
Logger logger("WireguardUtilsWindows");
|
Logger logger("WireguardUtilsWindows");
|
||||||
}; // namespace
|
}; // namespace
|
||||||
|
|
||||||
WireguardUtilsWindows::WireguardUtilsWindows(QObject* parent)
|
std::unique_ptr<WireguardUtilsWindows> WireguardUtilsWindows::create(
|
||||||
: WireguardUtils(parent), m_tunnel(this), m_routeMonitor(this) {
|
WindowsFirewall* fw, QObject* parent) {
|
||||||
|
if (!fw) {
|
||||||
|
logger.error() << "WireguardUtilsWindows::create: no wfp handle";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't use make_unique here as the Constructor is private :(
|
||||||
|
auto utils = new WireguardUtilsWindows(parent, fw);
|
||||||
|
return std::unique_ptr<WireguardUtilsWindows>(utils);
|
||||||
|
}
|
||||||
|
|
||||||
|
WireguardUtilsWindows::WireguardUtilsWindows(QObject* parent, WindowsFirewall* fw)
|
||||||
|
: WireguardUtils(parent), m_tunnel(this), m_firewall(fw) {
|
||||||
MZ_COUNT_CTOR(WireguardUtilsWindows);
|
MZ_COUNT_CTOR(WireguardUtilsWindows);
|
||||||
logger.debug() << "WireguardUtilsWindows created.";
|
logger.debug() << "WireguardUtilsWindows created.";
|
||||||
|
|
||||||
|
|
@ -114,13 +126,13 @@ bool WireguardUtilsWindows::addInterface(const InterfaceConfig& config) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_luid = luid.Value;
|
m_luid = luid.Value;
|
||||||
m_routeMonitor.setLuid(luid.Value);
|
m_routeMonitor = new WindowsRouteMonitor(luid.Value, this);
|
||||||
|
|
||||||
if (config.m_killSwitchEnabled) {
|
if (config.m_killSwitchEnabled) {
|
||||||
// Enable the windows firewall
|
// Enable the windows firewall
|
||||||
NET_IFINDEX ifindex;
|
NET_IFINDEX ifindex;
|
||||||
ConvertInterfaceLuidToIndex(&luid, &ifindex);
|
ConvertInterfaceLuidToIndex(&luid, &ifindex);
|
||||||
WindowsFirewall::instance()->enableKillSwitch(ifindex);
|
m_firewall->enableInterface(ifindex);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug() << "Registration completed";
|
logger.debug() << "Registration completed";
|
||||||
|
|
@ -128,7 +140,11 @@ bool WireguardUtilsWindows::addInterface(const InterfaceConfig& config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WireguardUtilsWindows::deleteInterface() {
|
bool WireguardUtilsWindows::deleteInterface() {
|
||||||
WindowsFirewall::instance()->disableKillSwitch();
|
if (m_routeMonitor) {
|
||||||
|
m_routeMonitor->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_firewall->disableKillSwitch();
|
||||||
m_tunnel.stop();
|
m_tunnel.stop();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -141,7 +157,7 @@ bool WireguardUtilsWindows::updatePeer(const InterfaceConfig& config) {
|
||||||
|
|
||||||
if (config.m_killSwitchEnabled) {
|
if (config.m_killSwitchEnabled) {
|
||||||
// Enable the windows firewall for this peer.
|
// Enable the windows firewall for this peer.
|
||||||
WindowsFirewall::instance()->enablePeerTraffic(config);
|
m_firewall->enablePeerTraffic(config);
|
||||||
}
|
}
|
||||||
logger.debug() << "Configuring peer" << publicKey.toHex()
|
logger.debug() << "Configuring peer" << publicKey.toHex()
|
||||||
<< "via" << config.m_serverIpv4AddrIn;
|
<< "via" << config.m_serverIpv4AddrIn;
|
||||||
|
|
@ -171,9 +187,9 @@ bool WireguardUtilsWindows::updatePeer(const InterfaceConfig& config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exclude the server address, except for multihop exit servers.
|
// Exclude the server address, except for multihop exit servers.
|
||||||
if (config.m_hopType != InterfaceConfig::MultiHopExit) {
|
if (m_routeMonitor && config.m_hopType != InterfaceConfig::MultiHopExit) {
|
||||||
m_routeMonitor.addExclusionRoute(IPAddress(config.m_serverIpv4AddrIn));
|
m_routeMonitor->addExclusionRoute(IPAddress(config.m_serverIpv4AddrIn));
|
||||||
m_routeMonitor.addExclusionRoute(IPAddress(config.m_serverIpv6AddrIn));
|
m_routeMonitor->addExclusionRoute(IPAddress(config.m_serverIpv6AddrIn));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString reply = m_tunnel.uapiCommand(message);
|
QString reply = m_tunnel.uapiCommand(message);
|
||||||
|
|
@ -186,13 +202,13 @@ bool WireguardUtilsWindows::deletePeer(const InterfaceConfig& config) {
|
||||||
QByteArray::fromBase64(qPrintable(config.m_serverPublicKey));
|
QByteArray::fromBase64(qPrintable(config.m_serverPublicKey));
|
||||||
|
|
||||||
// Clear exclustion routes for this peer.
|
// Clear exclustion routes for this peer.
|
||||||
if (config.m_hopType != InterfaceConfig::MultiHopExit) {
|
if (m_routeMonitor && config.m_hopType != InterfaceConfig::MultiHopExit) {
|
||||||
m_routeMonitor.deleteExclusionRoute(IPAddress(config.m_serverIpv4AddrIn));
|
m_routeMonitor->deleteExclusionRoute(IPAddress(config.m_serverIpv4AddrIn));
|
||||||
m_routeMonitor.deleteExclusionRoute(IPAddress(config.m_serverIpv6AddrIn));
|
m_routeMonitor->deleteExclusionRoute(IPAddress(config.m_serverIpv6AddrIn));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable the windows firewall for this peer.
|
// Disable the windows firewall for this peer.
|
||||||
WindowsFirewall::instance()->disablePeerTraffic(config.m_serverPublicKey);
|
m_firewall->disablePeerTraffic(config.m_serverPublicKey);
|
||||||
|
|
||||||
QString message;
|
QString message;
|
||||||
QTextStream out(&message);
|
QTextStream out(&message);
|
||||||
|
|
@ -238,6 +254,13 @@ void WireguardUtilsWindows::buildMibForwardRow(const IPAddress& prefix,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WireguardUtilsWindows::updateRoutePrefix(const IPAddress& prefix) {
|
bool WireguardUtilsWindows::updateRoutePrefix(const IPAddress& prefix) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
// Build the route
|
||||||
|
|
||||||
MIB_IPFORWARD_ROW2 entry;
|
MIB_IPFORWARD_ROW2 entry;
|
||||||
buildMibForwardRow(prefix, &entry);
|
buildMibForwardRow(prefix, &entry);
|
||||||
|
|
||||||
|
|
@ -255,6 +278,12 @@ bool WireguardUtilsWindows::updateRoutePrefix(const IPAddress& prefix) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WireguardUtilsWindows::deleteRoutePrefix(const IPAddress& prefix) {
|
bool WireguardUtilsWindows::deleteRoutePrefix(const IPAddress& prefix) {
|
||||||
|
if (m_routeMonitor && (prefix.prefixLength() == 0)) {
|
||||||
|
// Deactivate the route capture feature.
|
||||||
|
m_routeMonitor->setDetaultRouteCapture(false);
|
||||||
|
}
|
||||||
|
// Build the route
|
||||||
|
|
||||||
MIB_IPFORWARD_ROW2 entry;
|
MIB_IPFORWARD_ROW2 entry;
|
||||||
buildMibForwardRow(prefix, &entry);
|
buildMibForwardRow(prefix, &entry);
|
||||||
|
|
||||||
|
|
@ -272,9 +301,28 @@ bool WireguardUtilsWindows::deleteRoutePrefix(const IPAddress& prefix) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WireguardUtilsWindows::addExclusionRoute(const IPAddress& prefix) {
|
bool WireguardUtilsWindows::addExclusionRoute(const IPAddress& prefix) {
|
||||||
return m_routeMonitor.addExclusionRoute(prefix);
|
return m_routeMonitor->addExclusionRoute(prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WireguardUtilsWindows::deleteExclusionRoute(const IPAddress& prefix) {
|
bool WireguardUtilsWindows::deleteExclusionRoute(const IPAddress& prefix) {
|
||||||
return m_routeMonitor.deleteExclusionRoute(prefix);
|
return m_routeMonitor->deleteExclusionRoute(prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WireguardUtilsWindows::excludeLocalNetworks(
|
||||||
|
const QList<IPAddress>& addresses) {
|
||||||
|
// If the interface isn't up then something went horribly wrong.
|
||||||
|
Q_ASSERT(m_routeMonitor);
|
||||||
|
// For each destination - attempt to exclude it from the VPN tunnel.
|
||||||
|
bool result = true;
|
||||||
|
for (const IPAddress& prefix : addresses) {
|
||||||
|
if (!m_routeMonitor->addExclusionRoute(prefix)) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Permit LAN traffic through the firewall.
|
||||||
|
if (!m_firewall->enableLanBypass(addresses)) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,16 +9,21 @@
|
||||||
|
|
||||||
#include <QHostAddress>
|
#include <QHostAddress>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QPointer>
|
||||||
|
|
||||||
#include "daemon/wireguardutils.h"
|
#include "daemon/wireguardutils.h"
|
||||||
#include "windowsroutemonitor.h"
|
#include "windowsroutemonitor.h"
|
||||||
#include "windowstunnelservice.h"
|
#include "windowstunnelservice.h"
|
||||||
|
|
||||||
|
class WindowsFirewall;
|
||||||
|
class WindowsRouteMonitor;
|
||||||
|
|
||||||
class WireguardUtilsWindows final : public WireguardUtils {
|
class WireguardUtilsWindows final : public WireguardUtils {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WireguardUtilsWindows(QObject* parent);
|
static std::unique_ptr<WireguardUtilsWindows> create(WindowsFirewall* fw,
|
||||||
|
QObject* parent);
|
||||||
~WireguardUtilsWindows();
|
~WireguardUtilsWindows();
|
||||||
|
|
||||||
bool interfaceExists() override { return m_tunnel.isRunning(); }
|
bool interfaceExists() override { return m_tunnel.isRunning(); }
|
||||||
|
|
@ -39,15 +44,19 @@ class WireguardUtilsWindows final : public WireguardUtils {
|
||||||
bool addExclusionRoute(const IPAddress& prefix) override;
|
bool addExclusionRoute(const IPAddress& prefix) override;
|
||||||
bool deleteExclusionRoute(const IPAddress& prefix) override;
|
bool deleteExclusionRoute(const IPAddress& prefix) override;
|
||||||
|
|
||||||
|
bool WireguardUtilsWindows::excludeLocalNetworks(const QList<IPAddress>& addresses) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void backendFailure();
|
void backendFailure();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
WireguardUtilsWindows(QObject* parent, WindowsFirewall* fw);
|
||||||
void buildMibForwardRow(const IPAddress& prefix, void* row);
|
void buildMibForwardRow(const IPAddress& prefix, void* row);
|
||||||
|
|
||||||
quint64 m_luid = 0;
|
quint64 m_luid = 0;
|
||||||
WindowsTunnelService m_tunnel;
|
WindowsTunnelService m_tunnel;
|
||||||
WindowsRouteMonitor m_routeMonitor;
|
QPointer<WindowsRouteMonitor> m_routeMonitor;
|
||||||
|
QPointer<WindowsFirewall> m_firewall;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // WIREGUARDUTILSWINDOWS_H
|
#endif // WIREGUARDUTILSWINDOWS_H
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "windowsservicemanager.h"
|
#include "windowsservicemanager.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include "Windows.h"
|
#include "Windows.h"
|
||||||
|
|
@ -16,35 +17,44 @@ namespace {
|
||||||
Logger logger("WindowsServiceManager");
|
Logger logger("WindowsServiceManager");
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowsServiceManager::WindowsServiceManager(LPCWSTR serviceName) {
|
WindowsServiceManager::WindowsServiceManager(SC_HANDLE serviceManager,
|
||||||
|
SC_HANDLE service)
|
||||||
|
: QObject(qApp), m_serviceManager(serviceManager), m_service(service) {
|
||||||
|
m_timer.setSingleShot(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<WindowsServiceManager> WindowsServiceManager::open(
|
||||||
|
const QString serviceName) {
|
||||||
|
LPCWSTR service = (const wchar_t*)serviceName.utf16();
|
||||||
|
|
||||||
DWORD err = NULL;
|
DWORD err = NULL;
|
||||||
auto scm_rights = SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE |
|
auto scm_rights = SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE |
|
||||||
SC_MANAGER_QUERY_LOCK_STATUS | STANDARD_RIGHTS_READ;
|
SC_MANAGER_QUERY_LOCK_STATUS | STANDARD_RIGHTS_READ;
|
||||||
m_serviceManager = OpenSCManager(NULL, // local computer
|
auto manager = OpenSCManager(NULL, // local computer
|
||||||
NULL, // servicesActive database
|
NULL, // servicesActive database
|
||||||
scm_rights);
|
scm_rights);
|
||||||
err = GetLastError();
|
err = GetLastError();
|
||||||
if (err != NULL) {
|
if (err != NULL) {
|
||||||
logger.error() << " OpenSCManager failed code: " << err;
|
logger.error() << " OpenSCManager failed code: " << err;
|
||||||
return;
|
return {};
|
||||||
}
|
}
|
||||||
logger.debug() << "OpenSCManager access given - " << err;
|
logger.debug() << "OpenSCManager access given - " << err;
|
||||||
|
|
||||||
logger.debug() << "Opening Service - "
|
logger.debug() << "Opening Service - " << serviceName;
|
||||||
<< QString::fromWCharArray(serviceName);
|
|
||||||
// Try to get an elevated handle
|
// Try to get an elevated handle
|
||||||
m_service = OpenService(m_serviceManager, // SCM database
|
auto serviceHandle =
|
||||||
serviceName, // name of service
|
OpenService(manager, // SCM database
|
||||||
|
service, // name of service
|
||||||
(GENERIC_READ | SERVICE_START | SERVICE_STOP));
|
(GENERIC_READ | SERVICE_START | SERVICE_STOP));
|
||||||
err = GetLastError();
|
err = GetLastError();
|
||||||
if (err != NULL) {
|
if (err != NULL) {
|
||||||
|
CloseServiceHandle(manager);
|
||||||
WindowsUtils::windowsLog("OpenService failed");
|
WindowsUtils::windowsLog("OpenService failed");
|
||||||
return;
|
return {};
|
||||||
}
|
}
|
||||||
m_has_access = true;
|
|
||||||
m_timer.setSingleShot(false);
|
|
||||||
|
|
||||||
logger.debug() << "Service manager execute access granted";
|
logger.debug() << "Service manager execute access granted";
|
||||||
|
return std::make_unique<WindowsServiceManager>(manager, serviceHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowsServiceManager::~WindowsServiceManager() {
|
WindowsServiceManager::~WindowsServiceManager() {
|
||||||
|
|
@ -85,10 +95,6 @@ bool WindowsServiceManager::startPolling(DWORD goal_state, int max_wait_sec) {
|
||||||
|
|
||||||
SERVICE_STATUS_PROCESS WindowsServiceManager::getStatus() {
|
SERVICE_STATUS_PROCESS WindowsServiceManager::getStatus() {
|
||||||
SERVICE_STATUS_PROCESS serviceStatus;
|
SERVICE_STATUS_PROCESS serviceStatus;
|
||||||
if (!m_has_access) {
|
|
||||||
logger.debug() << "Need read access to get service state";
|
|
||||||
return serviceStatus;
|
|
||||||
}
|
|
||||||
DWORD dwBytesNeeded; // Contains missing bytes if struct is too small?
|
DWORD dwBytesNeeded; // Contains missing bytes if struct is too small?
|
||||||
QueryServiceStatusEx(m_service, // handle to service
|
QueryServiceStatusEx(m_service, // handle to service
|
||||||
SC_STATUS_PROCESS_INFO, // information level
|
SC_STATUS_PROCESS_INFO, // information level
|
||||||
|
|
@ -119,10 +125,6 @@ bool WindowsServiceManager::startService() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowsServiceManager::stopService() {
|
bool WindowsServiceManager::stopService() {
|
||||||
if (!m_has_access) {
|
|
||||||
logger.error() << "Need execute access to stop services";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto state = getStatus().dwCurrentState;
|
auto state = getStatus().dwCurrentState;
|
||||||
if (state != SERVICE_RUNNING && state != SERVICE_START_PENDING) {
|
if (state != SERVICE_RUNNING && state != SERVICE_START_PENDING) {
|
||||||
logger.warning() << ("Service stop not possible, as its not running");
|
logger.warning() << ("Service stop not possible, as its not running");
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
#include "Winsvc.h"
|
#include "Winsvc.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The WindowsServiceManager provides control over the MozillaVPNBroker
|
* @brief The WindowsServiceManager provides control over the a
|
||||||
* service via SCM
|
* service via SCM
|
||||||
*/
|
*/
|
||||||
class WindowsServiceManager : public QObject {
|
class WindowsServiceManager : public QObject {
|
||||||
|
|
@ -20,7 +20,10 @@ class WindowsServiceManager : public QObject {
|
||||||
Q_DISABLE_COPY_MOVE(WindowsServiceManager)
|
Q_DISABLE_COPY_MOVE(WindowsServiceManager)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WindowsServiceManager(LPCWSTR serviceName);
|
// Creates a WindowsServiceManager for the Named service.
|
||||||
|
// returns nullptr if
|
||||||
|
static std::unique_ptr<WindowsServiceManager> open(const QString serviceName);
|
||||||
|
WindowsServiceManager(SC_HANDLE serviceManager, SC_HANDLE service);
|
||||||
~WindowsServiceManager();
|
~WindowsServiceManager();
|
||||||
|
|
||||||
// true if the Service is running
|
// true if the Service is running
|
||||||
|
|
@ -45,8 +48,6 @@ class WindowsServiceManager : public QObject {
|
||||||
// See
|
// See
|
||||||
// SERVICE_STOPPED,SERVICE_STOP_PENDING,SERVICE_START_PENDING,SERVICE_RUNNING
|
// SERVICE_STOPPED,SERVICE_STOP_PENDING,SERVICE_START_PENDING,SERVICE_RUNNING
|
||||||
SERVICE_STATUS_PROCESS getStatus();
|
SERVICE_STATUS_PROCESS getStatus();
|
||||||
bool m_has_access = false;
|
|
||||||
LPWSTR m_serviceName;
|
|
||||||
SC_HANDLE m_serviceManager;
|
SC_HANDLE m_serviceManager;
|
||||||
SC_HANDLE m_service; // Service handle with r/w priv.
|
SC_HANDLE m_service; // Service handle with r/w priv.
|
||||||
DWORD m_state_target;
|
DWORD m_state_target;
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,6 @@ int IpcServer::createPrivilegedProcess()
|
||||||
qDebug() << "IpcServer::createPrivilegedProcess";
|
qDebug() << "IpcServer::createPrivilegedProcess";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
WindowsFirewall::instance()->init();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m_localpid++;
|
m_localpid++;
|
||||||
|
|
||||||
ProcessDescriptor pd(this);
|
ProcessDescriptor pd(this);
|
||||||
|
|
@ -195,7 +191,9 @@ void IpcServer::setLogsEnabled(bool enabled)
|
||||||
bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex)
|
bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
return WindowsFirewall::instance()->enableKillSwitch(vpnAdapterIndex);
|
auto firewallManager = WindowsFirewall::create(this);
|
||||||
|
Q_ASSERT(firewallManager != nullptr);
|
||||||
|
return firewallManager->enableInterface(vpnAdapterIndex);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
|
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
|
||||||
|
|
@ -284,7 +282,9 @@ bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterInd
|
||||||
bool IpcServer::disableKillSwitch()
|
bool IpcServer::disableKillSwitch()
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
return WindowsFirewall::instance()->disableKillSwitch();
|
auto firewallManager = WindowsFirewall::create(this);
|
||||||
|
Q_ASSERT(firewallManager != nullptr);
|
||||||
|
return firewallManager->disableKillSwitch();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
|
|
@ -349,7 +349,9 @@ bool IpcServer::enablePeerTraffic(const QJsonObject &configStr)
|
||||||
|
|
||||||
// killSwitch toggle
|
// killSwitch toggle
|
||||||
if (QVariant(configStr.value(amnezia::config_key::killSwitchOption).toString()).toBool()) {
|
if (QVariant(configStr.value(amnezia::config_key::killSwitchOption).toString()).toBool()) {
|
||||||
WindowsFirewall::instance()->enablePeerTraffic(config);
|
auto firewallManager = WindowsFirewall::create(this);
|
||||||
|
Q_ASSERT(firewallManager != nullptr);
|
||||||
|
firewallManager->enablePeerTraffic(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowsDaemon::instance()->prepareActivation(config, inetAdapterIndex);
|
WindowsDaemon::instance()->prepareActivation(config, inetAdapterIndex);
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,7 @@ if(WIN32)
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/windowsutils.h
|
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/windowsutils.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/windowspingsender.h
|
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/windowspingsender.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/windowsnetworkwatcher.h
|
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/windowsnetworkwatcher.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/../../client/daemon/daemonerrors.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCES ${SOURCES}
|
set(SOURCES ${SOURCES}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue