WireGuard for MacOS (#248)

* WireGuard for MacOS
* Fix openvpn block-outside-dns
This commit is contained in:
pokamest 2023-07-15 14:19:48 -07:00 committed by GitHub
parent ed5dc7cdfd
commit 35ecb8499d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
118 changed files with 5150 additions and 3486 deletions

View file

@ -0,0 +1,286 @@
/* 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 <QtMath>
#include "leakdetector.h"
IPAddress::IPAddress() { MZ_COUNT_CTOR(IPAddress); }
IPAddress::IPAddress(const QString& ip) {
MZ_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) {
MZ_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) {
MZ_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) {
MZ_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() { MZ_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)) {
newResults.append(ip);
} else if (exclude.subnetOf(ip) && exclude != ip) {
QList<IPAddress> range = ip.excludeAddresses(exclude);
newResults.append(range);
}
}
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;
}

View 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

View 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 MZ_DEBUG
static QMutex s_leakDetector;
QHash<QString, QHash<void*, uint32_t>> s_leaks;
#endif
LeakDetector::LeakDetector() {
#ifndef MZ_DEBUG
qFatal("LeakDetector _must_ be created in debug builds only!");
#endif
}
LeakDetector::~LeakDetector() {
#ifdef MZ_DEBUG
QTextStream out(stderr);
out << "== MZ - 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 MZ_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

View 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 MZ_DEBUG
# define MZ_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 MZ_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 MZ_COUNT_CTOR(_type)
# define MZ_COUNT_DTOR(_type)
#endif
class LeakDetector {
public:
LeakDetector();
~LeakDetector();
#ifdef MZ_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

View file

@ -0,0 +1,16 @@
/* 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 LOGLEVEL_H
#define LOGLEVEL_H
enum LogLevel {
Trace = 0,
Debug,
Info,
Warning,
Error,
};
#endif // LOGLEVEL_H

View file

@ -0,0 +1,76 @@
/* 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 "signalhandler.h"
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include "logger.h"
namespace {
Logger logger("SignalHandler");
int s_signalpipe = -1;
} // namespace
SignalHandler::SignalHandler() {
Q_ASSERT(s_signalpipe < 0);
int quitSignals[] = {SIGQUIT, SIGINT, SIGTERM, SIGHUP};
sigset_t mask;
sigemptyset(&mask);
for (auto sig : quitSignals) {
sigaddset(&mask, sig);
}
if (pipe(m_pipefds) != 0) {
logger.error() << "Unable to create signal wakeup pipe";
return;
}
fcntl(m_pipefds[0], F_SETFL, fcntl(m_pipefds[0], F_GETFL) | O_NONBLOCK);
s_signalpipe = m_pipefds[1];
m_notifier = new QSocketNotifier(m_pipefds[0], QSocketNotifier::Read, this);
connect(m_notifier, &QSocketNotifier::activated, this,
&SignalHandler::pipeReadReady);
struct sigaction sa;
sa.sa_handler = SignalHandler::saHandler;
sa.sa_mask = mask;
sa.sa_flags = 0;
for (auto sig : quitSignals) {
sigaction(sig, &sa, nullptr);
}
}
SignalHandler::~SignalHandler() {
s_signalpipe = -1;
if (m_pipefds[0] >= 0) {
close(m_pipefds[0]);
}
if (m_pipefds[1] >= 1) {
close(m_pipefds[1]);
}
}
void SignalHandler::pipeReadReady() {
int signal;
if (read(m_pipefds[0], &signal, sizeof(signal)) == sizeof(signal)) {
logger.debug() << "Signal" << signal;
emit quitRequested();
}
}
void SignalHandler::saHandler(int signal) {
if (s_signalpipe >= 0) {
if (write(s_signalpipe, &signal, sizeof(signal)) != sizeof(signal)) {
logger.warning() << "Unable to write in the pipe";
}
}
}

View file

@ -0,0 +1,31 @@
/* 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 SIGNALHANDLER_H
#define SIGNALHANDLER_H
#include <QObject>
#include <QSocketNotifier>
class SignalHandler final : public QObject {
Q_OBJECT
public:
SignalHandler();
~SignalHandler();
private slots:
void pipeReadReady();
private:
static void saHandler(int signal);
int m_pipefds[2] = {-1, -1};
QSocketNotifier* m_notifier = nullptr;
signals:
void quitRequested();
};
#endif // SIGNALHANDLER_H