WireGuard for MacOS (#248)
* WireGuard for MacOS * Fix openvpn block-outside-dns
This commit is contained in:
parent
ed5dc7cdfd
commit
35ecb8499d
118 changed files with 5150 additions and 3486 deletions
89
client/mozilla/controllerimpl.h
Normal file
89
client/mozilla/controllerimpl.h
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/* 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 CONTROLLERIMPL_H
|
||||
#define CONTROLLERIMPL_H
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <functional>
|
||||
|
||||
class Keys;
|
||||
class Device;
|
||||
class Server;
|
||||
class QDateTime;
|
||||
class IPAddress;
|
||||
class QHostAddress;
|
||||
|
||||
// This object is allocated when the VPN is about to be activated.
|
||||
// It's kept alive, basically forever, except in these scenarios, in which it's
|
||||
// recreated:
|
||||
// - the user does a logout
|
||||
// - there is an authentication falure
|
||||
class ControllerImpl : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ControllerImpl() = default;
|
||||
|
||||
virtual ~ControllerImpl() = default;
|
||||
|
||||
// This method is called to initialize the controller. The initialization
|
||||
// is completed when the signal "initialized" is emitted.
|
||||
virtual void initialize(const Device* device, const Keys* keys) = 0;
|
||||
|
||||
// This method is called when the VPN client needs to activate the VPN
|
||||
// tunnel. It's called only at the end of the initialization process. When
|
||||
// this method is called, the VPN client is in "connecting" state. This
|
||||
// state terminates when the "connected" (or the "disconnected") signal is
|
||||
// received.
|
||||
virtual void activate(const QJsonObject& config) = 0;
|
||||
|
||||
// This method terminates the VPN tunnel. The VPN client is in
|
||||
// "disconnecting" state until the "disconnected" signal is received.
|
||||
virtual void deactivate() = 0;
|
||||
|
||||
// This method is used to retrieve the VPN tunnel status (mainly the number
|
||||
// of bytes sent and received). It's called always when the VPN tunnel is
|
||||
// active.
|
||||
virtual void checkStatus() = 0;
|
||||
|
||||
// This method is used to retrieve the logs from the backend service. Use
|
||||
// the callback to report logs when available.
|
||||
virtual void getBackendLogs(
|
||||
std::function<void(const QString& logs)>&& callback) = 0;
|
||||
|
||||
// Cleanup the backend logs.
|
||||
virtual void cleanupBackendLogs() = 0;
|
||||
|
||||
// Whether the controller supports multihop
|
||||
virtual bool multihopSupported() { return false; }
|
||||
|
||||
virtual bool silentServerSwitchingSupported() const { return true; }
|
||||
|
||||
signals:
|
||||
// This signal is emitted when the controller is initialized. Note that the
|
||||
// VPN tunnel can be already active. In this case, "connected" should be set
|
||||
// to true and the "connectionDate" should be set to the activation date if
|
||||
// known.
|
||||
// If "status" is set to false, the backend service is considered unavailable.
|
||||
void initialized(bool status, bool connected,
|
||||
const QDateTime& connectionDate);
|
||||
|
||||
// These 2 signals can be dispatched at any time.
|
||||
void connected(const QString& pubkey,
|
||||
const QDateTime& connectionTimestamp = QDateTime());
|
||||
void disconnected();
|
||||
|
||||
// This method should be emitted after a checkStatus() call.
|
||||
// "serverIpv4Gateway" is the current VPN tunnel gateway.
|
||||
// "deviceIpv4Address" is the address of the VPN client.
|
||||
// "txBytes" and "rxBytes" contain the number of transmitted and received
|
||||
// bytes since the last statusUpdated signal.
|
||||
void statusUpdated(const QString& serverIpv4Gateway,
|
||||
const QString& deviceIpv4Address, uint64_t txBytes,
|
||||
uint64_t rxBytes);
|
||||
};
|
||||
|
||||
#endif // CONTROLLERIMPL_H
|
||||
117
client/mozilla/dnspingsender.cpp
Normal file
117
client/mozilla/dnspingsender.cpp
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/* 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 "dnspingsender.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <QNetworkDatagram>
|
||||
#include <QtEndian>
|
||||
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
constexpr const quint16 DNS_PORT = 53;
|
||||
|
||||
// A quick and dirty DNS Header structure definition from RFC1035,
|
||||
// Section 4.1.1: Header Section format.
|
||||
struct dnsHeader {
|
||||
quint16 id;
|
||||
quint16 flags;
|
||||
quint16 qdcount;
|
||||
quint16 ancount;
|
||||
quint16 nscount;
|
||||
quint16 arcount;
|
||||
};
|
||||
|
||||
// Bit definitions for the DNS flags field.
|
||||
#define DNS_FLAG_QR 0x8000
|
||||
#define DNS_FLAG_OPCODE 0x7800
|
||||
#define DNS_FLAG_OPCODE_QUERY (0x0 << 11)
|
||||
#define DNS_FLAG_OPCODE_IQUERY (0x1 << 11)
|
||||
#define DNS_FLAG_OPCODE_STATUS (0x2 << 11)
|
||||
#define DNS_FLAG_AA 0x0400
|
||||
#define DNS_FLAG_TC 0x0200
|
||||
#define DNS_FLAG_RD 0x0100
|
||||
#define DNS_FLAG_RA 0x0080
|
||||
#define DNS_FLAG_Z 0x0070
|
||||
#define DNS_FLAG_RCODE 0x000F
|
||||
#define DNS_FLAG_RCODE_NO_ERROR (0x0 << 0)
|
||||
#define DNS_FLAG_RCODE_FORMAT_ERROR (0x1 << 0)
|
||||
#define DNS_FLAG_RCODE_SERVER_FAILURE (0x2 << 0)
|
||||
#define DNS_FLAG_RCODE_NAME_ERROR (0x3 << 0)
|
||||
#define DNS_FLAG_RCODE_NOT_IMPLEMENTED (0x4 << 0)
|
||||
#define DNS_FLAG_RCODE_REFUSED (0x5 << 0)
|
||||
|
||||
namespace {
|
||||
Logger logger("DnsPingSender");
|
||||
}
|
||||
|
||||
DnsPingSender::DnsPingSender(const QHostAddress& source, QObject* parent)
|
||||
: PingSender(parent) {
|
||||
MZ_COUNT_CTOR(DnsPingSender);
|
||||
|
||||
if (source.isNull()) {
|
||||
m_socket.bind();
|
||||
} else {
|
||||
m_socket.bind(source);
|
||||
}
|
||||
|
||||
connect(&m_socket, &QUdpSocket::readyRead, this, &DnsPingSender::readData);
|
||||
}
|
||||
|
||||
DnsPingSender::~DnsPingSender() { MZ_COUNT_DTOR(DnsPingSender); }
|
||||
|
||||
void DnsPingSender::sendPing(const QHostAddress& dest, quint16 sequence) {
|
||||
QByteArray packet;
|
||||
|
||||
// Assemble a DNS query header.
|
||||
struct dnsHeader header;
|
||||
memset(&header, 0, sizeof(header));
|
||||
header.id = qToBigEndian<quint16>(sequence);
|
||||
header.flags = qToBigEndian<quint16>(DNS_FLAG_OPCODE_QUERY);
|
||||
header.qdcount = qToBigEndian<quint16>(1);
|
||||
header.ancount = 0;
|
||||
header.nscount = 0;
|
||||
header.arcount = 0;
|
||||
packet.append(reinterpret_cast<char*>(&header), sizeof(header));
|
||||
|
||||
// Add a query for the root nameserver: {<root>, type A, class IN}
|
||||
const char query[] = {0x00, 0x00, 0x01, 0x00, 0x01};
|
||||
packet.append(query, sizeof(query));
|
||||
|
||||
// Send the datagram.
|
||||
m_socket.writeDatagram(packet, dest, DNS_PORT);
|
||||
}
|
||||
|
||||
void DnsPingSender::readData() {
|
||||
while (m_socket.hasPendingDatagrams()) {
|
||||
QNetworkDatagram reply = m_socket.receiveDatagram();
|
||||
if (!reply.isValid()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Extract the header from the DNS response.
|
||||
QByteArray payload = reply.data();
|
||||
struct dnsHeader header;
|
||||
if (payload.length() < static_cast<int>(sizeof(header))) {
|
||||
logger.debug() << "Received bogus DNS reply: truncated header";
|
||||
continue;
|
||||
}
|
||||
memcpy(&header, payload.constData(), sizeof(header));
|
||||
|
||||
// Perfom some checks to ensure this is the reply we were expecting.
|
||||
quint16 flags = qFromBigEndian<quint16>(header.flags);
|
||||
if ((flags & DNS_FLAG_QR) == 0) {
|
||||
logger.debug() << "Received bogus DNS reply: QR == query";
|
||||
continue;
|
||||
}
|
||||
if ((flags & DNS_FLAG_OPCODE) != DNS_FLAG_OPCODE_QUERY) {
|
||||
logger.debug() << "Received bogus DNS reply: OPCODE != query";
|
||||
continue;
|
||||
}
|
||||
|
||||
emit recvPing(qFromBigEndian<quint16>(header.id));
|
||||
}
|
||||
}
|
||||
29
client/mozilla/dnspingsender.h
Normal file
29
client/mozilla/dnspingsender.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/* 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 DNSPINGSENDER_H
|
||||
#define DNSPINGSENDER_H
|
||||
|
||||
#include <QUdpSocket>
|
||||
|
||||
#include "pingsender.h"
|
||||
|
||||
class DnsPingSender final : public PingSender {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(DnsPingSender)
|
||||
|
||||
public:
|
||||
DnsPingSender(const QHostAddress& source, QObject* parent = nullptr);
|
||||
~DnsPingSender();
|
||||
|
||||
void sendPing(const QHostAddress& dest, quint16 sequence) override;
|
||||
|
||||
private:
|
||||
void readData();
|
||||
|
||||
private:
|
||||
QUdpSocket m_socket;
|
||||
};
|
||||
|
||||
#endif // DNSPINGSENDER_H
|
||||
384
client/mozilla/localsocketcontroller.cpp
Normal file
384
client/mozilla/localsocketcontroller.cpp
Normal file
|
|
@ -0,0 +1,384 @@
|
|||
/* 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 "protocols/protocols_defs.h"
|
||||
#include "localsocketcontroller.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QHostAddress>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include "ipaddress.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "models/server.h"
|
||||
|
||||
// How many times do we try to reconnect.
|
||||
constexpr int MAX_CONNECTION_RETRY = 10;
|
||||
|
||||
// How long do we wait between one try and the next one.
|
||||
constexpr int CONNECTION_RETRY_TIMER_MSEC = 500;
|
||||
|
||||
namespace {
|
||||
Logger logger("LocalSocketController");
|
||||
}
|
||||
|
||||
LocalSocketController::LocalSocketController() {
|
||||
MZ_COUNT_CTOR(LocalSocketController);
|
||||
|
||||
m_socket = new QLocalSocket(this);
|
||||
connect(m_socket, &QLocalSocket::connected, this,
|
||||
&LocalSocketController::daemonConnected);
|
||||
connect(m_socket, &QLocalSocket::disconnected, this,
|
||||
&LocalSocketController::disconnected);
|
||||
connect(m_socket, &QLocalSocket::errorOccurred, this,
|
||||
&LocalSocketController::errorOccurred);
|
||||
connect(m_socket, &QLocalSocket::readyRead, this,
|
||||
&LocalSocketController::readData);
|
||||
|
||||
m_initializingTimer.setSingleShot(true);
|
||||
connect(&m_initializingTimer, &QTimer::timeout, this,
|
||||
&LocalSocketController::initializeInternal);
|
||||
}
|
||||
|
||||
LocalSocketController::~LocalSocketController() {
|
||||
MZ_COUNT_DTOR(LocalSocketController);
|
||||
}
|
||||
|
||||
void LocalSocketController::errorOccurred(
|
||||
QLocalSocket::LocalSocketError error) {
|
||||
logger.error() << "Error occurred:" << error;
|
||||
|
||||
if (m_daemonState == eInitializing) {
|
||||
if (m_initializingRetry++ < MAX_CONNECTION_RETRY) {
|
||||
m_initializingTimer.start(CONNECTION_RETRY_TIMER_MSEC);
|
||||
return;
|
||||
}
|
||||
|
||||
emit initialized(false, false, QDateTime());
|
||||
}
|
||||
|
||||
qCritical() << "ControllerError";
|
||||
disconnectInternal();
|
||||
}
|
||||
|
||||
void LocalSocketController::disconnectInternal() {
|
||||
// We're still eReady as the Deamon is alive
|
||||
// and can make a new connection.
|
||||
m_daemonState = eReady;
|
||||
m_initializingRetry = 0;
|
||||
m_initializingTimer.stop();
|
||||
emit disconnected();
|
||||
}
|
||||
|
||||
void LocalSocketController::initialize(const Device* device, const Keys* keys) {
|
||||
logger.debug() << "Initializing";
|
||||
|
||||
Q_UNUSED(device);
|
||||
Q_UNUSED(keys);
|
||||
|
||||
Q_ASSERT(m_daemonState == eUnknown);
|
||||
m_initializingRetry = 0;
|
||||
|
||||
initializeInternal();
|
||||
}
|
||||
|
||||
void LocalSocketController::initializeInternal() {
|
||||
m_daemonState = eInitializing;
|
||||
|
||||
#ifdef MZ_WINDOWS
|
||||
QString path = "\\\\.\\pipe\\amneziavpn";
|
||||
#else
|
||||
QString path = "/var/run/amneziavpn/daemon.socket";
|
||||
if (!QFileInfo::exists(path)) {
|
||||
path = "/tmp/amneziavpn.socket";
|
||||
}
|
||||
#endif
|
||||
|
||||
logger.debug() << "Connecting to:" << path;
|
||||
m_socket->connectToServer(path);
|
||||
}
|
||||
|
||||
void LocalSocketController::daemonConnected() {
|
||||
logger.debug() << "Daemon connected";
|
||||
Q_ASSERT(m_daemonState == eInitializing);
|
||||
checkStatus();
|
||||
}
|
||||
|
||||
void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
|
||||
qDebug() << rawConfig;
|
||||
QJsonObject wgConfig = rawConfig.value("wireguard_config_data").toObject();
|
||||
|
||||
QJsonObject json;
|
||||
json.insert("type", "activate");
|
||||
// json.insert("hopindex", QJsonValue((double)hop.m_hopindex));
|
||||
json.insert("privateKey", wgConfig.value(amnezia::config_key::client_priv_key));
|
||||
json.insert("deviceIpv4Address", wgConfig.value(amnezia::config_key::client_ip));
|
||||
json.insert("deviceIpv6Address", "dead::1");
|
||||
json.insert("serverPublicKey", wgConfig.value(amnezia::config_key::server_pub_key));
|
||||
json.insert("serverPskKey", wgConfig.value(amnezia::config_key::psk_key));
|
||||
json.insert("serverIpv4AddrIn", wgConfig.value(amnezia::config_key::hostName));
|
||||
// json.insert("serverIpv6AddrIn", QJsonValue(hop.m_server.ipv6AddrIn()));
|
||||
json.insert("serverPort", wgConfig.value(amnezia::config_key::port).toInt());
|
||||
|
||||
json.insert("serverIpv4Gateway", wgConfig.value(amnezia::config_key::hostName));
|
||||
// json.insert("serverIpv6Gateway", QJsonValue(hop.m_server.ipv6Gateway()));
|
||||
json.insert("dnsServer", rawConfig.value(amnezia::config_key::dns1));
|
||||
|
||||
QJsonArray jsAllowedIPAddesses;
|
||||
|
||||
QJsonObject range_ipv4;
|
||||
range_ipv4.insert("address", "0.0.0.0");
|
||||
range_ipv4.insert("range", 0);
|
||||
range_ipv4.insert("isIpv6", false);
|
||||
jsAllowedIPAddesses.append(range_ipv4);
|
||||
|
||||
QJsonObject range_ipv6;
|
||||
range_ipv6.insert("address", "::");
|
||||
range_ipv6.insert("range", 0);
|
||||
range_ipv6.insert("isIpv6", true);
|
||||
jsAllowedIPAddesses.append(range_ipv6);
|
||||
|
||||
json.insert("allowedIPAddressRanges", jsAllowedIPAddesses);
|
||||
|
||||
|
||||
QJsonArray jsExcludedAddresses;
|
||||
jsExcludedAddresses.append(wgConfig.value(amnezia::config_key::hostName));
|
||||
json.insert("excludedAddresses", jsExcludedAddresses);
|
||||
|
||||
|
||||
// QJsonArray splitTunnelApps;
|
||||
// for (const auto& uri : hop.m_vpnDisabledApps) {
|
||||
// splitTunnelApps.append(QJsonValue(uri));
|
||||
// }
|
||||
// json.insert("vpnDisabledApps", splitTunnelApps);
|
||||
|
||||
write(json);
|
||||
}
|
||||
|
||||
void LocalSocketController::deactivate() {
|
||||
logger.debug() << "Deactivating";
|
||||
|
||||
if (m_daemonState != eReady) {
|
||||
logger.debug() << "No disconnect, controller is not ready";
|
||||
emit disconnected();
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject json;
|
||||
json.insert("type", "deactivate");
|
||||
write(json);
|
||||
}
|
||||
|
||||
void LocalSocketController::checkStatus() {
|
||||
logger.debug() << "Check status";
|
||||
|
||||
if (m_daemonState == eReady || m_daemonState == eInitializing) {
|
||||
Q_ASSERT(m_socket);
|
||||
|
||||
QJsonObject json;
|
||||
json.insert("type", "status");
|
||||
write(json);
|
||||
}
|
||||
}
|
||||
|
||||
void LocalSocketController::getBackendLogs(
|
||||
std::function<void(const QString&)>&& a_callback) {
|
||||
logger.debug() << "Backend logs";
|
||||
|
||||
if (m_logCallback) {
|
||||
m_logCallback("");
|
||||
m_logCallback = nullptr;
|
||||
}
|
||||
|
||||
if (m_daemonState != eReady) {
|
||||
std::function<void(const QString&)> callback = a_callback;
|
||||
callback("");
|
||||
return;
|
||||
}
|
||||
|
||||
m_logCallback = std::move(a_callback);
|
||||
|
||||
QJsonObject json;
|
||||
json.insert("type", "logs");
|
||||
write(json);
|
||||
}
|
||||
|
||||
void LocalSocketController::cleanupBackendLogs() {
|
||||
logger.debug() << "Cleanup logs";
|
||||
|
||||
if (m_logCallback) {
|
||||
m_logCallback("");
|
||||
m_logCallback = nullptr;
|
||||
}
|
||||
|
||||
if (m_daemonState != eReady) {
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject json;
|
||||
json.insert("type", "cleanlogs");
|
||||
write(json);
|
||||
}
|
||||
|
||||
void LocalSocketController::readData() {
|
||||
logger.debug() << "Reading";
|
||||
|
||||
Q_ASSERT(m_socket);
|
||||
Q_ASSERT(m_daemonState == eInitializing || m_daemonState == eReady);
|
||||
QByteArray input = m_socket->readAll();
|
||||
m_buffer.append(input);
|
||||
|
||||
while (true) {
|
||||
int pos = m_buffer.indexOf("\n");
|
||||
if (pos == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
QByteArray line = m_buffer.left(pos);
|
||||
m_buffer.remove(0, pos + 1);
|
||||
|
||||
QByteArray command(line);
|
||||
command = command.trimmed();
|
||||
|
||||
if (command.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
parseCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
void LocalSocketController::parseCommand(const QByteArray& command) {
|
||||
QJsonDocument json = QJsonDocument::fromJson(command);
|
||||
if (!json.isObject()) {
|
||||
logger.error() << "Invalid JSON - object expected";
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject obj = json.object();
|
||||
QJsonValue typeValue = obj.value("type");
|
||||
if (!typeValue.isString()) {
|
||||
logger.error() << "Invalid JSON - no type";
|
||||
return;
|
||||
}
|
||||
QString type = typeValue.toString();
|
||||
|
||||
logger.debug() << "Parse command:" << type;
|
||||
|
||||
if (m_daemonState == eInitializing && type == "status") {
|
||||
m_daemonState = eReady;
|
||||
|
||||
QJsonValue connected = obj.value("connected");
|
||||
if (!connected.isBool()) {
|
||||
logger.error() << "Invalid JSON for status - connected expected";
|
||||
return;
|
||||
}
|
||||
|
||||
QDateTime datetime;
|
||||
if (connected.toBool()) {
|
||||
QJsonValue date = obj.value("date");
|
||||
if (!date.isString()) {
|
||||
logger.error() << "Invalid JSON for status - date expected";
|
||||
return;
|
||||
}
|
||||
|
||||
datetime = QDateTime::fromString(date.toString());
|
||||
if (!datetime.isValid()) {
|
||||
logger.error() << "Invalid JSON for status - date is invalid";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
emit initialized(true, connected.toBool(), datetime);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_daemonState != eReady) {
|
||||
logger.error() << "Unexpected command";
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "status") {
|
||||
QJsonValue serverIpv4Gateway = obj.value("serverIpv4Gateway");
|
||||
if (!serverIpv4Gateway.isString()) {
|
||||
logger.error() << "Unexpected serverIpv4Gateway value";
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonValue deviceIpv4Address = obj.value("deviceIpv4Address");
|
||||
if (!deviceIpv4Address.isString()) {
|
||||
logger.error() << "Unexpected deviceIpv4Address value";
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonValue txBytes = obj.value("txBytes");
|
||||
if (!txBytes.isDouble()) {
|
||||
logger.error() << "Unexpected txBytes value";
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonValue rxBytes = obj.value("rxBytes");
|
||||
if (!rxBytes.isDouble()) {
|
||||
logger.error() << "Unexpected rxBytes value";
|
||||
return;
|
||||
}
|
||||
|
||||
emit statusUpdated(serverIpv4Gateway.toString(),
|
||||
deviceIpv4Address.toString(), txBytes.toDouble(),
|
||||
rxBytes.toDouble());
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "disconnected") {
|
||||
disconnectInternal();
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "connected") {
|
||||
QJsonValue pubkey = obj.value("pubkey");
|
||||
if (!pubkey.isString()) {
|
||||
logger.error() << "Unexpected pubkey value";
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug() << "Handshake completed with:"
|
||||
<< pubkey.toString();
|
||||
emit connected(pubkey.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "backendFailure") {
|
||||
qCritical() << "backendFailure";
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "logs") {
|
||||
// We don't care if we are not waiting for logs.
|
||||
if (!m_logCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonValue logs = obj.value("logs");
|
||||
m_logCallback(logs.isString() ? logs.toString().replace("|", "\n")
|
||||
: QString());
|
||||
m_logCallback = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
logger.warning() << "Invalid command received:" << command;
|
||||
}
|
||||
|
||||
void LocalSocketController::write(const QJsonObject& json) {
|
||||
Q_ASSERT(m_socket);
|
||||
m_socket->write(QJsonDocument(json).toJson(QJsonDocument::Compact));
|
||||
m_socket->write("\n");
|
||||
m_socket->flush();
|
||||
}
|
||||
67
client/mozilla/localsocketcontroller.h
Normal file
67
client/mozilla/localsocketcontroller.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/* 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 LOCALSOCKETCONTROLLER_H
|
||||
#define LOCALSOCKETCONTROLLER_H
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QLocalSocket>
|
||||
#include <QTimer>
|
||||
#include <functional>
|
||||
|
||||
#include "controllerimpl.h"
|
||||
|
||||
class QJsonObject;
|
||||
|
||||
class LocalSocketController final : public ControllerImpl {
|
||||
Q_DISABLE_COPY_MOVE(LocalSocketController)
|
||||
|
||||
public:
|
||||
LocalSocketController();
|
||||
~LocalSocketController();
|
||||
|
||||
void initialize(const Device* device, const Keys* keys) override;
|
||||
|
||||
void activate(const QJsonObject& rawConfig) override;
|
||||
|
||||
void deactivate() override;
|
||||
|
||||
void checkStatus() override;
|
||||
|
||||
void getBackendLogs(std::function<void(const QString&)>&& callback) override;
|
||||
|
||||
void cleanupBackendLogs() override;
|
||||
|
||||
bool multihopSupported() override { return true; }
|
||||
|
||||
private:
|
||||
void initializeInternal();
|
||||
void disconnectInternal();
|
||||
|
||||
void daemonConnected();
|
||||
void errorOccurred(QLocalSocket::LocalSocketError socketError);
|
||||
void readData();
|
||||
void parseCommand(const QByteArray& command);
|
||||
|
||||
void write(const QJsonObject& json);
|
||||
|
||||
private:
|
||||
enum {
|
||||
eUnknown,
|
||||
eInitializing,
|
||||
eReady,
|
||||
eDisconnected,
|
||||
} m_daemonState = eUnknown;
|
||||
|
||||
QLocalSocket* m_socket = nullptr;
|
||||
|
||||
QByteArray m_buffer;
|
||||
|
||||
std::function<void(const QString&)> m_logCallback = nullptr;
|
||||
|
||||
QTimer m_initializingTimer;
|
||||
uint32_t m_initializingRetry = 0;
|
||||
};
|
||||
|
||||
#endif // LOCALSOCKETCONTROLLER_H
|
||||
209
client/mozilla/models/server.cpp
Normal file
209
client/mozilla/models/server.cpp
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
/* 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 "server.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QRandomGenerator>
|
||||
|
||||
#include "leakdetector.h"
|
||||
|
||||
Server::Server() { MZ_COUNT_CTOR(Server); }
|
||||
|
||||
Server::Server(const QString& countryCode, const QString& cityName) {
|
||||
MZ_COUNT_CTOR(Server);
|
||||
m_countryCode = countryCode;
|
||||
m_cityName = cityName;
|
||||
}
|
||||
|
||||
Server::Server(const Server& other) {
|
||||
MZ_COUNT_CTOR(Server);
|
||||
*this = other;
|
||||
}
|
||||
|
||||
Server& Server::operator=(const Server& other) {
|
||||
if (this == &other) return *this;
|
||||
|
||||
m_hostname = other.m_hostname;
|
||||
m_ipv4AddrIn = other.m_ipv4AddrIn;
|
||||
m_ipv4Gateway = other.m_ipv4Gateway;
|
||||
m_ipv6AddrIn = other.m_ipv6AddrIn;
|
||||
m_ipv6Gateway = other.m_ipv6Gateway;
|
||||
m_portRanges = other.m_portRanges;
|
||||
m_publicKey = other.m_publicKey;
|
||||
m_weight = other.m_weight;
|
||||
m_socksName = other.m_socksName;
|
||||
m_multihopPort = other.m_multihopPort;
|
||||
m_countryCode = other.m_countryCode;
|
||||
m_cityName = other.m_cityName;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Server::~Server() { MZ_COUNT_DTOR(Server); }
|
||||
|
||||
bool Server::fromJson(const QJsonObject& obj) {
|
||||
// Reset.
|
||||
m_hostname = "";
|
||||
|
||||
QJsonValue hostname = obj.value("hostname");
|
||||
if (!hostname.isString()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonValue ipv4AddrIn = obj.value("ipv4_addr_in");
|
||||
if (!ipv4AddrIn.isString()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonValue ipv4Gateway = obj.value("ipv4_gateway");
|
||||
if (!ipv4Gateway.isString()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonValue ipv6AddrIn = obj.value("ipv6_addr_in");
|
||||
// If this object comes from the IOS migration, the ipv6_addr_in is missing.
|
||||
|
||||
QJsonValue ipv6Gateway = obj.value("ipv6_gateway");
|
||||
if (!ipv6Gateway.isString()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonValue publicKey = obj.value("public_key");
|
||||
if (!publicKey.isString()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonValue weight = obj.value("weight");
|
||||
if (!weight.isDouble()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonValue portRanges = obj.value("port_ranges");
|
||||
if (!portRanges.isArray()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// optional properties.
|
||||
QJsonValue socks5_name = obj.value("socks5_name");
|
||||
QJsonValue multihop_port = obj.value("multihop_port");
|
||||
|
||||
QList<QPair<uint32_t, uint32_t>> prList;
|
||||
QJsonArray portRangesArray = portRanges.toArray();
|
||||
for (const QJsonValue& portRangeValue : portRangesArray) {
|
||||
if (!portRangeValue.isArray()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonArray port = portRangeValue.toArray();
|
||||
if (port.count() != 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonValue a = port.at(0);
|
||||
if (!a.isDouble()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonValue b = port.at(1);
|
||||
if (!b.isDouble()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
prList.append(QPair<uint32_t, uint32_t>(a.toInt(), b.toInt()));
|
||||
}
|
||||
|
||||
m_hostname = hostname.toString();
|
||||
m_ipv4AddrIn = ipv4AddrIn.toString();
|
||||
m_ipv4Gateway = ipv4Gateway.toString();
|
||||
m_ipv6AddrIn = ipv6AddrIn.toString();
|
||||
m_ipv6Gateway = ipv6Gateway.toString();
|
||||
m_portRanges.swap(prList);
|
||||
m_publicKey = publicKey.toString();
|
||||
m_weight = weight.toInt();
|
||||
m_socksName = socks5_name.toString();
|
||||
m_multihopPort = multihop_port.toInt();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Server::fromMultihop(const Server& exit, const Server& entry) {
|
||||
m_hostname = exit.m_hostname;
|
||||
m_ipv4Gateway = exit.m_ipv4Gateway;
|
||||
m_ipv6Gateway = exit.m_ipv6Gateway;
|
||||
m_publicKey = exit.m_publicKey;
|
||||
m_socksName = exit.m_socksName;
|
||||
m_multihopPort = exit.m_multihopPort;
|
||||
|
||||
m_ipv4AddrIn = entry.m_ipv4AddrIn;
|
||||
m_ipv6AddrIn = entry.m_ipv6AddrIn;
|
||||
return forcePort(exit.m_multihopPort);
|
||||
}
|
||||
|
||||
bool Server::forcePort(uint32_t port) {
|
||||
m_portRanges.clear();
|
||||
m_portRanges.append(QPair<uint32_t, uint32_t>(port, port));
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
const Server& Server::weightChooser(const QList<Server>& servers) {
|
||||
static const Server emptyServer;
|
||||
Q_ASSERT(!emptyServer.initialized());
|
||||
if (servers.isEmpty()) {
|
||||
return emptyServer;
|
||||
}
|
||||
|
||||
uint32_t weightSum = 0;
|
||||
|
||||
for (const Server& server : servers) {
|
||||
weightSum += server.weight();
|
||||
}
|
||||
|
||||
quint32 r = QRandomGenerator::global()->generate() % (weightSum + 1);
|
||||
|
||||
for (const Server& server : servers) {
|
||||
if (server.weight() >= r) {
|
||||
return server;
|
||||
}
|
||||
|
||||
r -= server.weight();
|
||||
}
|
||||
|
||||
// This should not happen.
|
||||
Q_ASSERT(false);
|
||||
return emptyServer;
|
||||
}
|
||||
|
||||
uint32_t Server::choosePort() const {
|
||||
if (m_portRanges.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Count the total number of potential ports.
|
||||
quint32 length = 0;
|
||||
for (const QPair<uint32_t, uint32_t>& range : m_portRanges) {
|
||||
Q_ASSERT(range.first <= range.second);
|
||||
length += range.second - range.first + 1;
|
||||
}
|
||||
Q_ASSERT(length < 65536);
|
||||
Q_ASSERT(length > 0);
|
||||
|
||||
// Pick a port at random.
|
||||
quint32 r = QRandomGenerator::global()->generate() % length;
|
||||
quint32 port = 0;
|
||||
|
||||
for (const QPair<uint32_t, uint32_t>& range : m_portRanges) {
|
||||
if (r <= (range.second - range.first)) {
|
||||
port = r + range.first;
|
||||
break;
|
||||
}
|
||||
r -= (range.second - range.first + 1);
|
||||
}
|
||||
Q_ASSERT(port != 0);
|
||||
return port;
|
||||
}
|
||||
79
client/mozilla/models/server.h
Normal file
79
client/mozilla/models/server.h
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/* 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 SERVER_H
|
||||
#define SERVER_H
|
||||
|
||||
#include <QList>
|
||||
#include <QPair>
|
||||
#include <QString>
|
||||
|
||||
class QJsonObject;
|
||||
|
||||
class Server final {
|
||||
public:
|
||||
Server();
|
||||
Server(const QString& countryCode, const QString& cityName);
|
||||
Server(const Server& other);
|
||||
Server& operator=(const Server& other);
|
||||
~Server();
|
||||
|
||||
[[nodiscard]] bool fromJson(const QJsonObject& obj);
|
||||
bool fromMultihop(const Server& exit, const Server& entry);
|
||||
|
||||
static const Server& weightChooser(const QList<Server>& servers);
|
||||
|
||||
bool initialized() const { return !m_hostname.isEmpty(); }
|
||||
|
||||
const QString& hostname() const { return m_hostname; }
|
||||
|
||||
const QString& ipv4AddrIn() const { return m_ipv4AddrIn; }
|
||||
|
||||
const QString& ipv4Gateway() const { return m_ipv4Gateway; }
|
||||
|
||||
const QString& ipv6AddrIn() const { return m_ipv6AddrIn; }
|
||||
|
||||
const QString& ipv6Gateway() const { return m_ipv6Gateway; }
|
||||
|
||||
const QString& publicKey() const { return m_publicKey; }
|
||||
|
||||
const QString& socksName() const { return m_socksName; }
|
||||
|
||||
uint32_t weight() const { return m_weight; }
|
||||
|
||||
uint32_t choosePort() const;
|
||||
|
||||
uint32_t multihopPort() const { return m_multihopPort; }
|
||||
|
||||
const QString& countryCode() const { return m_countryCode; }
|
||||
|
||||
const QString& cityName() const { return m_cityName; }
|
||||
|
||||
bool forcePort(uint32_t port);
|
||||
|
||||
bool operator==(const Server& other) const {
|
||||
return m_publicKey == other.m_publicKey;
|
||||
}
|
||||
// Allow checking against QString, so we can easily search a QList<Server> for
|
||||
// a public key.
|
||||
bool operator==(const QString& otherPublicKey) const {
|
||||
return m_publicKey == otherPublicKey;
|
||||
}
|
||||
|
||||
private:
|
||||
QString m_hostname;
|
||||
QString m_ipv4AddrIn;
|
||||
QString m_ipv4Gateway;
|
||||
QString m_ipv6AddrIn;
|
||||
QString m_ipv6Gateway;
|
||||
QList<QPair<uint32_t, uint32_t>> m_portRanges;
|
||||
QString m_publicKey;
|
||||
QString m_socksName;
|
||||
uint32_t m_weight = 0;
|
||||
uint32_t m_multihopPort = 0;
|
||||
QString m_countryCode;
|
||||
QString m_cityName;
|
||||
};
|
||||
|
||||
#endif // SERVER_H
|
||||
106
client/mozilla/networkwatcher.cpp
Normal file
106
client/mozilla/networkwatcher.cpp
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
/* 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 "networkwatcher.h"
|
||||
|
||||
#include <QMetaEnum>
|
||||
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "networkwatcherimpl.h"
|
||||
#include "platforms/dummy/dummynetworkwatcher.h"
|
||||
|
||||
#ifdef MZ_WINDOWS
|
||||
//# include "platforms/windows/windowsnetworkwatcher.h"
|
||||
#endif
|
||||
|
||||
#ifdef MZ_LINUX
|
||||
//# include "platforms/linux/linuxnetworkwatcher.h"
|
||||
#endif
|
||||
|
||||
#ifdef MZ_MACOS
|
||||
# include "platforms/macos/macosnetworkwatcher.h"
|
||||
#endif
|
||||
|
||||
#ifdef MZ_WASM
|
||||
# include "platforms/wasm/wasmnetworkwatcher.h"
|
||||
#endif
|
||||
#ifdef MZ_ANDROID
|
||||
# include "platforms/android/androidnetworkwatcher.h"
|
||||
#endif
|
||||
|
||||
#ifdef MZ_IOS
|
||||
# include "platforms/ios/iosnetworkwatcher.h"
|
||||
#endif
|
||||
|
||||
// How often we notify the same unsecured network
|
||||
#ifndef UNIT_TEST
|
||||
constexpr uint32_t NETWORK_WATCHER_TIMER_MSEC = 20000;
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
Logger logger("NetworkWatcher");
|
||||
}
|
||||
|
||||
NetworkWatcher::NetworkWatcher() { MZ_COUNT_CTOR(NetworkWatcher); }
|
||||
|
||||
NetworkWatcher::~NetworkWatcher() { MZ_COUNT_DTOR(NetworkWatcher); }
|
||||
|
||||
void NetworkWatcher::initialize() {
|
||||
logger.debug() << "Initialize";
|
||||
|
||||
#if defined(MZ_WINDOWS)
|
||||
//m_impl = new WindowsNetworkWatcher(this);
|
||||
#elif defined(MZ_LINUX)
|
||||
//m_impl = new LinuxNetworkWatcher(this);
|
||||
#elif defined(MZ_MACOS)
|
||||
m_impl = new MacOSNetworkWatcher(this);
|
||||
#elif defined(MZ_WASM)
|
||||
m_impl = new WasmNetworkWatcher(this);
|
||||
#elif defined(MZ_ANDROID)
|
||||
m_impl = new AndroidNetworkWatcher(this);
|
||||
#elif defined(MZ_IOS)
|
||||
m_impl = new IOSNetworkWatcher(this);
|
||||
#else
|
||||
m_impl = new DummyNetworkWatcher(this);
|
||||
#endif
|
||||
|
||||
connect(m_impl, &NetworkWatcherImpl::unsecuredNetwork, this,
|
||||
&NetworkWatcher::unsecuredNetwork);
|
||||
connect(m_impl, &NetworkWatcherImpl::networkChanged, this,
|
||||
&NetworkWatcher::networkChange);
|
||||
|
||||
m_impl->initialize();
|
||||
|
||||
//TODO IMPL FOR AMNEZIA
|
||||
}
|
||||
|
||||
void NetworkWatcher::settingsChanged() {
|
||||
//TODO IMPL FOR AMNEZIA
|
||||
|
||||
if (m_active) {
|
||||
logger.debug()
|
||||
<< "Starting Network Watcher; Reporting of Unsecured Networks: "
|
||||
<< m_reportUnsecuredNetwork;
|
||||
m_impl->start();
|
||||
} else {
|
||||
logger.debug() << "Stopping Network Watcher";
|
||||
m_impl->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkWatcher::unsecuredNetwork(const QString& networkName,
|
||||
const QString& networkId) {
|
||||
logger.debug() << "Unsecured network:" << logger.sensitive(networkName)
|
||||
<< "id:" << logger.sensitive(networkId);
|
||||
|
||||
//TODO IMPL FOR AMNEZIA
|
||||
}
|
||||
|
||||
QString NetworkWatcher::getCurrentTransport() {
|
||||
auto type = m_impl->getTransportType();
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<NetworkWatcherImpl::TransportType>();
|
||||
return QString(metaEnum.valueToKey(type))
|
||||
.remove("TransportType_", Qt::CaseSensitive);
|
||||
}
|
||||
49
client/mozilla/networkwatcher.h
Normal file
49
client/mozilla/networkwatcher.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/* 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 NETWORKWATCHER_H
|
||||
#define NETWORKWATCHER_H
|
||||
|
||||
#include <QElapsedTimer>
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
|
||||
class NetworkWatcherImpl;
|
||||
|
||||
// This class watches for network changes to detect unsecured wifi.
|
||||
class NetworkWatcher final : public QObject {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(NetworkWatcher)
|
||||
|
||||
public:
|
||||
NetworkWatcher();
|
||||
~NetworkWatcher();
|
||||
|
||||
void initialize();
|
||||
|
||||
// public for the inspector.
|
||||
void unsecuredNetwork(const QString& networkName, const QString& networkId);
|
||||
|
||||
QString getCurrentTransport();
|
||||
|
||||
signals:
|
||||
void networkChange();
|
||||
|
||||
private:
|
||||
void settingsChanged();
|
||||
|
||||
private:
|
||||
bool m_active = false;
|
||||
bool m_reportUnsecuredNetwork = false;
|
||||
|
||||
// Platform-specific implementation.
|
||||
NetworkWatcherImpl* m_impl = nullptr;
|
||||
|
||||
QMap<QString, QElapsedTimer> m_networks;
|
||||
|
||||
// This is used to connect NotificationHandler lazily.
|
||||
bool m_firstNotification = true;
|
||||
};
|
||||
|
||||
#endif // NETWORKWATCHER_H
|
||||
54
client/mozilla/networkwatcherimpl.h
Normal file
54
client/mozilla/networkwatcherimpl.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/* 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 NETWORKWATCHERIMPL_H
|
||||
#define NETWORKWATCHERIMPL_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class NetworkWatcherImpl : public QObject {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(NetworkWatcherImpl)
|
||||
|
||||
public:
|
||||
NetworkWatcherImpl(QObject* parent) : QObject(parent) {}
|
||||
|
||||
virtual ~NetworkWatcherImpl() = default;
|
||||
|
||||
virtual void initialize() = 0;
|
||||
|
||||
virtual void start() { m_active = true; }
|
||||
virtual void stop() { m_active = false; }
|
||||
|
||||
bool isActive() const { return m_active; }
|
||||
|
||||
enum TransportType {
|
||||
TransportType_Unknown = 0,
|
||||
TransportType_Ethernet = 1,
|
||||
TransportType_WiFi = 2,
|
||||
TransportType_Cellular = 3, // In Case the API does not retun the gsm type
|
||||
TransportType_Other = 4, // I.e USB thethering
|
||||
TransportType_None = 5 // I.e Airplane Mode or no active network device
|
||||
};
|
||||
Q_ENUM(TransportType);
|
||||
|
||||
// Returns the current type of Network Connection
|
||||
virtual TransportType getTransportType() = 0;
|
||||
|
||||
signals:
|
||||
// Fires when the Device Connects to an unsecured Network
|
||||
void unsecuredNetwork(const QString& networkName, const QString& networkId);
|
||||
// Fires on when the connected WIFI Changes
|
||||
// TODO: Only windows-networkwatcher has this, the other plattforms should
|
||||
// too.
|
||||
void networkChanged(QString newBSSID);
|
||||
|
||||
// Fired when the Device changed the Type of Transport
|
||||
void transportChanged(NetworkWatcherImpl::TransportType transportType);
|
||||
|
||||
private:
|
||||
bool m_active = false;
|
||||
};
|
||||
|
||||
#endif // NETWORKWATCHERIMPL_H
|
||||
189
client/mozilla/pinghelper.cpp
Normal file
189
client/mozilla/pinghelper.cpp
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
/* 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 "pinghelper.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <cmath>
|
||||
|
||||
#include "dnspingsender.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "pingsender.h"
|
||||
#include "pingsenderfactory.h"
|
||||
|
||||
// Any X seconds, a new ping.
|
||||
constexpr uint32_t PING_TIMEOUT_SEC = 1;
|
||||
|
||||
// Maximum window size for ping statistics.
|
||||
constexpr int PING_STATS_WINDOW = 32;
|
||||
|
||||
namespace {
|
||||
Logger logger("PingHelper");
|
||||
}
|
||||
|
||||
PingHelper::PingHelper() {
|
||||
MZ_COUNT_CTOR(PingHelper);
|
||||
|
||||
m_sequence = 0;
|
||||
m_pingData.resize(PING_STATS_WINDOW);
|
||||
|
||||
connect(&m_pingTimer, &QTimer::timeout, this, &PingHelper::nextPing);
|
||||
}
|
||||
|
||||
PingHelper::~PingHelper() { MZ_COUNT_DTOR(PingHelper); }
|
||||
|
||||
void PingHelper::start(const QString& serverIpv4Gateway,
|
||||
const QString& deviceIpv4Address) {
|
||||
logger.debug() << "PingHelper activated for server:"
|
||||
<< logger.sensitive(serverIpv4Gateway);
|
||||
|
||||
m_gateway = QHostAddress(serverIpv4Gateway);
|
||||
m_source = QHostAddress(deviceIpv4Address.section('/', 0, 0));
|
||||
m_pingSender = PingSenderFactory::create(m_source, this);
|
||||
|
||||
// Some platforms require root access to send and receive ICMP pings. If
|
||||
// we happen to be on one of these unlucky devices, create a DnsPingSender
|
||||
// instead.
|
||||
if (!m_pingSender->isValid()) {
|
||||
delete m_pingSender;
|
||||
m_pingSender = new DnsPingSender(m_source, this);
|
||||
}
|
||||
|
||||
connect(m_pingSender, &PingSender::recvPing, this, &PingHelper::pingReceived,
|
||||
Qt::QueuedConnection);
|
||||
connect(m_pingSender, &PingSender::criticalPingError, this,
|
||||
[]() { logger.info() << "Encountered Unrecoverable ping error"; });
|
||||
|
||||
// Reset the ping statistics
|
||||
m_sequence = 0;
|
||||
for (int i = 0; i < PING_STATS_WINDOW; i++) {
|
||||
m_pingData[i].timestamp = -1;
|
||||
m_pingData[i].latency = -1;
|
||||
m_pingData[i].sequence = 0;
|
||||
}
|
||||
|
||||
m_pingTimer.start(PING_TIMEOUT_SEC * 1000);
|
||||
}
|
||||
|
||||
void PingHelper::stop() {
|
||||
logger.debug() << "PingHelper deactivated";
|
||||
|
||||
if (m_pingSender) {
|
||||
delete m_pingSender;
|
||||
m_pingSender = nullptr;
|
||||
}
|
||||
|
||||
m_pingTimer.stop();
|
||||
}
|
||||
|
||||
void PingHelper::nextPing() {
|
||||
#ifdef MZ_DEBUG
|
||||
logger.debug() << "Sending ping seq:" << m_sequence;
|
||||
#endif
|
||||
|
||||
// The ICMP sequence number is used to match replies with their originating
|
||||
// request, and serves as an index into the circular buffer. Overflows of
|
||||
// the sequence number acceptable.
|
||||
int index = m_sequence % PING_STATS_WINDOW;
|
||||
m_pingData[index].timestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
m_pingData[index].latency = -1;
|
||||
m_pingData[index].sequence = m_sequence;
|
||||
m_pingSender->sendPing(m_gateway, m_sequence);
|
||||
|
||||
m_sequence++;
|
||||
}
|
||||
|
||||
void PingHelper::pingReceived(quint16 sequence) {
|
||||
int index = sequence % PING_STATS_WINDOW;
|
||||
if (m_pingData[index].sequence == sequence) {
|
||||
qint64 sendTime = m_pingData[index].timestamp;
|
||||
m_pingData[index].latency = QDateTime::currentMSecsSinceEpoch() - sendTime;
|
||||
emit pingSentAndReceived(m_pingData[index].latency);
|
||||
#ifdef MZ_DEBUG
|
||||
logger.debug() << "Ping answer received seq:" << sequence
|
||||
<< "avg:" << latency()
|
||||
<< "loss:" << QString("%1%").arg(loss() * 100.0)
|
||||
<< "stddev:" << stddev();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
uint PingHelper::latency() const {
|
||||
int recvCount = 0;
|
||||
qint64 totalMsec = 0;
|
||||
|
||||
for (const PingSendData& data : m_pingData) {
|
||||
if (data.latency < 0) {
|
||||
continue;
|
||||
}
|
||||
recvCount++;
|
||||
totalMsec += data.latency;
|
||||
}
|
||||
|
||||
if (recvCount <= 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Add half the denominator to produce nearest-integer rounding.
|
||||
totalMsec += recvCount / 2;
|
||||
return static_cast<uint>(totalMsec / recvCount);
|
||||
}
|
||||
|
||||
uint PingHelper::stddev() const {
|
||||
int recvCount = 0;
|
||||
qint64 totalVariance = 0;
|
||||
uint average = PingHelper::latency();
|
||||
|
||||
for (const PingSendData& data : m_pingData) {
|
||||
if (data.latency < 0) {
|
||||
continue;
|
||||
}
|
||||
recvCount++;
|
||||
totalVariance += (average - data.latency) * (average - data.latency);
|
||||
}
|
||||
|
||||
if (recvCount <= 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return std::sqrt((double)totalVariance / recvCount);
|
||||
}
|
||||
|
||||
uint PingHelper::maximum() const {
|
||||
uint maxRtt = 0;
|
||||
|
||||
for (const PingSendData& data : m_pingData) {
|
||||
if (data.latency < 0) {
|
||||
continue;
|
||||
}
|
||||
if (data.latency > maxRtt &&
|
||||
data.latency < std::numeric_limits<uint>::max()) {
|
||||
maxRtt = static_cast<uint>(data.latency);
|
||||
}
|
||||
}
|
||||
return maxRtt;
|
||||
}
|
||||
|
||||
double PingHelper::loss() const {
|
||||
int sendCount = 0;
|
||||
int recvCount = 0;
|
||||
// Don't count pings that are possibly still in flight as losses.
|
||||
qint64 sendBefore =
|
||||
QDateTime::currentMSecsSinceEpoch() - (PING_TIMEOUT_SEC * 1000);
|
||||
|
||||
for (const PingSendData& data : m_pingData) {
|
||||
if (data.latency >= 0) {
|
||||
recvCount++;
|
||||
sendCount++;
|
||||
} else if ((data.timestamp > 0) && (data.timestamp < sendBefore)) {
|
||||
sendCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (sendCount <= 0) {
|
||||
return 0.0;
|
||||
}
|
||||
return (double)(sendCount - recvCount) / PING_STATS_WINDOW;
|
||||
}
|
||||
64
client/mozilla/pinghelper.h
Normal file
64
client/mozilla/pinghelper.h
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/* 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 PINGHELPER_H
|
||||
#define PINGHELPER_H
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
#include <QVector>
|
||||
|
||||
class PingSender;
|
||||
|
||||
class PingHelper final : public QObject {
|
||||
private:
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(PingHelper)
|
||||
|
||||
public:
|
||||
PingHelper();
|
||||
~PingHelper();
|
||||
|
||||
void start(const QString& serverIpv4Gateway,
|
||||
const QString& deviceIpv4Address);
|
||||
|
||||
void stop();
|
||||
uint latency() const;
|
||||
uint stddev() const;
|
||||
uint maximum() const;
|
||||
double loss() const;
|
||||
|
||||
signals:
|
||||
void pingSentAndReceived(qint64 msec);
|
||||
|
||||
private:
|
||||
void nextPing();
|
||||
|
||||
void pingReceived(quint16 sequence);
|
||||
|
||||
private:
|
||||
QHostAddress m_gateway;
|
||||
QHostAddress m_source;
|
||||
quint16 m_sequence = 0;
|
||||
|
||||
class PingSendData {
|
||||
public:
|
||||
PingSendData() {
|
||||
timestamp = -1;
|
||||
latency = -1;
|
||||
sequence = 0;
|
||||
}
|
||||
qint64 timestamp;
|
||||
qint64 latency;
|
||||
quint16 sequence;
|
||||
};
|
||||
QVector<PingSendData> m_pingData;
|
||||
|
||||
QTimer m_pingTimer;
|
||||
PingSender* m_pingSender = nullptr;
|
||||
};
|
||||
|
||||
#endif // PINGHELPER_H
|
||||
48
client/mozilla/pingsender.cpp
Normal file
48
client/mozilla/pingsender.cpp
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/* 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 "pingsender.h"
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
namespace {
|
||||
Logger logger("PingSender");
|
||||
}
|
||||
|
||||
quint16 PingSender::inetChecksum(const void* data, size_t len) {
|
||||
int nleft, sum;
|
||||
quint16* w;
|
||||
union {
|
||||
quint16 us;
|
||||
quint8 uc[2];
|
||||
} last;
|
||||
quint16 answer;
|
||||
|
||||
nleft = static_cast<int>(len);
|
||||
sum = 0;
|
||||
w = (quint16*)data;
|
||||
|
||||
/*
|
||||
* Our algorithm is simple, using a 32 bit accumulator (sum), we add
|
||||
* sequential 16 bit words to it, and at the end, fold back all the
|
||||
* carry bits from the top 16 bits into the lower 16 bits.
|
||||
*/
|
||||
while (nleft > 1) {
|
||||
sum += *w++;
|
||||
nleft -= 2;
|
||||
}
|
||||
|
||||
/* mop up an odd byte, if necessary */
|
||||
if (nleft == 1) {
|
||||
last.uc[0] = *(quint8*)w;
|
||||
last.uc[1] = 0;
|
||||
sum += last.us;
|
||||
}
|
||||
|
||||
/* add back carry outs from top 16 bits to low 16 bits */
|
||||
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
|
||||
sum += (sum >> 16); /* add carry */
|
||||
answer = ~sum; /* truncate to 16 bits */
|
||||
return (answer);
|
||||
}
|
||||
31
client/mozilla/pingsender.h
Normal file
31
client/mozilla/pingsender.h
Normal 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 PINGSENDER_H
|
||||
#define PINGSENDER_H
|
||||
|
||||
#include <QElapsedTimer>
|
||||
#include <QHostAddress>
|
||||
#include <QObject>
|
||||
|
||||
class PingSender : public QObject {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(PingSender)
|
||||
|
||||
public:
|
||||
PingSender(QObject* parent = nullptr) : QObject(parent) {}
|
||||
virtual ~PingSender() = default;
|
||||
|
||||
virtual bool isValid() { return true; };
|
||||
|
||||
virtual void sendPing(const QHostAddress& destination, quint16 sequence) = 0;
|
||||
|
||||
static quint16 inetChecksum(const void* data, size_t length);
|
||||
|
||||
signals:
|
||||
void recvPing(quint16 sequence);
|
||||
void criticalPingError();
|
||||
};
|
||||
|
||||
#endif // PINGSENDER_H
|
||||
32
client/mozilla/pingsenderfactory.cpp
Normal file
32
client/mozilla/pingsenderfactory.cpp
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/. */
|
||||
|
||||
#include "pingsenderfactory.h"
|
||||
|
||||
#if defined(MZ_LINUX) || defined(MZ_ANDROID)
|
||||
//# include "platforms/linux/linuxpingsender.h"
|
||||
#elif defined(MZ_MACOS) || defined(MZ_IOS)
|
||||
# include "platforms/macos/macospingsender.h"
|
||||
#elif defined(MZ_WINDOWS)
|
||||
// #include "platforms/windows/windowspingsender.h"
|
||||
#elif defined(MZ_DUMMY) || defined(UNIT_TEST)
|
||||
# include "platforms/dummy/dummypingsender.h"
|
||||
#else
|
||||
# error "Unsupported platform"
|
||||
#endif
|
||||
|
||||
PingSender* PingSenderFactory::create(const QHostAddress& source,
|
||||
QObject* parent) {
|
||||
#if defined(MZ_LINUX) || defined(MZ_ANDROID)
|
||||
return nullptr;
|
||||
//return new LinuxPingSender(source, parent);
|
||||
#elif defined(MZ_MACOS) || defined(MZ_IOS)
|
||||
return new MacOSPingSender(source, parent);
|
||||
#elif defined(MZ_WINDOWS)
|
||||
return nullptr;
|
||||
//return new WindowsPingSender(source, parent);
|
||||
#else
|
||||
return new DummyPingSender(source, parent);
|
||||
#endif
|
||||
}
|
||||
18
client/mozilla/pingsenderfactory.h
Normal file
18
client/mozilla/pingsenderfactory.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/* 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 PINGSENDERFACTORY_H
|
||||
#define PINGSENDERFACTORY_H
|
||||
|
||||
class PingSender;
|
||||
class QHostAddress;
|
||||
class QObject;
|
||||
|
||||
class PingSenderFactory final {
|
||||
public:
|
||||
PingSenderFactory() = delete;
|
||||
static PingSender* create(const QHostAddress& source, QObject* parent);
|
||||
};
|
||||
|
||||
#endif // PINGSENDERFACTORY_H
|
||||
286
client/mozilla/shared/ipaddress.cpp
Normal file
286
client/mozilla/shared/ipaddress.cpp
Normal 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;
|
||||
}
|
||||
53
client/mozilla/shared/ipaddress.h
Normal file
53
client/mozilla/shared/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
|
||||
75
client/mozilla/shared/leakdetector.cpp
Normal file
75
client/mozilla/shared/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 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
|
||||
41
client/mozilla/shared/leakdetector.h
Normal file
41
client/mozilla/shared/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 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
|
||||
16
client/mozilla/shared/loglevel.h
Normal file
16
client/mozilla/shared/loglevel.h
Normal 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
|
||||
76
client/mozilla/shared/signalhandler.cpp
Normal file
76
client/mozilla/shared/signalhandler.cpp
Normal 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";
|
||||
}
|
||||
}
|
||||
}
|
||||
31
client/mozilla/shared/signalhandler.h
Normal file
31
client/mozilla/shared/signalhandler.h
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue