The couple objects were added from Mozilla VPN for Killswitch functionality
This commit is contained in:
parent
8f23970ccc
commit
858537df1f
15 changed files with 1794 additions and 81 deletions
290
service/killswitch/ipaddress.cpp
Normal file
290
service/killswitch/ipaddress.cpp
Normal file
|
|
@ -0,0 +1,290 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
#include "ipaddress.h"
|
||||||
|
#include "leakdetector.h"
|
||||||
|
//#include "logger.h"
|
||||||
|
|
||||||
|
#include <QtMath>
|
||||||
|
|
||||||
|
//namespace {
|
||||||
|
//Logger logger(LOG_NETWORKING, "IPAddress");
|
||||||
|
//} // namespace
|
||||||
|
|
||||||
|
IPAddress::IPAddress() { MVPN_COUNT_CTOR(IPAddress); }
|
||||||
|
|
||||||
|
IPAddress::IPAddress(const QString& ip) {
|
||||||
|
MVPN_COUNT_CTOR(IPAddress);
|
||||||
|
if (ip.contains("/")) {
|
||||||
|
QPair<QHostAddress, int> p = QHostAddress::parseSubnet(ip);
|
||||||
|
m_address = p.first;
|
||||||
|
m_prefixLength = p.second;
|
||||||
|
} else {
|
||||||
|
m_address = QHostAddress(ip);
|
||||||
|
m_prefixLength = 999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||||
|
if (m_prefixLength >= 32) {
|
||||||
|
m_prefixLength = 32;
|
||||||
|
}
|
||||||
|
} else if (m_address.protocol() == QAbstractSocket::IPv6Protocol) {
|
||||||
|
if (m_prefixLength >= 128) {
|
||||||
|
m_prefixLength = 128;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Q_ASSERT(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IPAddress::IPAddress(const IPAddress& other) {
|
||||||
|
MVPN_COUNT_CTOR(IPAddress);
|
||||||
|
*this = other;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPAddress& IPAddress::operator=(const IPAddress& other) {
|
||||||
|
if (this == &other) return *this;
|
||||||
|
|
||||||
|
m_address = other.m_address;
|
||||||
|
m_prefixLength = other.m_prefixLength;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPAddress::IPAddress(const QHostAddress& address) : m_address(address) {
|
||||||
|
MVPN_COUNT_CTOR(IPAddress);
|
||||||
|
|
||||||
|
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||||
|
m_prefixLength = 32;
|
||||||
|
} else {
|
||||||
|
Q_ASSERT(address.protocol() == QAbstractSocket::IPv6Protocol);
|
||||||
|
m_prefixLength = 128;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IPAddress::IPAddress(const QHostAddress& address, int prefixLength)
|
||||||
|
: m_address(address), m_prefixLength(prefixLength) {
|
||||||
|
MVPN_COUNT_CTOR(IPAddress);
|
||||||
|
|
||||||
|
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||||
|
Q_ASSERT(prefixLength >= 0 && prefixLength <= 32);
|
||||||
|
} else {
|
||||||
|
Q_ASSERT(address.protocol() == QAbstractSocket::IPv6Protocol);
|
||||||
|
Q_ASSERT(prefixLength >= 0 && prefixLength <= 128);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IPAddress::~IPAddress() { MVPN_COUNT_DTOR(IPAddress); }
|
||||||
|
|
||||||
|
QAbstractSocket::NetworkLayerProtocol IPAddress::type() const {
|
||||||
|
return m_address.protocol();
|
||||||
|
}
|
||||||
|
|
||||||
|
QHostAddress IPAddress::netmask() const {
|
||||||
|
if (m_address.protocol() == QAbstractSocket::IPv6Protocol) {
|
||||||
|
Q_IPV6ADDR rawNetmask = {0};
|
||||||
|
Q_ASSERT(m_prefixLength <= 128);
|
||||||
|
memset(&rawNetmask, 0xff, m_prefixLength / 8);
|
||||||
|
if (m_prefixLength % 8) {
|
||||||
|
rawNetmask[m_prefixLength / 8] = 0xFF ^ (0xFF >> (m_prefixLength % 8));
|
||||||
|
}
|
||||||
|
return QHostAddress(rawNetmask);
|
||||||
|
} else if (m_address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||||
|
quint32 rawNetmask = 0xffffffff;
|
||||||
|
Q_ASSERT(m_prefixLength <= 32);
|
||||||
|
if (m_prefixLength < 32) {
|
||||||
|
rawNetmask ^= (0xffffffff >> m_prefixLength);
|
||||||
|
}
|
||||||
|
return QHostAddress(rawNetmask);
|
||||||
|
} else {
|
||||||
|
return QHostAddress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QHostAddress IPAddress::hostmask() const {
|
||||||
|
if (m_address.protocol() == QAbstractSocket::IPv6Protocol) {
|
||||||
|
Q_IPV6ADDR rawHostmask = {0};
|
||||||
|
int offset = (m_prefixLength + 7) / 8;
|
||||||
|
Q_ASSERT(m_prefixLength <= 128);
|
||||||
|
memset(&rawHostmask[offset], 0xff, sizeof(rawHostmask) - offset);
|
||||||
|
if (m_prefixLength % 8) {
|
||||||
|
rawHostmask[m_prefixLength / 8] = 0xFF >> (m_prefixLength % 8);
|
||||||
|
}
|
||||||
|
return QHostAddress(rawHostmask);
|
||||||
|
} else if (m_address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||||
|
if (m_prefixLength < 32) {
|
||||||
|
return QHostAddress(0xffffffff >> m_prefixLength);
|
||||||
|
} else {
|
||||||
|
quint32 zero = 0;
|
||||||
|
return QHostAddress(zero);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return QHostAddress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QHostAddress IPAddress::broadcastAddress() const {
|
||||||
|
if (m_address.protocol() == QAbstractSocket::IPv6Protocol) {
|
||||||
|
Q_IPV6ADDR rawAddress = m_address.toIPv6Address();
|
||||||
|
int offset = (m_prefixLength + 7) / 8;
|
||||||
|
memset(&rawAddress[offset], 0xff, sizeof(rawAddress) - offset);
|
||||||
|
if (m_prefixLength % 8) {
|
||||||
|
rawAddress[m_prefixLength / 8] |= 0xFF >> (m_prefixLength % 8);
|
||||||
|
}
|
||||||
|
return QHostAddress(rawAddress);
|
||||||
|
} else if (m_address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||||
|
quint32 rawAddress = m_address.toIPv4Address();
|
||||||
|
if (m_prefixLength < 32) {
|
||||||
|
rawAddress |= (0xffffffff >> m_prefixLength);
|
||||||
|
}
|
||||||
|
return QHostAddress(rawAddress);
|
||||||
|
} else {
|
||||||
|
return QHostAddress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPAddress::overlaps(const IPAddress& other) const {
|
||||||
|
if (m_prefixLength < other.m_prefixLength) {
|
||||||
|
return contains(other.m_address);
|
||||||
|
} else {
|
||||||
|
return other.contains(m_address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPAddress::contains(const QHostAddress& address) const {
|
||||||
|
if (address.protocol() != m_address.protocol()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (m_prefixLength == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_address.protocol() == QAbstractSocket::IPv6Protocol) {
|
||||||
|
Q_IPV6ADDR a = m_address.toIPv6Address();
|
||||||
|
Q_IPV6ADDR b = address.toIPv6Address();
|
||||||
|
int bytes = m_prefixLength / 8;
|
||||||
|
if (bytes > 0) {
|
||||||
|
if (memcmp(&a, &b, bytes) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_prefixLength % 8) {
|
||||||
|
quint8 diff = (a[bytes] ^ b[bytes]) >> (8 - m_prefixLength % 8);
|
||||||
|
return (diff == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||||
|
quint32 diff = m_address.toIPv4Address() ^ address.toIPv4Address();
|
||||||
|
if (m_prefixLength < 32) {
|
||||||
|
diff >>= (32 - m_prefixLength);
|
||||||
|
}
|
||||||
|
return (diff == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPAddress::operator==(const IPAddress& other) const {
|
||||||
|
return m_address == other.m_address && m_prefixLength == other.m_prefixLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPAddress::subnetOf(const IPAddress& other) const {
|
||||||
|
if (other.m_address.protocol() != m_address.protocol()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (m_prefixLength < other.m_prefixLength) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return other.contains(m_address);
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<IPAddress> IPAddress::subnets() const {
|
||||||
|
QList<IPAddress> list;
|
||||||
|
|
||||||
|
if (m_address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||||
|
if (m_prefixLength >= 32) {
|
||||||
|
list.append(*this);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 rawAddress = m_address.toIPv4Address();
|
||||||
|
list.append(IPAddress(QHostAddress(rawAddress), m_prefixLength + 1));
|
||||||
|
|
||||||
|
rawAddress |= (0x80000000 >> m_prefixLength);
|
||||||
|
list.append(IPAddress(QHostAddress(rawAddress), m_prefixLength + 1));
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(m_address.protocol() == QAbstractSocket::IPv6Protocol);
|
||||||
|
|
||||||
|
if (m_prefixLength >= 128) {
|
||||||
|
list.append(*this);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_IPV6ADDR rawAddress = m_address.toIPv6Address();
|
||||||
|
list.append(IPAddress(QHostAddress(rawAddress), m_prefixLength + 1));
|
||||||
|
|
||||||
|
rawAddress[m_prefixLength / 8] |= (0x80 >> (m_prefixLength % 8));
|
||||||
|
list.append(IPAddress(QHostAddress(rawAddress), m_prefixLength + 1));
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
QList<IPAddress> IPAddress::excludeAddresses(
|
||||||
|
const QList<IPAddress>& sourceList, const QList<IPAddress>& excludeList) {
|
||||||
|
QList<IPAddress> results = sourceList;
|
||||||
|
|
||||||
|
for (const IPAddress& exclude : excludeList) {
|
||||||
|
QList<IPAddress> newResults;
|
||||||
|
|
||||||
|
for (const IPAddress& ip : results) {
|
||||||
|
if (ip.overlaps(exclude)) {
|
||||||
|
QList<IPAddress> range = ip.excludeAddresses(exclude);
|
||||||
|
newResults.append(range);
|
||||||
|
} else {
|
||||||
|
newResults.append(ip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
results = newResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<IPAddress> IPAddress::excludeAddresses(const IPAddress& ip) const {
|
||||||
|
QList<IPAddress> sn = subnets();
|
||||||
|
Q_ASSERT(sn.length() >= 2);
|
||||||
|
|
||||||
|
QList<IPAddress> result;
|
||||||
|
while (sn[0] != ip && sn[1] != ip) {
|
||||||
|
if (ip.subnetOf(sn[0])) {
|
||||||
|
result.append(sn[1]);
|
||||||
|
sn = sn[0].subnets();
|
||||||
|
} else if (ip.subnetOf(sn[1])) {
|
||||||
|
result.append(sn[0]);
|
||||||
|
sn = sn[1].subnets();
|
||||||
|
} else {
|
||||||
|
Q_ASSERT(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sn[0] == ip) {
|
||||||
|
result.append(sn[1]);
|
||||||
|
} else if (sn[1] == ip) {
|
||||||
|
result.append(sn[0]);
|
||||||
|
} else {
|
||||||
|
Q_ASSERT(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
53
service/killswitch/ipaddress.h
Normal file
53
service/killswitch/ipaddress.h
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
#ifndef IPADDRESS_H
|
||||||
|
#define IPADDRESS_H
|
||||||
|
|
||||||
|
#include <QHostAddress>
|
||||||
|
|
||||||
|
class IPAddress final {
|
||||||
|
public:
|
||||||
|
static QList<IPAddress> excludeAddresses(const QList<IPAddress>& sourceList,
|
||||||
|
const QList<IPAddress>& excludeList);
|
||||||
|
|
||||||
|
IPAddress();
|
||||||
|
IPAddress(const QString& ip);
|
||||||
|
IPAddress(const QHostAddress& address);
|
||||||
|
IPAddress(const QHostAddress& address, int prefixLength);
|
||||||
|
IPAddress(const IPAddress& other);
|
||||||
|
IPAddress& operator=(const IPAddress& other);
|
||||||
|
~IPAddress();
|
||||||
|
|
||||||
|
QString toString() const {
|
||||||
|
return QString("%1/%2").arg(m_address.toString()).arg(m_prefixLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QHostAddress& address() const { return m_address; }
|
||||||
|
int prefixLength() const { return m_prefixLength; }
|
||||||
|
QHostAddress netmask() const;
|
||||||
|
QHostAddress hostmask() const;
|
||||||
|
QHostAddress broadcastAddress() const;
|
||||||
|
|
||||||
|
bool overlaps(const IPAddress& other) const;
|
||||||
|
|
||||||
|
bool contains(const QHostAddress& address) const;
|
||||||
|
|
||||||
|
bool operator==(const IPAddress& other) const;
|
||||||
|
bool operator!=(const IPAddress& other) const { return !operator==(other); }
|
||||||
|
|
||||||
|
bool subnetOf(const IPAddress& other) const;
|
||||||
|
|
||||||
|
QList<IPAddress> subnets() const;
|
||||||
|
|
||||||
|
QList<IPAddress> excludeAddresses(const IPAddress& ip) const;
|
||||||
|
|
||||||
|
QAbstractSocket::NetworkLayerProtocol type() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QHostAddress m_address;
|
||||||
|
int m_prefixLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // IPADDRESS_H
|
||||||
17
service/killswitch/killswitch.pri
Normal file
17
service/killswitch/killswitch.pri
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
INCLUDEPATH+= $$PWD
|
||||||
|
DEPENDPATH += $$PWD
|
||||||
|
QT += core network
|
||||||
|
HEADERS += \
|
||||||
|
$$PWD/leakdetector.h \
|
||||||
|
$$PWD/windowsfirewall.h \
|
||||||
|
$$PWD/ipaddress.h \
|
||||||
|
$$PWD/windowscommons.h
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
$$PWD/leakdetector.cpp \
|
||||||
|
$$PWD/windowsfirewall.cpp \
|
||||||
|
$$PWD/ipaddress.cpp \
|
||||||
|
$$PWD/windowscommons.cpp
|
||||||
|
|
||||||
|
# TARGET = libkillswitch
|
||||||
|
|
||||||
75
service/killswitch/leakdetector.cpp
Normal file
75
service/killswitch/leakdetector.cpp
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
#include "leakdetector.h"
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTextStream>
|
||||||
|
|
||||||
|
#ifdef MVPN_DEBUG
|
||||||
|
static QMutex s_leakDetector;
|
||||||
|
|
||||||
|
QHash<QString, QHash<void*, uint32_t>> s_leaks;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LeakDetector::LeakDetector() {
|
||||||
|
#ifndef MVPN_DEBUG
|
||||||
|
qFatal("LeakDetector _must_ be created in debug builds only!");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
LeakDetector::~LeakDetector() {
|
||||||
|
#ifdef MVPN_DEBUG
|
||||||
|
QTextStream out(stderr);
|
||||||
|
|
||||||
|
out << "== Mozilla VPN - Leak report ===================" << Qt::endl;
|
||||||
|
|
||||||
|
bool hasLeaks = false;
|
||||||
|
for (auto i = s_leaks.begin(); i != s_leaks.end(); ++i) {
|
||||||
|
QString className = i.key();
|
||||||
|
|
||||||
|
if (i->size() == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasLeaks = true;
|
||||||
|
out << className << Qt::endl;
|
||||||
|
|
||||||
|
for (auto l = i->begin(); l != i->end(); ++l) {
|
||||||
|
out << " - ptr: " << l.key() << " size:" << l.value() << Qt::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasLeaks) {
|
||||||
|
out << "No leaks detected." << Qt::endl;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MVPN_DEBUG
|
||||||
|
void LeakDetector::logCtor(void* ptr, const char* typeName, uint32_t size) {
|
||||||
|
QMutexLocker lock(&s_leakDetector);
|
||||||
|
|
||||||
|
QString type(typeName);
|
||||||
|
if (!s_leaks.contains(type)) {
|
||||||
|
s_leaks.insert(type, QHash<void*, uint32_t>());
|
||||||
|
}
|
||||||
|
|
||||||
|
s_leaks[type].insert(ptr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LeakDetector::logDtor(void* ptr, const char* typeName, uint32_t size) {
|
||||||
|
QMutexLocker lock(&s_leakDetector);
|
||||||
|
|
||||||
|
QString type(typeName);
|
||||||
|
Q_ASSERT(s_leaks.contains(type));
|
||||||
|
|
||||||
|
QHash<void*, uint32_t>& leak = s_leaks[type];
|
||||||
|
Q_ASSERT(leak.contains(ptr));
|
||||||
|
Q_ASSERT(leak[ptr] == size);
|
||||||
|
leak.remove(ptr);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
41
service/killswitch/leakdetector.h
Normal file
41
service/killswitch/leakdetector.h
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
#ifndef LEAKDETECTOR_H
|
||||||
|
#define LEAKDETECTOR_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#ifdef MVPN_DEBUG
|
||||||
|
# define MVPN_COUNT_CTOR(_type) \
|
||||||
|
do { \
|
||||||
|
static_assert(std::is_class<_type>(), \
|
||||||
|
"Token '" #_type "' is not a class type."); \
|
||||||
|
LeakDetector::logCtor((void*)this, #_type, sizeof(*this)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
# define MVPN_COUNT_DTOR(_type) \
|
||||||
|
do { \
|
||||||
|
static_assert(std::is_class<_type>(), \
|
||||||
|
"Token '" #_type "' is not a class type."); \
|
||||||
|
LeakDetector::logDtor((void*)this, #_type, sizeof(*this)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#else
|
||||||
|
# define MVPN_COUNT_CTOR(_type)
|
||||||
|
# define MVPN_COUNT_DTOR(_type)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class LeakDetector {
|
||||||
|
public:
|
||||||
|
LeakDetector();
|
||||||
|
~LeakDetector();
|
||||||
|
|
||||||
|
#ifdef MVPN_DEBUG
|
||||||
|
static void logCtor(void* ptr, const char* typeName, uint32_t size);
|
||||||
|
static void logDtor(void* ptr, const char* typeName, uint32_t size);
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LEAKDETECTOR_H
|
||||||
182
service/killswitch/windowscommons.cpp
Normal file
182
service/killswitch/windowscommons.cpp
Normal file
|
|
@ -0,0 +1,182 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
#include "windowscommons.h"
|
||||||
|
//#include "logger.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QtEndian>
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QSysInfo>
|
||||||
|
#include <QNetworkInterface>
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <iphlpapi.h>
|
||||||
|
|
||||||
|
#define TUNNEL_SERVICE_NAME L"WireGuardTunnel$mozvpn"
|
||||||
|
|
||||||
|
constexpr const char* VPN_NAME = "MozillaVPN";
|
||||||
|
|
||||||
|
constexpr const int WINDOWS_11_BUILD =
|
||||||
|
22000; // Build Number of the first release win 11 iso
|
||||||
|
//namespace {
|
||||||
|
//Logger logger(LOG_MAIN, "WindowsCommons");
|
||||||
|
//}
|
||||||
|
|
||||||
|
QString WindowsCommons::getErrorMessage() {
|
||||||
|
DWORD errorId = GetLastError();
|
||||||
|
LPSTR messageBuffer = nullptr;
|
||||||
|
size_t size = FormatMessageA(
|
||||||
|
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
||||||
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
nullptr, errorId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
(LPSTR)&messageBuffer, 0, nullptr);
|
||||||
|
|
||||||
|
std::string message(messageBuffer, size);
|
||||||
|
QString result(message.c_str());
|
||||||
|
LocalFree(messageBuffer);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A simple function to log windows error messages.
|
||||||
|
void WindowsCommons::windowsLog(const QString& msg) {
|
||||||
|
QString errmsg = getErrorMessage();
|
||||||
|
qDebug() << msg << "-" << errmsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString WindowsCommons::tunnelConfigFile() {
|
||||||
|
QStringList paths =
|
||||||
|
QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
|
||||||
|
for (const QString& path : paths) {
|
||||||
|
QDir dir(path);
|
||||||
|
if (!dir.exists()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir vpnDir(dir.filePath(VPN_NAME));
|
||||||
|
if (!vpnDir.exists()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString wireguardFile(vpnDir.filePath(QString("%1.conf").arg(VPN_NAME)));
|
||||||
|
if (!QFileInfo::exists(wireguardFile)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Found the current wireguard configuration:"
|
||||||
|
<< wireguardFile;
|
||||||
|
return wireguardFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const QString& path : paths) {
|
||||||
|
QDir dir(path);
|
||||||
|
|
||||||
|
QDir vpnDir(dir.filePath(VPN_NAME));
|
||||||
|
if (!vpnDir.exists() && !dir.mkdir(VPN_NAME)) {
|
||||||
|
qDebug() << "Failed to create path Mozilla under" << path;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vpnDir.filePath(QString("%1.conf").arg(VPN_NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Failed to create the right paths";
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString WindowsCommons::tunnelLogFile() {
|
||||||
|
QStringList paths =
|
||||||
|
QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
|
||||||
|
|
||||||
|
for (const QString& path : paths) {
|
||||||
|
QDir dir(path);
|
||||||
|
if (!dir.exists()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir vpnDir(dir.filePath(VPN_NAME));
|
||||||
|
if (!vpnDir.exists()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vpnDir.filePath("log.bin");
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
int WindowsCommons::AdapterIndexTo(const QHostAddress& dst) {
|
||||||
|
qDebug() << "Getting Current Internet Adapter that routes to"
|
||||||
|
<< dst.toString();
|
||||||
|
quint32_be ipBigEndian;
|
||||||
|
quint32 ip = dst.toIPv4Address();
|
||||||
|
qToBigEndian(ip, &ipBigEndian);
|
||||||
|
_MIB_IPFORWARDROW routeInfo;
|
||||||
|
auto result = GetBestRoute(ipBigEndian, 0, &routeInfo);
|
||||||
|
if (result != NO_ERROR) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
auto adapter =
|
||||||
|
QNetworkInterface::interfaceFromIndex(routeInfo.dwForwardIfIndex);
|
||||||
|
//logger.debug() << "Internet Adapter:" << adapter.name();
|
||||||
|
qDebug()<< "Internet Adapter:" << adapter.name();
|
||||||
|
return routeInfo.dwForwardIfIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
int WindowsCommons::VPNAdapterIndex() {
|
||||||
|
// For someReason QNetworkInterface::fromName(MozillaVPN) does not work >:(
|
||||||
|
auto adapterList = QNetworkInterface::allInterfaces();
|
||||||
|
for (const auto& adapter : adapterList) {
|
||||||
|
if (
|
||||||
|
adapter.humanReadableName().contains(IKEV2) ||
|
||||||
|
adapter.humanReadableName().contains(IKEV2)
|
||||||
|
) {
|
||||||
|
return adapter.index();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static
|
||||||
|
QString WindowsCommons::getCurrentPath() {
|
||||||
|
QByteArray buffer(2048, 0xFF);
|
||||||
|
auto ok = GetModuleFileNameA(NULL, buffer.data(), buffer.size());
|
||||||
|
|
||||||
|
if (ok == ERROR_INSUFFICIENT_BUFFER) {
|
||||||
|
buffer.resize(buffer.size() * 2);
|
||||||
|
ok = GetModuleFileNameA(NULL, buffer.data(), buffer.size());
|
||||||
|
}
|
||||||
|
if (ok == 0) {
|
||||||
|
WindowsCommons::windowsLog("Err fetching dos path");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return QString::fromLocal8Bit(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static
|
||||||
|
QString WindowsCommons::WindowsVersion() {
|
||||||
|
/* The Tradegy of Getting a somewhat working windows version:
|
||||||
|
- GetVersion() -> deprecated and Reports win 8.1 for MozillaVPN... its tied
|
||||||
|
to some .exe flags
|
||||||
|
- NetWkstaGetInfo -> Reports Windows 10 on windows 11
|
||||||
|
There is also the regirstry HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows
|
||||||
|
NT\CurrentVersion
|
||||||
|
-> CurrentMajorVersion reports 10 on win 11
|
||||||
|
-> CurrentBuild seems to be correct, so lets infer it
|
||||||
|
*/
|
||||||
|
|
||||||
|
QSettings regCurrentVersion(
|
||||||
|
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
|
||||||
|
QSettings::NativeFormat);
|
||||||
|
|
||||||
|
int buildNr = regCurrentVersion.value("CurrentBuild").toInt();
|
||||||
|
if (buildNr >= WINDOWS_11_BUILD) {
|
||||||
|
return "11";
|
||||||
|
}
|
||||||
|
return QSysInfo::productVersion();
|
||||||
|
}
|
||||||
32
service/killswitch/windowscommons.h
Normal file
32
service/killswitch/windowscommons.h
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
#ifndef WINDOWSCOMMONS_H
|
||||||
|
#define WINDOWSCOMMONS_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
constexpr char IKEV2[] {"AmneziaVPN IKEv2\0"};
|
||||||
|
|
||||||
|
class QHostAddress;
|
||||||
|
|
||||||
|
class WindowsCommons final {
|
||||||
|
public:
|
||||||
|
static QString getErrorMessage();
|
||||||
|
static void windowsLog(const QString& msg);
|
||||||
|
|
||||||
|
static QString tunnelConfigFile();
|
||||||
|
static QString tunnelLogFile();
|
||||||
|
|
||||||
|
// Returns the Interface Index of the VPN Adapter
|
||||||
|
static int VPNAdapterIndex();
|
||||||
|
// Returns the Interface Index that could Route to dst
|
||||||
|
static int AdapterIndexTo(const QHostAddress& dst);
|
||||||
|
// Returns the Path of the Current process
|
||||||
|
static QString getCurrentPath();
|
||||||
|
// Returns the major version of Windows
|
||||||
|
static QString WindowsVersion();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WINDOWSCOMMONS_H
|
||||||
857
service/killswitch/windowsfirewall.cpp
Normal file
857
service/killswitch/windowsfirewall.cpp
Normal file
|
|
@ -0,0 +1,857 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
#include "windowsfirewall.h"
|
||||||
|
//#include "logger.h"
|
||||||
|
#include "leakdetector.h"
|
||||||
|
//#include "windowscommons.h"
|
||||||
|
//#include "../../daemon/interfaceconfig.h"
|
||||||
|
//#include "../../ipaddress.h"
|
||||||
|
|
||||||
|
//#include <QApplication>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QNetworkInterface>
|
||||||
|
#include <QScopeGuard>
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <QtEndian>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <fwpmu.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <comdef.h>
|
||||||
|
#include <netfw.h>
|
||||||
|
//#include "winsock.h"
|
||||||
|
|
||||||
|
#include <initguid.h>
|
||||||
|
#include <guiddef.h>
|
||||||
|
#include <qaccessible.h>
|
||||||
|
|
||||||
|
#define IPV6_ADDRESS_SIZE 16
|
||||||
|
|
||||||
|
// ID for the Firewall Sublayer
|
||||||
|
DEFINE_GUID(ST_FW_WINFW_BASELINE_SUBLAYER_KEY, 0xc78056ff, 0x2bc1, 0x4211, 0xaa,
|
||||||
|
0xdd, 0x7f, 0x35, 0x8d, 0xef, 0x20, 0x2d);
|
||||||
|
// ID for the Mullvad Split-Tunnel Sublayer Provider
|
||||||
|
DEFINE_GUID(ST_FW_PROVIDER_KEY, 0xe2c114ee, 0xf32a, 0x4264, 0xa6, 0xcb, 0x3f,
|
||||||
|
0xa7, 0x99, 0x63, 0x56, 0xd9);
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
//Logger logger(LOG_WINDOWS, "WindowsFirewall");
|
||||||
|
WindowsFirewall* s_instance = nullptr;
|
||||||
|
|
||||||
|
// Note Filter Weight may be between 0-15!
|
||||||
|
constexpr uint8_t LOW_WEIGHT = 0;
|
||||||
|
constexpr uint8_t MED_WEIGHT = 7;
|
||||||
|
constexpr uint8_t HIGH_WEIGHT = 13;
|
||||||
|
constexpr uint8_t MAX_WEIGHT = 15;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
WindowsFirewall* WindowsFirewall::instance() {
|
||||||
|
if (s_instance == nullptr) {
|
||||||
|
s_instance = new WindowsFirewall(qApp);
|
||||||
|
}
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowsFirewall::WindowsFirewall(QObject* parent) : QObject(parent) {
|
||||||
|
MVPN_COUNT_CTOR(WindowsFirewall);
|
||||||
|
Q_ASSERT(s_instance == nullptr);
|
||||||
|
|
||||||
|
HANDLE engineHandle = NULL;
|
||||||
|
DWORD result = ERROR_SUCCESS;
|
||||||
|
// Use dynamic sessions for efficiency and safety:
|
||||||
|
// -> Filtering policy objects are deleted even when the application crashes/
|
||||||
|
// deamon goes down
|
||||||
|
FWPM_SESSION0 session;
|
||||||
|
memset(&session, 0, sizeof(session));
|
||||||
|
session.flags = FWPM_SESSION_FLAG_DYNAMIC;
|
||||||
|
|
||||||
|
qDebug() << "Opening the filter engine.";
|
||||||
|
|
||||||
|
result =
|
||||||
|
FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &engineHandle);
|
||||||
|
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
qDebug()<<"FwpmEngineOpen0 failed "<<GetLastError();
|
||||||
|
//WindowsCommons::windowsLog("FwpmEngineOpen0 failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qDebug() << "Filter engine opened successfully.";
|
||||||
|
m_sessionHandle = engineHandle;
|
||||||
|
|
||||||
|
//auto ret = WindowsCommons::VPNAdapterIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowsFirewall::~WindowsFirewall() {
|
||||||
|
MVPN_COUNT_DTOR(WindowsFirewall);
|
||||||
|
if (m_sessionHandle != INVALID_HANDLE_VALUE) {
|
||||||
|
CloseHandle(m_sessionHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowsFirewall::init() {
|
||||||
|
if (m_init) {
|
||||||
|
qDebug() << "Alread initialised FW_WFP layer";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (m_sessionHandle == INVALID_HANDLE_VALUE) {
|
||||||
|
qDebug() << "Cant Init Sublayer with invalid wfp handle";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// If we were not able to aquire a handle, this will fail anyway.
|
||||||
|
// We need to open up another handle because of wfp rules:
|
||||||
|
// If a wfp resource was created with SESSION_DYNAMIC,
|
||||||
|
// the session exlusively owns the resource, meaning the driver can't add
|
||||||
|
// filters to the sublayer. So let's have non dynamic session only for the
|
||||||
|
// sublayer creation. This means the Layer exists until the next Reboot.
|
||||||
|
DWORD result = ERROR_SUCCESS;
|
||||||
|
HANDLE wfp = INVALID_HANDLE_VALUE;
|
||||||
|
FWPM_SESSION0 session;
|
||||||
|
memset(&session, 0, sizeof(session));
|
||||||
|
|
||||||
|
qDebug()<< "Opening the filter engine";
|
||||||
|
result = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &wfp);
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
qDebug() << "FwpmEngineOpen0 failed. Return value:.\n" << result<<" "<<GetLastError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto cleanup = qScopeGuard([&] { FwpmEngineClose0(wfp); });
|
||||||
|
|
||||||
|
// Check if the Layer Already Exists
|
||||||
|
FWPM_SUBLAYER0* maybeLayer{nullptr};
|
||||||
|
result = FwpmSubLayerGetByKey0(wfp, &ST_FW_WINFW_BASELINE_SUBLAYER_KEY,
|
||||||
|
&maybeLayer);
|
||||||
|
if (result == ERROR_SUCCESS) {
|
||||||
|
qDebug() << "The Sublayer Already Exists!";
|
||||||
|
FwpmFreeMemory0((void**)&maybeLayer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 1: Start Transaction
|
||||||
|
result = FwpmTransactionBegin(wfp, NULL);
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
qDebug() << "FwpmTransactionBegin0 failed. Return value:.\n"
|
||||||
|
<< result<<" "<<GetLastError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Add Sublayer
|
||||||
|
FWPM_SUBLAYER0 subLayer;
|
||||||
|
memset(&subLayer, 0, sizeof(subLayer));
|
||||||
|
subLayer.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
|
||||||
|
subLayer.displayData.name = (PWSTR)L"MozillaVPN-SplitTunnel-Sublayer";
|
||||||
|
subLayer.displayData.description =
|
||||||
|
(PWSTR)L"Filters that enforce a good baseline";
|
||||||
|
subLayer.weight = 0xFFFF;
|
||||||
|
|
||||||
|
result = FwpmSubLayerAdd0(wfp, &subLayer, NULL);
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
qDebug() << "FwpmSubLayerAdd0 failed. Return value:.\n" << result<<" "<<GetLastError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Step 4: Commit!
|
||||||
|
result = FwpmTransactionCommit0(wfp);
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
qDebug() << "FwpmTransactionCommit0 failed. Return value:.\n"
|
||||||
|
<< result<<" "<<GetLastError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
qDebug() << "Initialised Sublayer";
|
||||||
|
m_init = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowsFirewall::enableKillSwitch(int vpnAdapterIndex) {
|
||||||
|
// Checks if the FW_Rule was enabled succesfully,
|
||||||
|
// disables the whole killswitch and returns false if not.
|
||||||
|
#define FW_OK(rule) \
|
||||||
|
{ \
|
||||||
|
auto result = FwpmTransactionBegin(m_sessionHandle, NULL); \
|
||||||
|
if (result != ERROR_SUCCESS) { \
|
||||||
|
disableKillSwitch(); \
|
||||||
|
return false; \
|
||||||
|
} \
|
||||||
|
if (!rule) { \
|
||||||
|
FwpmTransactionAbort0(m_sessionHandle); \
|
||||||
|
disableKillSwitch(); \
|
||||||
|
return false; \
|
||||||
|
} \
|
||||||
|
result = FwpmTransactionCommit0(m_sessionHandle); \
|
||||||
|
if (result != ERROR_SUCCESS) { \
|
||||||
|
qDebug()<<"FwpmTransactionCommit0 failed. Return value:.\n" \
|
||||||
|
<< result; \
|
||||||
|
return false; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Enabling Killswitch Using Adapter:" << vpnAdapterIndex;
|
||||||
|
FW_OK(allowTrafficOfAdapter(vpnAdapterIndex, MED_WEIGHT,
|
||||||
|
"Allow usage of VPN Adapter"));
|
||||||
|
FW_OK(allowDHCPTraffic(MED_WEIGHT, "Allow DHCP Traffic"));
|
||||||
|
//FW_OK(allowHyperVTraffic(MED_WEIGHT, "Allow Hyper-V Traffic"));
|
||||||
|
FW_OK(allowTrafficForAppOnAll(getCurrentPath(), MAX_WEIGHT,
|
||||||
|
"Allow all for AmneziaVPN-service.exe"));
|
||||||
|
|
||||||
|
// disable by 020221 //FW_OK(blockTrafficOnPort(53, MED_WEIGHT, "Block all DNS"));
|
||||||
|
FW_OK(allowLoopbackTraffic(MED_WEIGHT, "Allow Loopback traffic on device %1"));
|
||||||
|
|
||||||
|
qDebug() << "Killswitch on! Rules:" << m_activeRules.length();
|
||||||
|
return true;
|
||||||
|
#undef FW_OK
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) {
|
||||||
|
// Start the firewall transaction
|
||||||
|
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
disableKillSwitch();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto cleanup = qScopeGuard([&] {
|
||||||
|
FwpmTransactionAbort0(m_sessionHandle);
|
||||||
|
disableKillSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build the firewall rules for this peer.
|
||||||
|
logger.info() << "Enabling traffic for peer" << config.m_serverPublicKey;
|
||||||
|
if (!blockTrafficTo(config.m_allowedIPAddressRanges, LOW_WEIGHT,
|
||||||
|
"Block Internet", config.m_serverPublicKey)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!config.m_dnsServer.isEmpty()) {
|
||||||
|
if (!allowTrafficTo(QHostAddress(config.m_dnsServer), 53, HIGH_WEIGHT,
|
||||||
|
"Allow DNS-Server", config.m_serverPublicKey)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// In some cases, we might configure a 2nd DNS server for IPv6, however
|
||||||
|
// this should probably be cleaned up by converting m_dnsServer into
|
||||||
|
// a QStringList instead.
|
||||||
|
if (config.m_dnsServer == config.m_serverIpv4Gateway) {
|
||||||
|
if (!allowTrafficTo(QHostAddress(config.m_serverIpv6Gateway), 53,
|
||||||
|
HIGH_WEIGHT, "Allow extra IPv6 DNS-Server",
|
||||||
|
config.m_serverPublicKey)) {
|
||||||
|
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::disablePeerTraffic(const QString& pubkey) {
|
||||||
|
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
||||||
|
auto cleanup = qScopeGuard([&] {
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
FwpmTransactionAbort0(m_sessionHandle);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
qDebug() << "FwpmTransactionBegin0 failed. Return value:.\n"
|
||||||
|
<< result<<" "<<GetLastError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Disabling traffic for peer" << pubkey;
|
||||||
|
for (const auto& filterID : m_peerRules.values(pubkey)) {
|
||||||
|
FwpmFilterDeleteById0(m_sessionHandle, filterID);
|
||||||
|
m_peerRules.remove(pubkey, filterID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit!
|
||||||
|
result = FwpmTransactionCommit0(m_sessionHandle);
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
qDebug()<< "FwpmTransactionCommit0 failed. Return value:.\n"
|
||||||
|
<< result<<" "<<GetLastError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowsFirewall::disableKillSwitch() {
|
||||||
|
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
|
||||||
|
auto cleanup = qScopeGuard([&] {
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
FwpmTransactionAbort0(m_sessionHandle);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
qDebug() << "FwpmTransactionBegin0 failed. Return value:.\n"
|
||||||
|
<< result<<" "<<GetLastError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& filterID : m_peerRules.values()) {
|
||||||
|
FwpmFilterDeleteById0(m_sessionHandle, filterID);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& filterID : qAsConst(m_activeRules)) {
|
||||||
|
FwpmFilterDeleteById0(m_sessionHandle, filterID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit!
|
||||||
|
result = FwpmTransactionCommit0(m_sessionHandle);
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
qDebug() << "FwpmTransactionCommit0 failed. Return value:.\n"
|
||||||
|
<< result<<" "<<GetLastError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_peerRules.clear();
|
||||||
|
m_activeRules.clear();
|
||||||
|
qDebug() << "Firewall Disabled!";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowsFirewall::allowTrafficForAppOnAll(const QString& exePath,
|
||||||
|
int weight,
|
||||||
|
const QString& title) {
|
||||||
|
DWORD result = ERROR_SUCCESS;
|
||||||
|
Q_ASSERT(weight <= 15);
|
||||||
|
|
||||||
|
// Get the AppID for the Executable;
|
||||||
|
QString appName = QFileInfo(exePath).baseName();
|
||||||
|
std::wstring wstr = exePath.toStdWString();
|
||||||
|
PCWSTR appPath = wstr.c_str();
|
||||||
|
FWP_BYTE_BLOB* appID = NULL;
|
||||||
|
result = FwpmGetAppIdFromFileName0(appPath, &appID);
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
qDebug()<<"FwpmGetAppIdFromFileName0 failure:"<< GetLastError();
|
||||||
|
// WindowsCommons::windowsLog("FwpmGetAppIdFromFileName0 failure");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Condition: Request must come from the .exe
|
||||||
|
FWPM_FILTER_CONDITION0 conds;
|
||||||
|
conds.fieldKey = FWPM_CONDITION_ALE_APP_ID;
|
||||||
|
conds.matchType = FWP_MATCH_EQUAL;
|
||||||
|
conds.conditionValue.type = FWP_BYTE_BLOB_TYPE;
|
||||||
|
conds.conditionValue.byteBlob = appID;
|
||||||
|
|
||||||
|
// Assemble the Filter base
|
||||||
|
FWPM_FILTER0 filter;
|
||||||
|
memset(&filter, 0, sizeof(filter));
|
||||||
|
filter.filterCondition = &conds;
|
||||||
|
filter.numFilterConditions = 1;
|
||||||
|
filter.action.type = FWP_ACTION_PERMIT;
|
||||||
|
filter.weight.type = FWP_UINT8;
|
||||||
|
filter.weight.uint8 = weight;
|
||||||
|
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
|
||||||
|
filter.flags = FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT; // Make this decision
|
||||||
|
// only blockable by veto
|
||||||
|
// Build and add the Filters
|
||||||
|
// #1 Permit outbound IPv4 traffic.
|
||||||
|
{
|
||||||
|
QString desc("Permit (out) IPv4 Traffic of: " + appName);
|
||||||
|
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
|
||||||
|
if (!enableFilter(&filter, title, desc)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #2 Permit inbound IPv4 traffic.
|
||||||
|
{
|
||||||
|
QString desc("Permit (in) IPv4 Traffic of: " + appName);
|
||||||
|
filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4;
|
||||||
|
if (!enableFilter(&filter, title, desc)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowsFirewall::allowTrafficOfAdapter(int networkAdapter, uint8_t weight,
|
||||||
|
const QString& title) {
|
||||||
|
FWPM_FILTER_CONDITION0 conds;
|
||||||
|
memset(&conds, 0, sizeof(conds));
|
||||||
|
// Condition: Request must be targeting the TUN interface
|
||||||
|
conds.fieldKey = FWPM_CONDITION_INTERFACE_INDEX;
|
||||||
|
conds.matchType = FWP_MATCH_EQUAL;
|
||||||
|
conds.conditionValue.type = FWP_UINT32;
|
||||||
|
conds.conditionValue.uint32 = networkAdapter;
|
||||||
|
|
||||||
|
// Assemble the Filter base
|
||||||
|
FWPM_FILTER0 filter;
|
||||||
|
memset(&filter, 0, sizeof(filter));
|
||||||
|
filter.filterCondition = &conds;
|
||||||
|
filter.numFilterConditions = 1;
|
||||||
|
filter.action.type = FWP_ACTION_PERMIT;
|
||||||
|
filter.weight.type = FWP_UINT8;
|
||||||
|
filter.weight.uint8 = weight;
|
||||||
|
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
|
||||||
|
|
||||||
|
QString description("Allow %0 traffic on Adapter %1");
|
||||||
|
// #1 Permit outbound IPv4 traffic.
|
||||||
|
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
|
||||||
|
if (!enableFilter(&filter, title,
|
||||||
|
description.arg("out").arg(networkAdapter))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// #2 Permit inbound IPv4 traffic.
|
||||||
|
filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4;
|
||||||
|
if (!enableFilter(&filter, title,
|
||||||
|
description.arg("in").arg(networkAdapter))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// #3 Permit outbound IPv6 traffic.
|
||||||
|
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
|
||||||
|
if (!enableFilter(&filter, title,
|
||||||
|
description.arg("out").arg(networkAdapter))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// #4 Permit inbound IPv6 traffic.
|
||||||
|
filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6;
|
||||||
|
if (!enableFilter(&filter, title,
|
||||||
|
description.arg("in").arg(networkAdapter))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowsFirewall::allowTrafficTo(const QHostAddress& targetIP, uint port,
|
||||||
|
int weight, const QString& title,
|
||||||
|
const QString& peer) {
|
||||||
|
bool isIPv4 = targetIP.protocol() == QAbstractSocket::IPv4Protocol;
|
||||||
|
GUID layerOut =
|
||||||
|
isIPv4 ? FWPM_LAYER_ALE_AUTH_CONNECT_V4 : FWPM_LAYER_ALE_AUTH_CONNECT_V6;
|
||||||
|
GUID layerIn = isIPv4 ? FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4
|
||||||
|
: FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6;
|
||||||
|
|
||||||
|
quint32_be ipBigEndian;
|
||||||
|
quint32 ip = targetIP.toIPv4Address();
|
||||||
|
qToBigEndian(ip, &ipBigEndian);
|
||||||
|
|
||||||
|
// Allow Traffic to IP with PORT using any protocol
|
||||||
|
FWPM_FILTER_CONDITION0 conds[4];
|
||||||
|
conds[0].fieldKey = FWPM_CONDITION_IP_PROTOCOL;
|
||||||
|
conds[0].matchType = FWP_MATCH_EQUAL;
|
||||||
|
conds[0].conditionValue.type = FWP_UINT8;
|
||||||
|
conds[0].conditionValue.uint8 = (IPPROTO_UDP);
|
||||||
|
|
||||||
|
conds[1].fieldKey = FWPM_CONDITION_IP_PROTOCOL;
|
||||||
|
conds[1].matchType = FWP_MATCH_EQUAL;
|
||||||
|
conds[1].conditionValue.type = FWP_UINT8;
|
||||||
|
conds[1].conditionValue.uint16 = (IPPROTO_TCP);
|
||||||
|
|
||||||
|
conds[2].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
|
||||||
|
conds[2].matchType = FWP_MATCH_EQUAL;
|
||||||
|
conds[2].conditionValue.type = FWP_UINT16;
|
||||||
|
conds[2].conditionValue.uint16 = port;
|
||||||
|
|
||||||
|
conds[3].fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS;
|
||||||
|
conds[3].matchType = FWP_MATCH_EQUAL;
|
||||||
|
QByteArray buffer;
|
||||||
|
// will hold v6 Addess bytes if present
|
||||||
|
importAddress(targetIP, conds[3].conditionValue, &buffer);
|
||||||
|
|
||||||
|
// Assemble the Filter base
|
||||||
|
FWPM_FILTER0 filter;
|
||||||
|
memset(&filter, 0, sizeof(filter));
|
||||||
|
filter.filterCondition = conds;
|
||||||
|
filter.numFilterConditions = 4;
|
||||||
|
filter.action.type = FWP_ACTION_PERMIT;
|
||||||
|
filter.weight.type = FWP_UINT8;
|
||||||
|
filter.weight.uint8 = weight;
|
||||||
|
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
|
||||||
|
filter.flags = FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT; // Hard Permit!
|
||||||
|
|
||||||
|
QString description("Permit traffic %1 %2 on port %3");
|
||||||
|
filter.layerKey = layerOut;
|
||||||
|
if (!enableFilter(&filter, title,
|
||||||
|
description.arg("to").arg(targetIP.toString()).arg(port),
|
||||||
|
peer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
filter.layerKey = layerIn;
|
||||||
|
if (!enableFilter(&filter, title,
|
||||||
|
description.arg("from").arg(targetIP.toString()).arg(port),
|
||||||
|
peer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title) {
|
||||||
|
// Allow outbound DHCPv4
|
||||||
|
{
|
||||||
|
FWPM_FILTER_CONDITION0 conds[4];
|
||||||
|
// Condition: Request must be targeting the TUN interface
|
||||||
|
conds[0].fieldKey = FWPM_CONDITION_IP_PROTOCOL;
|
||||||
|
conds[0].matchType = FWP_MATCH_EQUAL;
|
||||||
|
conds[0].conditionValue.type = FWP_UINT8;
|
||||||
|
conds[0].conditionValue.uint8 = (IPPROTO_UDP);
|
||||||
|
|
||||||
|
conds[1].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
|
||||||
|
conds[1].matchType = FWP_MATCH_EQUAL;
|
||||||
|
conds[1].conditionValue.type = FWP_UINT16;
|
||||||
|
conds[1].conditionValue.uint16 = (68);
|
||||||
|
|
||||||
|
conds[2].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
|
||||||
|
conds[2].matchType = FWP_MATCH_EQUAL;
|
||||||
|
conds[2].conditionValue.type = FWP_UINT16;
|
||||||
|
conds[2].conditionValue.uint16 = 67;
|
||||||
|
|
||||||
|
conds[3].fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS;
|
||||||
|
conds[3].matchType = FWP_MATCH_EQUAL;
|
||||||
|
conds[3].conditionValue.type = FWP_UINT32;
|
||||||
|
conds[3].conditionValue.uint32 = (0xffffffff);
|
||||||
|
|
||||||
|
// Assemble the Filter base
|
||||||
|
FWPM_FILTER0 filter;
|
||||||
|
memset(&filter, 0, sizeof(filter));
|
||||||
|
filter.filterCondition = conds;
|
||||||
|
filter.numFilterConditions = 4;
|
||||||
|
filter.action.type = FWP_ACTION_PERMIT;
|
||||||
|
filter.weight.type = FWP_UINT8;
|
||||||
|
filter.weight.uint8 = weight;
|
||||||
|
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
|
||||||
|
|
||||||
|
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
|
||||||
|
|
||||||
|
if (!enableFilter(&filter, title, "Allow Outbound DHCP")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Allow inbound DHCPv4
|
||||||
|
{
|
||||||
|
FWPM_FILTER_CONDITION0 conds[3];
|
||||||
|
conds[0].fieldKey = FWPM_CONDITION_IP_PROTOCOL;
|
||||||
|
conds[0].matchType = FWP_MATCH_EQUAL;
|
||||||
|
conds[0].conditionValue.type = FWP_UINT8;
|
||||||
|
conds[0].conditionValue.uint8 = (IPPROTO_UDP);
|
||||||
|
|
||||||
|
conds[1].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
|
||||||
|
conds[1].matchType = FWP_MATCH_EQUAL;
|
||||||
|
conds[1].conditionValue.type = FWP_UINT16;
|
||||||
|
conds[1].conditionValue.uint16 = (68);
|
||||||
|
|
||||||
|
conds[2].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
|
||||||
|
conds[2].matchType = FWP_MATCH_EQUAL;
|
||||||
|
conds[2].conditionValue.type = FWP_UINT16;
|
||||||
|
conds[2].conditionValue.uint16 = 67;
|
||||||
|
|
||||||
|
// Assemble the Filter base
|
||||||
|
FWPM_FILTER0 filter;
|
||||||
|
memset(&filter, 0, sizeof(filter));
|
||||||
|
filter.filterCondition = conds;
|
||||||
|
filter.numFilterConditions = 3;
|
||||||
|
filter.action.type = FWP_ACTION_PERMIT;
|
||||||
|
filter.weight.type = FWP_UINT8;
|
||||||
|
filter.weight.uint8 = weight;
|
||||||
|
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
|
||||||
|
filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4;
|
||||||
|
|
||||||
|
if (!enableFilter(&filter, title, "Allow inbound DHCP")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow outbound DHCPv6
|
||||||
|
{
|
||||||
|
FWPM_FILTER_CONDITION0 conds[3];
|
||||||
|
// Condition: Request must be targeting the TUN interface
|
||||||
|
conds[0].fieldKey = FWPM_CONDITION_IP_PROTOCOL;
|
||||||
|
conds[0].matchType = FWP_MATCH_EQUAL;
|
||||||
|
conds[0].conditionValue.type = FWP_UINT8;
|
||||||
|
conds[0].conditionValue.uint8 = (IPPROTO_UDP);
|
||||||
|
|
||||||
|
conds[1].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
|
||||||
|
conds[1].matchType = FWP_MATCH_EQUAL;
|
||||||
|
conds[1].conditionValue.type = FWP_UINT16;
|
||||||
|
conds[1].conditionValue.uint16 = (68);
|
||||||
|
|
||||||
|
conds[2].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
|
||||||
|
conds[2].matchType = FWP_MATCH_EQUAL;
|
||||||
|
conds[2].conditionValue.type = FWP_UINT16;
|
||||||
|
conds[2].conditionValue.uint16 = 67;
|
||||||
|
|
||||||
|
// Assemble the Filter base
|
||||||
|
FWPM_FILTER0 filter;
|
||||||
|
memset(&filter, 0, sizeof(filter));
|
||||||
|
filter.filterCondition = conds;
|
||||||
|
filter.numFilterConditions = 3;
|
||||||
|
filter.action.type = FWP_ACTION_PERMIT;
|
||||||
|
filter.weight.type = FWP_UINT8;
|
||||||
|
filter.weight.uint8 = weight;
|
||||||
|
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
|
||||||
|
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
|
||||||
|
|
||||||
|
if (!enableFilter(&filter, title, "Allow outbound DHCPv6")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow inbound DHCPv6
|
||||||
|
{
|
||||||
|
FWPM_FILTER_CONDITION0 conds[3];
|
||||||
|
conds[0].fieldKey = FWPM_CONDITION_IP_PROTOCOL;
|
||||||
|
conds[0].matchType = FWP_MATCH_EQUAL;
|
||||||
|
conds[0].conditionValue.type = FWP_UINT8;
|
||||||
|
conds[0].conditionValue.uint8 = (IPPROTO_UDP);
|
||||||
|
|
||||||
|
conds[1].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
|
||||||
|
conds[1].matchType = FWP_MATCH_EQUAL;
|
||||||
|
conds[1].conditionValue.type = FWP_UINT16;
|
||||||
|
conds[1].conditionValue.uint16 = (68);
|
||||||
|
|
||||||
|
conds[2].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
|
||||||
|
conds[2].matchType = FWP_MATCH_EQUAL;
|
||||||
|
conds[2].conditionValue.type = FWP_UINT16;
|
||||||
|
conds[2].conditionValue.uint16 = 67;
|
||||||
|
|
||||||
|
// Assemble the Filter base
|
||||||
|
FWPM_FILTER0 filter;
|
||||||
|
memset(&filter, 0, sizeof(filter));
|
||||||
|
filter.filterCondition = conds;
|
||||||
|
filter.numFilterConditions = 3;
|
||||||
|
filter.action.type = FWP_ACTION_PERMIT;
|
||||||
|
filter.weight.type = FWP_UINT8;
|
||||||
|
filter.weight.uint8 = weight;
|
||||||
|
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
|
||||||
|
filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6;
|
||||||
|
if (!enableFilter(&filter, title, "Allow inbound DHCPv6")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allows the internal Hyper-V Switches to work.
|
||||||
|
bool WindowsFirewall::allowHyperVTraffic(uint8_t weight, const QString& title) {
|
||||||
|
FWPM_FILTER_CONDITION0 cond;
|
||||||
|
// Condition: Request must be targeting the TUN interface
|
||||||
|
cond.fieldKey = FWPM_CONDITION_L2_FLAGS;
|
||||||
|
cond.matchType = FWP_MATCH_EQUAL;
|
||||||
|
cond.conditionValue.type = FWP_UINT32;
|
||||||
|
cond.conditionValue.uint32 = FWP_CONDITION_L2_IS_VM2VM;
|
||||||
|
|
||||||
|
// Assemble the Filter base
|
||||||
|
FWPM_FILTER0 filter;
|
||||||
|
memset(&filter, 0, sizeof(filter));
|
||||||
|
filter.filterCondition = &cond;
|
||||||
|
filter.numFilterConditions = 1;
|
||||||
|
filter.action.type = FWP_ACTION_PERMIT;
|
||||||
|
filter.weight.type = FWP_UINT8;
|
||||||
|
filter.weight.uint8 = weight;
|
||||||
|
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
|
||||||
|
|
||||||
|
// #1 Permit Hyper-V => Hyper-V outbound.
|
||||||
|
filter.layerKey = FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE;
|
||||||
|
if (!enableFilter(&filter, title, "Permit Hyper-V => Hyper-V outbound")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// #2 Permit Hyper-V => Hyper-V inbound.
|
||||||
|
filter.layerKey = FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE;
|
||||||
|
if (!enableFilter(&filter, title, "Permit Hyper-V => Hyper-V inbound")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowsFirewall::blockTrafficTo(const IPAddress& addr, uint8_t weight,
|
||||||
|
const QString& title,
|
||||||
|
const QString& peer) {
|
||||||
|
QString description("Block 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_BLOCK;
|
||||||
|
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::blockTrafficTo(const QList<IPAddress>& rangeList,
|
||||||
|
uint8_t weight, const QString& title,
|
||||||
|
const QString& peer) {
|
||||||
|
for (auto range : rangeList) {
|
||||||
|
if (!blockTrafficTo(range, weight, title, peer)) {
|
||||||
|
qDebug() << "Setting Range of" << range.toString() << "failed";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the Path of the Current Executable this runs in
|
||||||
|
QString WindowsFirewall::getCurrentPath() {
|
||||||
|
const unsigned char initValue = 0xff;
|
||||||
|
QByteArray buffer(2048, initValue);
|
||||||
|
auto ok = GetModuleFileNameA(NULL, buffer.data(), buffer.size());
|
||||||
|
|
||||||
|
if (ok == ERROR_INSUFFICIENT_BUFFER) {
|
||||||
|
buffer.resize(buffer.size() * 2);
|
||||||
|
ok = GetModuleFileNameA(NULL, buffer.data(), buffer.size());
|
||||||
|
}
|
||||||
|
if (ok == 0) {
|
||||||
|
//WindowsCommons::windowsLog("Err fetching dos path");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString::fromLocal8Bit(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowsFirewall::importAddress(const QHostAddress& addr,
|
||||||
|
OUT FWP_VALUE0_& value,
|
||||||
|
OUT QByteArray* v6DataBuffer) {
|
||||||
|
const bool isV4 = addr.protocol() == QAbstractSocket::IPv4Protocol;
|
||||||
|
if (isV4) {
|
||||||
|
value.type = FWP_UINT32;
|
||||||
|
value.uint32 = addr.toIPv4Address();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto v6bytes = addr.toIPv6Address();
|
||||||
|
v6DataBuffer->append((const char*)v6bytes.c, IPV6_ADDRESS_SIZE);
|
||||||
|
value.type = FWP_BYTE_ARRAY16_TYPE;
|
||||||
|
value.byteArray16 = (FWP_BYTE_ARRAY16*)v6DataBuffer->data();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowsFirewall::importAddress(const QHostAddress& addr,
|
||||||
|
OUT FWP_CONDITION_VALUE0_& value,
|
||||||
|
OUT QByteArray* v6DataBuffer) {
|
||||||
|
const bool isV4 = addr.protocol() == QAbstractSocket::IPv4Protocol;
|
||||||
|
if (isV4) {
|
||||||
|
value.type = FWP_UINT32;
|
||||||
|
value.uint32 = addr.toIPv4Address();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto v6bytes = addr.toIPv6Address();
|
||||||
|
v6DataBuffer->append((const char*)v6bytes.c, IPV6_ADDRESS_SIZE);
|
||||||
|
value.type = FWP_BYTE_ARRAY16_TYPE;
|
||||||
|
value.byteArray16 = (FWP_BYTE_ARRAY16*)v6DataBuffer->data();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowsFirewall::blockTrafficOnPort(uint port, uint8_t weight,
|
||||||
|
const QString& title) {
|
||||||
|
// Allow Traffic to IP with PORT using any protocol
|
||||||
|
FWPM_FILTER_CONDITION0 conds[3];
|
||||||
|
conds[0].fieldKey = FWPM_CONDITION_IP_PROTOCOL;
|
||||||
|
conds[0].matchType = FWP_MATCH_EQUAL;
|
||||||
|
conds[0].conditionValue.type = FWP_UINT8;
|
||||||
|
conds[0].conditionValue.uint8 = (IPPROTO_UDP);
|
||||||
|
|
||||||
|
conds[1].fieldKey = FWPM_CONDITION_IP_PROTOCOL;
|
||||||
|
conds[1].matchType = FWP_MATCH_EQUAL;
|
||||||
|
conds[1].conditionValue.type = FWP_UINT8;
|
||||||
|
conds[1].conditionValue.uint8 = (IPPROTO_TCP);
|
||||||
|
|
||||||
|
conds[2].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
|
||||||
|
conds[2].matchType = FWP_MATCH_EQUAL;
|
||||||
|
conds[2].conditionValue.type = FWP_UINT16;
|
||||||
|
conds[2].conditionValue.uint16 = port;
|
||||||
|
|
||||||
|
// Assemble the Filter base
|
||||||
|
FWPM_FILTER0 filter;
|
||||||
|
memset(&filter, 0, sizeof(filter));
|
||||||
|
filter.filterCondition = conds;
|
||||||
|
filter.numFilterConditions = 3;
|
||||||
|
filter.action.type = FWP_ACTION_BLOCK;
|
||||||
|
filter.weight.type = FWP_UINT8;
|
||||||
|
filter.weight.uint8 = weight;
|
||||||
|
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
|
||||||
|
|
||||||
|
QString description("Block %1 on Port %2");
|
||||||
|
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
|
||||||
|
if (!enableFilter(&filter, title, description.arg("outgoing v6").arg(port))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
|
||||||
|
if (!enableFilter(&filter, title, description.arg("outgoing v4").arg(port))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4;
|
||||||
|
if (!enableFilter(&filter, title, description.arg("incoming v4").arg(port))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6;
|
||||||
|
if (!enableFilter(&filter, title, description.arg("incoming v6").arg(port))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowsFirewall::enableFilter(FWPM_FILTER0* filter, const QString& title,
|
||||||
|
const QString& description,
|
||||||
|
const QString& peer) {
|
||||||
|
|
||||||
|
uint64_t filterID = 0;
|
||||||
|
auto name = title.toStdWString();
|
||||||
|
auto desc = description.toStdWString();
|
||||||
|
filter->displayData.name = (PWSTR)name.c_str();
|
||||||
|
filter->displayData.description = (PWSTR)desc.c_str();
|
||||||
|
auto result = FwpmFilterAdd0(m_sessionHandle, filter, NULL, &filterID);
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
qDebug() << "Failed to enable filter: " << title << " "
|
||||||
|
<< description<< "with result "<<result<<" GetLastError:"<< GetLastError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
qDebug() << "Filter added: " << title << ":" << description;
|
||||||
|
if (peer.isEmpty()) {
|
||||||
|
m_activeRules.append(filterID);
|
||||||
|
} else {
|
||||||
|
m_peerRules.insert(peer, filterID);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowsFirewall::allowLoopbackTraffic(uint8_t weight,
|
||||||
|
const QString& title) {
|
||||||
|
QList<QNetworkInterface> networkInterfaces =
|
||||||
|
QNetworkInterface::allInterfaces();
|
||||||
|
for (const auto& iface : networkInterfaces) {
|
||||||
|
if (iface.type() != QNetworkInterface::Loopback) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!allowTrafficOfAdapter(iface.index(), weight,
|
||||||
|
title.arg(iface.name()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
69
service/killswitch/windowsfirewall.h
Normal file
69
service/killswitch/windowsfirewall.h
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
#ifndef WINDOWSFIREWALL_H
|
||||||
|
#define WINDOWSFIREWALL_H
|
||||||
|
|
||||||
|
#pragma comment(lib, "Fwpuclnt")
|
||||||
|
|
||||||
|
//#include "../../daemon/interfaceconfig.h"
|
||||||
|
|
||||||
|
#include "ipaddress.h"
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <fwpmu.h>
|
||||||
|
#include <QString>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <QByteArray>
|
||||||
|
class IpAdressRange;
|
||||||
|
struct FWP_VALUE0_;
|
||||||
|
struct FWP_CONDITION_VALUE0_;
|
||||||
|
|
||||||
|
class WindowsFirewall final : public QObject {
|
||||||
|
public:
|
||||||
|
~WindowsFirewall();
|
||||||
|
|
||||||
|
static WindowsFirewall* instance();
|
||||||
|
bool init();
|
||||||
|
|
||||||
|
bool enableKillSwitch(int vpnAdapterIndex);
|
||||||
|
//bool enablePeerTraffic(const InterfaceConfig& config);
|
||||||
|
bool disablePeerTraffic(const QString& pubkey);
|
||||||
|
bool disableKillSwitch();
|
||||||
|
|
||||||
|
private:
|
||||||
|
WindowsFirewall(QObject* parent);
|
||||||
|
HANDLE m_sessionHandle{INVALID_HANDLE_VALUE};
|
||||||
|
bool m_init = false;
|
||||||
|
QList<uint64_t> m_activeRules;
|
||||||
|
QMultiMap<QString, uint64_t> m_peerRules;
|
||||||
|
|
||||||
|
bool allowTrafficForAppOnAll(const QString& exePath, int weight,
|
||||||
|
const QString& title);
|
||||||
|
bool blockTrafficTo(const QList<IPAddress>& range, uint8_t weight,
|
||||||
|
const QString& title, const QString& peer = QString());
|
||||||
|
bool blockTrafficTo(const IPAddress& addr, uint8_t weight,
|
||||||
|
const QString& title, const QString& peer = QString());
|
||||||
|
bool blockTrafficOnPort(uint port, uint8_t weight, const QString& title);
|
||||||
|
bool allowTrafficTo(const QHostAddress& targetIP, uint port, int weight,
|
||||||
|
const QString& title, const QString& peer = QString());
|
||||||
|
bool allowTrafficOfAdapter(int networkAdapter, uint8_t weight,
|
||||||
|
const QString& title);
|
||||||
|
bool allowDHCPTraffic(uint8_t weight, const QString& title);
|
||||||
|
bool allowHyperVTraffic(uint8_t weight, const QString& title);
|
||||||
|
bool allowLoopbackTraffic(uint8_t weight, const QString& title);
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
QString getCurrentPath();
|
||||||
|
void importAddress(const QHostAddress& addr, OUT FWP_VALUE0_& value,
|
||||||
|
OUT QByteArray* v6DataBuffer);
|
||||||
|
void importAddress(const QHostAddress& addr, OUT FWP_CONDITION_VALUE0_& value,
|
||||||
|
OUT QByteArray* v6DataBuffer);
|
||||||
|
bool enableFilter(FWPM_FILTER0* filter, const QString& title,
|
||||||
|
const QString& description,
|
||||||
|
const QString& peer = QString());
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WINDOWSFIREWALL_H
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include "router.h"
|
#include "router.h"
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
|
#include "windowsfirewall.h"
|
||||||
#include "tapcontroller_win.h"
|
#include "tapcontroller_win.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -38,6 +39,9 @@ LocalServer::LocalServer(QObject *parent) : QObject(parent),
|
||||||
|
|
||||||
LocalServer::~LocalServer()
|
LocalServer::~LocalServer()
|
||||||
{
|
{
|
||||||
|
auto cf = WindowsFirewall::instance();
|
||||||
|
cf->disableKillSwitch();
|
||||||
|
qDebug()<<"KillSwitch deactivated";
|
||||||
qDebug() << "Local server stopped";
|
qDebug() << "Local server stopped";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
#include <QNetworkInterface>
|
||||||
|
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
#include "localserver.h"
|
#include "localserver.h"
|
||||||
|
|
@ -6,6 +7,25 @@
|
||||||
#include "systemservice.h"
|
#include "systemservice.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include "windowsfirewall.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class KillSwitch final {
|
||||||
|
public:
|
||||||
|
explicit KillSwitch(){
|
||||||
|
qDebug()<<__FUNCTION__;
|
||||||
|
auto cf = WindowsFirewall::instance();
|
||||||
|
cf->init();
|
||||||
|
//cf->disableKillSwitch();
|
||||||
|
}
|
||||||
|
~KillSwitch(){
|
||||||
|
qDebug()<<__FUNCTION__;
|
||||||
|
auto cf = WindowsFirewall::instance();
|
||||||
|
cf->disableKillSwitch();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
int runApplication(int argc, char** argv)
|
int runApplication(int argc, char** argv)
|
||||||
{
|
{
|
||||||
|
|
@ -19,9 +39,12 @@ int runApplication(int argc, char** argv)
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
Utils::initializePath(Utils::systemLogPath());
|
Utils::initializePath(Utils::systemLogPath());
|
||||||
|
|
||||||
Log::initialize();
|
Log::initialize();
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
KillSwitch ks;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (argc == 2) {
|
if (argc == 2) {
|
||||||
qInfo() << "Started as console application";
|
qInfo() << "Started as console application";
|
||||||
return runApplication(argc, argv);
|
return runApplication(argc, argv);
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,79 @@
|
||||||
#include "router_linux.h"
|
#include "router_linux.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include <QNetworkInterface>
|
||||||
|
#include "windowsfirewall.h"
|
||||||
|
//#include "netadpinfo.h"
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr char IKEV2[] {"AmneziaVPN IKEv2"};
|
||||||
|
constexpr char WG[] {"AmneziaVPN.WireGuard0"};
|
||||||
|
constexpr char OVPN[] {"TAP-Windows Adapter V9"};
|
||||||
|
|
||||||
|
bool get_eth_name(const QString &adp_name){
|
||||||
|
//char buff[128]{};
|
||||||
|
PIP_ADAPTER_INFO pAdapterInfo{};
|
||||||
|
pAdapterInfo = (IP_ADAPTER_INFO *) malloc(sizeof(IP_ADAPTER_INFO));
|
||||||
|
ULONG buflen = sizeof(IP_ADAPTER_INFO);
|
||||||
|
if(GetAdaptersInfo(pAdapterInfo, &buflen) == ERROR_BUFFER_OVERFLOW)
|
||||||
|
{
|
||||||
|
free(pAdapterInfo);
|
||||||
|
pAdapterInfo = (IP_ADAPTER_INFO *)
|
||||||
|
malloc(buflen);
|
||||||
|
}
|
||||||
|
if(GetAdaptersInfo(pAdapterInfo, &buflen) == NO_ERROR) {
|
||||||
|
PIP_ADAPTER_INFO pAdapter = pAdapterInfo;
|
||||||
|
while (pAdapter) {
|
||||||
|
qDebug()<<"Adapter Name: "<< pAdapter->AdapterName;
|
||||||
|
qDebug()<<"Adapter Desc: "<<pAdapter->Description;
|
||||||
|
if (adp_name == pAdapter->Description)
|
||||||
|
return true;
|
||||||
|
pAdapter = pAdapter->Next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void enable_killswitch(){
|
||||||
|
auto VPN_LIST = []()->int{
|
||||||
|
auto adapterList = QNetworkInterface::allInterfaces();
|
||||||
|
for (const auto& adapter : adapterList) {
|
||||||
|
bool finded{false};
|
||||||
|
finded = get_eth_name(OVPN);
|
||||||
|
if ( adapter.humanReadableName().contains(IKEV2) ||
|
||||||
|
adapter.humanReadableName().contains(WG) ||
|
||||||
|
finded ||
|
||||||
|
adapter.humanReadableName().contains(OVPN)
|
||||||
|
) {
|
||||||
|
return adapter.index();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
const auto ¤t_vpn = VPN_LIST();
|
||||||
|
if (current_vpn != -1){
|
||||||
|
qInfo()<<"KillSwitch activated";
|
||||||
|
auto cf = WindowsFirewall::instance();
|
||||||
|
//cf->enableKillSwitch(current_vpn);
|
||||||
|
cf->disableKillSwitch();
|
||||||
|
}else{
|
||||||
|
qCritical().noquote() <<"No any adapters was found";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}// end namespace
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
int Router::routeAddList(const QString &gw, const QStringList &ips)
|
int Router::routeAddList(const QString &gw, const QStringList &ips)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
return RouterWin::Instance().routeAddList(gw, ips);
|
auto value = RouterWin::Instance().routeAddList(gw, ips);
|
||||||
|
enable_killswitch();
|
||||||
|
return value;
|
||||||
|
//return RouterWin::Instance().routeAddList(gw, ips);
|
||||||
#elif defined (Q_OS_MAC)
|
#elif defined (Q_OS_MAC)
|
||||||
return RouterMac::Instance().routeAddList(gw, ips);
|
return RouterMac::Instance().routeAddList(gw, ips);
|
||||||
#elif defined Q_OS_LINUX
|
#elif defined Q_OS_LINUX
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,6 @@ int RouterWin::routeAddList(const QString &gw, const QStringList &ips)
|
||||||
// qDebug().noquote() << QString("ROUTE ADD List: IPs:\n%1")
|
// qDebug().noquote() << QString("ROUTE ADD List: IPs:\n%1")
|
||||||
// .arg(ips.join("\n"));
|
// .arg(ips.join("\n"));
|
||||||
|
|
||||||
|
|
||||||
if (!Utils::checkIPv4Format(gw)) {
|
if (!Utils::checkIPv4Format(gw)) {
|
||||||
qCritical().noquote() << "Trying to add invalid route, gw: " << gw;
|
qCritical().noquote() << "Trying to add invalid route, gw: " << gw;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ class RouterWin : public QObject
|
||||||
public:
|
public:
|
||||||
static RouterWin& Instance();
|
static RouterWin& Instance();
|
||||||
|
|
||||||
|
|
||||||
int routeAddList(const QString &gw, const QStringList &ips);
|
int routeAddList(const QString &gw, const QStringList &ips);
|
||||||
bool clearSavedRoutes();
|
bool clearSavedRoutes();
|
||||||
int routeDeleteList(const QString &gw, const QStringList &ips);
|
int routeDeleteList(const QString &gw, const QStringList &ips);
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ SOURCES = \
|
||||||
systemservice.cpp
|
systemservice.cpp
|
||||||
|
|
||||||
win32 {
|
win32 {
|
||||||
|
include(../killswitch/killswitch.pri)
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
tapcontroller_win.h \
|
tapcontroller_win.h \
|
||||||
router_win.h
|
router_win.h
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue